Showing posts with label announcements. Show all posts
Showing posts with label announcements. Show all posts

Wednesday, July 8, 2009

Assignment 03B - Final

As a continuation of the final assignment, you should now try to script your idea. Most of the ideas posted deal with collisions, and reactions according to those collisions. As this was not part of our original crowd system example given in class, here is an example script which deals with collision detection:

cs_help.py

Here is also an example .mb file where the system above is already placed in the scene:

cs_help.mb

The rule which deals with the collision can be found in the hit function, right in the beginning of the script. this system not perfect, has tons of caveats, but you can get the idea.

In this script you can also see how to create an enclosure for your agents, something such as a box which contains the whole system inside, so that your agents are "trapped".

If you have too much difficulty, post your doubt on the blog. I'll check it daily to answer it!

The deadline for this assignment is this Sunday (12.07).

w08 - script

Here is the finalized script for the crowd system done in the past two classes. It also includes the global forces option in it. For details, read the comments.

crowdSystem.py

Tuesday, June 23, 2009

w07 - recap + script

Yesterday we started looking at our third and final topic: self-organizing systems and dynamics/expressions in Maya. We started some simple crowd system class, and saw some principles of dynamics in Maya (rigid bodies) and expressions (to make objects move).

Here is the script until the part we accomplish together yesterday:

crowdSystem_w07.py

In the next class we should finish it by applying fields forces (for attraction and repulsion between elements) and create the desired number of leaders and followers, connecting their dynamic forces to make them interact with each other.

Wednesday, June 17, 2009

w06 - recap and script

Last class we saw how to parse a Delaunay 2D text file generated by qhull and convert it to objects in Maya's stage. Here is the link from the finished script, which also contains the Delaunay3D and ConvexHull classes:

qhull_w06.py

Wednesday, June 10, 2009

Assignment 02A

In this assignment you should take a look at the script from last class and try to write it further. The idea is to create a new class definition on it, either to produce Delaunay triangulations or convex hulls. Here are some tips:
  • The qhull commands you need to run are already in the runQhull method of the Qhull class. You don't need to worry about them. They will return you a text file containing all the information you need to assemble your objects
  • The structure of the new class should be very similar from the Voronoi class structure. You should write a load method to read the file you generated from qhull and parse it, saving vertex and region information on the regions and vertices attributes of the class.
  • Then you should then concentrate on the draw function. Keep in mind some of the workarounds done in the Voronoi class, like re-ordering the vertices, are not necessary in the Delaunay or convex hull. Just read all the vertices, then draw the regions using the vertices indexes.
  • You can find some help on the web. Some sites:

You should post whatever results you get on the blog by this Sunday, 14.06. You should also post any questions you might encounter.

Week 05 - script

Here is a link to download the working script from last class, which already contains the new voronoiShatter method. It is entirely commented and you should be able to understand it from what was explained in class.

Week 05 - recap

On this class we took a look on all the functions contained in the voronoi script we are working on: point generation function, qhull commands, voronoi creation. We also wrote our own voronoi shatter method to the voronoi class, going through concepts of vector math along the way.

Monday, June 8, 2009

Week 05 - file to download

In our class tomorrow we will take a close look at a ready-made script containing advanced methods for the generation of Voronoi diagrams using Qhull.

After that, we will add one more method to the class, which uses pure mathematics to perform voronoi calculations, so we can learn a bit about vector math and some other scripting techniques.

For that, you should download this script and save it in your script folder.

Wednesday, May 20, 2009

Week 03 – code (part 2) - classes

Classes are the workhorses of object-oriented programming languages (OOP) like Python. As we saw, everything in Python is an object. Strings, lists, functions and even modules are objects and, as objects, they all have attributes and functions (which in the case of objects are called methods) associated with them. Let’s take the example of a list:

myList = [1 ,3.4, 5, 66, 298] 
myList.append(243)
myList.sort( )

In the above examples, append() and sort() are methods of the object list, and can be called by using the construction objectName.method(arguments).

Lists, integers, strings, etc, are built-in objects in Python. But you can create your own objects by using classes. Classes are object definitions created by the user, and work as a place to collect functions and attributes related to the object you want to create.

