thabir – Scope

thabir-praxinoscope-output

I was extremely interested in the behavior of circles for this weeks assignments. I wanted to see how I could make them move and interact with each other. To this end, I made a small personal circle library with a Circle class and some methods that allowed me to stretch circles, join circles, and pull circles apart.

I then used this library to implement a design which consists of circles bouncing inside of another circle. I then also implemented a method which allows you to pull one circle out of the other while making it appear like there is some kind of cohesive force between them. I was inspired by bubbles and water droplets.

Somethings I wanted to accomplish but wasn’t able to, was to calculate intersection areas inside the large black circle and color an intersection black if it fell inside an odd number of circles and white otherwise. However, I wasn’t able to think of an efficient way to do this.

These were some of my preparatory sketches:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
class Circle {
  int x;
  int y;
  float r;
  color c;
 
  int[][] samplePoints = new int[120][2];
 
  Circle(int a, int b, float d, color e) {
    x = a;
    y = b;
    r = d;
    c = e;
 
    //sample points from the circle
    for(int i=0; i<samplePoints.length; i++) {
      float angle = radians(i*(360.0/samplePoints.length));
      samplePoints[i][0] = x + round(cos(angle)*r);
      samplePoints[i][1] = y + round(sin(angle)*r);
    }
  }
 
  void move(int a, int b) {
    x = a;
    y = b;
 
    //sample points from the circle
    for(int i=0; i<samplePoints.length; i++) {
      float angle = radians(i*(360.0/samplePoints.length));
      samplePoints[i][0] = x + round(cos(angle)*r);
      samplePoints[i][1] = y + round(sin(angle)*r);
    }
  }
 
  void Resize(float a) {
    r = a;
 
    //sample points from the circle
    for(int i=0; i<samplePoints.length; i++) {
      float angle = radians(i*(360.0/samplePoints.length));
      samplePoints[i][0] = x + round(cos(angle)*r);
      samplePoints[i][1] = y + round(sin(angle)*r);
    }
  }
 
  //projects circle rotated around y axis
  void projectCircle(float angle) {
    fill(c);
    ellipse(x, y, ((cos(angle)*r)*2), (r*2));
  }
 
  void drawCircle() {
    fill(c);
    ellipse(x, y, (r*2), (r*2));
  }
 
}
 
 
//connects circles at the same y coordinate with the same radius with C1 being to the left of C2
void connectCircles(Circle C1, Circle C2) {
  C1.drawCircle();
  fill(C1.c);
  int topLeftX = C1.samplePoints[91][0];
  int topLeftY = C1.samplePoints[91][1];
  int xDistance = C2.samplePoints[31][0] - topLeftX;
  int yDistance = C2.samplePoints[31][1] - topLeftY;
  rect(topLeftX, topLeftY, xDistance, yDistance);
  C2.drawCircle();
}
 
//time is a ratio between 0 and 1 and angle is between -89 and 90
void splitCircle(Circle C1, Circle C2, int angle, float d1, float d2, float time) {
  int dist1 = round(d1*time);
  int dist2 = round(d2*time);
  pushMatrix();
  translate(C1.x, C1.y);
  rotate(radians(angle));
  Circle newC1 = new Circle(-dist1, 0, C1.r, C1.c);
  Circle newC2 = new Circle(dist2, 0, C2.r, C2.c);
  connectCircles(newC1, newC2);
  popMatrix();
}
 
void joinCircle(Circle C1, Circle C2, int angle, float d1, float d2, float time) {
  int dist1 = round(d1*time);
  int dist2 = round(d2*time);
  int x = round(cos(radians(angle))*d1);
  int y = round(sin(radians(angle))*d1);
  pushMatrix();
  translate(C1.x+x, C1.y+y);
  rotate(radians(angle));
  Circle newC1 = new Circle(round(dist1-d1), 0, C1.r, C1.c);
  Circle newC2 = new Circle(round(d2-dist2), 0, C2.r, C2.c);
  connectCircles(newC1, newC2);
  popMatrix();
}
 
