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.

```import igeo.*;
import processing.opengl.*;

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.

```import igeo.*;
import processing.opengl.*;

void setup(){
size(480, 360, IG.GL);

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

new Gravity(IG.v(0.1, 0, 0)); //gravity toward x direction
new Attractor(IG.v(7, 7, 7)).clr(1.0, 0, 0);
}

class Gravity extends IAgent{
IVec gravity;
Gravity(IVec g){ gravity=g; }
void interact(IDynamics agent){
if(agent instanceof IParticle){
IParticle particle = (IParticle)agent;
particle.push(gravity);
}
}
}

class Attractor extends IPointAgent{
double attraction = -0.05;
double threshold = 9;
Attractor(IVec p){ super(p); }

void interact(IDynamics agent){
if(agent instanceof IParticle){
IParticle particle = (IParticle)agent;
double dist = particle.pos().dist(pos());
if(dist < threshold){
IVec frc = pos.dif(particle.pos());
frc.len( (threshold - dist) * attraction );
particle.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()).

```import igeo.*;
import processing.opengl.*;

void 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.

```import igeo.*;
import processing.opengl.*;

void setup(){
size(480, 360, IG.GL);
IG.open("lines2.3dm");
ITensileNet.particleClass(MyBoid.class); //custom particle class
ITensileNet.create(IG.curves(), IG.points());

new Gravity(IG.v(0, 0, 0.5)); //gravity toward z
}

class MyBoid extends IBoid{
MyBoid(IVec pos, IVec vel){
super(pos, vel);
cohesionDist(2);
cohesionRatio(1);
separationDist(4);
separationRatio(2);
alignmentDist(5);
alignmentRatio(3);
}

void update(){
if(IRand.pct(2.0)){ //random occasion of 2%
push(IRand.pt(-50,-50,-50,50,50,50)); //random force
}
}
}

class Gravity extends IAgent{
IVec gravity;
Gravity(IVec g){ gravity=g; }
void interact(IDynamics agent){
if(agent instanceof IParticle){
IParticle particle = (IParticle)agent;
particle.push(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.

```import igeo.*;
import processing.opengl.*;

void setup(){
size(480, 360, IG.GL);
IG.open("lines3.3dm");
IG.duration(500);
ITensileNet.tensionClass(MyTension.class); //custom tension class
ITensileNet.create(IG.curves(), IG.points());

new Gravity(IG.v(0, 0, 5)); //gravity toward z
}

class MyTension extends ITensionLine{

MyTension(IParticle p1, IParticle p2){
super(p1,p2);
}

void update(){
if(IG.time()%10==0){
IVec p1 = pos1().cp();
IVec p2 = pos2().cp();
new ICurve(p1, p2).clr(IG.time()*0.002);
}
}
}

class Gravity extends IAgent{
IVec gravity;
Gravity(IVec g){ gravity=g; }
void interact(IDynamics agent){
if(agent instanceof IParticle){
IParticle particle = (IParticle)agent;
particle.push(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().

```import igeo.*;
import processing.opengl.*;

void setup(){
size(480, 360, IG.GL);
IG.open("lines3.3dm");
IG.duration(500);
ITensileNet.tensionClass(MyTension.class); //custom tension class
ITensileNet.create(IG.curves(), IG.points());

new Gravity(IG.v(0, 0, 5)); //gravity toward z
}

class MyTension extends ITensionLine{
IVec prevPos1, prevPos2;

MyTension(IParticle p1, IParticle p2){
super(p1,p2);
}

void update(){
double thickness = 0.8;
if(IG.time()%15==0){
IVec p1 = pos1().cp();
IVec p2 = pos2().cp();
if(prevPos1!=null && prevPos2!=null && IRand.pct(80)){
if( !prevPos1.eq(p1) || !prevPos2.eq(p2) ){ //only when moved
// mesh slab (box)
IVec hdir1 = p2.dif(p1);
IVec hdir2 = prevPos2.dif(prevPos1);
IVec vdir1 = p1.dif(prevPos1);
IVec vdir2 = p2.dif(prevPos2);
if(p1.eq(prevPos1)){ vdir1 = vdir2; } //avoid zero vector
if(p2.eq(prevPos2)){ vdir2 = vdir1; } //avoid zero vector

IVec vertex1 = p1.cp().add(hdir1.cross(vdir1).len(thickness/2));
IVec vertex2 = p2.cp().add(hdir1.cross(vdir2).len(thickness/2));
IVec vertex3 = prevPos2.cp().add(hdir2.cross(vdir2).len(thickness/2));
IVec vertex4 = prevPos1.cp().add(hdir2.cross(vdir1).len(thickness/2));
IVec vertex5 = p1.cp().sub(hdir1.cross(vdir1).len(thickness/2));
IVec vertex6 = p2.cp().sub(hdir1.cross(vdir2).len(thickness/2));
IVec vertex7 = prevPos2.cp().sub(hdir2.cross(vdir2).len(thickness/2));
IVec vertex8 = prevPos1.cp().sub(hdir2.cross(vdir1).len(thickness/2));
IG.meshBox(vertex1, vertex2, vertex3, vertex4,
vertex5, vertex6, vertex7, vertex8).clr(IG.time()*0.002);
}
}
prevPos1 = p1;
prevPos2 = p2;
}
}
}

class Gravity extends IAgent{
IVec gravity;
Gravity(IVec g){ gravity=g; }
void interact(IDynamics agent){
if(agent instanceof IParticle){
IParticle particle = (IParticle)agent;
particle.push(gravity);
}
}
}
```

(back to the list of tutorials)