When you put functions and variables into a class, they have a way to “talk” to each other and keep information together. Here is an better explanation and example taken from a tutorial on the web:

For example, imagine you have a golf club. It has information about it (i.e. variables) like the length of the shaft, the material of the grip, and the material of the head. It also has functions associated with it, like the function of swinging your golf club, or the function of breaking it in pure frustration. For those functions, you need to know the variables of the shaft length, head material, etc. (…)

What happens if each time you use your golf club, the shaft gets weaker, the grip on the handle wears away a little, you get that little more frustrated, and a new scratch is formed on the head of the club? A function cannot [handle] that. A function only makes one output, not four or five, or five hundred. What is needed is a way to group functions and variables that are closely related into one place so that they can interact with each other.

Chances are that you also have more than one golf club. Without classes, you need to write a whole heap of code for each different golf club. This is a pain, seeing that all clubs share common features, it is just that some have changed properties - like what the shaft is made of, and it's weight. The ideal situation would be to have a design of your basic golf club. Each time you create a new club, simply specify its attributes - the length of its shaft, its weight, etc. (…)

These problems that a thing called object-oriented-programming solves. It puts functions and variables together in a way that they can see each other and work together, be replicated, and altered as needed, and not when unneeded. And we use a thing called a 'class' to do this.

To use classes, the same way when you create functions, you have first to define them (class definition), and then call them by creating instances of this class. Like what we did in class:

#### CLASSES
#defining a class
class Student:
#first define the class constructor
#which is the function that is executed
#every time you create an instance of this class
def __init__(self, name, attendance):
#in this function, we pass three arguments:
#self should ALWAYS be passed as the first argument
#in every function inside of a class definition
#the following arguments are defined by you
#depending on what you want to pass for the instance
#when you are creating it
self.name = name
self.attendance = attendance
self.marks = []
#in the lines above we declare a series of variables
#inhrent to the class, which will later work as
#attributes of each instance of the class you create
#as use of the "self.nameOfVariable" syntax indicates
numberOfAssignments = 0
#above we created a local variable which will exist only
#inside the class, and cannot be accessed as attributes
#of the instances you create (note we don't use "self"
print "New student was created"
print "this student did", numberOfAssignments, " assignments"
#the lines above are simple print statements which will
#show when you create an instance of the class,
#because they are located on the __init__ function

#now we leave the __init__ function and we can than create
#as many internal functions as we want
#(which are called "methods" of this class
#note that all of them MUST take as a first argument
#the variable self, so that they always refer to the
#instance itself

def addMark(self, mark):
#this is a simple function which
#appends whatever value we pass as an argument
#to the internal list "marks", which was
#created in the __init__ function
self.marks.append(mark)

def addAttendance(self):
#the same with this method, which doesn't take
#any additional arguments, but still makes modifications
#(by adding one unit) to the attendance attribute of the class
self.attendance += 1

Now that we have our class define, we can create instances of it and call its attributes and functions easily:

## creating an instance of the class		
myStudent = Student("Liu", 10)
#as you see, we create instances of a class
#by simply assigning it to a variable and
#passing the necessary intial arguments
#required by the __init__ function
#Note that we don't ever need to pass "self" as an argument
#on the instance creation, as Python automatically
#does it internally

#now we can access the attribute of these functions, also
#defined on the __init__ function, by using a simple construction:
print myStudent.name
print myStudent.attendance

#Here lies one of the powers of classes: I can create
#as many instances of that class as I need,
#and all of them inherit the methods and attributes of the class
myStudent2 = Student("Grisha", 20)
print myStudent2.name
print myStudent2.marks

#the same way as I accessed attributes aobve, I can
#call the class's methods:
myStudent2.addMark(10)
print myStudent2.marks

This was a short and small example of classes. You can do many powerful things with it as we will see next class. So keep in mind that the class definition works as a blueprint, from which as many instances as you want can be generated.

Tuesday, May 19, 2009

Week 03 – code (part 1)

We started by creating a function which would create an object on stage by recursion:

import maya.cmds as cmds

