Python Tutorials | (back to the list of tutorials) |
The example below shows how to re-write the line branching agents shown before (without collision detection) to have them on a surface.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(120) IG.open("surface13.3dm") srf = IG.surface(0) #first surface in the server srf.del() for i in range(120) : LineAgentOnSurface(IVec(IRand.get(),0,0),\ #random only in x IVec(0,0.01,0),\ #direction is y. length is less than 1.0 srf).clr(0) class LineAgentOnSurface(IAgent) : def __init__(self, pt, dir, s) : self.pos = pt self.dir = dir self.surf = s def update(self) : if self.pos.x() < 0 or self.pos.x() > 1.0 : #out of u-v boundary self.del() return pos2 = self.pos.dup().add(self.dir) srfPt = self.surf.pt(self.pos.x(), self.pos.y()) srfPt2 = self.surf.pt(pos2.x(), pos2.y()) ICurve(srfPt, srfPt2).clr(self.clr()) for i in range(2) : if IRand.pct(50) : dir2 = self.dir.dup() angle = IRand.get(-PI/20, PI/20) dir2.rot(IVec(0, 0, 1), angle) r = self.clr().getRed() + IRand.getInt(-10, 10) g = self.clr().getGreen() + IRand.getInt(-10, 10) b = self.clr().getBlue() + IRand.getInt(-10, 10) LineAgentOnSurface(pos2, dir2, self.surf).clr(r,g,b) self.del()
The below is the input surface to put the agents on. The file of the surface used in this example is this file.
This is the result of the agents running on the input surface.
at the line of
if(pos.x < 0 || pos.x > 1.0){
it checks if the x position of the agent is inside the boundary of
u-v parameter of the surface ( 0.0 - 1.0 ) and if it's outside, it deletes itself.
Then at these two lines,
IVec srfPt = surf.pt(pos.x, pos.y);
IVec srfPt2 = surf.pt(pos2.x, pos2.y);
The position of the agent is interpreted as u-v parameter of the surface and
mapped onto the 3 dimensional location (IVec) on the surface.
Another thing to be noted is that in the setup() method at the constructor
of LineAgentOnSurface class,
   
new LineAgentOnSurface(new IVec(IRandom.get(),0,0),
                   
               
             
new IVec(0,0.01,0),
srf)
the first argument is a vector variable to specify the starting
location of the agent and it's set to be bottom of v direction on the surface.
The second argument is another vector variable to specify the direction and the
length of the line and the length is set to be smaller than 1.0
because the full range of the v parameter on the surface is 1.0.
And at the third argument, it passes the base surface into the agent.
The input surface used in the code is this file.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(300) IG.open("surface1.3dm") srf = IG.surface(0) #first surface in the server for i in range(20) : MyAgent(IVec(IRand.get(),0,0),0.5,srf).clr(0) IG.transparent() #transparent graphic mode class MyAgent(IAgent) : def __init__(self, pt, sz, s) : self.pos = pt self.size = sz self.surf = s def update(self) : surfPt = self.surf.pt(self.pos.x(), self.pos.y()) IBox(surfPt, self.size).clr(self.clr()) nextPos = self.pos.dup() move = 0.01 #less than 1.0 #random direction dir = IRand.getInt(0, 2) if dir==0 : nextPos.add(move, 0, 0) #right elif dir==1 : nextPos.add(-move, 0, 0) #left elif dir==2 : nextPos.add(0, move, 0) #up # slightly chaning the color r = self.clr().getRed() + IRand.getInt(-10, 10) g = self.clr().getGreen() + IRand.getInt(-10, 10) b = self.clr().getBlue() + IRand.getInt(-10, 10) MyAgent(nextPos, self.size, self.surf).clr(r,g,b) self.del()
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(300) IG.open("surface1.3dm") surf = IG.surface(0) for i in range(20) : MyAgent(IVec(IRand.get(),0,0),0.01,surf).clr(0) IG.transparent() #transparent graphic mode class MyAgent(IAgent) : def __init__(self, pt, sz, s) : self.pos = pt self.size = sz self.surf = s def update(self) : utan = self.surf.utan(self.pos.x(), self.pos.y()) vtan = self.surf.vtan(self.pos.x(), self.pos.y()) nml = self.surf.nml(self.pos.x(), self.pos.y()) surfPt = self.surf.pt(self.pos.x(), self.pos.y()) utan.mul(self.size) #original length of u tangent vector is almost full width of surface in u. vtan.mul(self.size) #original length of v tangent vector is almost full height of surface in v. nml.mul(self.size*self.size) #original length of normal vector is multiplication of u tangent and v tangent IBox(surfPt,utan,vtan,nml).clr(self.clr()) nextPos = self.pos.dup() #random direction dir = IRand.getInt(0, 2) if dir==0 : nextPos.add(self.size, 0, 0) #right elif dir==1 : nextPos.add(-self.size, 0, 0) #left elif dir==2 : nextPos.add(0, self.size, 0) #up # slightly chaning the color r = self.clr().getRed() + IRand.getInt(-10, 10) g = self.clr().getGreen() + IRand.getInt(-10, 10) b = self.clr().getBlue() + IRand.getInt(-10, 10) MyAgent(nextPos, self.size, self.surf).clr(r, g, b) self.del()
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(200) IG.open("surface13.3dm") surf = IG.surface(0) surf.del() LineAgent(IVec(0,0,0), IVec(1,0,0),surf) class LineAgent(IAgent) : length = 0.01 #length in u-v space, less than 1.0 clearance = 0.0099 #less than length def __init__(self, pt, dir, s) : self.pt1 = pt self.pt2 = pt.dup().add(dir.dup().len(LineAgent.length)) self.surf = s self.isColliding = False def interact(self, agents) : if self.time() == 0 : #only in the first time for agent in agents : if isinstance(agent, LineAgent) : if agent is not self : # checking clearance of end point if agent.pt2.dist(self.pt2) < LineAgent.clearance : self.isColliding=True def update(self) : # is inside surface? if self.pt2.x() < 0.0 or self.pt2.x() > 1.0 or \ self.pt2.y() < 0.0 or self.pt2.y() > 1.0 : self.isColliding = True if self.isColliding : self.del() elif self.time() == 0 : #if not colliding surfPt1 = self.surf.pt(self.pt1.x(), self.pt1.y()) surfPt2 = self.surf.pt(self.pt2.x(), self.pt2.y()) ICurve(surfPt1,surfPt2).clr(0) dir = self.pt2.dif(self.pt1) if IRand.pct(40) : #bend LineAgent(self.pt2,dir.dup().rot(PI/3),self.surf) if IRand.pct(40) : #bend the other way LineAgent(self.pt2,dir.dup().rot(-PI/3),self.surf) if IRand.pct(80) : #straight LineAgent(self.pt2,dir.dup(),self.surf)
The code below creates a fin surface instead of line. It gets offset points on the surface in a specified depth to get two more points out of the two end points of the line. Additionally, color differentiation algorithm in gray scale is also inserted.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(200) IG.open("surface13.3dm") surf = IG.surface(0) surf.del() LineAgent(IVec(0,0,0), IVec(1,0,0),surf).clr(0.2) class LineAgent(IAgent) : length = 0.01 #length in u-v space, less than 1.0 clearance = 0.0099 #less than length def __init__(self, pt, dir, s) : self.pt1 = pt self.pt2 = pt.dup().add(dir.dup().len(LineAgent.length)) self.surf = s self.isColliding = False def interact(self, agents) : if self.time() == 0 : #only in the first time for agent in agents : if isinstance(agent, LineAgent) : if agent is not self : # checking clearance of end point if agent.pt2.dist(self.pt2) < LineAgent.clearance : self.isColliding=True def update(self) : # is inside surface? if self.pt2.x() < 0.0 or self.pt2.x() > 1.0 or \ self.pt2.y() < 0.0 or self.pt2.y() > 1.0 : self.isColliding = True if self.isColliding : self.del() elif self.time() == 0 : #if not colliding surfPt1 = self.surf.pt(self.pt1.x(), self.pt1.y()) surfPt2 = self.surf.pt(self.pt2.x(), self.pt2.y()) offsetDepth = 1 surfPt1d = self.surf.pt(self.pt1.x(), self.pt1.y(), offsetDepth) surfPt2d = self.surf.pt(self.pt2.x(), self.pt2.y(), offsetDepth) ISurface(surfPt1,surfPt2,surfPt2d,surfPt1d).clr(self.clr()) # slightly chaning the gray color gray = (self.clr().getRed()+self.clr().getGreen()+self.clr().getBlue())/3 gray += IRand.getInt(-10, 10) dir = self.pt2.dif(self.pt1) if IRand.pct(40) : #bend LineAgent(self.pt2,dir.dup().rot(PI/3),self.surf).clr(gray) if IRand.pct(40) : #bend the other way LineAgent(self.pt2,dir.dup().rot(-PI/3),self.surf).clr(gray) if IRand.pct(80) : #straight LineAgent(self.pt2,dir.dup(),self.surf).clr(gray)
The code below combines the code above and the geometric technique to put continous curvature keeping the same tangency shown in this section. The surface is created taking 3 by 2 control points and 3 of them are midpoint of the current agent, end point of the current agent and midpoint of the next agent. Other 3 are offset points of those 3 points on the surface. Additionally, the offset depth of the second control point of the first 3 points is controlled by the agent too. It alternates the value making it smaller or larger than the offset depth of the end points.
add_library('igeo') def setup() : size(480, 360, IG.GL) IG.duration(200) IG.open("surface13.3dm") surf = IG.surface(0) surf.del() LineAgent(IVec(0,0,0), IVec(1,0,0),surf,0.5,0.1).clr(0.5) class LineAgent(IAgent) : length = 0.01 #length in u-v space, less than 1.0 clearance = 0.0099 #less than length def __init__(self, pt, dir, s, depth1, depth2) : self.pt1 = pt self.pt2 = pt.dup().add(dir.dup().len(LineAgent.length)) self.surf = s self.isColliding = False self.offsetDepth1 = depth1 self.offsetDepth2 = depth2 def interact(self, agents) : if self.time() == 0 : #only in the first time for agent in agents : if isinstance(agent, LineAgent) : if agent is not self : # checking clearance of end point if agent.pt2.dist(self.pt2) < LineAgent.clearance : self.isColliding=True def update(self) : # is inside surface? if self.pt2.x() < 0.0 or self.pt2.x() > 1.0 or \ self.pt2.y() < 0.0 or self.pt2.y() > 1.0 : self.isColliding = True if self.isColliding : self.del() elif self.time() == 0 : #if not colliding #midpoint of the current agent mid = self.pt1.mid(self.pt2) surfPt1 = self.surf.pt(mid.x(), mid.y()) surfPt1d = self.surf.pt(mid.x(), mid.y(), self.offsetDepth1) #end point of the current agent surfPt2 = self.surf.pt(self.pt2.x(), self.pt2.y()) surfPt2d = self.surf.pt(self.pt2.x(), self.pt2.y(), self.offsetDepth2) nextDepth = 0 #alternating offsetDepth2 if self.offsetDepth2 > self.offsetDepth1 : nextDepth = self.offsetDepth1-(self.offsetDepth2-self.offsetDepth1) else : nextDepth = self.offsetDepth1+(self.offsetDepth1-self.offsetDepth2) # slightly chaning the color r = self.clr().getRed() + IRand.getInt(-10, 10) g = self.clr().getGreen() + IRand.getInt(-10, 10) b = self.clr().getBlue() + IRand.getInt(-10, 10) dir = self.pt2.dif(self.pt1) if IRand.pct(40) : #bend nextDir = dir.dup().rot(PI/3) #midpoint of the next agent mid2 = self.pt2.cp(nextDir.dup().len(LineAgent.length/2)) surfPt3 = self.surf.pt(mid2.x(),mid2.y()) surfPt3d = self.surf.pt(mid2.x(),mid2.y(),self.offsetDepth1) #3 by 2 control points cpts = [ [surfPt1, surfPt1d],\ [surfPt2, surfPt2d],\ [surfPt3, surfPt3d] ] #u degree 2, v degree 1 surface ISurface(cpts, 2, 1).clr(self.clr()) LineAgent(self.pt2,nextDir,self.surf,self.offsetDepth1,nextDepth).clr(r,g,b) if IRand.pct(40) : #bend the other way nextDir = dir.dup().rot(-PI/3) #midpoint of the next agent mid2 = self.pt2.cp(nextDir.dup().len(LineAgent.length/2)) surfPt3 = self.surf.pt(mid2.x(),mid2.y()) surfPt3d = self.surf.pt(mid2.x(),mid2.y(),self.offsetDepth1) #3 by 2 control points cpts = [ [surfPt1, surfPt1d],\ [surfPt2, surfPt2d],\ [surfPt3, surfPt3d] ] #u degree 2, v degree 1 surface ISurface(cpts, 2, 1).clr(self.clr()) LineAgent(self.pt2,nextDir,self.surf,self.offsetDepth1,nextDepth).clr(r,g,b) if IRand.pct(80) : #straight nextDir = dir #midpoint of the next agent mid2 = self.pt2.cp(nextDir.dup().len(LineAgent.length/2)) surfPt3 = self.surf.pt(mid2.x(),mid2.y()) surfPt3d = self.surf.pt(mid2.x(),mid2.y(),self.offsetDepth1) cpts = [ [surfPt1, surfPt1d],\ [surfPt2, surfPt2d],\ [surfPt3, surfPt3d] ] #u degree 2, v degree 1 surface ISurface(cpts, 2, 1).clr(self.clr()) LineAgent(self.pt2,nextDir,self.surf,self.offsetDepth1,nextDepth).clr(r,g,b)
add_library('igeo') def setup() : size(480, 360, IG.GL) IConfig.syncDrawAndDynamics=True IG.duration(60) IG.open("surface13.3dm") surf = IG.surface(0) # put automaton as panel unum = 50 vnum = 50 uinc=1.0/unum vinc=1.0/vnum automata = [] for i in range(unum) : automata.append([]) for j in range(vnum) : automata[i].append(MyAutomaton(IVec(i*uinc, j*vinc, 0),\ uinc, vinc, -1, surf)) #connecting adjacent automata for i in range(unum) : for j in range(vnum) : if i > 0 : automata[i][j].left = automata[i-1][j] if i < unum-1 : automata[i][j].right = automata[i+1][j] if j > 0 : automata[i][j].down = automata[i][j-1] if j < vnum-1 : automata[i][j].up = automata[i][j+1] if i==0 : automata[i][j].state = 1 # activated surf.del() IG.fill() class MyAutomaton(IAgent) : def __init__(self, pt, w, h, d, s) : self.pos = pt self.width = w self.height = h self.depth = d self.surf = s self.state = 0 # current state self.lst = 0 # left state self.rst = 0 # right state self.dst = 0 # down state self.ust = 0 # up state self.box = None self.left = None # left automaton self.right = None # right automaton self.up = None # up automaton self.down = None # down automaton def interact(self, agents) : # reset states self.lst=self.rst=self.dst=self.ust=0 if self.left is not None : self.lst = self.left.state if self.right is not None : self.rst = self.right.state if self.down is not None : self.dst = self.down.state if self.up is not None : self.ust = self.up.state def update(self) : if self.state==0 and self.box is not None : self.box.del() self.box = None elif self.state==1 and self.box is None : boxPts = [[[ self.surf.pt(self.pos.x(), self.pos.y()),\ self.surf.pt(self.pos.x(), self.pos.y(), self.depth) ],\ [ self.surf.pt(self.pos.x(), self.pos.y()+self.height),\ self.surf.pt(self.pos.x(), self.pos.y()+self.height, self.depth) ]],\ [[ self.surf.pt(self.pos.x()+self.width, self.pos.y()),\ self.surf.pt(self.pos.x()+self.width, self.pos.y(), self.depth) ],\ [ self.surf.pt(self.pos.x()+self.width, self.pos.y()+self.height),\ self.surf.pt(self.pos.x()+self.width, self.pos.y()+self.height, self.depth)]]] self.box = IBox(boxPts) # automaton update rule table if self.lst==0 and self.rst==0 and self.dst==0 and self.ust==0 : self.state=0 elif self.lst==1 and self.rst==0 and self.dst==0 and self.ust==0 : self.state=1 elif self.lst==0 and self.rst==1 and self.dst==0 and self.ust==0 : self.state=1 elif self.lst==1 and self.rst==1 and self.dst==0 and self.ust==0 : self.state=1 elif self.lst==0 and self.rst==0 and self.dst==1 and self.ust==0 : self.state=1 elif self.lst==1 and self.rst==0 and self.dst==1 and self.ust==0 : self.state=0 elif self.lst==0 and self.rst==1 and self.dst==1 and self.ust==0 : self.state=0 elif self.lst==1 and self.rst==1 and self.dst==1 and self.ust==0 : self.state=0 elif self.lst==0 and self.rst==0 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==1 and self.rst==0 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==0 and self.rst==1 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==1 and self.rst==1 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==0 and self.rst==0 and self.dst==1 and self.ust==1 : self.state=0 elif self.lst==1 and self.rst==0 and self.dst==1 and self.ust==1 : self.state=1 elif self.lst==0 and self.rst==1 and self.dst==1 and self.ust==1 : self.state=1 elif self.lst==1 and self.rst==1 and self.dst==1 and self.ust==1 : self.state=0
The code above had interact method with the longer version of interact(ArrayList< IDynamics >). This is because the state variables of lst, rst, dst, ust are updated only once at the whole system update process. If you use interact(IDynamics), they are updated unnecessarily as many as the number of total agents in the system.
The code below shows an example to put diferent type of geometries on an automaton from a box. When the state of the automaton is 0, it puts simple rectangular panel, but when the state is 1, it puts a surface with an aperture whose size and depth corresponds the time length of the state being 1, by the integer variable count.
add_library('igeo') def setup() : size(480, 360, IG.GL) IConfig.syncDrawAndDynamics=True IG.duration(60) IG.open("surface13.3dm") surf = IG.surface(0) # put automaton as panel unum = 50 vnum = 50 uinc=1.0/unum vinc=1.0/vnum automata = [] for i in range(unum) : automata.append([]) for j in range(vnum) : automata[i].append(MyAutomaton(IVec(i*uinc, j*vinc, 0),\ uinc, vinc, 1, surf)) #connecting adjacent automata for i in range(unum) : for j in range(vnum) : if i > 0 : automata[i][j].left = automata[i-1][j] if i < unum-1 : automata[i][j].right = automata[i+1][j] if j > 0 : automata[i][j].down = automata[i][j-1] if j < vnum-1 : automata[i][j].up = automata[i][j+1] if i==0 : automata[i][j].state = 1 # activated surf.del() IG.fill() class MyAutomaton(IAgent) : def __init__(self, pt, w, h, d, s) : self.pos = pt self.width = w self.height = h self.depth = d self.surf = s self.state = 0 # current state self.lst = 0 # left state self.rst = 0 # right state self.dst = 0 # down state self.ust = 0 # up state self.panelSurf = None self.left = None # left automaton self.right = None # right automaton self.up = None # up automaton self.down = None # down automaton self.count = 0 def interact(self, agents) : # reset states self.lst=self.rst=self.dst=self.ust=0 if self.left is not None : self.lst = self.left.state if self.right is not None : self.rst = self.right.state if self.down is not None : self.dst = self.down.state if self.up is not None : self.ust = self.up.state def update(self) : prevState = self.state # automaton update rule table if self.lst==0 and self.rst==0 and self.dst==0 and self.ust==0 : self.state=0 elif self.lst==1 and self.rst==0 and self.dst==0 and self.ust==0 : self.state=1 elif self.lst==0 and self.rst==1 and self.dst==0 and self.ust==0 : self.state=1 elif self.lst==1 and self.rst==1 and self.dst==0 and self.ust==0 : self.state=1 elif self.lst==0 and self.rst==0 and self.dst==1 and self.ust==0 : self.state=1 elif self.lst==1 and self.rst==0 and self.dst==1 and self.ust==0 : self.state=0 elif self.lst==0 and self.rst==1 and self.dst==1 and self.ust==0 : self.state=0 elif self.lst==1 and self.rst==1 and self.dst==1 and self.ust==0 : self.state=0 elif self.lst==0 and self.rst==0 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==1 and self.rst==0 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==0 and self.rst==1 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==1 and self.rst==1 and self.dst==0 and self.ust==1 : self.state=0 elif self.lst==0 and self.rst==0 and self.dst==1 and self.ust==1 : self.state=0 elif self.lst==1 and self.rst==0 and self.dst==1 and self.ust==1 : self.state=1 elif self.lst==0 and self.rst==1 and self.dst==1 and self.ust==1 : self.state=1 elif self.lst==1 and self.rst==1 and self.dst==1 and self.ust==1 : self.state=0 if self.state != prevState : # state change if self.panelSurf is not None : self.panelSurf.del() if self.state==0 : # just rectangular panel panelPts = [[ self.surf.pt(self.pos.x(), self.pos.y()), self.surf.pt(self.pos.x(), self.pos.y()+self.height) ], [ self.surf.pt(self.pos.x()+self.width, self.pos.y()), self.surf.pt(self.pos.x()+self.width, self.pos.y()+self.height) ]] self.panelSurf = ISurface(panelPts).clr(self.count*0.02) elif self.state==1 : #3 by 4 control points panelPts = [] panelPts.append([]) panelPts.append([]) panelPts.append([]) #rectangular border panelPts[0].append(self.surf.pt(self.pos.x(), self.pos.y())) panelPts[0].append(self.surf.pt(self.pos.x()+self.width, self.pos.y())) panelPts[0].append(self.surf.pt(self.pos.x()+self.width, self.pos.y()+self.height)) panelPts[0].append(self.surf.pt(self.pos.x(), self.pos.y()+self.height)) #rotated central opening angle = self.count*PI*0.0025 factor = self.count*0.015 center = self.surf.pt(self.pos.x()+self.width/2, self.pos.y()+self.height/2) nml = self.surf.nml(self.pos.x()+self.width/2, self.pos.y()+self.height/2) panelPts[1].append(panelPts[0][0].dup().scale(center,factor).rot(center,nml,angle)) panelPts[1].append(panelPts[0][1].dup().scale(center,factor).rot(center,nml,angle)) panelPts[1].append(panelPts[0][2].dup().scale(center,factor).rot(center,nml,angle)) panelPts[1].append(panelPts[0][3].dup().scale(center,factor).rot(center,nml,angle)) #rotated and pushed central opening shift = nml.dup().len(self.depth*self.count*0.02) panelPts[2].append(panelPts[1][0].dup().add(shift).rot(center,nml,angle)) panelPts[2].append(panelPts[1][1].dup().add(shift).rot(center,nml,angle)) panelPts[2].append(panelPts[1][2].dup().add(shift).rot(center,nml,angle)) panelPts[2].append(panelPts[1][3].dup().add(shift).rot(center,nml,angle)) # u degree 1, v degree 2, closed in u, open in v self.panelSurf = ISurface(panelPts,2,1,False,True).clr(self.count*0.02) if self.state > 0 : self.count += 1 # count time of state being positive