# Random movement of ideal random-walk chains

Each chain is moving is independently, the first segment completely random, the next ones restricted by segment distance and step length. Segment distance and step length have a random variation.

Each chain is created with an angle restriction between segments (low angle expandend chain, high angle compact chain).

``````Coil[] coil;
int boxsize = 500;
float camangle = 90; //camera angle
float angle = 90; //maximum anlge between coil[j].segments, the larger the more flexible
int snumber = 75; //number of coil[j].segments in coil
int cnumber = 5; // number of coils
int sdistance = 10; //distance between coil[j].segments
float segdistdev = 0.5; //deviation from possible sdistance (0.1 = 10%)
float stepdev = 0.5; // deviation from possible speed/speed (0.1 = 10%)
int speed = 10; //movement speed

void setup(){
size(900, 700, P3D);
coil = new Coil[cnumber];
for (int j=0; j < coil.length; j++){
coil[j] = new Coil(sdistance, snumber, speed);
}
}

void draw(){
background(129,200,273);
lights();
translate(450,350,0);
rotateY(camangle);
noFill();
stroke(0);
box(boxsize);
println("Start: Check");

for (int j=0; j < coil.length; j++){
for (int i = 1; i < coil[j].segments.length; i++){
PVector point0 = new PVector();
PVector point1 = new PVector();
point0 = coil[j].segments[i-1].location.copy();
point1= coil[j].segments[i].location.copy();
//check that segments are within box; while instead of if to cover cases with coordinates larger than boxsize/2
while (point0.x > boxsize/2){ point0.x -=boxsize; }
while (point0.x < -boxsize/2){ point0.x +=boxsize; }
while (point0.y > boxsize/2){ point0.y -=boxsize; }
while (point0.y < -boxsize/2){ point0.y +=boxsize; }
while (point0.z > boxsize/2){ point0.z -=boxsize; }
while (point0.z < -boxsize/2){ point0.z +=boxsize; }
while (point1.x > boxsize/2){ point1.x -=boxsize; }
while (point1.x < -boxsize/2){ point1.x +=boxsize; }
while (point1.y > boxsize/2){ point1.y -=boxsize; }
while (point1.y < -boxsize/2){ point1.y +=boxsize; }
while (point1.z > boxsize/2){ point1.z -=boxsize; }
while (point1.z < -boxsize/2){ point1.z +=boxsize; }

//check if points are on opposite ends of box, then no line is drawn, 2*sdistance to accommodate deviation
if (abs(point1.x - point0.x) < 2*sdistance && abs(point1.y - point0.y) < 2*sdistance && abs(point1.z - point0.z) < 2*sdistance*sdistance){
strokeWeight(5);
line(point0.x,point0.y, point0.z, point1.x, point1.y, point1.z);
strokeWeight(1);
}
}
}

if(mousePressed == true){
camangle += 0.1;
}

for (int j=0; j < coil.length; j++){
for (int i=0; i < coil[j].segments.length; i++){
println("Start Movement: Check");
PVector alastnew = new PVector(); //axis of last to new segment
PVector teststep = new PVector(); //temporary step to check conditions for Segment 0 step
PVector testaxis = new PVector(); //temporary axis to check conditions for Segment 0 step
float taxislength = 0;
float seg1step = 0; //actual step length calculated
float seg1distance = 0;
PVector seg0 = new PVector();
PVector segment0 = new PVector();
PVector segment1 = new PVector();

if (i == 0){
//random step for first segment
segment0 = coil[j].segments.location.copy();
segment1 = coil[j].segments.location.copy();
while (abs(taxislength) < sdistance || abs(taxislength) > sdistance+speed) {
seg0 = PVector.random3D();
float step1 = speed*random(1-stepdev,1+stepdev);
seg0.setMag(step1);
PVector.sub(teststep,segment1,testaxis);
taxislength = testaxis.mag();
}
segment0 = teststep.copy(); //1st segment updated
coil[j].segments.location = segment0.copy();
} else {
segment0 = coil[j].segments[i-1].location.copy();
segment1 = coil[j].segments[i].location.copy();

PVector.sub(segment0,segment1,alastnew);
float axislength = alastnew.mag();

while (seg1step < abs(axislength-seg1distance) || seg1step > abs(axislength+seg1distance)){
seg1distance = sdistance*random(1-segdistdev,1+segdistdev);
seg1step=speed*random(1-stepdev,1+stepdev);
}
println("Movement calculation completed for segment", i);
println(frameRate);
segment1 = movement(segment0,segment1,seg1distance,seg1step);
coil[j].segments[i].location = segment1.copy();
}
}
}
}