//credit to Paul Bourke's method of finding the intersection points of 2 circles
float[] drawIntersectionCircles(Circle C1, Circle C2, float roffset) {
  float r0 = C1.r+roffset; float r1 = C2.r+roffset;
  int x0 = C1.x; int x1 = C2.x;
  int y0 = C1.y; int y1 = C2.y;
  int d = abs(y0 - y1);
  int x1fin = 0; int y1fin = 0; int x2fin = 0; int y2fin = 0;
  float a = (pow(r0, 2)-pow(r1, 2)+pow(d, 2))/(2*d);
  float h = sqrt(pow(r0, 2)-pow(a, 2));
  float x2 = x0 + (a*(x1-x0)/d);
  float y2 = y0 + (a*(y1-y0)/d);
  if(d<=C1.r+C2.r) {
    x1fin = round(x2 + (h*(y1-y0))/d);
    x2fin = round(x2 - (h*(y1-y0))/d);
    y1fin = round(y2 + (h*(x1-x0))/d);
    y2fin = round(y2 - (h*(x1-x0))/d);
    noFill();
    float angle1 = atan(((y1fin-y1)*1.0)/(x1fin-x1)) + PI;
    float angle2 = atan(((y1fin-y0)*1.0)/(x1fin-x0)) + PI;
    arc(x1fin, y1fin, 2*roffset, 2*roffset, angle1, angle2);
    float angle3 = atan(((y2fin-y1)*1.0)/(x2fin-x1));
    float angle4 = atan(((y2fin-y0)*1.0)/(x2fin-x0));
    arc(x2fin, y2fin, 2*roffset, 2*roffset, angle4, angle3);
    float[] angles = new float[2]; angles[0] = angle1; angles[1] = angle3;
    return angles;
  }
  else {
    float[] angles = new float[2]; angles[0] = 3*PI; angles[1] = 3*PI;
    return angles;
  }
}
 
int num = 10;
int r0 = 30;
int rblob = 10;
Circle[] circles = new Circle[0];
 
void init() {
  for(int i=0; i<num; i++) {
    if(i==0) {
      Circle C = new Circle(0, 0, r0, color(0));
      circles = (Circle[])append(circles, C);
    }
    else {
      int r = round(circles[i-1].r * (2.0/3));
      Circle C = new Circle(r-r0, 0, r, color(255));
      circles = (Circle[])append(circles, C);
    }
  }
}
 
void drawBlob(int whichFrame) {
  int y = (2*(r0/3)+15)/5;
  int r = rblob/5;
  Circle C = new Circle(0, 0, rblob, color(255));
  if(whichFrame<5) {
    C.y = (r0/2) + ((whichFrame+1)*y);
    float angles[] = drawIntersectionCircles(circles[0], C, 80.0);
    if(angles[0]<=2*PI && angles[1]<=2*PI) {
      angles[0] = (PI-angles[0])*(-1);
      angles[1] = PI+angles[1];
      arc(C.x, C.y, 2*C.r, 2*C.r, angles[0], angles[1]);
    }
    else C.drawCircle();
  }
  else {
    C.y = (r0/2)+(4*y);
    C.r = rblob - ((whichFrame-4)*r);
    C.drawCircle();
  }
}
 
void drawArtFrame (int whichFrame) {
  pushMatrix();
  rotate(PI/2);
  if(frameCount==1) init();
 
  strokeWeight(2);
  circles[0].drawCircle();
  for(int i=1; i<num; i++) { int rCenter = round(r0 - circles[i].r); float centerAngle = 360.0/(i+1); float sideAngle = radians((180-centerAngle)/2); float sideLength; if (sideAngle>0) sideLength = (rCenter * sin(radians(centerAngle)))/sin(sideAngle);
    else sideLength = rCenter * 2.0;
    float perimeter = sideLength * (i+1);
    float step = perimeter/10.0;
    float exteriorAngle = PI-(2*sideAngle);
 
    pushMatrix();
    translate(-rCenter, 0);
    rotate(-1*sideAngle);
    int current = 0;
    for(int j=0; j<((whichFrame-i+11)%10); j++) {
      if(floor(current+step)<=sideLength) {
        current = floor(current+step);
      }
      else {
        float increment = step - (sideLength - current);
        translate(round(sideLength), 0);
        rotate(exteriorAngle);
        current = floor(increment);
      }
    }
    circles[i].x = current;
    circles[i].drawCircle();
    popMatrix();
  }
  popMatrix();
 
  pushMatrix();
  rotate(PI);
  drawBlob(whichFrame);
  popMatrix();
  drawBlob((whichFrame+5)%10);
}