Im trying to build a hierarchy of ellipses such that each child ellipse has to stay within a certain range of its parent.
I understand how it needs to work, Im just not sure how to go about it in processing.
I built a prototype in 3D software that works perfectly.
starting with the smallest; each circle has a larger child which is constrained to it. (see .gif)
Do you want this to happen in 2D (ellipses) or in 3D (ellipses with different depth) ?
Thoughts:
If the radius of the inner ellipse gets moved, the outer ellipse move.
So the hierarchy is from inside out.
The outer ellipse (number 1) moves when its predecessor (inner one, number 0) touches it on the inside; this can be seen as dist ( x0,y0 , x1,y1 ) < r0 or something (x,y being position and r radius).
If this is the case we got to move (1) (not sure how; atan2 comes to mind).
I wrote a really long answer giving very complicated and partially wrong instructions on how you could accomplish this, but then removed it and just did the code.
Circle[] circles;
void setup() {
size(640, 480, P2D);
// Initialize and populate the array
circles = new Circle[20];
for (int i=0; i<circles.length; i++) {
circles[i]=new Circle(300, 300, 25+i*5);
}
}
void draw() {
// Draw everything in blackness
background(0);
// If a mouse button is held, move first circle
if (mousePressed) {
circles[0].posX+=mouseX-pmouseX;
circles[0].posY+=mouseY-pmouseY;
}
// Various drawing parameters - no fill, white border
noFill();
stroke(255);
// Iterate over all circles
for (int i=0; i<circles.length; i++) {
// If statement to not run this on first circle - will give nullPointerException
if (i!=0) {
// Move this circle so that it contains the previous one within itself
circles[i].occludeCircle(circles[i-1]);
}
// Draw all circles
circles[i].draw();
}
}
class Circle {
float posX, posY, radius;
Circle(float posX, float posY, float radius) {
this.posX=posX;
this.posY=posY;
this.radius=radius;
}
void draw() {
circle(posX, posY, radius*2); // *2 is because this function takes diameter
}
// Function that keeps the "other" circle inside of this one by moving this one
void occludeCircle(Circle other) {
float distance=dist(posX, posY, other.posX, other.posY);
float distanceOffset=radius-other.radius;
// Check if the other circle is partially or completely outside of this one
if (distance>distanceOffset) {
// Get angle and distance needed to travel
float angle = angle(other.posX-posX, other.posY-posY);
float travelDistance=distance-distanceOffset;
// Transform them into X/Y coordinates to add to current circle's position
posX+=cos(radians(angle))*travelDistance;
posY+=sin(radians(angle))*travelDistance;
}
}
}
// Function that returns the angle on a 2D plane we should go forward to if we want to go to input coordinates
// Made during one really busy angry night of hacking things with edge cases like result being 180 degrees and stuff
// Probably best to replace with something less horribly looking - but so far this works. :V
// Eh, triggonometry makes me ultra hacky automatically for some reason, sorry for this weird thing!
float angle(float x, float y) {
return y!=0?(x<0?(180*Math.signum(y)-(atan(y/abs(x))/(PI)*180)):atan(y/abs(x))/(PI)*180):Math.signum(x)<0?(180):0;
}
P.S. Looks like the software you are using is taking a completely different approach that does not care about the circles and instead drags the chain of those red dots around, drawing circles with varied diameters around them. With that in mind, my approach here is different, but as long as what is shown in your GIF is what you wanted, this does the thing.
Good point. I’d say guard it by checking the minimum length – this could be either 1 or 2, depending on if you want a single item to be rendered, or require a pair at a minimum.
Of course, you can also guard against null, etc. etc. It really depends on the rest of the sketch.