class Coil{
Segments[] segments;
int csdistance;
int csnumber;
int cspeed;
float crgcounter = 0;
float cRg = 0;

Coil(int sdistance_ , int snumber_ , int speed_){
csdistance = sdistance_;
csnumber = snumber_;
cspeed = speed_;
segments = new Segments[csnumber];
startcond();
println(segments.location, segments[snumber-1].location);
}

void startcond(){
//create starting point for coil
PVector loc; //starting vector
PVector loc_old = new PVector(); //preceding vector
float test = 0; //angle test
loc = new PVector (map(random(1),0,1,-boxsize/4,boxsize/4),map(random(1),0,1,-boxsize/4,boxsize/4),map(random(1),0,1,-boxsize/4,boxsize/4));
segments = new Segments(loc);
loc_old = loc.copy();
//create random segments
for (int i = 1; i < segments.length; i++){
PVector loc2 = loc_old;
while (degrees(test) > angle || degrees(test)==0 || loc2 == loc_old){
loc2=PVector.random3D();
test = PVector.angleBetween(loc2,loc_old);
}
loc2 = loc2.setMag(csdistance);
loc_old=loc2.copy();
segments[i] = new Segments(loc2);
println(i, degrees(test), loc2);
}
}
}

PVector movement(PVector lastseg, PVector newseg, float segdistance, float step){
PVector out = new PVector();
PVector axis = new PVector();
PVector proj = new PVector();

//axis vector through circle center
PVector.sub(lastseg,newseg,axis);

//vector to projection point of axis, which is middle point of circle
float projection = (step*step - segdistance*segdistance + axis.magSq())/ (2*axis.mag());
proj = axis.copy();
proj.setMag(projection);

//Identify axis coordinate with smallest absolute value to create perpendicular vector
float ax = abs(axis.x);
float ay = abs(axis.y);
float az = abs(axis.z);
PVector b = new PVector();
if (ax <= ay && ax <= az){
b = new PVector(1,0,0);
} else if (ax > ay && ay <= az){
b = new PVector(0,1,0);
} else {
b = new PVector(0,0,1);
}

//create vectors in plane with intersect circle
PVector iv = new PVector();
PVector.cross(axis,b,iv);
PVector jv = new PVector();
PVector.cross(axis, iv, jv);
iv.normalize();
jv.normalize();

PVector c2 = new PVector();
PVector c3 = new PVector();
PVector p = new PVector();
float cang = random(TWO_PI);
PVector.mult(iv, cos(cang), c2);
PVector.mult(jv, sin(cang), c3);

out = p.copy();
return out;
}

class Segments{
PVector location;

Segments(PVector location_){
location = location_;
}
}
``````
2 Likes

Update of the code to provide radius of gyration (based on end-to-end distance) for a selected single chain or over all chains.
Chains can be selected with the up and down arrows. Cube can be turned with mouse

``````Coil[] coil;
int boxsize = 500;
float camangle = 90; //camera angle
float angle = 120; //maximum anlge between coil[j].segments, the larger the more flexible
int snumber = 50; //number of coil[j].segments in coil
int cnumber = 5; // number of coils
int sdistance = 10; //distance between coil[j].segments
float segdistdev = 0.1; //deviation from possible sdistance (0.1 = 10%)
float stepdev = 0.1; // deviation from possible speed/speed (0.1 = 10%)
int speed = 10; //movement speed
float rgcounter = 0;
float Rg = 0;
float rgmin = 0;
float rgmax = 0;
int coilpointer = 0; //pointer for chain selection
FloatList rghistory;

void setup(){
size(900, 700, P3D);
coil = new Coil[cnumber];
for (int j=0; j < coil.length; j++){
coil[j] = new Coil(sdistance, snumber, speed);
}
rghistory = new FloatList();
}

