import element.*; public class SmoothRoller { public static double degrees(double r) { return r/Math.PI*180; } public static double radians(double d) { return d*Math.PI/180; } public static double sqr(double x) { return x*x;} public static void roller(DrawingWindow d, Pt center, double radius, int n, double offsetDegrees) // pre: d, center are non-null, n odd and greater than 2 // post: draws a constant radius disc with n sides, // diameter 2*radius, n sides, rotated offsetDegrees { double angle; int minorDiam = (int)Math.round(radius*Math.sqrt(sqr(1+Math.cos(Math.PI/n))+sqr(Math.sin(Math.PI/n)))); int x,y; int i; /* for (i = 0; i <= n; i++) { angle = i*360.0/n+offsetDegrees; x = (int)Math.round(center.x()+radius*Math.cos(radians(angle))); y = (int)Math.round(center.y()+radius*Math.sin(radians(angle))); if (i == 0) d.moveTo(x,y); else d.lineTo(x,y); }*/ for (i = 0; i < n; i++) { angle = i*360.0/n+offsetDegrees; x = (int)Math.round(center.x()+radius*Math.cos(radians(angle))); y = (int)Math.round(center.y()-radius*Math.sin(radians(angle))); Arc a = new Arc(x-minorDiam,y-minorDiam,2*minorDiam,2*minorDiam, (int)angle+180-(int)Math.round(90.0/n), (int)Math.round(180.0/n)); d.fill(a); } } public static void roll(DrawingWindow d, double distance,double radius, int n) { int minorDiam = (int)Math.round(radius*Math.sqrt(sqr(1+Math.cos(Math.PI/n))+sqr(Math.sin(Math.PI/n)))); // interior angle between points double interior = 2.0*Math.PI/n; // angle between two points opposite another double exterior = interior/2; // angle of rotation of the roller double angle = (-Math.PI/2.0)-exterior/2-distance/minorDiam; double centAngle; int section = (int)(distance/minorDiam/exterior); double baseline = d.bounds().center().y()+minorDiam/2; double topline = d.bounds().center().y()-minorDiam/2; int x,y; if (section % 2 == 0) { // rolls along bottom centAngle = (distance/minorDiam)%exterior - exterior/2.0; y = (int)Math.round(baseline-minorDiam+radius*Math.cos(centAngle)); double baseDistance = ((int)(distance/exterior/minorDiam/2))*exterior*minorDiam+distance%(exterior*minorDiam); x = (int)Math.round(baseDistance-radius*Math.sin(centAngle)); } else { // pivots on bottom point centAngle = (distance/minorDiam)%exterior - exterior/2.0; y = (int)Math.round(baseline-radius*Math.cos(centAngle)); x = (int)Math.round((((int)(distance/minorDiam/exterior/2))+1)*exterior*minorDiam+radius*Math.sin(centAngle)); } Pt center = new Pt(x,y); d.paintMode(); d.draw(center); d.invertMode(); roller(d,center,radius,n,degrees(angle)); } public static void main(String args[]) { DrawingWindow d = new DrawingWindow(1000,200); int i; final int N = 3; final int n = N; final double radius = 50.0; int minorDiam = (int)Math.round(radius*Math.sqrt(sqr(1+Math.cos(Math.PI/n))+sqr(Math.sin(Math.PI/n)))); roller(d,new Pt(d.bounds().center()),radius,n,0); d.awaitMouseClick(); d.moveTo(0,d.bounds().center().y()-minorDiam/2-1); d.line(d.bounds().width(),0); d.moveTo(0,d.bounds().center().y()+minorDiam/2); d.line(d.bounds().width(),0); Rect block = new Rect(-5,d.bounds().center().y()-minorDiam/2-10,10,10); d.clear(d.bounds()); d.invertMode(); for (i = 0; i < 2*d.bounds().width(); i+=d.mousePressed()?-1:1) { Pt p = block.center(); p.x(i); block.center(p); d.fill(block); roll(d,(double)i,50.0,N); long stop = 10+System.currentTimeMillis(); while (System.currentTimeMillis() <= stop); d.fill(block); roll(d,(double)i,50.0,N); } } } /* */