def fakeRecursion( iterations ):
for i in range(iterations): #range(0, iterations, 1)
#create the objects
cmds.circle( n="myCircle_%d" % i )
cmds.nurbsSquare( n="mySquare_%d" % i )
#rotate objs
cmds.rotate(i*10, i*10, i*10, "myCircle_%d"%i)
cmds.rotate(-i*10,- i*10,- i*10, "mySquare_%d"%i)
#move objs
cmds.move(0,0,i/10, "myCircle_%d"%i)
cmds.move(0,0,i/10, "mySquare_%d"%i)
#collect points for polyFacet
pos1 = cmds.pointPosition("topmySquare_%d.cv[0]" %i)
pos2 = cmds.pointPosition("myCircle_%d.cv[0]" % i)
pos3 = cmds.pointPosition("rightmySquare_%d.cv[0]" %i)
pos4 = cmds.pointPosition("myCircle_%d.cv[1]" % i)
pos5 = cmds.pointPosition("bottommySquare_%d.cv[0]" %i)
pos6 = cmds.pointPosition("myCircle_%d.cv[2]" % i)
pos7 = cmds.pointPosition("leftmySquare_%d.cv[0]" %i)
pos8 = cmds.pointPosition("myCircle_%d.cv[3]" % i)

cmds.polyCreateFacet(n="myFacet_%d" % i,
p=[pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8]
)
cmds.group("myCircle_%d" % i,
"mySquare_%d" % i,
"myFacet_%d" % i )

fakeRecursion(30)


As we saw, the code above works, but has a series of coding problem which makes it non-flexible and difficult to read and modify. The first modification we did was to convert the recursion, which in this case was made by using a for loop, to what we could call “real” recursion, when the function calls itself:



def realRecursion( iterations ):
i = iterations
#create the objects
cmds.circle( n="myCircle_%d" % i )
cmds.nurbsSquare( n="mySquare_%d" % i )
#rotate objs
cmds.rotate(i*10, i*10, i*10, "myCircle_%d"%i)
cmds.rotate(-i*10,- i*10,- i*10, "mySquare_%d"%i)
#move objs
cmds.move(0,0,i/10, "myCircle_%d"%i)
cmds.move(0,0,i/10, "mySquare_%d"%i)
#collect points for polyFacet
pos1 = cmds.pointPosition("topmySquare_%d.cv[0]" %i)
pos2 = cmds.pointPosition("myCircle_%d.cv[0]" % i)
pos3 = cmds.pointPosition("rightmySquare_%d.cv[0]" %i)
pos4 = cmds.pointPosition("myCircle_%d.cv[1]" % i)
pos5 = cmds.pointPosition("bottommySquare_%d.cv[0]" %i)
pos6 = cmds.pointPosition("myCircle_%d.cv[2]" % i)
pos7 = cmds.pointPosition("leftmySquare_%d.cv[0]" %i)
pos8 = cmds.pointPosition("myCircle_%d.cv[3]" % i)

cmds.polyCreateFacet(n="myFacet_%d" % i,
p=[pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8]
)
cmds.group("myCircle_%d" % i,
"mySquare_%d" % i,
"myFacet_%d" % i )

#recursion part
if iterations == 0:
return "Done."
else:
iterations -= 1
realRecursion(iterations)

realRecursion( 30 )


As we see it keeps producing the same result as the function before. Also, in terms of speed and memory, the difference is not noticeable. But as we saw, when you start to use complex and long functions, the use of “real” recursion is regarded as much more efficient.



The second step on the optimization of the function was to remove all the hard-coded bits. This allows for greater flexibility, as we don’t really need to care about the names of the created objects, simply by using variables to store them and to refer to them later:



def realRecursionOpt1( iterations ):
i = iterations
#create the objects
myCircle = cmds.circle( )
mySquare = cmds.nurbsSquare( )
#rotate objs
cmds.rotate(i*10, i*10, i*10, myCircle )
cmds.rotate(-i*10,- i*10,- i*10, mySquare)
#move objs
cmds.move(0,0,i/10, myCircle )
cmds.move(0,0,i/10, mySquare )