void draw(){
background(129,200,273);
perspective();
lights();
translate(450,350,0);
rotateY(camangle);
noFill();
stroke(0);
strokeWeight(1);
box(boxsize);
println("Start: Check");
//println(coil.segments.location);

for (int j=0; j < coil.length; j++){
for (int i = 1; i < coil[j].segments.length; i++){
//check for active chain
if (coilpointer == j){
stroke(125);
} else {
stroke(0);
}

PVector point0 = new PVector();
PVector point1 = new PVector();
point0 = coil[j].segments[i-1].location.copy();
point1= coil[j].segments[i].location.copy();
//check that segments are within box; while instead of if to cover cases with coordinates larger than boxsize/2
while (point0.x > boxsize/2){ point0.x -=boxsize; }
while (point0.x < -boxsize/2){ point0.x +=boxsize; }
while (point0.y > boxsize/2){ point0.y -=boxsize; }
while (point0.y < -boxsize/2){ point0.y +=boxsize; }
while (point0.z > boxsize/2){ point0.z -=boxsize; }
while (point0.z < -boxsize/2){ point0.z +=boxsize; }
while (point1.x > boxsize/2){ point1.x -=boxsize; }
while (point1.x < -boxsize/2){ point1.x +=boxsize; }
while (point1.y > boxsize/2){ point1.y -=boxsize; }
while (point1.y < -boxsize/2){ point1.y +=boxsize; }
while (point1.z > boxsize/2){ point1.z -=boxsize; }
while (point1.z < -boxsize/2){ point1.z +=boxsize; }

//check if points are on opposite ends of box, then no line is drawn, 2*sdistance to accommodate deviation
if (abs(point1.x - point0.x) < 2*sdistance && abs(point1.y - point0.y) < 2*sdistance && abs(point1.z - point0.z) < 2*sdistance*sdistance){
strokeWeight(5);
line(point0.x,point0.y, point0.z, point1.x, point1.y, point1.z);
strokeWeight(1);
}
}
}

//Data visualization
textSize(18);
hint(DISABLE_DEPTH_TEST);
int rectx = 175;
int recty = height - 140;
int rectsz = 110;
camera();
ortho();
stroke(255);
strokeWeight(3);
// Single Coil Data Visualization
textSize(20);
text("Radius of gyration of selected chain", rectx-150, recty-20);
textSize(18);
text("Max", rectx-150, recty+9);
text(coil[coilpointer].rgmax, rectx-100, recty+9);
text("Avg", rectx-150, recty+rectsz/2+9);
text(coil[coilpointer].cRg, rectx-100, recty+rectsz/2+9);
rect(rectx, recty,240,110);
for (int i = 1; i < coil[coilpointer].rghistory.size(); i++){
float y0 = map(coil[coilpointer].rghistory.get(i-1),coil[coilpointer].rgmin,coil[coilpointer].rgmax,rectsz,0);
float y1 = map(coil[coilpointer].rghistory.get(i),coil[coilpointer].rgmin,coil[coilpointer].rgmax,rectsz,0);
stroke(225);
line(rectx+(i-1)*12,recty+y0,rectx+i*12,recty+y1);
}
text("Min", rectx-150, recty+rectsz+9);
text(coil[coilpointer].rgmin, rectx-100, recty+rectsz+9);
//All coil average datea visualization
rectx = 625;
stroke(255);
textSize(20);
text("Radius of gyration over all chains", rectx-150, recty-20);
textSize(18);
text("Max", rectx-150, recty+9);
text(rgmax, rectx-100, recty+9);
text("Avg", rectx-150, recty+rectsz/2+9);
text(Rg, rectx-100, recty+rectsz/2+9);
rect(rectx, recty,240,rectsz);
for (int i = 1; i < rghistory.size(); i++){
float y0 = map(rghistory.get(i-1),rgmin,rgmax,rectsz,0);
float y1 = map(rghistory.get(i),rgmin,rgmax,rectsz,0);
stroke(225);
line(rectx+(i-1)*12,recty+y0,rectx+i*12,recty+y1);
}
text("Min", rectx-150, recty+rectsz+9);
text(rgmin, rectx-100, recty+rectsz+9);
hint(ENABLE_DEPTH_TEST);

if(mousePressed == true){
camangle += 0.1;
}

if(keyPressed == true && keyCode == UP){
if (coilpointer == coil.length -1){
coilpointer = 0;
} else {
coilpointer++;
}
}

if(keyPressed == true && keyCode == DOWN){
if (coilpointer == 0){
coilpointer = coil.length -1;
} else {
coilpointer--;
}
}

for (int j=0; j < coil.length; j++){
for (int i=0; i < coil[j].segments.length; i++){
println("Start Movement: Check");
PVector alastnew = new PVector(); //axis of last to new segment
PVector teststep = new PVector(); //temporary step to check conditions for Segment 0 step
PVector testaxis = new PVector(); //temporary axis to check conditions for Segment 0 step
float taxislength = 0;
float seg1step = 0; //actual step length calculated
float seg1distance = 0;
PVector seg0 = new PVector();
PVector segment0 = new PVector();
PVector segment1 = new PVector();

if (i == 0){
//random step for first segment
segment0 = coil[j].segments.location.copy();
segment1 = coil[j].segments.location.copy();
while (abs(taxislength) < sdistance || abs(taxislength) > sdistance+speed) {
seg0 = PVector.random3D();
float step1 = speed*random(1-stepdev,1+stepdev);
seg0.setMag(step1);
PVector.sub(teststep,segment1,testaxis);
taxislength = testaxis.mag();
}
segment0 = teststep.copy(); //1st segment updated
coil[j].segments.location = segment0.copy();
} else {
segment0 = coil[j].segments[i-1].location.copy();
segment1 = coil[j].segments[i].location.copy();

PVector.sub(segment0,segment1,alastnew);
float axislength = alastnew.mag();

while (seg1step < abs(axislength-seg1distance) || seg1step > abs(axislength+seg1distance)){
seg1distance = sdistance*random(1-segdistdev,1+segdistdev);
seg1step=speed*random(1-stepdev,1+stepdev);
}
println("Movement calculation completed for segment", i);
println(frameRate);
segment1 = movement(segment0,segment1,seg1distance,seg1step);
coil[j].segments[i].location = segment1.copy();
}
}

sizecalc(j);

}
}

