Python Tutorials (back to the list of tutorials)

## Tensile Line Network

### Tensile Network from An Input File (requires iGeo version 7.6.4 or higher)

The tutorial page of Tension to Particles shows methods to connect particles with tension lines. However, to have more control of configuration of line connection, it'd be easier to set up what lines to be connected each other and which points are fixed manually in other modeling software beforehand.

The code below shows a method to convert line geometries in a tensile line network by the method ITensileNet.create(lines, fixedPoints). In the code, all the lines and all the points in the file are fed into the method and lines become tension lines creating particles at every end points and points become fixing points if they are on the end points of the lines.

The input Rhino file used in the example is this one below.

```add_library('igeo')

size(480, 360, IG.GL)

IG.open("lines1.3dm")
ITensileNet.create(IG.curves(), IG.points())
```

This image is before applying tension.

The below is after applying tension.

### Applying Force to Tensile Network

You can apply forces like gravities or attractors to tensile line networks by creating agents to apply forces to particles. In default configuration, the class of particles created inside the tensile network method is IParticle and if your force agent class interact with instances of IParticle class, those particles react to the force.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)

IG.open("lines1.3dm")
ITensileNet.create(IG.curves(), IG.points())

Gravity(IG.v(0.1, 0, 0)) #gravity toward x direction
Attractor(IG.v(7, 7, 7)).clr(1.0, 0, 0)

class Gravity(IAgent) :
def __init__(self, g) :
self.gravity = g

def interact(self, agents) :
for agent in agents :
if isinstance(agent, IParticle) :
agent.push(self.gravity)

class Attractor(IPointAgent) :
def __init__(self, p) :
IPointAgent.__init__(self,p)

def interact(self, agents) :
attraction = -0.05
threshold = 9
for agent in agents :
if isinstance(agent, IParticle) :
dist = agent.pos().dist(self.pos())
if dist < threshold :
frc = self.pos().dif(agent.pos())
frc.len( (threshold - dist) * attraction )
agent.push(frc)

```

### Tensile Network Parameters

There are some parameters to adjust the properties of a tensile network.
ITensileNet.tension(50) sets strength of tension lines in the network.
ITensileNet.friction(0.05) sets friction of particles in the network. If you set zero in the friction, the tensile network oscillates endlessly.
ITensileNet.tolerance(0.01) sets tolerance to judge if two lines are connected by measuring distance of end points.
ITensileNet.fixOpenEnd(false) turns on/off an option to automatically fix the end point of line which is not connected to any other lines.
ITensileNet.pointColor(0,0,1.0) sets color of node particles.
ITensileNet.pointLayer("tension node") sets the name of a layer to put node particles.
ITensileNet.fixedPointColor(0,1.0,1.0) sets color of fixed particles.
ITensileNet.fixedPointLayer("fixed node") sets the name of a layer to put fixed particles.
You need to call those setting methods before calling ITensileNet.create(IG.curves(),IG.points()).

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)

IG.open("lines1.3dm")

ITensileNet.tension(50) #strength of tension line
ITensileNet.friction(0.05) #friction of particles
ITensileNet.tolerance(0.01) #tolerance to judge if 2 end points are connected
ITensileNet.fixOpenEnd(False) #fixing open end line or not
ITensileNet.pointColor(0,0,1.0) #color of particle points
ITensileNet.pointLayer("tension node") #layer to put particle points
ITensileNet.fixedPointColor(0,1.0,1.0) #color of fixed particle points
ITensileNet.fixedPointLayer("fixed node") #layer to put fixed particle points

ITensileNet.create(IG.curves(), IG.points())

```

### Tensile Network with Custom Particle Class

Although in default setting, the class of particles created in the tensile network is IPartcleAgent, you can also use your custom particle class defined in your code. In the example code below, the custom particle class is MyBoid (boid is a type of particle) and this MyBoid is fed into the tensile network by the method
ITensileNet.particleClass(MyBoid.class)
This methods specify the tensile network to use the input class. Please note that the input is MyBoid.class not just MyBoid. Another note is that when you use a custom particle class, that class has to have a definition of a constructor with one input argument of IVec as a position like MyBoid(IVec pos) or with two input arguments of IVec as a position and a velocity like MyBoid(IVec pos, IVec vel).

The input Rhino file used in the example is this one below.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.open("lines2.3dm")
ITensileNet.particleFactory(ParticleFactory())#custom particle class
ITensileNet.create(IG.curves(), IG.points())

Gravity(IG.v(0, 0, 0.5)) #gravity toward z

