Python Tutorials | (back to the list of tutorials) |
add_library('igeo') class MyModule : def __init__(self) : self.position = IVec(0,0,0) self.size = 1.0
This code above defines a class named "MyModule", containing several data. "class" is a keyword to define a class, followed by a name of the class. Usually name of a class starts with a capital letter.
In the following tutorial, some key topics of object-oriented programming are shown. For more comprehensive description about object-oriented programming, please see the Processing's tutorial and the Java tutorial.
add_library('igeo') size(480, 360, IG.GL) module = MyModule()
The defined class is used as a type in
declaration of a variable like
However, the code above alone doesn't work by itself. Those two codes above need to be combined. Before combining, there is one thing to prepare. Here we introduce new programming mode "continuous mode" in Processing, whereas what we were writing so far was called "basic mode". See this page at processing.org for more information about programming modes in Processing.
The code above is rewritten in "continuous mode" below.
add_library('igeo') def setup() : size(480, 360, IG.GL) module = MyModule()
To write in "continuous mode", you need to put all the lines we were writing into "void setup(){" ... "}" except "import" statements.
Now we can combine the class definition with the main code and run the code in Processing without compile errors.
add_library('igeo') def setup() : size(480, 360, IG.GL) module = MyModule() class MyModule : def __init__(self) : self.position = IVec(0,0,0) self.size = 1.0
When you run this code, it doesn't generate any geometry because we haven't define any behavior in the class yet.
You can access to the fields to read a value or to assign a value to by writing "." and the name of the field after the instance. See the example below.
add_library('igeo') def setup() : size(480, 360, IG.GL) module = MyModule() module.position = IVec(0,0,0) module.size = 20.0 print("size of module is "+str(module.size)) class MyModule : # instance fields def __init__(self) : self.position = IVec(0,0,0) self.size = 1.0
add_library('igeo') def setup() : size(480, 360, IG.GL) module = MyModule(IVec(0,0,0), 20) class MyModule : # instance methods def __init__(self, pos, sz) : self.position = pos self.size = sz
You can also add multiple constructors with different input arguments. When you use one of constructors, you put a specific set of types of variables in the input arguments of the constructor.
add_library('igeo') def setup() : size(480, 360, IG.GL) module1 = MyModule(IVec(0,0,0), 20) module2 = MyModule(IVec(50,0,0)) class MyModule : # instance methods def __init__(self, pos, sz=1.0) : # default size self.position = pos self.size = sz
add_library('igeo') def setup() : size(480, 360, IG.GL) module1 = MyModule(IVec(0,0,0), 20) module1.createModule() module2 = MyModule(IVec(50,0,0)) module2.createModule() class MyModule : # instance methods def __init__(self, pos, sz=10.0) : self.position = pos self.size = sz def createModule(self) : IBox(self.position, self.size)
In this example code, name of the method is
"createModule",
preceded by a keyword "void" which specifies that
this method has no return value, and followed by
"()" to specify that this method has no input argument.
This method is actually executed inside "setup()" method at the
line of
The following code is an example of an instance method with input arguments.
add_library('igeo') def setup() : size(480, 360, IG.GL) module1 = MyModule(IVec(0,0,0), 20) module1.createModule() module2 = MyModule(IVec(50,0,0)) module2.createModuleWithArm(5, 40) class MyModule : # instance methods def __init__(self,pos, sz=10.0) : self.position = pos; self.size = sz; def createModule(self) : IBox(self.position, self.size) def createModuleWithArm(self, armSize, armLen) : IBox(self.position, self.size) armPos1 = self.position.dup().add(-armLen/2,0,0) armPos1.add(0,0,self.size) IBox(armPos1, armLen, armSize, armSize) armPos2 = armPos1.dup().add(armLen/2,-armLen/2,0) armPos2.add(0,0,armSize) IBox(armPos2, armSize, armLen, armSize)
To set up input arguments, you declare a variable inside the parenthesis "()" after the method name and the declared variables can be used inside the method as input values passed from outside of the method.
add_library('igeo') def setup() : size(480, 360, IG.GL) module1 = MyModule(IVec(0,0,0), 20) box = module1.createModule() box.clr(1.0,0,0) module2 = MyModule(IVec(50,0,0)) boxes = module2.createModuleWithArm(5, 40) for bx in boxes : bx.clr(0,0,1.0) class MyModule : # instance methods def __init__(self, pos, sz = 10) : self.position = pos self.size = sz def createModule(self) : return IBox(self.position, self.size) def createModuleWithArm(self, armSize, armLen) : boxes = [] boxes.append(IBox(self.position, self.size)) armPos1 = self.position.dup().add(-armLen/2, 0, 0) armPos1.add(0,0,self.size) boxes.append(IBox(armPos1, armLen, armSize, armSize)) armPos2 = armPos1.dup().add(armLen/2, -armLen/2, 0) armPos2.add(0,0,armSize) boxes.append(IBox(armPos2, armSize, armLen, armSize)) return boxes
To define a return value of a method, you first put the type of return value before the method name, instead of "void". Then inside the body of the method, you add a return statement with the keyword "return" and the variable containing the data you want to output at the end of the body.
add_library('igeo') def setup() : size(480, 360, IG.GL) module1 = MyModule(IVec(0,0,0), 20) box = module1.createModule() box.clr(1.0,0,0) module2 = MyModule(IVec(50,0,0)) boxes = module2.createModuleWithArm(5, 40) for bx in boxes : bx.clr(0,0,1.0) print("default size is " + str(MyModule.defaultSize)) class MyModule : # static field (not with "self.") defaultSize = 10.0 # instance methods def __init__(self, pos, sz = defaultSize) : self.position = pos self.size = sz print(MyModule.defaultSize) def createModule(self) : return IBox(self.position, self.size) def createModuleWithArm(self, armSize, armLen) : boxes = [] boxes.append(IBox(self.position, self.size)) armPos1 = self.position.dup().add(-armLen/2, 0, 0) armPos1.add(0,0,self.size) boxes.append(IBox(armPos1, armLen, armSize, armSize)) armPos2 = armPos1.dup().add(armLen/2, -armLen/2, 0) armPos2.add(0,0,armSize) boxes.append(IBox(armPos2, armSize, armLen, armSize)) return boxes
Inside the class you can access to static fields in the same way with instance fields but when you change the value, the change is reflected on all class instances which read the static field. Outside the class you can access to static fields by putting the class name and "." before the name of the field like "MyModule.defaultSize".
One detailed technical issue which beginners can ignore is the "static" keyword at the beginning of the class definition "class MyModule{". This issue happens when you write classes in a same file in Processing. In "continuous mode" in Processing, there is one thing hidden for convenience, which is the class definition of the sketch itself. Internally the whole sketch is inside a sketch class (this shows up in "Java mode") and classes written inside the same sketch are treated as inner classes inside the sketch class. To use static fields or static methods, an inner class needs to be a "static class". That's why there's the keyword "static" at the beginning of the class definition.
You can write classes outside of the sketch class if you create separate files with the file extension of ".java" in new tabs in Processing. For more information to write separate Java files in Processing, please see this page at processing.org.
add_library('igeo') def setup() : size(480, 360, IG.GL) module1 = MyModule(IVec(0,0,0), 20) box = module1.createModule() box.clr(1.0,0,0) module2 = MyModule(IVec(50,0,0)) boxes = module2.createModuleWithArm(5, 40) for bx in boxes : bx.clr(0,0,1.0) class MyModule : # static fields defaultSize = 10.0 # static methods @staticmethod def randomXShift(pos, min, max) : shifted = pos.dup() shifted.add(IRand.get(min,max),0,0) return shifted @staticmethod def randomYShift(pos, min, max) : shifted = pos.dup() shifted.add(0,IRand.get(min,max),0) return shifted # instance methods def __init__(self, pos, sz=defaultSize) : self.position = pos self.size = sz def createModule(self) : return IBox(self.position, self.size) def createModuleWithArm(self, armSize, armLen) : boxes = [] boxes.append(IBox(self.position, self.size)) armPos1 = MyModule.randomXShift(self.position, -(armLen-self.size), 0) armPos1.add(0,0,self.size) boxes.append(IBox(armPos1, armLen, armSize, armSize)) armPos2 = MyModule.randomXShift(armPos1, 0, armLen-self.size) armPos2 = MyModule.randomYShift(armPos2, -(armLen-self.size), 0) armPos2.add(0,0,armSize) boxes.append(IBox(armPos2, armSize, armLen, armSize)) return boxes
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.open("surface12.3dm") surfaces = IG.surfaces() unum=40 vnum=40 uinc=1.0/unum vinc=1.0/vnum for surf in surfaces : for i in range(unum) : for j in range(vnum) : pt = surf.pt(i*uinc, j*vinc) module = MyModule(pt, 1.5) if IRand.pct(50) : box = module.createModule() box.clr(0.7,0,0) else : armLength = IRand.get(2,10) boxes = module.createModuleWithArm(0.5,armLength) for bx in boxes : bx.clr(IRand.gray()) surf.del() class MyModule : # static fields defaultSize = 10.0 # static methods @staticmethod def randomXShift(pos, min, max) : shifted = pos.dup() shifted.add(IRand.get(min,max),0,0) return shifted @staticmethod def randomYShift(pos, min, max) : shifted = pos.dup() shifted.add(0,IRand.get(min,max),0) return shifted # instance methods def __init__(self, pos, sz=defaultSize) : self.position = pos self.size = sz def createModule(self) : return IBox(self.position, self.size) def createModuleWithArm(self, armSize, armLen) : boxes = [] boxes.append(IBox(self.position, self.size)) armPos1 = MyModule.randomXShift(self.position, -(armLen-self.size), 0) armPos1.add(0,0,self.size) boxes.append(IBox(armPos1, armLen, armSize, armSize)) armPos2 = MyModule.randomXShift(armPos1, 0, armLen-self.size) armPos2 = MyModule.randomYShift(armPos2, -(armLen-self.size), 0) armPos2.add(0,0,armSize) boxes.append(IBox(armPos2, armSize, armLen, armSize)) return boxes
add_library('igeo') size( 480, 360, IG.GL ) IG.open("surface1.3dm") #input geometry from 3dm file surfs = IG.surfaces() for surf in surfs : unum = 8 vnum = 8 uinc = 1.0/unum vinc = 1.0/vnum for i in range(unum) : for j in range(vnum) : pt11 = surf.pt( i*uinc, j*vinc ) pt21 = surf.pt( (i+1)*uinc, j*vinc ) pt12 = surf.pt( i*uinc, (j+1)*vinc ) pt22 = surf.pt( (i+1)*uinc, (j+1)*vinc ) ISurface(pt11,pt21,pt22).clr(i*uinc,0,j*vinc) ISurface(pt22,pt12,pt11).clr(1-i*uinc,1-j*vinc,1) depth = -2 spt11 = surf.pt(i*uinc, j*vinc, depth) spt21 = surf.pt((i+1)*uinc, j*vinc, depth) spt12 = surf.pt(i*uinc, (j+1)*vinc, depth) spt22 = surf.pt((i+1)*uinc, (j+1)*vinc, depth) frameSize = 0.2 IG.squarePipe([spt11, spt21, spt22, spt12], 1, True, frameSize) radius = 0.1 ICylinder(pt11,spt11,radius) ICylinder(pt21,spt21,radius) ICylinder(pt12,spt12,radius) ICylinder(pt22,spt22,radius) surf.del()
The code above is re-written with a class "MyPanel" below.
Note that the code below creates frames overlapping
with adjacent frames due to the simplicity of the tutorial code.
add_library('igeo') def setup() : size( 480, 360, IG.GL ) IG.open("surface1.3dm") #input geometry from 3dm file surfs = IG.surfaces() for surf in surfs : unum = 8 vnum = 8 uinc = 1.0/unum vinc = 1.0/vnum for i in range(unum) : for j in range(vnum) : depth = -2 panel = \ MyPanel(surf.pt(i*uinc, j*vinc), \ surf.pt((i+1)*uinc, j*vinc), \ surf.pt(i*uinc, (j+1)*vinc), \ surf.pt((i+1)*uinc, (j+1)*vinc), \ surf.pt(i*uinc, j*vinc, depth), \ surf.pt((i+1)*uinc, j*vinc, depth), \ surf.pt(i*uinc, (j+1)*vinc, depth), \ surf.pt((i+1)*uinc,(j+1)*vinc,depth)) panel.createPanel(i*uinc, 0, j*vinc, \ 1-i*uinc, 1-j*vinc, 1) surf.del() class MyPanel : # static field frameSize = 0.2 connectionRadius = 0.1 def __init__(self,p11,p21,p12,p22,s11,s21,s12,s22) : self.pt11 = p11 self.pt21 = p21 self.pt12 = p12 self.pt22 = p22 self.spt11 = s11 self.spt21 = s21 self.spt12 = s12 self.spt22 = s22 def createPanel(self,red1,green1,blue1,red2,green2,blue2) : ISurface(self.pt11,self.pt21,self.pt22).clr(red1,green1,blue1) ISurface(self.pt22,self.pt12,self.pt11).clr(red2,green2,blue2) IG.squarePipe([ self.spt11,self.spt21,self.spt22,self.spt12 ], 1, True, MyPanel.frameSize) ICylinder(self.pt11,self.spt11,MyPanel.connectionRadius) ICylinder(self.pt21,self.spt21,MyPanel.connectionRadius) ICylinder(self.pt12,self.spt12,MyPanel.connectionRadius) ICylinder(self.pt22,self.spt22,MyPanel.connectionRadius)