class Coil{
Segments[] segments;
int csdistance;
int csnumber;
int cspeed;
//size calculation variables
FloatList rghistory; //stores calcluated rg for diagram
float rgmax; //maximum value
float rgmin; //minimum value
float crgcounter = 0; //counter for sum
float cRg = 0;

Coil(int sdistance_ , int snumber_ , int speed_){
csdistance = sdistance_;
csnumber = snumber_;
cspeed = speed_;
segments = new Segments[csnumber];
rghistory = new FloatList();
startcond();
println(segments.location, segments[snumber-1].location);
}

void startcond(){
//create starting point for coil
PVector loc; //starting vector
PVector loc_old = new PVector(); //preceding vector
float test = 0; //angle test
loc = new PVector (map(random(1),0,1,-boxsize/4,boxsize/4),map(random(1),0,1,-boxsize/4,boxsize/4),map(random(1),0,1,-boxsize/4,boxsize/4));
segments = new Segments(loc);
loc_old = loc.copy();
//create random segments
for (int i = 1; i < segments.length; i++){
PVector loc2 = loc_old;
while (degrees(test) > angle || degrees(test)==0 || loc2 == loc_old){
loc2=PVector.random3D();
test = PVector.angleBetween(loc2,loc_old);
}
loc2 = loc2.setMag(csdistance);
loc_old=loc2.copy();
segments[i] = new Segments(loc2);
println(i, degrees(test), loc2);
}
}
}

PVector movement(PVector lastseg, PVector newseg, float segdistance, float step){
PVector out = new PVector();
PVector axis = new PVector();
PVector proj = new PVector();

//axis vector through circle center
PVector.sub(lastseg,newseg,axis);

//vector to projection point of axis, which is middle point of circle
float projection = (step*step - segdistance*segdistance + axis.magSq())/ (2*axis.mag());
proj = axis.copy();
proj.setMag(projection);

//Identify axis coordinate with smallest absolute value to create perpendicular vector
float ax = abs(axis.x);
float ay = abs(axis.y);
float az = abs(axis.z);
PVector b = new PVector();
if (ax <= ay && ax <= az){
b = new PVector(1,0,0);
} else if (ax > ay && ay <= az){
b = new PVector(0,1,0);
} else {
b = new PVector(0,0,1);
}

//create vectors in plane with intersect circle
PVector iv = new PVector();
PVector.cross(axis,b,iv);
PVector jv = new PVector();
PVector.cross(axis, iv, jv);
iv.normalize();
jv.normalize();

PVector c2 = new PVector();
PVector c3 = new PVector();
PVector p = new PVector();
float cang = random(TWO_PI);
PVector.mult(iv, cos(cang), c2);
PVector.mult(jv, sin(cang), c3);

out = p.copy();
return out;
}

