Python Tutorials | (back to the list of tutorials) |
Cell Division and Growth by Particle and RepulsionThe tutorial codes in this page explore formation made by the mechanism inspired by cell division and growth process.
In the first code below, it defines an agent class "Cell" inheriting IParticle class to let the cell agents to move around by external or internal forces. This agent implements the following three behaviors.
The code below also control the timing of growth and division in update method by growthInterval and divisionInterval and also stop them after growthDuration time frame. The division is also controlled to happen randomly in 50 percent probability.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(1.0,0,0)
class Cell(IParticle) :
growthDuration = 2000 #duration of growth and division
growthInterval = 10
divisionInterval = 100
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if IRand.pct(50) :
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
if self.time()%Cell.growthInterval==0 :
self.radius += 0.1
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
The code below only reorganize the first code above by extracting part of the code of division
growth from update method and putting them into grow method and
divide method. The grow method also gets maxRadius parameter not to
grow the size too much.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(1.0,0,0)
class Cell(IParticle) :
growthDuration = 2000 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if IRand.pct(50) :
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
Division Control by Location
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(1.0,0,0)
class Cell(IParticle) :
growthDuration = 2000 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
probability = (self.pos().y()+50)*0.8 # divide when bigger than 5
if IRand.pct(probability) :
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1));
self.pos().sub(dir) # move to the other way
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
Division Control by Size
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10)
class Cell(IParticle) :
growthDuration = 2000 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.growthSpeed = IRand.get(0.05, 0.1)
self.hsb(self.growthSpeed*10, 1, 1)
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if self.radius > 5 : # divide when bigger than 5
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
self.pos().sub(dir) # move to the other way
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += self.growthSpeed
Division Control by Attractor
Cell agent class also adds active boolean field to control division any time before update method and Nutrition agent turn cell's active field to true to activate division in its feed method. active field is set to be false in divide method to reset the activation every time.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
for i in range(8) :
Nutrition(IRand.pt(-100,-100,100,100), 20)
for i in range(20) :
Cell(IRand.pt(-100,-100,100,100), 10)
class Nutrition (IAgent) :
def __init__(self, p, rad) :
self.pos = p
self.radius = rad
self.circle = ICircle(self.pos, self.radius).clr(1.,0,0).weight(2)
def feed(self, c) :
if self.pos.dist(c.pos()) < self.radius :
c.active = True # activate cell
self.radius -= 0.05 # shrink
self.circle.radius(self.radius)
class Cell(IParticle) :
growthDuration = 1400 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
if isinstance(a, Nutrition) :
if self.time()%Cell.divisionInterval==0 and IG.time() < Cell.growthDuration :
a.feed(self)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
self.hsb(self.radius/10, 1, 0.8)
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius).clr(self)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
self.pos().sub(dir) # move to the other way
self.active = False #reset after division
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
Division Control by Neighboring Cells
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(1.0,0,0)
class Cell(IParticle) :
growthDuration = 1800 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
neighborCount = 0
neighborDist = 1
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
if a.pos().dist(self.pos()) < a.radius+self.radius+neighborDist :
neighborCount += 1 # count close neighbors
# activate when not many neighbors
if neighborCount <=3 :
self.active = True
else :
self.active = False
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
self.active = False #reset after division
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
The code below has an opposite rule of the previous one in the division control.
Instead of activating division when a cell sees small number of neighbors,
it activates division when a cell sees many neighbors (> 5).
Because it starts with one cell, cells are randomly activated for division in the
first 600 time frame, and then after that, it activates only when it finds more than
5 neighbors.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(1.0,0,0)
class Cell(IParticle) :
growthDuration = 1000 #duration of growth and division
growthInterval = 10
divisionInterval = 50
maxRadius = 100
growthSpeed = 0.2
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
neighborCount = 0
neighborDist = 2
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
if a.pos().dist(self.pos()) < a.radius+self.radius+neighborDist :
neighborCount += 1 # count close neighbors
if IG.time() >= 600 :
# activate when it has too many neighbors
if neighborCount > 5 :
self.active = True
else :
self.active = False
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if IG.time() < 600 : # random division
if IRand.pct(50) :
self.active = True
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
self.active = False #reset after division
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
Cell Junction by Tensile Link
The code below introduces a new class CellLink to implement the connection and adhesion. To simulate the adhesion by spring-like force, CellLink has an interact method to calculate a force vector from a difference vector of two positions of the linked cells and pull them if they are farther than the summation of two radii or push them if they are too close. An instance of CellLink is created at Cell class's divide method by passing the cell itself and a new child cell.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(0,0,1.0)
class Cell(IParticle) :
growthDuration = 1800 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if IRand.pct(50) : # random division
self.active = True
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
self.active = False #reset after division
CellLink(self, child)
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
class CellLink(IAgent) : # a link to connect 2 cells with spring force
def __init__(self, c1, c2) :
IAgent.__init__(self)
self.cell1 = c1
self.cell2 = c2
self.line = ICurve(c1.pos(), c2.pos()).clr(1.0,0,0)
def interact(self, agents) :
# spring force
dif = self.cell1.pos().dif(self.cell2.pos())
force = (dif.len()-(self.cell1.radius+self.cell2.radius))/(self.cell1.radius+self.cell2.radius)*200
dif.len(force)
self.cell1.pull(dif)
self.cell2.push(dif)
Just to show the effect of tensile links and the network structure of links clearer, the code below adds another repulsion behavior in interact method to push other cells when they are within a certain distance range (30) in addition to the original repulsion force not to have their body defined by radius overlapped.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
Cell(IVec(0,0,0), 10).clr(0,0,1.0)
class Cell(IParticle) :
growthDuration = 1800 #duration of growth and division
growthInterval = 10
divisionInterval = 100
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
# additional repulsion for smoother organization
if a.pos().dist(self.pos()) < a.radius+self.radius + 30 :
dif = a.pos().dif(self.pos())
dif.len(1) # constant force
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if IRand.pct(50) : # random division
self.active = True
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
self.active = False #reset after division
CellLink(self, child)
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
class CellLink(IAgent) : # a link to connect 2 cells with spring force
def __init__(self, c1, c2) :
IAgent.__init__(self)
self.cell1 = c1
self.cell2 = c2
self.line = ICurve(c1.pos(), c2.pos()).clr(1.0,0,0)
def interact(self, agents) :
# spring force
dif = self.cell1.pos().dif(self.cell2.pos())
force = (dif.len()-(self.cell1.radius+self.cell2.radius))/(self.cell1.radius+self.cell2.radius)*200
dif.len(force)
self.cell1.pull(dif)
self.cell2.push(dif)
Cascading Activation Control on Division
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
cell = Cell(IVec(0,0,0), 10)
cell.clr(0,0,1.0)
cell.active = True
class Cell(IParticle) :
growthDuration = 2000 #duration of growth and division
growthInterval = 10
divisionInterval = 50
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
# additional repulsion for smoother organization
if a.pos().dist(self.pos()) < a.radius+self.radius + 100 :
dif = a.pos().dif(self.pos())
dif.len(1) # constant force
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
child.active = True #activate child
self.active = False #deactivate itself
CellLink(self, child)
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
class CellLink(IAgent) : # a link to connect 2 cells with spring force
def __init__(self, c1, c2) :
IAgent.__init__(self)
self.cell1 = c1
self.cell2 = c2
self.line = ICurve(c1.pos(), c2.pos()).clr(1.0,0,0)
def interact(self, agents) :
# spring force
dif = self.cell1.pos().dif(self.cell2.pos())
force = (dif.len()-(self.cell1.radius+self.cell2.radius))/(self.cell1.radius+self.cell2.radius)*200
dif.len(force)
self.cell1.pull(dif)
self.cell2.push(dif)
Branching Activation Control on Division
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
cell = Cell(IVec(0,0,0), 10).clr(0,0,1.0)
cell.clr(0,0,1.0)
cell.active = True
class Cell(IParticle) :
growthDuration = 1000 #duration of growth and division
growthInterval = 10
divisionInterval = 50
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.active = False
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
# additional repulsion for smoother organization
if a.pos().dist(self.pos()) < a.radius+self.radius + 50 :
dif = a.pos().dif(self.pos())
dif.len(1) # constant force
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
child.active = True #activate child
if IRand.pct(70) : # some deactivated, others stay activated
self.active = False #deactivate itself
CellLink(self, child)
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
class CellLink(IAgent) : # a link to connect 2 cells with spring force
def __init__(self, c1, c2) :
IAgent.__init__(self)
self.cell1 = c1
self.cell2 = c2
self.line = ICurve(c1.pos(), c2.pos()).clr(0,0.5,0)
def interact(self, agents) :
# spring force
dif = self.cell1.pos().dif(self.cell2.pos())
force = (dif.len()-(self.cell1.radius+self.cell2.radius))/(self.cell1.radius+self.cell2.radius)*200
dif.len(force)
self.cell1.pull(dif)
self.cell2.push(dif)
Storing Links and Control Division by Link Count
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
cell = Cell(IVec(0,0,0), 10).clr(0,0,1.0)
cell.clr(0,0,1.0)
cell.active = True
class Cell(IParticle) :
growthDuration = 1800 #duration of growth and division
growthInterval = 10
divisionInterval = 50
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.links = [] #store links
self.active = False
self.fric(0.1)
def interact(self, agents) :
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
# additional repulsion for smoother organization
if a.pos().dist(self.pos()) < a.radius+self.radius + 100 :
dif = a.pos().dif(self.pos())
dif.len(1) # constant force
a.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if len(self.links)==2 and IRand.pct(3) : # random division
self.active = True
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
dir = IRand.dir(IG.zaxis)
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
child.active = True #activate child
self.active = False #deactivate itself
CellLink(self, child)
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
class CellLink(IAgent) : # a link to connect 2 cells with spring force
def __init__(self, c1, c2) :
IAgent.__init__(self)
self.cell1 = c1
self.cell2 = c2
self.cell1.links.append(self) # register this link to cells{
self.cell2.links.append(self)
self.line = ICurve(c1.pos(), c2.pos()).clr(0,0.5,0)
def interact(self, agents) :
# spring force
dif = self.cell1.pos().dif(self.cell2.pos())
force = (dif.len()-(self.cell1.radius+self.cell2.radius))/(self.cell1.radius+self.cell2.radius)*200
dif.len(force)
self.cell1.pull(dif)
self.cell2.push(dif)
Link Division and Closed Links
When a child cell is inserted at an existing link, the code actually deletes the existing link and create a new links from the parent to the child and another link from the child to the cell at the opposite end of the existing link. To delete a link, CellLink class adds a method delete and this method unregisters the link from the list of links inside each cell. For a cell to find the cell at the opposite end of link, CellLink class also adds a method oppositeCell.
In the method divide in Cell class, the part of the code to create a child cell is extracted and put inside a new method createChild. divide method adds more codes to change the behavior depending on the number of links in the cell. When a cell has no link, it just creates one link to the new child which is the same behavior with previous codes. If the cell has one link, which means this cell is at open end of links, it creates two links from the cell to its child cell, and also from the child to the opposite end of the existing link. This creates a closed triangular links. If the number of links is two, then it performs the insertion behavior by first selecting which of two links to insert and second deleting the link and the third put two links from the parent to the child and the child to the opposite of the deleted link.
The code below also adds another behavior in interact method. It's a type of repulsion behavior which is same with the separation behavior in Boid algorithm. The behavior lets the cell to move away from the center of neighbors.
![]()
![]()
![]()
add_library('igeo')
def setup() :
size(480,360,IG.GL)
IConfig.syncDrawAndDynamics=True #not to crash when some geometry is deleted while drawing
Cell(IVec(0,0,0), 10).clr(0,0,1.0)
class Cell(IParticle) :
growthDuration = 1000 #duration of growth and division
growthInterval = 10
divisionInterval = 50
maxRadius = 100
growthSpeed = 0.1
def __init__(self, pos, rad) :
IParticle.__init__(self, pos, IVec(0,0,0))
self.radius = rad
self.circle = None
self.links = [] #store links
self.active = False
self.fric(0.1)
def interact(self, agents) :
neighborCenter = IVec(0,0,0)
neighborCount=0
for a in agents :
if isinstance(a, Cell) :
if a is not self :
# push if closer than two radii
if a.pos().dist(self.pos()) < a.radius+self.radius :
dif = a.pos().dif(self.pos())
dif.len(((a.radius+self.radius)-dif.len())*100+50) # the closer the harder to push
a.push(dif)
# count neighbors and calculate their center
if a.pos().dist(self.pos()) < a.radius+self.radius + self.radius*2 :
neighborCenter.add(a.pos())
neighborCount+=1
if neighborCount > 0 : # push from center of neighbors
neighborCenter.div(neighborCount)
dif = self.pos().dif(neighborCenter).len(20) # constant force
self.push(dif)
def update(self) :
if IG.time() < Cell.growthDuration :
if self.time() > 0 and self.time()%Cell.divisionInterval==0 :
if IRand.pct(50) : # random division
self.active = True
if self.active : # divide when active flag is on
self.divide()
if self.time()%Cell.growthInterval==0 :
self.grow()
# update geometry
if self.circle is None :
self.circle = ICircle(self.pos(), self.radius).clr(self)
else :
self.circle.center(self.pos()).radius(self.radius)
def divide(self) : # cell division
if len(self.links)==0 : # dot state
child = self.createChild(IRand.dir(IG.zaxis))
CellLink(self, child)
elif len(self.links)==1 : # line state
child = self.createChild(IRand.dir(IG.zaxis))
CellLink(child, self.links[0].cell1) # making a triangle loop
CellLink(child, self.links[0].cell2)
elif len(self.links)==2 : # string state
dividingLink = self.links[IRand.getInt(0,1)] # pick one link out of two
c = dividingLink.oppositeCell(self) # other cell on the link
dir = c.pos().dif(self.pos()) # dividing direction is link direction
child = self.createChild(dir)
dividingLink.delete() # delete picked link
CellLink(self, child) # create two new links
CellLink(child, c)
def grow(self) : # growing cell size
if self.radius < Cell.maxRadius :
self.radius += Cell.growthSpeed
def createChild(self, dir) :
self.radius *= 0.5 #make both cell size half
dir.len(self.radius)
child = Cell(self.pos().cp(dir), self.radius)
child.hsb(self.hue()+IRand.get(-.1,.1),self.saturation()+IRand.get(-.1,.1),self.brightness()+IRand.get(-.1,.1))
self.pos().sub(dir) # move to the other way
self.active = False #reset activation
return child
class CellLink(IAgent) : # a link to connect 2 cells with spring force
def __init__(self, c1, c2) :
IAgent.__init__(self)
self.cell1 = c1
self.cell2 = c2
self.cell1.links.append(self) # register this link to cells{
self.cell2.links.append(self)
self.line = ICurve(c1.pos(), c2.pos()).clr(1.0,0,0)
def interact(self, agents) :
# spring force
dif = self.cell1.pos().dif(self.cell2.pos())
force = (dif.len()-(self.cell1.radius+self.cell2.radius))/(self.cell1.radius+self.cell2.radius)*200
dif.len(force)
self.cell1.pull(dif)
self.cell2.push(dif)
def delete(self) :
self.cell1.links.remove(self) # unregister from cells
self.cell2.links.remove(self)
self.line.del() # delete line geometry
self.del() # stop agent
def oppositeCell(self, c) : # find other cell on the link
if self.cell1 is c :
return self.cell2
if self.cell2 is c :
return self.cell1
print("Link does not contain the input cell")
return None
HOME
FOR PROCESSING
DOWNLOAD
DOCUMENTS
TUTORIALS (Java /
Python)
GALLERY
SOURCE CODE(GitHub)
PRIVACY POLICY
ABOUT/CONTACT