class TwisterElem { pt origin = new pt(0,0,0); float radius = 20; float angle = 0; boolean handleSelected = false; boolean originSelected = false; pt handle = new pt(0,0,0); pt constrainPt = new pt(0,0,0); void saveTo (ArrayList a ) { origin.saveTo (a); a.add ( "" + radius ); a.add ("" + angle ); } void readFrom (ArrayList a ) { origin.readFrom (a); radius = Float.parseFloat ( (String) a.remove(0) ); angle = Float.parseFloat ( (String) a.remove(0) ); updateHandle (); } void drawTwisterElem (boolean area) { //center stroke(255,0,0); fill (255,0,0); ellipseMode(CENTER); ellipse(origin.x, origin.y,5,5); // circle if (area) { fill (0,0,0,0); stroke(0,255,0); ellipse ( origin.x, origin.y, 2*radius, 2*radius ); } fill (0,0,255); stroke(0,0,255); updateHandle (); ellipse ( handle.x, handle.y, 5,5); line ( origin.x, origin.y, handle.x, handle.y ); } void updateHandle () { handle.x = origin.x + radius * cos(angle); handle.y = origin.y + radius * sin(angle); } boolean handlePressEvent() { pt mouse = new pt(0,0,0); mouse.setToMouse (); if ( origin.sub(mouse).norm () < 7 ) { originSelected = true; constrainPt = mouse; return true; } if ( handle.sub(mouse).norm() < 7 ) { handleSelected = true; constrainPt = mouse; return true; } return false; } void dragEventImpl () { pt mouse = new pt(0,0,0); mouse.setToMouse (); if ( originSelected ) { origin.addPt ( mouse.sub(constrainPt)); } if ( handleSelected ) { handle.addPt ( mouse.sub ( constrainPt )); pt diff = handle.sub(origin); radius = diff.norm(); angle = atan2 ( diff.y, diff.x ); } constrainPt = mouse; } void handleDragEvent () { dragEventImpl(); } void handleReleaseEvent () { dragEventImpl (); handleSelected = false; originSelected = false; } } class Twister { TwisterElem first = new TwisterElem(); TwisterElem second = new TwisterElem(); float b; float radius; pt M = new pt(0,0,0); boolean twistPoint ( pt A, pt B, float t, pt P ) { // am I resposible ? float d = A.sub(first.origin).norm (); if ( d > first.radius ) return false; float frac; if ( P == null ) frac = sq ( cos ( d / first.radius * HALF_PI ) ); else frac = sq ( cos ( P.sub(first.origin).norm() / first.radius * HALF_PI) ); B.setTo ( A.sub (M) ); if (abs(b) < 1e-6 ) // just translate B.addPt ( second.origin.sub ( first.origin).sca(frac) ); else B.rotate ( (float) t * b * frac ); B.addPt (M); return true; } void updateTwist () { b = second.angle - first.angle; M = first.origin.add ( second.origin ).sca(0.5); pt D = second.origin.sub ( first.origin ); pt N = D.left ().unit(); float h = D.norm () * 0.5 / tan(b*0.5); M.addPt ( N.sca(h) ); radius = first.origin.sub(M).norm(); } void drawTwister () { first.drawTwisterElem (true); second.drawTwisterElem (false); ellipseMode(CENTER); noFill (); stroke(0,0,255); ellipse (M.x, M.y, 7,7); beginShape (LINES) ; vertex ( first.origin.x, first.origin.y ); if ( abs(b) > 0 ) { for ( int i = 0 ; i < 50; ++i ) { pt v = first.origin.sub (M); v.rotate ( (float) i * b / 50.0 ); v.addPt (M); vertex ( v.x, v.y ); vertex ( v.x, v.y ); } } vertex ( second.origin.x, second.origin.y ); endShape (); } boolean handlePressEvent() { boolean b = first.handlePressEvent (); b = b || second.handlePressEvent (); return b; } void handleDragEvent () { first.handleDragEvent (); second.handleDragEvent (); updateTwist(); } void handleReleaseEvent () { first.handleReleaseEvent (); second.handleReleaseEvent (); updateTwist(); } void saveTo (ArrayList a) { first.saveTo (a); second.saveTo (a); } void readFrom ( ArrayList a ) { first.readFrom (a); second.readFrom (a); updateTwist (); } }