class Segments{
PVector location;

Segments(PVector location_){
location = location_;
}
}

void sizecalc(int j_){
PVector etev = new PVector();
float rgtemp = 0;
int j = j_;

coil[j].crgcounter++;
PVector.sub(coil[j].segments.location,coil[j].segments[coil[j].segments.length-1].location,etev);
rgtemp = sqrt (etev.magSq()/6);
if (coil[j].rghistory.size() <= 20){
coil[j].rghistory.append(rgtemp);
} else {
coil[j].rghistory.remove(0);
coil[j].rghistory.append(rgtemp);
}
if (rgtemp > coil[j].rgmax){
coil[j].rgmax = rgtemp;
} else if (rgtemp < coil[j].rgmin || coil[j].rgmin==0){
coil[j].rgmin = rgtemp;
}

coil[j].cRg = (coil[j].crgcounter-1)*coil[j].cRg/coil[j].crgcounter + rgtemp/coil[j].crgcounter;
rgcounter++;
Rg = ((rgcounter-1)*Rg)/rgcounter + rgtemp/rgcounter;
if (rghistory.size() <= 20){
rghistory.append(Rg);
} else {
rghistory.remove(0);
rghistory.append(Rg);
}

if (Rg > rgmax){
rgmax = Rg;
} else if (Rg < rgmin || rgmin==0){
rgmin = Rg;
}

}
``````
1 Like

In the last version of the code the stiffness of the chain was not maintained.

This code now takes care of this. Movement was separated between movement of the whole chain (possible in all directions) and free rotation of a segment between the previous and last segment. The angle is changing but within the restriction provided for stiffness.

Other optimization for a more efficient code were done as well. Next step will be to add a quadtree to include interactions between chain segments.
Frame rate is limited to 20, so that movements is well visible.

``````Coil[] coils;
int cnumber = 50; //number of coils
int snumber = 200; //number of segments per coil
int snumberdev = 5; //deviation from snumber, standard deviation for gaussian
int sdistance = 10; //distance between segments
float segdistdev = sdistance*0.1; //deviation from possible sdistance (0.1 = 10%)
int step = 10; //segment movement per step
float stepdev = step*0.1; // deviation from possible speed/speed (0.1 = 10%)
int angle = 90; //the smaller the more flexible, angle between vectors to next and previous segment
float angledev = angle*0.1;
int counter;
FloatList rghistory; //Rg history for visualization for all chains
float rgcounter = 0; //counter for sum for total rg
float Rg = 0; //total rg
float rgmin = 0; //Minimum average Rg
float rgmax = 0; //Maximum average Rg

int boxsize = 500;
float camangle = 90; //camera angle

void setup() {
size(900, 700, P3D);
frameRate(20);

rghistory = new FloatList();
coils = new Coil[cnumber];
for (int j=0; j < cnumber; j++) {
coils[j] = new Coil();
if (j == 0) {
coils[j].selected = true;
}
}
}

void draw() {
background(129, 200, 273);
perspective();
lights();
translate(450, 350, 0);
rotateY(camangle);
noFill();
stroke(0);
strokeWeight(1);
box(boxsize);

if (mousePressed == true) {
camangle += 0.1;
}

for (Coil i : coils) {
i.show();
i.movement();
i.sizecalc();
}
//should be after coil.show so that translate applies to coil drawing
datascreen();
}

void keyReleased() {
if (keyCode == UP) {
for (int j = 0; j < cnumber; j++) {
if ( coils[j].selected == true) {
if  (j == cnumber-1) {
coils.selected = true;
} else {
coils[j+1].selected = true;
}
coils[j].selected = false;
j = cnumber-1;
}
}
}
if (keyCode == DOWN) {
for (int j = 0; j < cnumber; j++) {
if ( coils[j].selected == true) {
if  (j == 0) {
coils[cnumber-1].selected = true;
} else {
coils[j-1].selected = true;
}
coils[j].selected = false;
j = cnumber-1;
}
}
}
}

