Tuesday, May 21, 2019

A 3D Python Maze for Art of Illusion

I've always had a fascination with mazes. While messing with the Python plugin for Art of Illusion I decided to write a maze generator in Python. The image to the right is a rendering of the maze that is produced by that code. I did it as a Scripted Object, which was probably not the best idea since it recalculates the maze whenever something changes, but my excuse is that I was testing out some things related to Python scripted objects in Art of Illusion.

I'm including the code below, but with the caveat that it has a defect at one of the boundaries that I haven't figured out yet. It doesn't remove a wall for one or two of the cells. Aside from that, it will produce a good 3D maze.

import random
mazeWidth = 25
mazeLength = 25
wallLength = 1.2
wallHeight = 2.25
wallThickness = 0.05


class cell():
  X = 0
  Y = 0
  visited = False
def createmaze(width, length):
  m = []
  for l in range(length):
    row = []
    for w in range(width):
      _cell = cell()
      _cell.X = w
      _cell.Y = l
  for l in range(length):
    m[l][0].N = WALL_FIXED
    m[l][width-1].S = WALL_FIXED
  for w in range(width):
    m[0][w].W = WALL_FIXED
    m[length-1][w].E = WALL_FIXED
  return m
def createWall(length, height, thickness):
  wall = Cube(thickness, length, height)
  return ObjectInfo(wall, CoordinateSystem(), "")
def drawMaze():
  for i in range(mazeLength):
    for j in range(mazeWidth):
      if maze[i][j].W != WALL_DOWN:
        obj = createWall(wallLength, wallHeight, wallThickness)
        if not obj is None:
          obj.getCoords().setOrientation(90.0, 0.0, 0.0)
          obj.getCoords().setOrigin(Vec3(j*wallLength-(wallLength*0.5)-(mazeWidth*wallLength/2), wallHeight/2, i*wallLength-(mazeLength*wallLength/2)))
      if maze[i][j].N != WALL_DOWN:
        obj = createWall(wallLength, wallHeight, wallThickness)
        if not obj is None:
          obj.getCoords().setOrientation(90.0, 90.0, 0.0)
          obj.getCoords().setOrigin(Vec3(j*wallLength-(mazeWidth*wallLength/2), wallHeight/2, i*wallLength-(wallLength*0.5)-(mazeLength*wallLength/2)))
  for i in range(mazeLength):
    obj = createWall(wallLength, wallHeight, wallThickness)
    if not obj is None:
      obj.getCoords().setOrientation(90.0, 0.0, 0.0)
      obj.getCoords().setOrigin(Vec3(mazeWidth*wallLength-(wallLength*0.5)-(mazeWidth*wallLength/2), wallHeight/2, i*wallLength-(mazeLength*wallLength/2)))
  for j in range(mazeWidth):
    obj = createWall(wallLength, wallHeight, wallThickness)
    if not obj is None:
      obj.getCoords().setOrientation(90.0, 90.0, 0.0)
      obj.getCoords().setOrigin(Vec3(j*wallLength-(mazeWidth*wallLength/2), wallHeight/2, mazeLength*wallLength-(wallLength*0.5)-(mazeLength*wallLength/2)))
def chooseUnvisited():
  neighbors = []
  if (current.N == WALL_UP) and (current.Y-1 >=0) and (not maze[current.Y-1][current.X].visited):
  if (current.S == WALL_UP) and (current.Y+1 < mazeLength) and (not maze[current.Y+1][current.X].visited):
  if (current.W == WALL_UP) and (current.X-1 >=0) and (not maze[current.Y][current.X-1].visited):
  if (current.E == WALL_UP) and (current.X+1 < mazeWidth) and (not maze[current.Y][current.X+1].visited):
  if len(neighbors) > 0:
    return random.choice(neighbors)
    return current