#collect points for polyFacet
#select mySquare
cmds.select( mySquare, r=1 )
sides = cmds.filterExpand( sm=9 )
pos1 = cmds.pointPosition( sides[0]+".cv[0]" )
pos2 = cmds.pointPosition( myCircle[0] + ".cv[0]" )
pos3 = cmds.pointPosition( sides[1]+".cv[0]" )
pos4 = cmds.pointPosition( myCircle[0] + ".cv[1]")
pos5 = cmds.pointPosition( sides[2]+".cv[0]" )
pos6 = cmds.pointPosition( myCircle[0] + ".cv[2]")
pos7 = cmds.pointPosition( sides[3]+".cv[0]" )
pos8 = cmds.pointPosition( myCircle[0] + ".cv[3]")

myFacet = cmds.polyCreateFacet(
p=[pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8]
)
cmds.group( myCircle, mySquare, myFacet )

#recursion part
if iterations == 0:
return "Done."
else:
iterations -= 1
realRecursionOpt1(iterations)

realRecursionOpt1( 30 )


As you can see, we do not have to know any names of objects if we store them in variables and use them to refer to them later.



As a last step on the optmization process, we saw how when you repeat a command too many times, it almost always means you can convert it into a loop. The same is valid for numbers or strings which are being repeated too many times: you should always recur to variables or arguments to define values which are used several times:



def realRecursionOpt2( iterations , amt=10  ):
i = iterations
#create the objects
myCircle = cmds.circle( )
mySquare = cmds.nurbsSquare( )
#rotate objs
cmds.rotate(i*amt, i*amt, i*amt, myCircle )
cmds.rotate(-i*amt,- i*amt,- i*amt, mySquare)
#move objs
cmds.move(0,0,i/amt, myCircle )
cmds.move(0,0,i/amt, mySquare )

#collect points for polyFacet
#select mySquare
cmds.select( mySquare, r=1 )
sides = cmds.filterExpand( sm=9 )

#create a list to store the positions
positions = []
for i in range( len(sides) ):
pos1 = cmds.pointPosition( sides[i] +".cv[0]" )
pos2 = cmds.pointPosition( myCircle[0] + ".cv[%d]" % i )
positions.append(pos1)
positions.append(pos2)


myFacet = cmds.polyCreateFacet(
p=positions
)
cmds.group( myCircle, mySquare, myFacet )

#recursion part
if iterations == 0:
return "Done."
else:
iterations -= 1
realRecursionOpt2(iterations, iterations )

realRecursionOpt2( 30 )


Another thing we saw on the example above is the creation and usage of optional function arguments, in this case, amt=10. In this case, as we already assign a value to amt in the function definition,  it means that if you don’t pass this argument in the function call, Maya will assume its value as 10. In case you do supply an argument, Maya will use this value instead.

Week 03 – recap

Yesterday in our third class we started by taking a look on how to optimize your code. In the example, we saw how to avoid common mistakes on scripting, such as hard-coding and repetition instead of loops. Also, we saw again how to convert a loop recursion into a “real” recursion.

In the end we had a initial and quick introduction to classes in Python, which will be our main topic next class. We will then start to look into voronoi diagrams, delaunay tessellations, and convex hulls.

Thursday, May 14, 2009

Assignment 01B

Now it is time to start scripting your idea. Don’t worry about making it perfect, just start simple and small and go step-by-step. Simplification is the key. Here some guidelines:

  • Make your diagrams and logic (Assignment 01A) very clear. Divide the script into smaller tasks to check which tasks you know and which you don’t know how to perform
  • Define initial condition for your function to work. If it is very clear, you know that after you reach the initial condition again, you can run your function one more time. That’s where the recursion is.
  • Define a clear final condition
  • Make one iteration of your function work and only then make it recursive
  • If you reach a point where you can’t proceed, either look for help on the web, or post your progress on the blog, so I can make some comments

Please, post your progress by Sunday (17.05) morning on the blog, as I need time to take a look at it and prepare for the class on Monday, to be able to help you.

Don’t forget to post and updated diagram and logic if you changed it or developed further. We need to understand what you want to achieve!

If you manage the whole script by Sunday, great. If not, please make sure you tried really hard!

Assignment 01B – Help Functions

