It is possible but the implementation will take a bit of design.
Here I have done the demonstration. I hope you can use it and you can gain something for duture demos.
Kf
//INSTRUCTIONS:
// * Drag any static color box into inside the resizing colored areas and experience
// the coupling between static and dynamic figures:
// - Orange couples with yellow
// - Green couples with red
// - Violet couples with blue
color red = #FF0004;
color yellow = #F2F700;
color blue = #0A30FF;
color black = #000000;
color white = #FFFFFF;
color green = #08FF79;
color purple = #E108FF;
color orange = #FFA908;
Oscillator2D randomPosition;
StaticSquare sq1;
StaticSquare sq2;
StaticSquare sq3;
DynamicSquare dynamicSq1;
DynamicSquare dynamicSq2;
DynamicSquare dynamicSq3;
void settings() {
size(600, 600);
}
void setup() {
background(black);
//frameRate(50);
surface.setTitle("Mondrian");
randomPosition=new Oscillator2D();
float rx=randomPosition.getX();
float ry=randomPosition.getY();
// * Normal Squares are defined by top left corner's position, width and height
// * Static and dynamic squares follow this internally.
// * However, dynamic squares are constructed using the top left corner and bottom
// right corner and internalized to top left corner, witdth and height
dynamicSq3=new DynamicSquare(yellow);
dynamicSq3.setCorners(0, 120, rx, ry);
dynamicSq1=new DynamicSquare(red);
dynamicSq1.setCorners(0, ry, rx, height);
dynamicSq2=new DynamicSquare(blue);
dynamicSq2.setCorners(rx, ry, width, height);
sq1=new StaticSquare(160, 20, 80, 80, green);
sq2=new StaticSquare(260, 20, 80, 80, purple);
sq3=new StaticSquare(360, 20, 80, 80, orange);
}
void draw() {
background(black);
randomPosition.update();
float rx=randomPosition.getX();
float ry=randomPosition.getY();
dynamicSq3.updateX2(rx);
dynamicSq3.updateY2(ry);
dynamicSq1.updateX2(rx);
dynamicSq1.updateY1(ry);
dynamicSq2.updateX1(rx);
dynamicSq2.updateY1(ry);
dynamicSq1.draw(sq1);
dynamicSq2.draw(sq2);
dynamicSq3.draw(sq3);
sq1.draw();
sq2.draw();
sq3.draw();
}
void mouseReleased() {
sq1.releaseLock();
sq2.releaseLock();
sq3.releaseLock();
}
//================================================
//
//================================================
class NormalSquare {
float px;
float py;
float w;
float h;
color c;
NormalSquare(float px, float py, float width, float height, color c) {
this.px=px;
this.py=py;
this.w=width;
this.h=height;
this.c=c;
}
void draw() {
draw(c);
}
void draw(color thisColor) {
fill(thisColor);
noStroke();
rect(px, py, w, h);
}
void printObj() {
println(px, py, w, h, c);
}
}
//================================================
//
//================================================
class DynamicSquare extends NormalSquare {
DynamicSquare(color c) {
super(0, 0, 0, 0, c);
}
void setCorners(float px1, float py1, float px2, float py2) {
this.px=px1;
this.py=py1;
this.w=px2-px1; //Calculates width
this.h=py2-py1; //Calculates height
}
void updateX1(float val) {
px=val;
w=width-px;
}
void updateY1(float val) {
py=val;
h=height-py;
}
void updateX2(float val) {
w=val-px;
}
void updateY2(float val) {
h=val-py;
}
void draw(NormalSquare other) {
if (isOnTop(other))
draw(other.c);
else
draw();
}
boolean isOnTop(NormalSquare other) {
return px<other.px && px+w>other.px+other.w && py<other.py && py+h>other.py+other.h;
}
}
//================================================
//
//================================================
class StaticSquare extends NormalSquare {
final float INVALID=-1;
boolean mouseLockOn;
float mx=INVALID; //Offset for mouse, negative means not valid
float my=INVALID;
float initX;
float initY;
StaticSquare(float px, float py, float width, float height, color c) {
super(px, py, width, height, c);
initX=px;
initY=py;
mouseLockOn=false;
}
//@Override
// void draw() {
// draw(c);
//}
@Override
void draw(color thisColor) {
if (!mouseLockOn && mousePressed && mouseIsOnTop()) {
mouseLockOn=true;
if (mx==INVALID) {
//Calculate offsets
mx=mouseX-px;
my=mouseY-py;
}
}
if (mouseLockOn) {
px=mouseX-mx;
py=mouseY-my;
}
fill(thisColor);
noStroke();
rect(px, py, w, h);
}
boolean mouseIsOnTop(float cornerX1, float cornerX2, float cornerY1, float cornerY2) {
return mouseX>cornerX1 && mouseX < cornerX2 && mouseY>cornerY1 && mouseY < cornerY2;
}
boolean mouseIsOnTop() {
return mouseIsOnTop(px, px+w, py, py+h);
}
void releaseLock() {
mouseLockOn=false;
px=initX;
py=initY;
mx=INVALID;
my=INVALID;
}
}
//================================================
//
//================================================
class Oscillator2D {
class OscillatorEngine {
float minAbsoluteVal;
float maxAbsoluteVal;
float targetPosMin;
float targetPosMax;
float vel;
float cp; //current pos
float cv; //current vel
OscillatorEngine(float min, float max, float vel) {
minAbsoluteVal=min;
maxAbsoluteVal=max;
this.vel=vel;
initOscillator();
}
//Returns random 1 or -1 for direction
float randomLeftOrRight() {
return random(1) > 0.5 ? 1.0 : -1.0;
}
void initOscillator() {
cp=random(minAbsoluteVal, maxAbsoluteVal);
cv=vel*randomLeftOrRight();
targetPosMin=floor(random(minAbsoluteVal, cp));
targetPosMax=ceil(random(cp, maxAbsoluteVal));
}
void updatePosition() {
cp = cp + cv;
if (cp<targetPosMin) {
cp=targetPosMin;
cv=random(vel, 2*vel)*cv/abs(cv)*(-1);
//Re-caulculate next limit for bounce
targetPosMax=ceil(random(cp, maxAbsoluteVal));
}
if (cp>targetPosMax) {
cp=targetPosMax;
cv=random(vel, 2*vel)*cv/abs(cv)*(-1);
//Re-caulculate next limit for bounce
targetPosMin=floor(random(minAbsoluteVal, cp));
}
}
}
protected OscillatorEngine xOscillator;
protected OscillatorEngine yOscillator;
Oscillator2D() {
xOscillator=new OscillatorEngine(0, width, 1.5);
yOscillator=new OscillatorEngine(120, height, 1.5);
}
void updateX() {
xOscillator.updatePosition();
}
void updateY() {
yOscillator.updatePosition();
}
void update() {
updateX();
updateY();
}
float getX() {
return xOscillator.cp;
}
float getY() {
return yOscillator.cp;
}
}