Tutorials | (back to the list of tutorials) |
Line Agent with Particle and SpringThe first code below has a line agent class LineAgent which reproduces a child line agent in a specific direction but the agent class inherits IParticle class instead of inheriting IAgent class in the previous line agent classes like the ones in this page. In the update method, the agent instantiates ISpringLine which is a line with spring force to maintain the distance of two end point particles instead of a simple line in the previous line agents.
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
new LineAgent(new IParticle(0,0,0).hide().fric(0.2),
new IVec(0,2,0));
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void update(){
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
//line between this and parent with spring force 100
new ISpringLine(this,parent,100).clr(0);
}
new LineAgent(this, dir);
}
}
}
The below codes shows the response of particle-based line agents to some force fields like attractors.
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
new LineAgent(new IParticle(0,0,0).hide().fric(0.2),
new IVec(0,2,0));
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,0,0,200,400,0)).linear(100).intensity(-200); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void update(){
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
new ISpringLine(this,parent,100).clr(0); //line with spring force
}
new LineAgent(this, dir);
}
}
}
Branching Particle-based Line Agent
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2));
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,0,0,200,400,0)).linear(100).intensity(-200); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void update(){
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100); //line with spring force
double t = -cos(IG.time()*0.06)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2); //color by time
}
new LineAgent(this, dir);
IVec dir2 = dir.cp();
dir2.rot(PI*0.12);
if( IRand.pct(5) ){ // 5% branching probability
new LineAgent(this,dir2);
}
}
}
}
Collision Detection
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2));
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,0,0,200,400,0)).linear(100).intensity(-200); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
boolean isColliding;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
isColliding=false;
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void interact(ArrayList agents){
if(time()==0){ //only in the first time
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this){ //agents include "this"
if(lineAgent.parent!=null && lineAgent.pos().dist(pos()) < pos().dist(parent.pos())*2){
IVec intxn = IVec.intersectSegment(lineAgent.parent.pos(),lineAgent.pos(),parent.pos(),pos());
if(intxn != null && !intxn.eq(parent.pos()) && !lineAgent.isColliding ){
isColliding = true;
return;
}
}
}
}
}
}
}
void update(){
if( isColliding ){
del();
}
else{
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100);
double t = -cos(IG.time()*0.06)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 1.0); // color by time
}
new LineAgent(this, dir);
IVec dir2 = dir.cp();
dir2.rot(PI*0.12);
if( IRand.pct(5) ){ // 5% branching probability
new LineAgent(this,dir2);
}
}
}
}
}
Branching and Growth Angle Control![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2));
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,-200,0,200,200,0)).linear(50).intensity(-100); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
boolean isColliding;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
isColliding=false;
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void interact(ArrayList agents){
if(time()==0){ //only in the first time
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this){ //agents include "this"
if(lineAgent.parent!=null && lineAgent.pos().dist(pos()) < pos().dist(parent.pos())*2){
IVec intxn = IVec.intersectSegment(lineAgent.parent.pos(),lineAgent.pos(),parent.pos(),pos());
if( intxn != null && !intxn.eq(parent.pos()) && !lineAgent.isColliding ){
isColliding = true;
return;
}
}
}
}
}
}
}
void update(){
if( isColliding ){
del();
}
else{
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100);
double t = -cos(IG.time()*0.06)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 1.0); // color by time
}
IVec dir2 = dir.cp();
double angle = PI*0.12 ;
if( IRand.pct(50) ){
dir.rot(angle);
dir2.rot(-angle);
}
else{
dir.rot(-angle);
dir2.rot(angle);
}
new LineAgent(this, dir);
if( IRand.pct(20) ){ // 20% branching probability
new LineAgent(this,dir2);
}
}
}
}
}
Setting Spring Length
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2));
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,-200,0,200,200,0)).linear(50).intensity(-100); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
boolean isColliding;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
isColliding=false;
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void interact(ArrayList agents){
if(time()==0){ //only in the first time
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this){ //agents include "this"
if(lineAgent.parent!=null && lineAgent.pos().dist(pos()) < pos().dist(parent.pos())*2){
IVec intxn = IVec.intersectSegment(lineAgent.parent.pos(),lineAgent.pos(),parent.pos(),pos());
if( intxn != null && !intxn.eq(parent.pos()) && !lineAgent.isColliding ){
isColliding = true;
return;
}
}
}
}
}
}
}
void update(){
if( isColliding ){
del();
}
else{
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100);
double t = -cos(IG.time()*0.06)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 1.0);
ln.len(ln.len()*2); //set spring length longer
}
IVec dir2 = dir.cp();
double angle = PI*0.12 ;
if( IRand.pct(50) ){
dir.rot(angle);
dir2.rot(-angle);
}
else{
dir.rot(-angle);
dir2.rot(angle);
}
new LineAgent(this, dir);
if( IRand.pct(20) ){ // 20% branching probability
new LineAgent(this,dir2);
}
}
}
}
}
As a result of setting the spring length to be the double of the original branch length, it expands after it's created making the lines jaggy.
Smoothing Branch Lines with Straightener![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2));
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,-200,0,200,200,0)).linear(50).intensity(-30); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent;
boolean isColliding;
IVec dir;
LineAgent(IParticle parent, IVec dir){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
isColliding=false;
hide(); // hide point
this.dir = dir;
fric(0.2);
}
void interact(ArrayList agents){
if(time()==0){ //only in the first time
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this){ //agents include "this"
if(lineAgent.parent!=null && lineAgent.pos().dist(pos()) < pos().dist(parent.pos())*2){
IVec intxn = IVec.intersectSegment(lineAgent.parent.pos(),lineAgent.pos(),parent.pos(),pos());
if( intxn != null && !intxn.eq(parent.pos()) && !lineAgent.isColliding ){
isColliding = true;
return;
}
}
}
}
}
}
}
void update(){
if( isColliding ){
del();
}
else{
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100);
double t = -cos(IG.time()*0.015)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 0.5); // color by time
if(parent.parent!=null && parent.parent.alive()){
IStraightenerCurve st =
new IStraightenerCurve(this,parent,parent.parent).tension(100);
st.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 0.5);
}
}
}
if(time()==4){
IVec dir2 = dir.cp();
double angle = PI*0.12 ;
if( IRand.pct(50) ){
dir.rot(angle);
dir2.rot(-angle);
}
else{
dir.rot(-angle);
dir2.rot(angle);
}
new LineAgent(this, dir);
if( IRand.pct(20) ){ // 20% branching probability
new LineAgent(this,dir2);
}
}
}
}
}
Bundling Close Branches
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2), null);
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,-200,0,200,200,0)).linear(50).intensity(-30); //repulsion force
}
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent, root;
boolean isColliding;
IVec dir;
LineAgent(IParticle parent, IVec dir, LineAgent root){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
isColliding=false;
hide(); // hide point
this.dir = dir;
fric(0.2);
if(root!=null){ this.root = root; }
else{ this.root = this; }
}
void interact(ArrayList< IDynamics > agents){
if(time()==0){ //only in the first time
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this){ //agents include "this"
if(lineAgent.parent!=null && lineAgent.pos().dist(pos()) < pos().dist(parent.pos())*2){
IVec intxn = IVec.intersectSegment(lineAgent.parent.pos(),lineAgent.pos(),parent.pos(),pos());
if( intxn != null && !intxn.eq(parent.pos()) && !lineAgent.isColliding ){
isColliding = true;
return;
}
}
}
}
}
}
else if(time()==1){
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this && lineAgent.alive()){ //agents include "this"
if(lineAgent.root != root){ // different branch
IVec dif = lineAgent.pos().dif(pos());
if(dif.len() < dir.len()*2.5 && dif.len()>dir.len()){ // within threshold
ISpringLine sp =
new ISpringLine(lineAgent,this,5,dir.len()*1.5).tension(5);
double t = -cos(IG.time()*0.015)*0.5+0.5;
sp.hsb(0.7-t*0.2, 1, 0.8-t*0.2, 0.2);
}
}
}
}
}
}
}
void update(){
if( isColliding ){
del();
}
else{
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100);
double t = -cos(IG.time()*0.015)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 0.2); // color by tim
if(parent.parent!=null && parent.parent.alive()){
IStraightenerCurve st
= new IStraightenerCurve(this,parent,parent.parent).tension(100);
st.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 0.2);
}
}
}
if(time()==4){
IVec dir2 = dir.cp();
double angle = PI*0.12 ;
if( IRand.pct(50) ){
dir.rot(angle);
dir2.rot(-angle);
}
else{
dir.rot(-angle);
dir2.rot(angle);
}
new LineAgent(this, dir, root);
if( IRand.pct(20) ){ // 20% branching probability
new LineAgent(this,dir2, null);
}
}
}
}
}
Applying More Force Field
![]()
![]()
![]()
import igeo.*;
import processing.opengl.*;
void setup(){
size(480,360,IG.GL);
for(int i=0; i < 4; i++){
new LineAgent(new IParticle(IRand.pt(-40,-40,0,40,40,0)).hide().fric(0.2), IRand.dir(IG.zaxis).len(2), null);
}
for(int i=0; i < 20; i++){
new IAttractor(IRand.pt(-200,-200,0,200,200,0)).linear(50).intensity(-30);//repulsion force
}
new IPointCurlField(new IVec(0,0,0), new IVec(0,0,1)).intensity(50); //spinning force
IG.bg(1.0,1.0,1.0);
IG.top();
}
class LineAgent extends IParticle{
LineAgent parent, root;
boolean isColliding;
IVec dir;
LineAgent(IParticle parent, IVec dir, LineAgent root){
super(parent.pos().cp(dir));
if(parent instanceof LineAgent){
this.parent = (LineAgent)parent;
}
isColliding=false;
hide(); // hide point
this.dir = dir;
fric(0.2);
if(root!=null){ this.root = root; }
else{ this.root = this; }
}
void interact(ArrayList< IDynamics > agents){
if(time()==0){ //only in the first time
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this){ //agents include "this"
if(lineAgent.parent!=null && lineAgent.pos().dist(pos()) < pos().dist(parent.pos())*2){
IVec intxn = IVec.intersectSegment(lineAgent.parent.pos(),lineAgent.pos(),parent.pos(),pos());
if( intxn != null && !intxn.eq(parent.pos()) && !lineAgent.isColliding ){
isColliding = true;
return;
}
}
}
}
}
}
else if(time()==1){
for(int i=0; i < agents.size(); i++){
if(agents.get(i) instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agents.get(i);
if(lineAgent!=this && lineAgent.alive()){ //agents include "this"
if(lineAgent.root != root){ // different branch
IVec dif = lineAgent.pos().dif(pos());
if(dif.len() < dir.len()*2.5 && dif.len()>dir.len()){ // within threshold
ISpringLine sp =
new ISpringLine(lineAgent,this,5,dir.len()*1.5).tension(5);
double t = -cos(IG.time()*0.015)*0.5+0.5;
sp.hsb(0.7-t*0.2, 1, 0.8-t*0.2, 0.2);
}
}
}
}
}
}
}
void update(){
if( isColliding ){
del();
}
else{
if(time()==0 && alive()){
if(parent!=null && parent.alive()){
ISpringLine ln = new ISpringLine(this, parent,100);
double t = -cos(IG.time()*0.015)*0.5+0.5;
ln.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 0.2); // color by tim
if(parent.parent!=null && parent.parent.alive()){
IStraightenerCurve st
= new IStraightenerCurve(this,parent,parent.parent).tension(100);
st.hsb( 0.7-t*0.2, 1, 0.8-t*0.2, 0.2);
}
}
}
if(time()==4){
IVec dir2 = dir.cp();
double angle = PI*0.12 ;
if( IRand.pct(50) ){
dir.rot(angle);
dir2.rot(-angle);
}
else{
dir.rot(-angle);
dir2.rot(angle);
}
new LineAgent(this, dir, root);
if( IRand.pct(20) ){ // 20% branching probability
new LineAgent(this,dir2, null);
}
}
}
}
}
HOME
FOR PROCESSING
DOWNLOAD
DOCUMENTS
TUTORIALS (Java /
Python)
GALLERY
SOURCE CODE(GitHub)
PRIVACY POLICY
ABOUT/CONTACT