For the completion of Assignment 01, I prepared some small help functions and procedures to help you out in the process of scripting. They are based on things I observed on most submissions. They are not intended to provide ready solutions, but to offer some hints. Therefore they might seem a bit random and out of context. Any help you need to use them, please post on the comments.

gsii_A01_helpFunctions.py

The serpentine() function

We started the script by defining the main logic behind it, according to the diagrams posted last week.

# Basic logic
# 1) create an initial square (4 sides)
# 2) loop thorugh the sides:
# 2.1) get current line (line1)
# 2.2) get next line (line2)
# 2.3) define the percentages
# 2.4) find the point on curve
# 2.5) connect these points
# 3) After finished, select all 4 new lines and run again


As we see by this logic, what we need to run the loop each time is 4 curves through which we will loop and generate a new set of 4 lines. You can notice that as soon as we reach the initial condition, we can run the whole loop again (3): this is where recursion comes in handy.



#creating initial square
initialSquare = cmds.nurbsSquare( sl1=10, sl2=10 )
isSides = cmds.filterExpand(sm=9)
print isSides


This is how we start everything. With this command we have the 4 initial curve which make our connection loop work. Most important is that, by the way Maya created a nurbsSquare, all curves are selected in the right order on which they compose the square.



#lets connect line after line in a loop
for i in range(0, len(isSides), 1):
print i
#get the name of the current line
line1 = isSides[i]
#get the name of the following line
#but first check if we are already on the last line
#because then we have to pick back the first line
if i == len(isSides)-1:
#this means it is in the last element
#so we have to get the first curve
line2 = isSides[0]
else:
#otherwise, get next curve
line2 = isSides[i+1]

print line1, line2 #to check the line pair
#define the percentages on which to connect the lines
#this could be defined before the loop, because it is fixed
#but in case we wanted to make it variable, we need to
#define them here
perc1 = 1./2
perc2 = 1./3
#get the coordinates on the lines on the percentages
poc1 = cmds.pointOnCurve( line1, pr=perc1, top=True, p=True )
poc2 = cmds.pointOnCurve( line2, pr=perc2, top=True, p=True )
#create the curve to connect both lines
cmds.curve(ep=(poc1, poc2), d=1)


This is the basic main loop we need. Having a set of four consecutive curves selected, it works by going through each one of them and connecting them with a straight line from certain points in each line (define by percentages). By the end of this loop, we have 4 new curves, which we can use to feed the next iteration of the function.



To convert this basic part into a recursive function, we just need some small adjustments. The most important is the if/else statement after the loop which checks for the final condition. This ensures us the loop will not be infinite, and that the function will call itself again in case the end condition is still not met, creating the recursive effect we need.



#turning this into a recursive function
def serpentine(iterations):
#we assume that 4 lines are selected on stage
#and that these lines are in correct order
#this is the initial condition for the loop to work
isSides = cmds.filterExpand(sm=9)
#create a group to store the new lines
#in the end all we need is to select the group
#and we'll have the condition to run the function again
group = cmds.group( n="newCurves", em=True )
#loop through the curves in isSides
for i in range(0, len(isSides),1):
#get first curve name
line1 = isSides[i]
#get second curve name
if i == len(isSides)-1:
#this means it is in the last element
#so I have to get the first
line2 = isSides[0]
else:
#otherwise, get next element
line2 = isSides[i+1]
#print to check the pair
print line1, line2
#define the percentages
perc1 = 1./2
perc2 = 1./3
#get the coordinates on the lines
poc1 = cmds.pointOnCurve( line1, pr=perc1, top=True, p=True )
poc2 = cmds.pointOnCurve( line2, pr=perc2, top=True, p=True )
#create the curve
crv = cmds.curve( ep=(poc1, poc2), d=1 )
#put the curve in the group
cmds.parent( crv, group )

## now the recursive part
#check for the final condition
if iterations == 0:
#finished stop!
return "Done."
else:
#did not finish yet...
#remove one unit form iterations
#(coutdown)
iterations –= 1
#select the group
cmds.select( group, r=True )
#call the function again
serpentine( iterations )

