Simplifying Computer Art in Python
Holloway, Catherine
Catherine Holloway - Simplifying Computer Art in Python The Processing project demonstrated that computer art can attract a wider audience to programming. Python has a robust catalog of libraries, including two interfaces to OpenGL. However, none of these libraries replicate Processing’s simplicity when drawing to the screen. I will present my solution to this problem: a re- implementation of VPython’s visual module purely in python called PygletHelper. - Processing is a programming language originally developed by the MIT media lab with the goal of allowing artists, educators, and many others develop striking computer generated or assisted projects without requiring deep knowledge of software engineering or computer graphics. Like Processing, Python has become a favourite language of users from diverse backgrounds, such as web development, education, and science. Unlike Processing, python lacks a simple and easy to use library for drawing shapes. Python’s existing libraries for scientific computing and data analysis could be made even more awesome when combined with a simple drawing library. VPython contains a module called visual that established a simple API and convention for drawing shapes, however it was written in C++, prior to the development of pyglet, and thus is not entirely cross- platform. In this talk, I will demonstrate my solution to this problem: a re-implementation of visual purely in Python called PygletHelper. Pyglet, an existing python library, provides a python interface to OpenGL. PygletHelper is built on pyglet but obscures all of the OpenGL calls, such that the user can draw simple geometric shapes to the screen and animate them without needing to know about computer graphics terminology, memory usage, or C data types. I will also show some need visualizations of science and music in my talk, as well as the graphical glitches encountered implementing the library.
 Good afternoon. I want you all to welcome Catherine showing us how to do computer art with Python. Hi, thank you for coming. My name is Catherine and for five years I was a graduate student in physics at the University of Waterloo. Now the University of Waterloo in their Faculty of Science offers a computational science degree. It's a cross departmental degree program that tries to teach people without very much programming experience but a lot of enthusiasm for science how to use programming to be more efficient at science. And during my time as a grad student I was the teaching assistant for the computational physics classes which used Python but very, very basic Python to teach physics concepts such as classical mechanics, integrating differential equations, stuff like that. And today I'm going to tell you about a Python library that I have been writing that was inspired by my time as a teaching assistant. So every great software development project begins with a great user story. So for this presentation let's imagine that our user is the freshman meme guy. So the freshman meme guy is a computational science student. He has no prior programming experience to the computational physics class that he took and he's also a big fan of Quidditch. And the midterm for computational physics came on the day after the intramural Quidditch finals so he flunked his midterm and now he's looking for some extra credit. And his university professor says, okay, the midterm mark will be ignored if you can make me a nice pretty animated visualization of your Keplerian planet simulation. So the freshman meme guy goes off to Google and he looks for 3D drawing options in Python and the first thing that he finds is processing in Python mode. So has anyone used processing in this audience? It's pretty awesome in my humble opinion. It was created by Ben Fry and Casey Reese in 2001 while they were part of the MIT Media Lab. Processing is the name of the project, the IDE and the programming language and it's targeted at artists and beginners. And in recent years it has a Python programming mode. So the freshman meme guy looks up the sample code to draw a red spinning cube and he finds this and it's all human readable. He doesn't quite understand variable scoping so the global word is kind of magic to him but otherwise it makes sense. There's one method called setup that executes once when the program starts and then there's one method called draw that executes every single time the frame or the window is drawn to. And it produces something sensible that looks like this. So the freshman meme guy is happy, he goes off with processing and implements his planets and goes to try to pull out his simulation and put it in processing. But the first thing he encounters when he tries to import sci-pi is this import error, no module, named sci-pi. And now this makes our freshman meme guy very upset because he is absolutely sure that he has sci-pi installed on his computer and in his Python environment. I mean, he's been using it all semester for his classes. So he encounters this problem because the Python mode in processing is kind of a veneer over a Java underbelly. So he gives up on that approach and goes back to Google. The next thing he finds is pyopengl. So pyopengl is one of the Python bindings to opengl. It's like how pyqt is to qt. So he looks up the sample draw spinning cube example in pyopengl. So the imports look friendly. He just has to import the graphics library and then the graphics library utility tools. Like with the processing demo, there's one method that executes once on setup. It's really confusing to him because he doesn't know what any of these things are doing. But it only happens once, so that's fine. He can just ignore it for now. Then he looks at the method that is going to execute every single time. And you should not do this, but the method that comes up the most often in really basic opengl tutorials to draw a cube is to draw six quadrilaterals, one for each side. Now, you should not do this if you're going to do any high performance stuff, but it's kind of the simple way of drawing a cube. But to do this, you have to draw four vertexes, one for each corner of each quadrilateral. And this is what the entire code to draw a cube looks like. And then you can create a window using the utility tools. So our undergrad looks at the example. It's like, okay, well, this makes sense. This is what I expected to be drawn. And then he goes off and says, well, I'll just figure out how to draw spheres with this. So he goes to the opengl group and downloads the opengl cheat sheet. Now, the problem with that is that the opengl cheat sheet is not actually an opengl cheat sheet. It's not a single sheet. It is actually a 13-page Adobe PDF document. And the first page in nine-point font is everything just about buffers. Now, keep in mind that this is a computational science student and not a computer science student. He has only had maybe three months at most of programming, and he does not even know what a pointer is. And to be honest, as a Python user, I don't really like to use pointers either, so I can understand his woes. So this makes him upset. He goes back to Google. He looks again. He finds a module called Piglet. So Piglet is also an opengl binding, but it has submodules for windowing in multimedia. And it can produce the same sorts of animations that PyOpengl does. So the undergrad looks at the code. It all seems logical. The imports look logical. The code to actually update his animation and draw a window looks really nice. Like, he can understand what a window is. He can understand having a clock that he has to schedule to update, and he can understand running his app. So this makes him happy. So then he's like, okay, great. I'm going to continue with this. Let's look at the actual code for drawing a cube. And he sees exactly the same thing that he saw in PyOpengl. And now on top of that, there's the scary at symbol because he has not been taught decorators, and he doesn't even want to try to figure out what that is. And again, the actual drawing of the cube is the same. So this makes him unhappy. All right. So the final option he comes across is called vPython. So just in background on vPython, it was created by David Sherer in 2000 to replace CT, which is what they were using to teach computational physics at Carnegie Mellon. It is currently maintained by Bruce Sherwood, who was one of the original developers of CT. And it was designed specifically for helping students learn physics through programming. How it works is that all of the actual OpenGL calls are written in C++, and that gets compiled to a module called C Visual. And then the actual windowing is based on WX Python and sort of just hooks into this compiled Python module. And just as an example of how this is used in modern, in classes that are going on today, this is some vPython for teaching students Newton's second law, F equals MA. If you remember back to your high school physics, you might have had a similar lab to this that involved launching beanbags at your classmates. This is a great innovation because it prevents any beanbag launching related injuries. But basically you create a ball, you add a velocity to it, you apply the force of gravity to it, and then you integrate the force of gravity onto the velocity. And then you can also demonstrate the additivity of forces by subtracting a drag force. So just to give you an example, this is what it would actually look like on your screen, and you can see what the effect of drag is. No beanbags hurt in the process. All right. So the undergrad looks up the code for drawing a red spinning cube, and it is five lines. It's super nice. And it all makes sense to him. And on his Windows laptop, it looks like this. So based on that, he creates his visualization. It all looks good on his laptop. And then he uploads it to the physics marking server, which is running Ubuntu. And a few minutes later, he gets an email from his prof while he's still in this euphoric mood that says, there is no Debian package for WX Python 3. Can you please come to my office and show me your animation? And now this is a problem for the undergrad meme because his physics prof is a nice guy, but he tends to go on about how great physics and programming is, and he really just wants to get back on the soccer field and continue his quidditch practice. So that ultimately makes him upset. So what I'm trying to communicate in this user story is that writing really simple graphics libraries that are kind of platform independent is really hard. So what if we had an open GL helper? So the idea here is, V Python established a really nice API. So what if we extract that from the C++ code? But let some other project, some other Python project that has already done a lot of hard work on making open GL cross platform Python hooks do the heavy lifting. And then the final thing we want to do is obscure the windowing. So this is one advantage that Piglet has over PyOpenGL is that they make the windowing really easy. All right. So the first thing that I have to do if I'm going to build this thing is decide which open GL binding I'm going to use. And PyOpenGL has support for a lot more of open GL, and it also currently has support for 3.5, while as Piglet appears to be kind of a dead project right now. And so this is where I made the first fatal flaw in creating this library because I picked Piglet. And the reasons I did that were just number one, it has much better documentation, something to keep in mind when I'm writing my own stuff. And secondly, I was kind of an open GL noob at the start, and the extra app and window libraries make things really easy. All right. So this is the package that I created. It's a helper for Piglet or Piglet helper. It has a sub module called objects which contains geometric primitives, and it has a sub module called utils that has mathematical primitives for doing transformations. And I let Piglet.gl interact with open GL, and then I use Piglet's window and app things to handle the display. All right. So I'm going to do a few just comments on implementation before I just go off and show the animated GIFs. So converting from C++ to Python is not, unfortunately, is not as easy as just doing a search replace on curly braces. There are several things you can do in C++ that you cannot do in Python, for example, having multiple method declarations. And the way that I chose to do this and still have the same functionality is to use the get item, set item, and length operators on classes. So just as in the previous slide, I could initialize a vector by passing it three zeros, or sorry, passing it three numbers, passing it nothing, which would initialize it to zero, or passing it another pointer to an array. I can pass it any object which has length greater than two or three, and then assign the first element to x, second element to y, third element to z. Now, the second fatal flaw that I did was decide to go with pep styling on this project. And so this project was built back in 2001 where I think, I don't even think pep 8 was written back then. So that meant that I changed everything slightly, so it doesn't like lower case object names, it also doesn't like single character properties. So these are the types of changes I made. And now, given that I'm making all these changes to the API, how do I make sure things don't break? And since this is a beginner talk, I'm just going to explain how to use continuous integration. So I use Travis CI, which is free as in beer, but not free as in speech. So every time I commit a new change to GitHub, it runs set up pi for Python 2.7 through 3.4, it pilots, and then it runs the unit tests. Now, this provides a unique challenge in this situation, which is that Travis CI runs on a headless server, but open GL requires a display to draw to, even if you just want to load the modules. And this is something that I had trouble with because my naive solution is just, well, let's render to a still image and then we'll just compare the images from previous builds to this current build. However, I'm having trouble with that because it seems like I should be able to use X dummy, but that requires the NVIDIA X driver, and then the NVIDIA X driver doesn't seem to work correctly on headless systems, but I know this should be theoretically possible because other projects have done these things, and if you have done this before, please talk to me afterwards. So my alternative right now is just to mock GL. So in the mock module in Python, there is a useful decorator called patch, and what that allows you to do is that in your unit test, you can just replace any time that an object is called, you replace it with another object. So in box, box will import piglet.gl as GL, and then in my patch, I'm just going to replace GL with my own fake GL, and then my own fake GL module has all the function primitives, but it does essentially nothing. Now, this is kind of cheating in a way, but I don't, I'm not really testing open GL. I'm really just testing that my modules all work when they call each other, and that everything imports correctly. All right. So now that I've talked about some boring implementation details, I'm just going to go over some functionality. So these are the geometric primitives implemented. There's an arrow, sphere, cube, cone, cylinder, ellipsoid, ring, and pyramid. And then there's also things called lights and scenes. So a scene object contains information about the camera position and some details about the default lights. The lights are non-rendable objects that you can stick in the scene. And then the objects can either be drawn into the scene by passing them to the scene as an, or sorry, the scene has a list of objects. You can either add the object to that list, or you can pass the scene to the object's draw method. So just to clarify what I mean, I have a box and a scene. I can either render the box in the scene by passing scene as an argument to render, or by appending the box to the scene and then setting up the scene. All right. So just to finalize, this is what the freshman's code would look like in Pig What Helper. So I've, in addition to everything else, just created a default scene with a default camera position and some default lights. And you can just create that using VSetup, and it will automatically add any objects you call after that to that scene. You define an update function just as you would with any of the other ones, and then you can run the scene. And I've also added a really useful utility for generating objects. And this is really great if you want to spam your Twitter profile with animated GIFs. And that is what it looks like. All right. So I started this project last year after getting really frustrated about getting visual working with Python 3. The original developers of the Python had the same problem, but they decided to take a different approach, which was, since they do mostly teaching stuff, to switch to a Jupyter Notebooks type environment. So they switched from OpenGL to WebGL, and they created this JavaScript library called Glowscript, which allows you to use the Python syntax in WebGL. And then they created this website called Glowscript.org, where you can type in Python code and have it render on the Web. However, just like processing in Python mode, this doesn't give you access to everything else in Python. And then the other module they created was called the Python Jupyter. So they use 0MQ to actually do the communication and to feed some JSON back into Jupyter to render in the Jupyter Notebook. And they're using the actual Glowscript JavaScript library that they've hosted on Glowscript.org to actually render this. And this is what it looks like. It's not quite the same because you have to define these scene variables, but again, it looks very, very similar to VPython Classic Edition. All right. So for future work on Piglet Helper, so I made two fatal flaws, as I said. The first one is that I made styling changes to match modern styling. And I think this was a really bad idea because the people who use this library all operate out of textbooks, and textbooks don't update as fast as the Internet. So it doesn't make sense to make a library that doesn't match the old API. So I think my first step is going to be to undo that. I also haven't implemented 3D text, which is something that is available in VPython. And then finally, I think I need to switch off of Piglet because it does not have support for Python 3.5. And now, a few days before the conference, it came to my attention that Vispy probably could do a very similar thing. And a year ago, when I started this, I looked into Vispy, and they had not yet implemented some objects trying, but now they have. So if you're looking for something that is more fully featured, that is probably a lot more stable. And another benefit is that it implements, it works with several different backends. So it works with PyQt4, PyQt5, and Piglet and PyOpenGL with a little bit of work. You should really check out Vispy. So this is the sample rotating cube code that I have found that is the simplest, I think, for Vispy. And as you can see, it's not quite as dead easy as you might want it to be, but it's still very, very nice. So that's what I would suggest you use instead of my library. But just as a final experiment, I really want to try to communicate what sort of wonderful things can happen when you lower the barriers of entry to 3D drawing, just like what processing has been able to do. Because a lot of us here in the audience are experts in a certain domain, but we probably aren't experts in OpenGL, but we are still humans, and humans have eyeballs, and humans like looking at things. So just as a crazy experiment, a few days before the conference, I decided what if I feed Piglet Helper into the distributed evolutionary algorithms project and use scikit image to judge the similarity of images generated. So the idea is I'm going to try to generate the Python logo using Piglet Helper attached to an evolutionary algorithm. And so this is what you see. It's not the greatest because of the artificial limitations I put onto the search space for the evolutionary algorithm, but you can see that it eventually learns from just a mix of chaos to put yellow spheres in the bottom half and blue spheres in the top half. Anyway, thank you. Next question. So you said Piglet is kind of a dead project. Have you got in touch with the developers or saw the mailing list because there's appearing to be some activity around Piglet now? Oh, no, I hadn't. But that's very exciting to hear. Did you also have a look at game engines because they might provide similar functionalities or maybe sometimes an easier package? Can you repeat what that? Did you have a look at game engines? There are some game engines. Oh, game engines. Yeah, I looked into Pygame and it had a lot of cruft that I wasn't particularly interested in for game development, but perhaps I should look at it again. Anymore? No, that's not. Hi, thanks for the talk. It's really interesting. You've used the kind of college university student as the target. I wonder what are your thoughts for creating 3D graphics for somebody who might be seven years old? So the reason that I use a university undergraduate student is that in Canada, where I'm from, we have really terrible computer science education. So even though students, I believe personally that students who are seven who can read English would totally be able to use V-Python, typically students don't encounter their first programming class until their first year of university. One more question to you, Haiz Bither. What do you think about processing Python mode? So I should have just pressed the home button, but whatever, my finger will get a workout. So the first one that I went over was processing in Python mode. And as I said, this didn't really work for the application because you aren't able to access the full Python stack. And I've looked into trying to incorporate the Python stack into processing in Python mode, and the thing is that I hate Java way too much to invest any more time in that. Thank you, Catherine. Thank you all.
 Good afternoon. I want you all to welcome Catherine showing us how to do computer  art with Python. Hi, thank you for coming. My name is Catherine and for five years I  was a graduate student in physics at the University of Waterloo. Now the University  of Waterloo in their Faculty of Science offers a computational science degree. It's a cross  departmental degree program that tries to teach people without very much programming experience  but a lot of enthusiasm for science how to use programming to be more efficient at science.  And during my time as a grad student I was the teaching assistant for the computational  physics classes which used Python but very, very basic Python to teach physics concepts  such as classical mechanics, integrating differential equations, stuff like that. And today I'm  going to tell you about a Python library that I have been writing that was inspired  by my time as a teaching assistant. So every great software development project begins  with a great user story. So for this presentation let's imagine that our user is the freshman  meme guy. So the freshman meme guy is a computational science student. He has no prior programming  experience to the computational physics class that he took and he's also a big fan of Quidditch.  And the midterm for computational physics came on the day after the intramural Quidditch  finals so he flunked his midterm and now he's looking for some extra credit. And his university  professor says, okay, the midterm mark will be ignored if you can make me a nice pretty  animated visualization of your Keplerian planet simulation. So the freshman meme guy goes  off to Google and he looks for 3D drawing options in Python and the first thing that  he finds is processing in Python mode. So has anyone used processing in this audience?  It's pretty awesome in my humble opinion. It was created by Ben Fry and Casey Reese  in 2001 while they were part of the MIT Media Lab. Processing is the name of the project,  the IDE and the programming language and it's targeted at artists and beginners. And in  recent years it has a Python programming mode. So the freshman meme guy looks up the sample  code to draw a red spinning cube and he finds this and it's all human readable. He doesn't  quite understand variable scoping so the global word is kind of magic to him but otherwise  it makes sense. There's one method called setup that executes once when the program  starts and then there's one method called draw that executes every single time the frame  or the window is drawn to. And it produces something sensible that looks like this. So  the freshman meme guy is happy, he goes off with processing and implements his planets  and goes to try to pull out his simulation and put it in processing. But the first thing  he encounters when he tries to import sci-pi is this import error, no module, named sci-pi.  And now this makes our freshman meme guy very upset because he is absolutely sure that he  has sci-pi installed on his computer and in his Python environment. I mean, he's been  using it all semester for his classes. So he encounters this problem because the Python  mode in processing is kind of a veneer over a Java underbelly. So he gives up on that  approach and goes back to Google. The next thing he finds is pyopengl. So pyopengl is  one of the Python bindings to opengl. It's like how pyqt is to qt. So he looks up the  sample draw spinning cube example in pyopengl. So the imports look friendly. He just has  to import the graphics library and then the graphics library utility tools. Like with  the processing demo, there's one method that executes once on setup. It's really confusing  to him because he doesn't know what any of these things are doing. But it only happens  once, so that's fine. He can just ignore it for now. Then he looks at the method that  is going to execute every single time. And you should not do this, but the method that  comes up the most often in really basic opengl tutorials to draw a cube is to draw six quadrilaterals,  one for each side. Now, you should not do this if you're going to do any high performance  stuff, but it's kind of the simple way of drawing a cube. But to do this, you have to  draw four vertexes, one for each corner of each quadrilateral. And this is what the entire  code to draw a cube looks like. And then you can create a window using the utility tools.  So our undergrad looks at the example. It's like, okay, well, this makes sense. This is  what I expected to be drawn. And then he goes off and says, well, I'll just figure out how  to draw spheres with this. So he goes to the opengl group and downloads the opengl cheat  sheet. Now, the problem with that is that the opengl cheat sheet is not actually an opengl  cheat sheet. It's not a single sheet. It is actually a 13-page Adobe PDF document. And  the first page in nine-point font is everything just about buffers. Now, keep in mind that  this is a computational science student and not a computer science student. He has only  had maybe three months at most of programming, and he does not even know what a pointer is.  And to be honest, as a Python user, I don't really like to use pointers either, so I can  understand his woes. So this makes him upset. He goes back to Google. He looks again. He  finds a module called Piglet. So Piglet is also an opengl binding, but it has submodules  for windowing in multimedia. And it can produce the same sorts of animations that PyOpengl  does. So the undergrad looks at the code. It all seems logical. The imports look logical.  The code to actually update his animation and draw a window looks really nice. Like,  he can understand what a window is. He can understand having a clock that he has to schedule  to update, and he can understand running his app. So this makes him happy. So then he's  like, okay, great. I'm going to continue with this. Let's look at the actual code for drawing  a cube. And he sees exactly the same thing that he saw in PyOpengl. And now on top of  that, there's the scary at symbol because he has not been taught decorators, and he doesn't  even want to try to figure out what that is. And again, the actual drawing of the cube  is the same. So this makes him unhappy. All right. So the final option he comes across  is called vPython. So just in background on vPython, it was created by David Sherer in  2000 to replace CT, which is what they were using to teach computational physics at Carnegie  Mellon. It is currently maintained by Bruce Sherwood, who was one of the original developers  of CT. And it was designed specifically for helping students learn physics through programming.  How it works is that all of the actual OpenGL calls are written in C++, and that gets compiled  to a module called C Visual. And then the actual windowing is based on WX Python and sort of  just hooks into this compiled Python module. And just as an example of how this is used  in modern, in classes that are going on today, this is some vPython for teaching students  Newton's second law, F equals MA. If you remember back to your high school physics, you might  have had a similar lab to this that involved launching beanbags at your classmates. This  is a great innovation because it prevents any beanbag launching related injuries. But  basically you create a ball, you add a velocity to it, you apply the force of gravity to it,  and then you integrate the force of gravity onto the velocity. And then you can also demonstrate  the additivity of forces by subtracting a drag force. So just to give you an example,  this is what it would actually look like on your screen, and you can see what the effect  of drag is. No beanbags hurt in the process. All right. So the undergrad looks up the code  for drawing a red spinning cube, and it is five lines. It's super nice. And it all makes  sense to him. And on his Windows laptop, it looks like this. So based on that, he creates  his visualization. It all looks good on his laptop. And then he uploads it to the physics  marking server, which is running Ubuntu. And a few minutes later, he gets an email from  his prof while he's still in this euphoric mood that says, there is no Debian package  for WX Python 3. Can you please come to my office and show me your animation? And now  this is a problem for the undergrad meme because his physics prof is a nice guy, but he tends  to go on about how great physics and programming is, and he really just wants to get back on  the soccer field and continue his quidditch practice. So that ultimately makes him upset.  So what I'm trying to communicate in this user story is that writing really simple graphics  libraries that are kind of platform independent is really hard. So what if we had an open GL  helper? So the idea here is, V Python established a really nice API. So what if we extract that  from the C++ code? But let some other project, some other Python project that has already  done a lot of hard work on making open GL cross platform Python hooks do the heavy lifting.  And then the final thing we want to do is obscure the windowing. So this is one advantage  that Piglet has over PyOpenGL is that they make the windowing really easy. All right.  So the first thing that I have to do if I'm going to build this thing is decide which  open GL binding I'm going to use. And PyOpenGL has support for a lot more of open GL, and  it also currently has support for 3.5, while as Piglet appears to be kind of a dead project  right now. And so this is where I made the first fatal flaw in creating this library  because I picked Piglet. And the reasons I did that were just number one, it has much  better documentation, something to keep in mind when I'm writing my own stuff. And secondly,  I was kind of an open GL noob at the start, and the extra app and window libraries make  things really easy. All right. So this is the package that I created. It's a helper  for Piglet or Piglet helper. It has a sub module called objects which contains geometric  primitives, and it has a sub module called utils that has mathematical primitives for  doing transformations. And I let Piglet.gl interact with open GL, and then I use Piglet's  window and app things to handle the display. All right. So I'm going to do a few just comments  on implementation before I just go off and show the animated GIFs. So converting from  C++ to Python is not, unfortunately, is not as easy as just doing a search replace on  curly braces. There are several things you can do in C++ that you cannot do in Python,  for example, having multiple method declarations. And the way that I chose to do this and still  have the same functionality is to use the get item, set item, and length operators on  classes. So just as in the previous slide, I could initialize a vector by passing it  three zeros, or sorry, passing it three numbers, passing it nothing, which would initialize  it to zero, or passing it another pointer to an array. I can pass it any object which  has length greater than two or three, and then assign the first element to x, second  element to y, third element to z. Now, the second fatal flaw that I did was decide to  go with pep styling on this project. And so this project was built back in 2001 where  I think, I don't even think pep 8 was written back then. So that meant that I changed everything  slightly, so it doesn't like lower case object names, it also doesn't like single character  properties. So these are the types of changes I made. And now, given that I'm making all  these changes to the API, how do I make sure things don't break? And since this is a beginner  talk, I'm just going to explain how to use continuous integration. So I use Travis CI,  which is free as in beer, but not free as in speech. So every time I commit a new change  to GitHub, it runs set up pi for Python 2.7 through 3.4, it pilots, and then it runs the  unit tests. Now, this provides a unique challenge in this situation, which is that Travis CI  runs on a headless server, but open GL requires a display to draw to, even if you just want  to load the modules. And this is something that I had trouble with because my naive solution  is just, well, let's render to a still image and then we'll just compare the images from  previous builds to this current build. However, I'm having trouble with that because it seems  like I should be able to use X dummy, but that requires the NVIDIA X driver, and then  the NVIDIA X driver doesn't seem to work correctly on headless systems, but I know this should  be theoretically possible because other projects have done these things, and if you have done  this before, please talk to me afterwards. So my alternative right now is just to mock  GL. So in the mock module in Python, there is a useful decorator called patch, and what  that allows you to do is that in your unit test, you can just replace any time that an  object is called, you replace it with another object. So in box, box will import piglet.gl  as GL, and then in my patch, I'm just going to replace GL with my own fake GL, and then  my own fake GL module has all the function primitives, but it does essentially nothing.  Now, this is kind of cheating in a way, but I don't, I'm not really testing open GL. I'm  really just testing that my modules all work when they call each other, and that everything  imports correctly. All right. So now that I've talked about some boring  implementation details, I'm just going to go over some functionality. So these are the  geometric primitives implemented. There's an arrow, sphere, cube, cone, cylinder, ellipsoid,  ring, and pyramid. And then there's also things called lights and scenes. So a scene object  contains information about the camera position and some details about the default lights.  The lights are non-rendable objects that you can stick in the scene. And then the objects  can either be drawn into the scene by passing them to the scene as an, or sorry, the scene  has a list of objects. You can either add the object to that list, or you can pass the  scene to the object's draw method. So just to clarify what I mean, I have a box  and a scene. I can either render the box in the scene by passing scene as an argument  to render, or by appending the box to the scene and then setting up the scene.  All right. So just to finalize, this is what the freshman's code would look like in Pig  What Helper. So I've, in addition to everything else, just created a default scene with a  default camera position and some default lights. And you can just create that using VSetup,  and it will automatically add any objects you call after that to that scene. You define  an update function just as you would with any of the other ones, and then you can run  the scene. And I've also added a really useful utility for generating objects. And this is  really great if you want to spam your Twitter profile with animated GIFs. And that is what  it looks like. All right. So I started this project last year after getting really frustrated  about getting visual working with Python 3. The original developers of the Python had  the same problem, but they decided to take a different approach, which was, since they  do mostly teaching stuff, to switch to a Jupyter Notebooks type environment. So they switched  from OpenGL to WebGL, and they created this JavaScript library called Glowscript, which  allows you to use the Python syntax in WebGL. And then they created this website called  Glowscript.org, where you can type in Python code and have it render on the Web. However,  just like processing in Python mode, this doesn't give you access to everything else  in Python. And then the other module they created was called the Python Jupyter. So  they use 0MQ to actually do the communication and to feed some JSON back into Jupyter to  render in the Jupyter Notebook. And they're using the actual Glowscript JavaScript library  that they've hosted on Glowscript.org to actually render this. And this is what it looks like.  It's not quite the same because you have to define these scene variables, but again, it  looks very, very similar to VPython Classic Edition. All right. So for future work on Piglet  Helper, so I made two fatal flaws, as I said. The first one is that I made styling changes  to match modern styling. And I think this was a really bad idea because the people who  use this library all operate out of textbooks, and textbooks don't update as fast as the  Internet. So it doesn't make sense to make a library that doesn't match the old API.  So I think my first step is going to be to undo that. I also haven't implemented 3D text,  which is something that is available in VPython. And then finally, I think I need to switch  off of Piglet because it does not have support for Python 3.5. And now, a few days before  the conference, it came to my attention that Vispy probably could do a very similar thing.  And a year ago, when I started this, I looked into Vispy, and they had not yet implemented  some objects trying, but now they have. So if you're looking for something that is more  fully featured, that is probably a lot more stable. And another benefit is that it implements,  it works with several different backends. So it works with PyQt4, PyQt5, and Piglet  and PyOpenGL with a little bit of work. You should really check out Vispy. So this is  the sample rotating cube code that I have found that is the simplest, I think, for Vispy.  And as you can see, it's not quite as dead easy as you might want it to be, but it's  still very, very nice. So that's what I would suggest you use instead of my library. But  just as a final experiment, I really want to try to communicate what sort of wonderful  things can happen when you lower the barriers of entry to 3D drawing, just like what processing  has been able to do. Because a lot of us here in the audience are experts in a certain domain,  but we probably aren't experts in OpenGL, but we are still humans, and humans have eyeballs,  and humans like looking at things. So just as a crazy experiment, a few days before the  conference, I decided what if I feed Piglet Helper into the distributed evolutionary algorithms  project and use scikit image to judge the similarity of images generated. So the idea  is I'm going to try to generate the Python logo using Piglet Helper attached to an evolutionary  algorithm. And so this is what you see. It's not the greatest because of the artificial  limitations I put onto the search space for the evolutionary algorithm, but you can see  that it eventually learns from just a mix of chaos to put yellow spheres in the bottom  half and blue spheres in the top half. Anyway, thank you.  Next question. So you said Piglet is kind of a dead project. Have you got in touch with  the developers or saw the mailing list because there's appearing to be some activity around  Piglet now? Oh, no, I hadn't. But that's very exciting to hear.  Did you also have a look at game engines because they might provide similar functionalities  or maybe sometimes an easier package? Can you repeat what that?  Did you have a look at game engines? There are some game engines.  Oh, game engines. Yeah, I looked into Pygame and it had a lot of cruft that I wasn't particularly  interested in for game development, but perhaps I should look at it again.  Anymore? No, that's not.  Hi, thanks for the talk. It's really interesting. You've used the kind of college university  student as the target. I wonder what are your thoughts for creating 3D graphics for somebody  who might be seven years old? So the reason that I use a university undergraduate  student is that in Canada, where I'm from, we have really terrible computer science education.  So even though students, I believe personally that students who are seven who can read English  would totally be able to use V-Python, typically students don't encounter their first programming  class until their first year of university. One more question to you, Haiz Bither.  What do you think about processing Python mode?  So I should have just pressed the home button, but whatever, my finger will get a workout.  So the first one that I went over was processing in Python mode. And as I said, this didn't  really work for the application because you aren't able to access the full Python stack.  And I've looked into trying to incorporate the Python stack into processing in Python  mode, and the thing is that I hate Java way too much to invest any more time in that.  Thank you, Catherine. Thank you all.
Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_0_203.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_204_251.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_252_299.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_300_1715.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_1716_3719.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_3720_4667.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_4668_5195.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_5196_10295.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_10296_10487.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_10488_10583.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_10584_11831.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_11832_11927.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_11928_12167.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_12168_12227.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_12228_12299.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_12300_12431.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_12432_12479.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_12480_13679.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_13680_13835.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_13836_14039.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_14040_14363.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_14364_14579.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_14580_15071.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_15072_16691.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_16692_19067.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_19068_25583.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_25584_27587.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_27588_27779.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_27780_28355.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_28356_29543.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_29544_29711.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_29712_34511.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_34512_36227.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_36228_38027.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_38028_39719.png Catherine_Holloway_-_Simplifying_Computer_Art_in_Python_39720_39815.png
Good afternoon. I want you all to welcome Catherine showing us how to do computer art with Python. Hi, thank you for coming. My name is Catherine and for five years I was a graduate student in physics at the University of Waterloo. Now the University of Waterloo in their Faculty of Science offers a computational science degree. It's a cross departmental degree program that tries to teach people without very much programming experience but a lot of enthusiasm for science how to use programming to be more efficient at science. And during my time as a grad student I was the teaching assistant for the computational physics classes which used Python but very, very basic Python to teach physics concepts such as classical mechanics, integrating differential equations, stuff like that. And today I'm going to tell you about a Python library that I have been writing that was inspired by my time as a teaching assistant. So every great software development project begins with a great user story. So for this presentation let's imagine that our user is the freshman meme guy. So the freshman meme guy is a computational science student. He has no prior programming experience to the computational physics class that he took and he's also a big fan of Quidditch. And the midterm for computational physics came on the day after the intramural Quidditch finals so he flunked his midterm and now he's looking for some extra credit. And his university professor says, okay, the midterm mark will be ignored if you can make me a nice pretty animated visualization of your Keplerian planet simulation. So the freshman meme guy goes off to Google and he looks for 3D drawing options in Python and the first thing that he finds is processing in Python mode. So has anyone used processing in this audience? It's pretty awesome in my humble opinion. It was created by Ben Fry and Casey Reese in 2001 while they were part of the MIT Media Lab. Processing is the name of the project, the IDE and the programming language and it's targeted at artists and beginners. And in recent years it has a Python programming mode. So the freshman meme guy looks up the sample code to draw a red spinning cube and he finds this and it's all human readable. He doesn't quite understand variable scoping so the global word is kind of magic to him but otherwise it makes sense. There's one method called setup that executes once when the program starts and then there's one method called draw that executes every single time the frame or the window is drawn to. And it produces something sensible that looks like this. So the freshman meme guy is happy, he goes off with processing and implements his planets and goes to try to pull out his simulation and put it in processing. But the first thing he encounters when he tries to import sci-pi is this import error, no module, named sci-pi. And now this makes our freshman meme guy very upset because he is absolutely sure that he has sci-pi installed on his computer and in his Python environment. I mean, he's been using it all semester for his classes. So he encounters this problem because the Python mode in processing is kind of a veneer over a Java underbelly. So he gives up on that approach and goes back to Google. The next thing he finds is pyopengl. So pyopengl is one of the Python bindings to opengl. It's like how pyqt is to qt. So he looks up the sample draw spinning cube example in pyopengl. So the imports look friendly. He just has to import the graphics library and then the graphics library utility tools. Like with the processing demo, there's one method that executes once on setup. It's really confusing to him because he doesn't know what any of these things are doing. But it only happens once, so that's fine. He can just ignore it for now. Then he looks at the method that is going to execute every single time. And you should not do this, but the method that comes up the most often in really basic opengl tutorials to draw a cube is to draw six quadrilaterals, one for each side. Now, you should not do this if you're going to do any high performance stuff, but it's kind of the simple way of drawing a cube. But to do this, you have to draw four vertexes, one for each corner of each quadrilateral. And this is what the entire code to draw a cube looks like. And then you can create a window using the utility tools. So our undergrad looks at the example. It's like, okay, well, this makes sense. This is what I expected to be drawn. And then he goes off and says, well, I'll just figure out how to draw spheres with this. So he goes to the opengl group and downloads the opengl cheat sheet. Now, the problem with that is that the opengl cheat sheet is not actually an opengl cheat sheet. It's not a single sheet. It is actually a 13-page Adobe PDF document. And the first page in nine-point font is everything just about buffers. Now, keep in mind that this is a computational science student and not a computer science student. He has only had maybe three months at most of programming, and he does not even know what a pointer is. And to be honest, as a Python user, I don't really like to use pointers either, so I can understand his woes. So this makes him upset. He goes back to Google. He looks again. He finds a module called Piglet. So Piglet is also an opengl binding, but it has submodules for windowing in multimedia. And it can produce the same sorts of animations that PyOpengl does. So the undergrad looks at the code. It all seems logical. The imports look logical. The code to actually update his animation and draw a window looks really nice. Like, he can understand what a window is. He can understand having a clock that he has to schedule to update, and he can understand running his app. So this makes him happy. So then he's like, okay, great. I'm going to continue with this. Let's look at the actual code for drawing a cube. And he sees exactly the same thing that he saw in PyOpengl. And now on top of that, there's the scary at symbol because he has not been taught decorators, and he doesn't even want to try to figure out what that is. And again, the actual drawing of the cube is the same. So this makes him unhappy. All right. So the final option he comes across is called vPython. So just in background on vPython, it was created by David Sherer in 2000 to replace CT, which is what they were using to teach computational physics at Carnegie Mellon. It is currently maintained by Bruce Sherwood, who was one of the original developers of CT. And it was designed specifically for helping students learn physics through programming. How it works is that all of the actual OpenGL calls are written in C++, and that gets compiled to a module called C Visual. And then the actual windowing is based on WX Python and sort of just hooks into this compiled Python module. And just as an example of how this is used in modern, in classes that are going on today, this is some vPython for teaching students Newton's second law, F equals MA. If you remember back to your high school physics, you might have had a similar lab to this that involved launching beanbags at your classmates. This is a great innovation because it prevents any beanbag launching related injuries. But basically you create a ball, you add a velocity to it, you apply the force of gravity to it, and then you integrate the force of gravity onto the velocity. And then you can also demonstrate the additivity of forces by subtracting a drag force. So just to give you an example, this is what it would actually look like on your screen, and you can see what the effect of drag is. No beanbags hurt in the process. All right. So the undergrad looks up the code for drawing a red spinning cube, and it is five lines. It's super nice. And it all makes sense to him. And on his Windows laptop, it looks like this. So based on that, he creates his visualization. It all looks good on his laptop. And then he uploads it to the physics marking server, which is running Ubuntu. And a few minutes later, he gets an email from his prof while he's still in this euphoric mood that says, there is no Debian package for WX Python 3. Can you please come to my office and show me your animation? And now this is a problem for the undergrad meme because his physics prof is a nice guy, but he tends to go on about how great physics and programming is, and he really just wants to get back on the soccer field and continue his quidditch practice. So that ultimately makes him upset. So what I'm trying to communicate in this user story is that writing really simple graphics libraries that are kind of platform independent is really hard. So what if we had an open GL helper? So the idea here is, V Python established a really nice API. So what if we extract that from the C++ code? But let some other project, some other Python project that has already done a lot of hard work on making open GL cross platform Python hooks do the heavy lifting. And then the final thing we want to do is obscure the windowing. So this is one advantage that Piglet has over PyOpenGL is that they make the windowing really easy. All right. So the first thing that I have to do if I'm going to build this thing is decide which open GL binding I'm going to use. And PyOpenGL has support for a lot more of open GL, and it also currently has support for 3.5, while as Piglet appears to be kind of a dead project right now. And so this is where I made the first fatal flaw in creating this library because I picked Piglet. And the reasons I did that were just number one, it has much better documentation, something to keep in mind when I'm writing my own stuff. And secondly, I was kind of an open GL noob at the start, and the extra app and window libraries make things really easy. All right. So this is the package that I created. It's a helper for Piglet or Piglet helper. It has a sub module called objects which contains geometric primitives, and it has a sub module called utils that has mathematical primitives for doing transformations. And I let Piglet.gl interact with open GL, and then I use Piglet's window and app things to handle the display. All right. So I'm going to do a few just comments on implementation before I just go off and show the animated GIFs. So converting from C++ to Python is not, unfortunately, is not as easy as just doing a search replace on curly braces. There are several things you can do in C++ that you cannot do in Python, for example, having multiple method declarations. And the way that I chose to do this and still have the same functionality is to use the get item, set item, and length operators on classes. So just as in the previous slide, I could initialize a vector by passing it three zeros, or sorry, passing it three numbers, passing it nothing, which would initialize it to zero, or passing it another pointer to an array. I can pass it any object which has length greater than two or three, and then assign the first element to x, second element to y, third element to z. Now, the second fatal flaw that I did was decide to go with pep styling on this project. And so this project was built back in 2001 where I think, I don't even think pep 8 was written back then. So that meant that I changed everything slightly, so it doesn't like lower case object names, it also doesn't like single character properties. So these are the types of changes I made. And now, given that I'm making all these changes to the API, how do I make sure things don't break? And since this is a beginner talk, I'm just going to explain how to use continuous integration. So I use Travis CI, which is free as in beer, but not free as in speech. So every time I commit a new change to GitHub, it runs set up pi for Python 2.7 through 3.4, it pilots, and then it runs the unit tests. Now, this provides a unique challenge in this situation, which is that Travis CI runs on a headless server, but open GL requires a display to draw to, even if you just want to load the modules. And this is something that I had trouble with because my naive solution is just, well, let's render to a still image and then we'll just compare the images from previous builds to this current build. However, I'm having trouble with that because it seems like I should be able to use X dummy, but that requires the NVIDIA X driver, and then the NVIDIA X driver doesn't seem to work correctly on headless systems, but I know this should be theoretically possible because other projects have done these things, and if you have done this before, please talk to me afterwards. So my alternative right now is just to mock GL. So in the mock module in Python, there is a useful decorator called patch, and what that allows you to do is that in your unit test, you can just replace any time that an object is called, you replace it with another object. So in box, box will import piglet.gl as GL, and then in my patch, I'm just going to replace GL with my own fake GL, and then my own fake GL module has all the function primitives, but it does essentially nothing. Now, this is kind of cheating in a way, but I don't, I'm not really testing open GL. I'm really just testing that my modules all work when they call each other, and that everything imports correctly. All right. So now that I've talked about some boring implementation details, I'm just going to go over some functionality. So these are the geometric primitives implemented. There's an arrow, sphere, cube, cone, cylinder, ellipsoid, ring, and pyramid. And then there's also things called lights and scenes. So a scene object contains information about the camera position and some details about the default lights. The lights are non-rendable objects that you can stick in the scene. And then the objects can either be drawn into the scene by passing them to the scene as an, or sorry, the scene has a list of objects. You can either add the object to that list, or you can pass the scene to the object's draw method. So just to clarify what I mean, I have a box and a scene. I can either render the box in the scene by passing scene as an argument to render, or by appending the box to the scene and then setting up the scene. All right. So just to finalize, this is what the freshman's code would look like in Pig What Helper. So I've, in addition to everything else, just created a default scene with a default camera position and some default lights. And you can just create that using VSetup, and it will automatically add any objects you call after that to that scene. You define an update function just as you would with any of the other ones, and then you can run the scene. And I've also added a really useful utility for generating objects. And this is really great if you want to spam your Twitter profile with animated GIFs. And that is what it looks like. All right. So I started this project last year after getting really frustrated about getting visual working with Python 3. The original developers of the Python had the same problem, but they decided to take a different approach, which was, since they do mostly teaching stuff, to switch to a Jupyter Notebooks type environment. So they switched from OpenGL to WebGL, and they created this JavaScript library called Glowscript, which allows you to use the Python syntax in WebGL. And then they created this website called Glowscript.org, where you can type in Python code and have it render on the Web. However, just like processing in Python mode, this doesn't give you access to everything else in Python. And then the other module they created was called the Python Jupyter. So they use 0MQ to actually do the communication and to feed some JSON back into Jupyter to render in the Jupyter Notebook. And they're using the actual Glowscript JavaScript library that they've hosted on Glowscript.org to actually render this. And this is what it looks like. It's not quite the same because you have to define these scene variables, but again, it looks very, very similar to VPython Classic Edition. All right. So for future work on Piglet Helper, so I made two fatal flaws, as I said. The first one is that I made styling changes to match modern styling. And I think this was a really bad idea because the people who use this library all operate out of textbooks, and textbooks don't update as fast as the Internet. So it doesn't make sense to make a library that doesn't match the old API. So I think my first step is going to be to undo that. I also haven't implemented 3D text, which is something that is available in VPython. And then finally, I think I need to switch off of Piglet because it does not have support for Python 3.5. And now, a few days before the conference, it came to my attention that Vispy probably could do a very similar thing. And a year ago, when I started this, I looked into Vispy, and they had not yet implemented some objects trying, but now they have. So if you're looking for something that is more fully featured, that is probably a lot more stable. And another benefit is that it implements, it works with several different backends. So it works with PyQt4, PyQt5, and Piglet and PyOpenGL with a little bit of work. You should really check out Vispy. So this is the sample rotating cube code that I have found that is the simplest, I think, for Vispy. And as you can see, it's not quite as dead easy as you might want it to be, but it's still very, very nice. So that's what I would suggest you use instead of my library. But just as a final experiment, I really want to try to communicate what sort of wonderful things can happen when you lower the barriers of entry to 3D drawing, just like what processing has been able to do. Because a lot of us here in the audience are experts in a certain domain, but we probably aren't experts in OpenGL, but we are still humans, and humans have eyeballs, and humans like looking at things. So just as a crazy experiment, a few days before the conference, I decided what if I feed Piglet Helper into the distributed evolutionary algorithms project and use scikit image to judge the similarity of images generated. So the idea is I'm going to try to generate the Python logo using Piglet Helper attached to an evolutionary algorithm. And so this is what you see. It's not the greatest because of the artificial limitations I put onto the search space for the evolutionary algorithm, but you can see that it eventually learns from just a mix of chaos to put yellow spheres in the bottom half and blue spheres in the top half. Anyway, thank you. Next question. So you said Piglet is kind of a dead project. Have you got in touch with the developers or saw the mailing list because there's appearing to be some activity around Piglet now? Oh, no, I hadn't. But that's very exciting to hear. Did you also have a look at game engines because they might provide similar functionalities or maybe sometimes an easier package? Can you repeat what that? Did you have a look at game engines? There are some game engines. Oh, game engines. Yeah, I looked into Pygame and it had a lot of cruft that I wasn't particularly interested in for game development, but perhaps I should look at it again. Anymore? No, that's not. Hi, thanks for the talk. It's really interesting. You've used the kind of college university student as the target. I wonder what are your thoughts for creating 3D graphics for somebody who might be seven years old? So the reason that I use a university undergraduate student is that in Canada, where I'm from, we have really terrible computer science education. So even though students, I believe personally that students who are seven who can read English would totally be able to use V-Python, typically students don't encounter their first programming class until their first year of university. One more question to you, Haiz Bither. What do you think about processing Python mode? So I should have just pressed the home button, but whatever, my finger will get a workout. So the first one that I went over was processing in Python mode. And as I said, this didn't really work for the application because you aren't able to access the full Python stack. And I've looked into trying to incorporate the Python stack into processing in Python mode, and the thing is that I hate Java way too much to invest any more time in that. Thank you, Catherine. Thank you all.
Catherine showing us how to do computer art with for science how to use programming to be more concepts such as classical mechanics, integrating every great software development project begins with a great user story. So for this presentation processing in this audience? It's pretty awesome the MIT Media Lab. Processing is the name of the project, the IDE and the programming language and there's one method called draw that executes this problem because the Python mode in library and then the graphics library utility tools. Like with the processing demo, there's one this. Let's look at the actual code for drawing a background on vPython, it was created by David the actual OpenGL calls are written in C++, and that gets compiled to a module called C Visual. had a similar lab to this that involved launching And then you can also demonstrate the additivity actually look like on your screen, and you can animation? And now this is a problem for the really simple graphics libraries that are kind of Python established a really nice API. So what if cross platform Python hooks do the heavy lifting. which contains geometric primitives, and it has a and show the animated GIFs. So converting from C++ to Python is not, unfortunately, is not as the types of changes I made. And now, given that I'm making all these changes to the API, how do I Now, this provides a unique challenge in this with because my naive solution is just, well, to work correctly on headless systems, but I know information about the camera position and some getting visual working with Python 3. The Web. However, just like processing in Python something that is available in VPython. And then similar functionalities or maybe sometimes an science education. So even though students, I