class Coil {
Segments[] segments;
boolean selected = false;
FloatList crghistory;//stores calculated rg for diagram
float crgmax; //stores maximum rg value
float crgmin = 0; //stores minimum rg value
float crgcounter = 0; //counter for Rg sum
float cRg = 0; //Average Rg value for coil

Coil() {
int clength = int(randomGaussian()*snumberdev+snumber);
segments = new Segments[clength];
crghistory = new FloatList();
startcond();
}

void startcond() {
//create starting point for coil
PVector loc; //starting vector
PVector loc_old = new PVector(); //preceding vector
float test = 0; //angle test
loc = new PVector (map(random(1), 0, 1, -boxsize/4, boxsize/4), map(random(1), 0, 1, -boxsize/4, boxsize/4), map(random(1), 0, 1, -boxsize/4, boxsize/4));
segments = new Segments(loc);
loc_old = loc.copy();
//create random segments
for (int i = 1; i < segments.length; i++) {
PVector loc2 = loc_old;
while (degrees(test) > 180-angle || degrees(test)==0 || loc2 == loc_old) {
loc2=PVector.random3D();
test = PVector.angleBetween(loc2, loc_old);
}
loc2 = loc2.setMag(sdistance);
loc_old=loc2.copy();
segments[i] = new Segments(loc2);
}
}

void show() {
for (int i = 1; i < segments.length; i++) {
//check for active chain
if (selected == true) {
stroke(255, 0, 0);
} else {
stroke(0);
}
PVector point0 = new PVector();
PVector point1 = new PVector();
point0 = segments[i-1].location.copy();
point1= segments[i].location.copy();
//check that segments are within box; while instead of if to cover cases with coordinates larger than boxsize/2
while (point0.x > boxsize/2) {
point0.x -=boxsize;
}
while (point0.x < -boxsize/2) {
point0.x +=boxsize;
}
while (point0.y > boxsize/2) {
point0.y -=boxsize;
}
while (point0.y < -boxsize/2) {
point0.y +=boxsize;
}
while (point0.z > boxsize/2) {
point0.z -=boxsize;
}
while (point0.z < -boxsize/2) {
point0.z +=boxsize;
}
while (point1.x > boxsize/2) {
point1.x -=boxsize;
}
while (point1.x < -boxsize/2) {
point1.x +=boxsize;
}
while (point1.y > boxsize/2) {
point1.y -=boxsize;
}
while (point1.y < -boxsize/2) {
point1.y +=boxsize;
}
while (point1.z > boxsize/2) {
point1.z -=boxsize;
}
while (point1.z < -boxsize/2) {
point1.z +=boxsize;
}

//check if points are on opposite ends of box, then no line is drawn, 2*sdistance to accommodate deviation
if (abs(point1.x - point0.x) < sdistance+2*segdistdev && abs(point1.y - point0.y) < sdistance+2*segdistdev && abs(point1.z - point0.z) < sdistance+2*segdistdev) {
strokeWeight(5);
line(point0.x, point0.y, point0.z, point1.x, point1.y, point1.z);
strokeWeight(1);
}
}
}

void movement() {
//all chain movement
PVector coilmov = PVector.random3D();
float coilstep = randomGaussian()*stepdev+step;
coilmov = coilmov.setMag(coilstep);
for (Segments s : segments) {
}
//movement of every segment based on chosen segmentdistance and angle
for (int i = 1; i < segments.length-2; i++) {
PVector lastseg = segments[i-1].location.copy();
PVector nextseg = segments[i+1].location.copy();
PVector axis = PVector.sub(lastseg, nextseg);
float axislength = PVector.dist(lastseg, nextseg);
PVector proj = new PVector();
//using random for angle selection stretches chains over time
//if you want to see the effect of flexibility (low/large angle) use Gaussian angle selection
float distnew = random(sdistance-segdistdev, sdistance+segdistdev);
//calculation of distance between previous and next segment
float newdistance = sqrt(2*distnew*distnew - 2*distnew*distnew*cos(newangle));
//adjustment of distance between previous and next, if necessary
if (newdistance != axislength) {
PVector d = axis.copy();
d.setMag(axislength-newdistance);
//all segments after i need to be moved, so that chain stays intact
for (int j = i+1; j < segments.length; j++) {
}
}

axis = PVector.sub(segments[i-1].location, segments[i+1].location);
proj = axis.copy();
proj.setMag(newdistance/2);

//Identify axis coordinate with smallest absolute value to create perpendicular vector
float ax = abs(axis.x);
float ay = abs(axis.y);
float az = abs(axis.z);
PVector b = new PVector();
if (ax <= ay && ax <= az) {
b = new PVector(1, 0, 0);
} else if (ax > ay && ay <= az) {
b = new PVector(0, 1, 0);
} else {
b = new PVector(0, 0, 1);
}

//create vectors in plane with intersect circle
PVector iv = new PVector();
PVector.cross(axis, b, iv);
PVector jv = new PVector();
PVector.cross(axis, iv, jv);
iv.normalize();
jv.normalize();

PVector c2 = new PVector();
PVector c3 = new PVector();
PVector p = new PVector();
float cang = random(TWO_PI);
PVector.mult(iv, cos(cang), c2);
PVector.mult(jv, sin(cang), c3);

segments[i].location = p.copy();
}
}

void sizecalc() {
PVector etev = new PVector();
float crgtemp = 0;

crgcounter++;
PVector.sub(segments.location, segments[segments.length-1].location, etev);
crgtemp = sqrt (etev.magSq()/6);
if (crghistory.size() <= 20) {
crghistory.append(crgtemp);
} else {
crghistory.remove(0);
crghistory.append(crgtemp);
}
if (crgtemp > crgmax) {
crgmax = crgtemp;
} else if (crgtemp < crgmin || crgmin==0) {
crgmin = crgtemp;
}

cRg = (crgcounter-1)*cRg/crgcounter + crgtemp/crgcounter;

rgcounter++;
Rg = ((rgcounter-1)*Rg)/rgcounter + crgtemp/rgcounter;
if (rghistory.size() <= 20) {
rghistory.append(Rg);
} else {
rghistory.remove(0);
rghistory.append(Rg);
}

if (Rg > rgmax) {
rgmax = Rg;
} else if (Rg < rgmin || rgmin==0) {
rgmin = Rg;
}
}
}