Now, all you need is to create an initial square on stage, select it, and run the function, passing as a parameter the number of iterations you want it to perform.

Week 02 - recap

Last Monday we translated the rules from the Serpentine Pavilion to a full working script. In it, you saw how to find points in a curve, how to turn an initial loop into a recursive function, and how to create workaround for the lack of full 2D drafting support in Maya.

Also, we reviewed your submissions for assignment 01A (all comments are on the blog, under each post).

As a continuation of the assignment, you should give the first go at scripting your idea, keeping in mind the comments made, and what you should improve. Some of you should post new diagrams showing the improved logic of your script, according to the comments.

Friday, April 24, 2009

Calendar

I created a Google Calendar for the course.

As you can see, for the next two Mondays we won't have any classes. But that will mean that when everybody is back we will have extra Scripting classes, at least in the first 1 or 2 weeks.

You can check the calendar on this blog's sidebar, or go to the calendar address to see it in its entirety.

Thursday, April 23, 2009

Assignment 01A

As discussed in last class, you are supposed to deliver a small set of diagrams (like the ones from the post below), showing an idea of a recursive algorithm using basic 2D geometric shapes (square, circle, triangle, straight line, pentagon etc).

You should define a simple rule and show in a diagram how the iteration of this rule is going to create complexity. Also, define initial state and final condition.

The results should be posted here on the blog by Saturday, 25.04, so that I can take a look at it on Sunday and choose some to further develop in class.

The goal of this first assignment is to create the basic graphical idea to be further explored and developed into a pavilion-like structure.

Recursion

Our first topic this semester is recursion. Here are some randomly collected notes about recursion:

  • Recursion is a way of thinking about and solving problems
  • one of the central ideas of computer science
  • Solving a problem using recursion means that the solution depends on solutions to smaller instances of the same problem
  • Function called in itself
  • Loops (for, while) are a way of creating recursive functions without the advent of calling the function inside itself
  • A common method of simplification is to divide a problem into sub-problems of the same type. For example:
    • How do you move a stack of 100 boxes?
    • Answer: you move one box, remember where you put it, and then solve the smaller problem: how do you move a stack of 99 boxes?
    • Eventually, you're left with the problem of how to move a single box
  • Here is another, perhaps simpler way to understand recursive processes:
    • Are we done yet?
    • If so, return the results. Without such a termination condition a recursion would go on forever.
    • If not, simplify the problem, solve the simpler problem(s), and assemble the results into a solution for the original problem.
    • Then return that solution

Source, among others: en.wikipedia.org/wiki/Recursion_(computer_science)

The concept of recursion can be easily observed in the case of the Serpentine Pavilion. You start with a simple rule of connecting the middle of one size of the square with the first third of the adjacent side:

diagram_01_rules

So instead of following a 1/2 to 1/2 rule, which would create a simple square spiraling inside itself, the algorithm follow a 1/2 to 1/3 rule, which creates a spiraling square with intersects with itself. This slight change in the rule ends up increasing the complexity of the final result and creates more support points for the structure of the building.

The 1/2 to 1/3 rule is iterated only 7 times. By the dimensions of the pavilion (17x17m), this is enough to end up with efficient beam vs. holes sizes:

diagram_01_iterations

As the final step, all lines from all squares are extended. Then the borders are "folded" down to form the box, so that the pattern/structure continues until the floor.

diagrams_final

The final result is beautiful, complex, and most important, extremely efficient structure-wise and construction-wise.

Week 1 - recap

Last Monday we had our first class, where I introduced the first part of the course. In the next days, we'll be working on the concept of recursion, creating complexity out of very simple rules.

The basis for this idea can be found in the Serpentine Pavillion 2002, by Toyo Ito in collaboration with Cecil Balmond. As I showed in class, this impressive piece of architecture creates a whole new language of complexity and holistic design which parts from a simple recursive rule.

We also started to create some fairly simple recursive functions, as well as an approach to the recursive squares as used at the Serpentine Pavillion.

As a result of this first week, you are required to deliver a small assignment, as detailed in the next blog post.

Monday, April 20, 2009

Script editors

Another list, this time with Script Editors. It is highly recommended that you use a script editor to write your scripts.