class MyBoid(IBoid) :
def __init__(self, pos, vel) :
IBoid.__init__(self,pos,vel)
self.cohesionDist(2)
self.cohesionRatio(1)
self.separationDist(4)
self.separationRatio(2)
self.alignmentDist(5)
self.alignmentRatio(3)

def update(self) :
if IRand.pct(2.0) : #random occasion of 2%
self.push(IRand.pt(-50,-50,-50,50,50,50)) #random force

class ParticleFactory(IParticleFactory):
def create(self, pos, vel):
return MyBoid(pos,vel)

class Gravity(IAgent) :
def __init__(self, g) :
self.gravity = g

def interact(self, agents) :
for agent in agents :
if isinstance(agent, IParticle) :
agent.push(self.gravity)

```

Initial state.

After simulation.

### Tensile Network with Custom Tension Class

You can set your custom tension class to construct a tensile network in the similar way with the previous example. To create a custom tension class, you'd write a class inheriting ITensionLine. The tension class has to have have a constructor with two input arguments of IParticle like MyTension(IParticle p1, IParticle p2) or with three input arguments of two IPariticleAnge and one double for the strength of tension like MyTension(IParticle p1, IParticle p2, double tension).

The input Rhino file used in the example is this one below.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.open("lines3.3dm")
IG.duration(500)
ITensileNet.tensionFactory(TensionFactory()) #custom tension class
ITensileNet.create(IG.curves(), IG.points())

Gravity(IG.v(0, 0, 5)) #gravity toward z

class MyTension(ITensionLine) :
def __init__(self, p1, p2, tension) :
ITensionLine.__init__(self, p1, p2, tension)

def update(self) :
if IG.time()%10==0 :
p1 = self.pos1().cp()
p2 = self.pos2().cp()
ICurve(p1, p2).clr(IG.time()*0.002)

class TensionFactory(ITensionFactory) :
def create(self, p1, p2, tension) :
return MyTension(p1,p2,tension)

class Gravity(IAgent) :
def __init__(self, g) :
self.gravity = g

def interact(self, agents) :
for agent in agents :
if isinstance(agent, IParticle) :
agent.push(self.gravity)
```

Initial state.

After simulation.

The code below is putting polygon mesh geometries instead of lines. Using the current two points of a tension line and the previous two points of the line, it forms a rectangular shape. On top of this rectangle, thickness is added creating total eight points and then these eight points are fed into IG.meshBox().

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.open("lines3.3dm")
IG.duration(500)
ITensileNet.tensionFactory(TensionFactory()) #custom tension class
ITensileNet.create(IG.curves(), IG.points())

Gravity(IG.v(0, 0, 5)) #gravity toward z

class MyTension(ITensionLine) :
def __init__(self, p1, p2, tension) :
ITensionLine.__init__(self, p1, p2, tension)
self.prevPos1 = None
self.prevPos2 = None

def update(self) :
thickness = 0.8
if IG.time()%15==0 :
p1 = self.pos1().cp()
p2 = self.pos2().cp()

if self.prevPos1 is not None and self.prevPos2 is not None and IRand.pct(80) :
if  not self.prevPos1.eq(p1) and not self.prevPos2.eq(p2) :  #only when moved
# mesh slab (box)
hdir1 = p2.dif(p1)
hdir2 = self.prevPos2.dif(self.prevPos1)
vdir1 = p1.dif(self.prevPos1)
vdir2 = p2.dif(self.prevPos2)
if p1.eq(self.prevPos1) :
vdir1 = vdir2 #avoid zero vector
if p2.eq(self.prevPos2) :
vdir2 = vdir1 #avoid zero vector

v5 = p1.cp().sub(hdir1.cross(vdir1).len(thickness/2))
v6 = p2.cp().sub(hdir1.cross(vdir2).len(thickness/2))
v7 = self.prevPos2.cp().sub(hdir2.cross(vdir2).len(thickness/2))
v8 = self.prevPos1.cp().sub(hdir2.cross(vdir1).len(thickness/2))
IG.meshBox(v1,v2,v3,v4,v5,v6,v7,v8).clr(IG.time()*0.002)

self.prevPos1 = p1
self.prevPos2 = p2

class TensionFactory(ITensionFactory) :
def create(self, p1, p2, tension) :
return MyTension(p1,p2,tension)

class Gravity(IAgent) :
def __init__(self, g) :
self.gravity = g

def interact(self, agents) :
for agent in agents :
if isinstance(agent, IParticle) :
agent.push(self.gravity)
```

(back to the list of tutorials)