def removeWall(cur, nxt):
  print("Cur "+str(cur.X)+" "+str(cur.Y))
  print("Nxt "+str(nxt.X)+" "+str(nxt.Y))
  if nxt != cur:
    if cur.Y < nxt.Y and cur.S != WALL_FIXED:
      print("removeWall 1")
      maze[cur.Y][cur.X].S = WALL_DOWN
      maze[nxt.Y][nxt.X].N = WALL_DOWN
    elif cur.Y > nxt.Y and cur.N != WALL_FIXED:
      print("removeWall 2")
      maze[cur.Y][cur.X].N = WALL_DOWN
      maze[nxt.Y][nxt.X].S = WALL_DOWN
    elif cur.X < nxt.X and cur.E != WALL_FIXED:
      print("removeWall 3")   
      maze[cur.Y][cur.X].E = WALL_DOWN
      maze[nxt.Y][nxt.X].W = WALL_DOWN
    elif cur.X > nxt.X and cur.W != WALL_FIXED:
      print("removeWall 4")   
      maze[cur.Y][cur.X].W = WALL_DOWN
      maze[nxt.Y][nxt.X].E = WALL_DOWN    
      print("removeWall 5")   

maze = createmaze(mazeWidth, mazeLength)
theStack = []
current = maze[0][0] # choose initial
theStack.append(current) # push current

# choose unvisited neighbor, pop if none found
next = chooseUnvisited()
next.visited = True
print("Next "+str(next.X)+" "+str(next.Y))
while len(theStack) > 0:
  while next == current and len(theStack) > 0:
    current = theStack.pop()
    next = chooseUnvisited()
    next.visited = True
    print("Next "+str(next.X)+" "+str(next.Y))
  removeWall(current, next)
  current = next
  next = chooseUnvisited()
  next.visited = True
  if next != current:

Tuesday, May 14, 2019

Advantage of Python over Java

Here's a question for you: what is the advantage of using Python over Java? Because of the recent publication of the second edition of Extending Art of Illusion I've been spending time working on scripts for Art of Illusion. Straight out of the box, Art of Illusion provides scripting capability for Groovy and BeanShell. This makes sense because Art of Illusion is written in Java and both of these scripting languages are based on Java. But most other graphics programs use some variation on Python as their scripting language. In a post on Python Scripts in Art of Illusion I wrote about creating a plugin that adds Python to the languages that Art of Illusion supports. I use Python for things I do in my day job mostly because it's available. But with adding it to Art of Illusion I have a direct comparison between Java and Python. It got me thinking. Is there some reason to choose to script in Python rather than using Java, Groovy, or BeanShell?

When I setup the interpreter, I did my best to provide consistency between what can be done with Python and what can be done with Groovy. The types of scripts are the same. The editor windows are the same. The libraries linked in from Art of Illusion are the same. I'm using the Jython interpreter, so everything that can be called from a Java class can be called from Python. So, in considering the question it really does come down to language differences.

Groovy and Python handle blocks differently. Java is very similar to C, so a block is whatever is within curly braces. In Python blocks are handled by indenting. There's nothing particularly advantageous for one over the other. It is personal preference. Personally, I've fine with both. Comments are also handled differently. Groovy uses C++ style comments while Python uses # to mark a comment. Again, that's just personal preference. Strings are handled by both and what you can do with strings is very similar.

The biggest difference between Python and Java is that Python has powerful list processing capability built right in while Java treats it as more of an afterthought. Given that 3D graphics scenes are lists of objects and objects are lists of polygons and polygons are lists of vertices, how a scripting language processes lists is important. In Python, a list is denoted by square brackets, []. In Art of Illusion we're most likely to get a list by using values returned from some Java function. In addition to lists, Python has dictionaries (square brackets {}), which are an unordered table that maps keys to values. It also has sets, which are unordered collections of unique objects.

A list might simply be every object in the scene or every object of a certain type and we want to perform some operation on each of the objects in the list. We might use a dictionary if we want to refer to objects by name. Sets are only truly useful in combination. We might have a set of edge vertices and a set of interior vertices. We might have a set of visible vertices and a set of invisible vertices. If we wanted to perform some operation on all of the visible edge vertices then we could get the intersection of the edge vertices set and the visible set and then loop through that set rather than having a check in our loop for whether a set of conditions is met. Of course, it is possible to do this with Java as well, but having it inherent in the language is nice.

Sunday, May 12, 2019

Python Scripts in Art of Illusion