void datascreen() {
textSize(18);
hint(DISABLE_DEPTH_TEST);
int rectx = 175;
int recty = height - 140;
int rectsz = 110;
camera();
ortho();

noStroke();
fill(129, 200, 273, 200);
rect(0, recty-50, width, recty+15);

stroke(255);
strokeWeight(3);
noFill();
rect(rectx, recty, 240, 110);
fill(255);
// Single Coil Data Visualization
textSize(20);
text("Current Radius of gyration of selected chain", rectx-150, recty-20);
textSize(18);
text("Max", rectx-150, recty+9);
for (Coil c : coils) {
if (c.selected == true) {
text(c.crgmax, rectx-100, recty+9);
text("Avg", rectx-150, recty+rectsz/2+9);
text(c.cRg, rectx-100, recty+rectsz/2+9);
for (int i = 1; i < c.crghistory.size(); i++) {
float y0 = map(c.crghistory.get(i-1), c.crgmin, c.crgmax, rectsz, 0);
float y1 = map(c.crghistory.get(i), c.crgmin, c.crgmax, rectsz, 0);
stroke(125);
line(rectx+(i-1)*12, recty+y0, rectx+i*12, recty+y1);
}
text("Min", rectx-150, recty+rectsz+9);
text(c.crgmin, rectx-100, recty+rectsz+9);
}
}
//All coil average datea visualization
rectx = 625;
stroke(255);
textSize(20);
text("Radius of gyration over all chains", rectx-150, recty-20);
textSize(18);
text("Max", rectx-150, recty+9);
text(rgmax, rectx-100, recty+9);
text("Avg", rectx-150, recty+rectsz/2+9);
text(Rg, rectx-100, recty+rectsz/2+9);
text("Min", rectx-150, recty+rectsz+9);
text(rgmin, rectx-100, recty+rectsz+9);
noFill();
rect(rectx, recty, 240, rectsz);
for (int i = 1; i < rghistory.size(); i++) {
float y0 = map(rghistory.get(i-1), rgmin, rgmax, rectsz, 0);
float y1 = map(rghistory.get(i), rgmin, rgmax, rectsz, 0);
stroke(125);
line(rectx+(i-1)*12, recty+y0, rectx+i*12, recty+y1);
}
hint(ENABLE_DEPTH_TEST);
}

class Segments {
PVector location;

Segments(PVector loc) {
location = loc.copy();
}
}
``````
1 Like