Thursday, July 25, 2019

Engaging the Lost

After watching several videos from the One for Israel Ministry (www.oneforisrael.org) I began to notice a trend. Many of these Jews who have come to accept Jesus as their messiah declare that they were surprised to learn that Jesus was a Jew. Some chose not to read the New Testament because the rabbis told them not to and they assumed that the New Testament was instructions to Christians to persecute Jews. “Jesus is not for us” is a common theme that these people were taught. So when they began to learn about Jesus being a Jew, and reading the New Testament that is written by Jews and about the Jewish scriptures they were shocked.

I don’t know many Jews, but I encounter plenty of gentiles who speak of God in ways that are inconsistent with who I know God to be. They speak of God as being an evil being and I question how anyone could read the Bible and come to that conclusion. But that’s just it. They haven’t read the Bible. They don’t know my Jesus. These people aren’t rejecting Jesus based on what they know about him but out of their ignorance. They speak as if they know. They speak as if they have read the scriptures. They may even quote from the scriptures, but they haven’t read to understand.

Just as Jews need to be shown passages like Isaiah 53 to help them see that Jesus is the prophesied Messiah, those who hate God need to be shown the scriptures in such a way that they can see that God isn’t what they think he is. We should not assume that just because they say they have read the Bible or that just because they reference scripture that they know what Christianity is about.

I think it is important for us to engage people on social media in such a way as to oppose the sin of the world. The more controversial the more important it is that we engage in these conversations because this is our opportunity to engage the lost. But it must not stop there. It isn’t enough to state that abortion is wrong, the divorce is wrong, that homosexuality is wrong, that the sins of the world are wrong. Doing that will make people angry enough to argue with us, but if we don’t turn the conversation to teaching God’s word then it is of no value. If we do use these conversations to teach God’s word then it can be of great value. These people may never read the Bible except for what we spoon feed them in these conversations. I don’t expect that I will ever win someone to Christ during one of these arguments, but it may convince someone to go read for themselves. If we can do that then they may be persuaded.

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

WALL_UP = 0
WALL_DOWN = 1
WALL_FIXED = 2

class cell():
  X = 0
  Y = 0
  N = WALL_UP
  S = WALL_UP
  E = WALL_UP
  W = WALL_UP
  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
      row.append(_cell)
    m.append(row)
   
  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)))
          self.addObject(obj)
     
      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)))
          self.addObject(obj)
 
  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)))
      self.addObject(obj)
   
  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)))
      self.addObject(obj)
     
def chooseUnvisited():
  neighbors = []
  if (current.N == WALL_UP) and (current.Y-1 >=0) and (not maze[current.Y-1][current.X].visited):
    neighbors.append(maze[current.Y-1][current.X])
  if (current.S == WALL_UP) and (current.Y+1 < mazeLength) and (not maze[current.Y+1][current.X].visited):
    neighbors.append(maze[current.Y+1][current.X])
  if (current.W == WALL_UP) and (current.X-1 >=0) and (not maze[current.Y][current.X-1].visited):
    neighbors.append(maze[current.Y][current.X-1])
  if (current.E == WALL_UP) and (current.X+1 < mazeWidth) and (not maze[current.Y][current.X+1].visited):
    neighbors.append(maze[current.Y][current.X+1])
 
  if len(neighbors) > 0:
    return random.choice(neighbors)
  else:
    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    
    else:
      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:
    theStack.append(current)  
   
drawMaze()

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)
window.updateImage()
window.setUndoRecord(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);                      
window.updateImage();
window.setUndoRecord(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:
  theScript.addObject(obj)
 
obj = createAxis(length, size)
obj.getCoords().setOrientation(90.0, 0.0, 0.0)
if not obj is None:
  theScript.addObject(obj)

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


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)));
   script.addObject(infoS);
  }
  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.