I took a break from the cloth simulator to look at adding Python as a scripting language for Art of Illusion. I had some success at pulling in the Jython library and was able to get the following Tool Script to add a Cube to the scene:

undo = UndoRecord(window, True)
obj = Cube(1.0, 1.0, 1.0)
objInfo = ObjectInfo(obj, CoordinateSystem(), "Cube "+str(1))
window.addObject(objInfo, undo)

In a video I demonstrated that a Groovy script could be used to add a Cube to the scene. The Groovy script was as follows:

undo = new UndoRecord(window, true);
obj = new Cube(1.0, 1.0, 1.0);
objInfo = new ObjectInfo(obj, new CoordinateSystem(), "Cube "+1);

window.addObject(objInfo, undo);                      

As you can tell, the code is essentially the same except Python doesn't use the new keyword, True is capitalized in Python, and Python won't concatenate an integer to a string without a conversion function. I'm sure there will be more noticeable differences once I start using it along with the language features of Python, but this was just a proof of concept.

Unfortunately, I've been less successful at implementing the code required to use Python to create a Scripted Object in Art of Illusion. It might be easier if I were modifying the Art of Illusion code rather than trying to do this as a plugin, but I keep running into one typecasting issue or another. I had hoped that I might use this as the vehicle to demonstrate the value of the reference material at the back of Extending Art of Illusion. I can say that I've had the book open to that material the whole time I've been working on this and I have put it to good use. I can also say that I'm very glad this book is available in hardback. It costs a little extra, but being able to open it and leave it open makes a world of difference.

UPDATE: After writing this I found a way to get it working for Scripted Objects. I put it in a repository at https://github.com/TimothyFish/AOI_with_python.git so I wouldn't lose what I had working. It's not ready for general use, but it is working. To use it, you will need to have the Jython.jar file in your class path for Art of Illusion. If you know what that means then have fun. If not then it's probably better that you don't mess with it. I would explain it but I only just got it working myself and I don't know that I can give you a clean cut set of instructions yet.

For anyone who is interested, the following is the Python version of the Axis script that is in Extending Art of Illusion:

from artofillusion.script import ScriptedObject
scene = theScript.getScene()
count = 0

length = 5.0
size = 0.01

def createAxis(length, diameter):
  axis = Cylinder(length, diameter, diameter, 1.0)
  return ObjectInfo(axis, CoordinateSystem(), "")
obj = createAxis(length, size)
if not obj is None:
obj = createAxis(length, size)
obj.getCoords().setOrientation(90.0, 0.0, 0.0)
if not obj is None:

obj = createAxis(length, size)
obj.getCoords().setOrientation(0.0, 0.0, 90.0)
if not obj is None:

You may notice that I'm using "theScript" here instead of "script". This is because there is a conflict between "script" and "artofillusion.script" with Jython interprets the code.

Friday, May 10, 2019

Art of Illusion Examples

For each of the first ten chapters of Extending Art of Illusion I have created a video that demonstrates some aspect of it. One of the things I found when writing the book was that it is difficult to demonstrate animation on the printed page. In chapter 7 I give code for a tracker object that will rotate an object or objects so that the object is always pointing at one of the other scene objects, no matter where it is located in the scene. In the book I tried to show this with a sequence of still images taken from the animation. But in the video I have been able to show the video of a head with eyes that follow an object as it moves around the scene. Not only that, but it is possible to show that these changes are occurring even as the scene is being edited.

Below is the list of videos:

Chapter 1: Use a Script to Add a Cube to Art of Illusion
Chapter 2: Storing and Loading
Chapter 3: Position Objects on Floor
Chapter 4: Point at Objects
Chapter 5: Resting One Object on Another
Chapter 6: Adding Custom Objects
Chapter 7: Tracking Movement
Chapter 8: Procedural Modules
Chapter 9: The Room
Chapter 10: Modeling Cloth in Art of Illusion

Monday, April 29, 2019

Why Buy My Book About Scripts and Plugins

After the second edition of Extending Art of Illusion went to press my attention turned once again to the question of getting it into the hands of readers. Outside of people called Mom, people don't generally buy books just because someone wrote it. I've purchased a few books just because I knew the author, but I don't buy second and third books "just because."  Those of us who love books need a reason to buy a book. No, that's not right. We want a reason to buy a book. We are begging authors and publishers to give us a reason to buy a book. We want to see a book and say, "If I buy this book this is the knowledge I will gain or the story I will enjoy." We are cheering for authors to provide us with a good book. It's in that context that I make the case for people to buy the latest edition of Extending Art of Illusion.

I stopped to ask myself why anyone would need this book. It may seem like it is a little late to be asking that question. Over 550 pages into a project and with it already coming off the press is not the time to be asking whether it is worth doing, but there's something a little odd about doing a second edition of a book. On this edition, my focus was on very specific aspects of the book rather than on the project as a whole. It was important to me to update the reference material at the back of the book so that it matches the current release of Art of Illusion. But a higher priority than that was the cloth simulator described in Chapter 10. I have this urge to tell people that they should buy the book because it provides a cloth simulation for Art of Illusion, but the cloth simulator isn't a reason to buy the book. It is a reason to install the plugin, but it isn't a reason to buy the book.

As I thought about that, I wrote a script that I thought might help illustrate why people need to write scripts and plugins for Art of Illusion. If they understand that then it is easier to make the case for them to buy a book that will teach them how to write scripts and plugins.

The picture to the right is one of the images that I rendered from that script. The script adds 1000 sphere to the scene. Adding 1000 of anything isn't something you would want to do by hand. Not only would it take a long time but it would be impossible to get all of the positions exactly right. With a script you can let the computer do the heavy lifting. The Groovy script I used is below:

size = 0.2;
spacing = 4.0*size;
t = script.getTime();
maxTime = 10.0;
timeDelta = maxTime/1000.0;
myTime = 0.0;

for(k = 0; k < 10; k++){
 for(j = 0; j < 10; j++){
   for(i = 0; i < 10; i++){
   myTime += timeDelta;
   if(myTime > t) break;
   S = new Sphere(size, size, size);
   infoS = new ObjectInfo(S, new CoordinateSystem(), "Sphere");
   infoS.coords.setOrigin(new Vec3(spacing*(i-5.5), spacing*(j-5.5), spacing*(k-5.5)));
  if(myTime > t) break;
 if(myTime > t) break;

You would place this script in a Scripted Object. The code is simple enough, but how would you change it if you wanted to use Cubes instead of Spheres? What if you wanted the objects spinning or pointing at the same thing? Imagine a thousand eyeballs looking at the camera. How would you achieve that? This is why people who want to do scripting in Art of Illusion need the book. Not only does it provide plenty of examples but it provides a listing of every public interface in Art of Illusion. My name is on the cover and yet I found myself reaching for this book as I was writing this script. I could have looked up the functions I needed in the source code, but I found it preferable to have the book open in front of me. And it is my belief that if you are writing scripts for Art of Illusion you will find that beneficial as well.

You can buy the paperback from Amazon.com.
There is also a hardback version available.

Thursday, March 28, 2019

Is Freewill a Myth?

Robots don’t sin. Of course we’ve all seen movies where some robot makes decisions concerning right and wrong, but the reality is that robots just run programs. The brain of a robot is nothing more than a device that reads data and writes data. It’s actions are controlled by what that data tells it to do. Robots don’t have a soul. If a robot does something that it shouldn’t do, we may shut it down. We may repair it. We may throw it in the landfill. But we don’t do that out of a sense of justice. If there is anyone held responsible for the evil done by a robot it is the designer, the programmer, or the operator, not the robot.

Robots don’t sin because they have no capability to decide to go against their programming. But if the inability to decide to go against their programming keeps a robot from sin then that implies that anything that can sin can decide to go against their programming. I mention this because I recently heard someone claim that “freewill is a myth.” Their basis for this claim was Ephesian 1:4 which includes the statement “he chose us in him before the foundation of the world.” Their claim was that this selection could not be based on foreknowledge of what we would do. This quickly degrades into circular reasoning. If we have no freewill then everything we do is the predetermined will of God. But if we are only doing what God said to do then we are not sinners. Even if we reject Jesus that is the will of God. Without freewill we are nothing more than moist robots and robots don’t sin.

Many people have this image of God prior to creating the world sitting there and picking people. “I’m going to save this one, but not that one…” When he is finished he creates a world ideal for the salvation of those people. The problem with this is that it implies that God’s nature is finite. If it is correct that God has always known everything that is knowable then there has never been a time when God didn’t know who would be saved and who would be lost. It’s possible that is what Paul was saying. In this light it lends support to the verses around it that talk about us being blessed with all blessings and being predestined. Predestination doesn’t make sense without foreknowledge, but there is nothing about predestination that precludes freewill.

Let’s lay aside the issue of sin for moment. Another thing that robots don’t do is love. Love does not exist without freewill. Imagine if your spouse had this button. Anytime you started feeling like she didn’t love you you could just push that button and she would instantly begin showering you with love. Is that love? Of course not. Love can only come from someone who has the choice not to love. For God to have people who loved him he had to create a world in which people could choose not to love him. This is not the violation of his sovereignty that some would suggest but he delegated to us the freedom to choose.

I see a couple of possibilities one is that God looked at the people throughout time and saw certain one’s who would believe and who would love him. These were not perfect people, but people whiling to be shaped by him into the kind of people he wanted to spend time with and these were the people he chose. The other is that he arbitrarily picked some people and over time has been shaping them to be the kind of people he wants them to be. To me, the first option is one in which human beings all have innate value even if they ultimately choose to reject him. With the second option only those he has chosen have value and the rest are simply here to live out their lives and go to hell.

I believe God has given us freewill because he wants us to be able to love him. We are not robots. If we were, then we could not sin and it would be unjust for us to be punished for our sin. If God is love and God is just then freewill is not a myth.

Tuesday, October 9, 2018

12 Things Republicans and Democrats Agree On

Politics in America according to social media is nasty. We’re divided along party lines and there are a lot of angry people yelling at each other. You would think that there’s no way to bridge the divide, but all of us have friends who are on the other side. We may avoid certain conversations when we are with them, but somehow we get along. Why? If the other side is so bad, then how is it that our friends can’t see that? Could it be that the two sides agree on much more than they care to admit? Here are twelve things that Republicans and Democrats agree on.

  1. Mass murder is evil.
  2. A strong economy is good.
  3. Children should be protected.
  4. The strong should not be allowed to prey on the weak.
  5. Cops who break the law should be prosecuted.
  6. Skin color doesn’t make one person better than another.
  7. Protecting our environment is important.
  8. Non-citizen criminals should not be allowed into our country.
  9. Individuals should be able to express their opinion.
  10. The police have a responsibility to protect us.
  11. Teachers are important.
  12. People who work should be paid fairly.
I can already hear you thinking, “Yeah, but…” and it’s for that reason that I’ve left these unexplained. I challenge you to lay this list down beside the political platform of any candidate on either side and see how many you can find that they don’t agree with. Ask your friends if they agree with these. Don’t ask for an explanation, just yes or no, do they agree with these or not?

These are not unimportant issues. These are among the most important if not the most important issues that exist in our political debate. If we agree on the most important things then why are we so divided?

It goes something like this: I and my opponent have a common goal, but we disagree on how to achieve it. I can’t see how what my opponent wants to do will achieve the goal, so I assume that my opponent doesn’t want to achieve the same goal as me. Since my opponent doesn’t support what any good person would support then my opponent must be evil. Since my opponent is evil, no one should listen to what my opponent has to say. If they do then they are also evil.

A better approach is this: My opponent advocates something that I believe harms something good. I assume that my opponent doesn’t want to harm this thing, so my opponent either placed a greater priority on some other good thing or doesn’t have enough information to see how his position will do harm. Or it could be that I don’t have all of the information I need.

Given that we’re dealing with individuals, it’s quite possible that the person in question is acting out of evil intentions, but the general case is that those who disagree with us believe they are fighting the good fight. They believe they are fighting to protect the children or putting an end to mass murder or fighting for the oppressed. Telling them they are not (no matter how certain we are) will only make them angry. Instead, we should try to find out what it is they are fighting for. It is probably one of the twelve things in the list above. From there, we can try to show them a better way to achieve what they are trying to achieve. And the good thing is that it probably something that we also want to achieve.