/************************************************************************** Synopsis: Animation involving circular blocks Author: Kannan Balasubramanian Last Updated Date: 11/23/2007 **************************************************************************/ import javafx.ui.*; import javafx.ui.filter.*; import javafx.ui.canvas.*; import java.lang.System; import java.awt.Color as AWTColor; import java.awt.Paint as AWTPaint; import java.awt.Toolkit; import java.lang.Math; import java.util.Random; import java.awt.GraphicsEnvironment; import java.awt.Dimension; /* * Documented: * Press key 'c' or 'C' to toggle circle colors * Press 'left/right' arrow to increase/decrease circle count * Press 'up/down' arrow to increase/decrease circle radius * Press key 'p' to pause/resume animation * Press 'Esc' key to exit demo * * Undocumented: * Press key '3' to draw circles in 3d shape * Press key '2' to draw circles in 2d shape */ var screensize = Toolkit.getDefaultToolkit().getScreenSize(); var model = CircleModel { }; System.out.println("Screen Resolution: {screensize.width} * {screensize.height}"); class FXCircle extends Circle { attribute direction: Number; } class CircleModel { attribute MIN_RADIUS:Number; attribute MAX_RADIUS:Number; attribute circles: FXCircle*; attribute elapsed: Number; attribute rand: Random; attribute pause: Boolean; attribute screensize: Dimension; attribute customColor: Color; attribute colorFlag: Boolean; attribute b3dFlag: Boolean; attribute zoomLevel: Number; operation animateCircles(); operation fillCircles(flag:Boolean); operation set3D(flag:Boolean); operation is3D(): Boolean; operation modifyCircleRadius(radius:Number); operation getDirection(x:Number, y:Number, sx:Number, sy:Number, sw:Number, sh:Number): Number; } attribute CircleModel.elapsed = bind [0..5000] dur 200000 linear; attribute CircleModel.rand = new Random(); attribute CircleModel.screensize = Toolkit.getDefaultToolkit().getScreenSize(); attribute CircleModel.pause = false; attribute CircleModel.colorFlag = false; attribute CircleModel.b3dFlag = false; attribute CircleModel.zoomLevel = 0; attribute CircleModel.MIN_RADIUS= 2; attribute CircleModel.MAX_RADIUS= 12; attribute CircleModel.customColor = Color { red: 238/255 blue: 154/255 green: 73/255 }; trigger on CircleModel.elapsed = newValue { animateCircles(); } operation CircleModel.animateCircles() { var offset = 15; if (pause) { return; } for (c in circles) { if (c.direction == 0) { c.cx = c.cx + offset; c.cy = c.cy + offset; } else if (c.direction == 1) { c.cx = c.cx - offset; c.cy = c.cy + offset; } else if (c.direction == 2) { c.cx = c.cx + offset; c.cy = c.cy - offset; } else if (c.direction == 3) { c.cx = c.cx - offset; c.cy = c.cy - offset; } else if (c.direction == 4) { c.cx = c.cx - offset; } else if (c.direction == 5) { c.cy = c.cy - offset; } else if (c.direction == 6) { c.cx = c.cx + offset; } else if (c.direction == 7) { c.cy = c.cy + offset; } var screensize = Toolkit.getDefaultToolkit().getScreenSize(); var midwidth = screensize.width/2; var sx = midwidth/4; var sy = screensize.height/4; var wx = midwidth/2; var wy = screensize.height/2; if (c.cx <= 0 or c.cx >= midwidth) { c.cx = sx + Math.random() * wx; c.cy = sy + Math.random() * wy; c.direction = getDirection(c.cx, c.cy, sx, sy, wx, wy); continue; } if (c.cy <= 0 or c.cy >= screensize.height) { c.cx = sx + Math.random() * wx; c.cy = sy + Math.random() * wy; c.direction = getDirection(c.cx, c.cy, sx, sy, wx, wy); continue; } } } operation CircleModel.getDirection(x:Number, y:Number, sx:Number, sy:Number, sw:Number, sh:Number) { var midx = sx + sw/2; var midy = sy + sh/2; var direction; //var v = (int) (Math.random() * 10) % 3; var v = rand.nextInt(10) % 3; if (x <= midx and y <= midy) { //1st Quadrant if (v == 0) { direction = 3; } else if (v == 1) { direction = 4; } else if (v == 2) { direction = 5; } } else if (x > midx and y <= midy) { //2nd Quadrant if (v == 0) { direction = 2; } else if (v == 1) { direction = 5; } else if (v == 2) { direction = 6; } } else if (x <= midx and y > midy) { //4th Quadrant if (v == 0) { direction = 1; } else if (v == 1) { direction = 4; } else if (v == 2) { direction = 7; } } else if (x > midx and y > midy) { //3rd Quadrant if (v == 0) { direction = 0; } else if (v == 1) { direction = 6; } else if (v == 2) { direction = 7; } } return direction; } operation CircleModel.is3D() { return b3dFlag; } operation CircleModel.set3D(bFlag: Boolean) { b3dFlag = bFlag; if (not b3dFlag) { fillCircles(colorFlag); return; } for (c in circles) { c.fill = RadialGradient { cx: c.cx cy: c.cy radius: c.radius focusX: c.cx focusY: c.cy spreadMethod: REPEAT:SpreadMethod stops: [Stop { color: customColor offset: 0 }, Stop { color: white offset: 1 }] }; } } operation CircleModel.fillCircles(f: Boolean) { if (pause) { return; } if (not f) { for (c in circles) { c.fill = white; } return; } for (c in circles) { c.fill = Color { red: Math.random() blue: Math.random() green: Math.random() }; } } operation CircleModel.modifyCircleRadius(offset:Number) { for (c in circles) { if (offset >= 0) { if (c.radius + offset > MAX_RADIUS) { continue; } } else if (c.radius + offset < MIN_RADIUS) { continue; } c.radius = c.radius + offset; zoomLevel = c.radius; } } for (i in [1..5]) { var midwidth = screensize.width/2; var sx = midwidth/4; var sy = screensize.height/4; var wx = midwidth/2; var wy = screensize.height/2; var c = FXCircle { cx: sx + Math.random() * wx cy: sy + Math.random() * wy }; c.direction = model.getDirection(c.cx, c.cy, sx, sy, wx, wy); insert c into model.circles; } trigger on new FXCircle { fill = white; var val = Math.random() * 10; if (val <= 5) { radius = 4; } else { radius = 5; } } var frame = Frame { width: screensize.width/2 height: screensize.height undecorated: true disposeOnClose: true content: Canvas { background: black focusable: true focused: true doubleBuffered: true var circleText = "No. of circles: {sizeof model.circles}" content: bind [model.circles, Text { x: screensize.width/4-30 y: 20 fill: white outline: true content: "JAVAFX" font: Font { size:20 style:BOLD faceName: "Courier" } }, Text { x: 20 y: 40 fill: white content: "Press c to toggle circle colors" font: Font { size:12 style:BOLD faceName: "Courier" } }, Text { x: 20 y: 60 fill: magenta content: bind circleText font: Font { size:16 style:BOLD faceName: "Courier" } }, Text { x: 20 y: 80 fill: white content: "Press left or right arrow to +/- circles count" font: Font { size:12 style:BOLD faceName: "Courier" } }, Text { x: 20 y: 100 fill: white content: "Press up or down arrow to +/- zoomin/zoomout" font: Font { size:12 style:BOLD faceName: "Courier" } }, Text { x: 20 y: 120 fill: white content: "Press p to pause/resume" font: Font { size:12 style:BOLD faceName: "Courier" } }, Text { x: 20 y: 140 fill: white content: "Press Esc to exit" font: Font { size:12 style:BOLD faceName: "Courier" } }, Rect { x: screensize.width/8 y: screensize.height/4 width: screensize.width/4 height: screensize.height/2 }, ] keyboardAction: KeyboardAction { enabled: true } onKeyTyped: operation(e:KeyEvent) { if (not model.pause and not model.is3D() and (e.keyChar == 'c' or e.keyChar == 'C')) { model.colorFlag = not model.colorFlag; model.fillCircles(model.colorFlag); } else if (e.keyChar == 'p' or e.keyChar == 'P') { model.pause = not model.pause; } else if (e.keyChar == '3') { model.set3D(true); } else if (e.keyChar == '2') { model.set3D(false); } } onKeyDown: operation(e:KeyEvent) { if (model.pause) { return; } if (e.keyStroke == ESCAPE:KeyStroke) { System.exit(0); } if (e.keyStroke == LEFT:KeyStroke) { if (sizeof model.circles <= 100) { for (i in [1..10]) { var midwidth = screensize.width/2; var sx = midwidth/4; var sy = screensize.height/4; var wx = midwidth/2; var wy = screensize.height/2; var c = FXCircle { cx: sx + Math.random() * wx cy: sy + Math.random() * wy }; c.direction = model.getDirection(c.cx, c.cy, sx, sy, wx, wy); var val = Math.random() * 10; if (model.zoomLevel <> 0) { c.radius = model.zoomLevel; } else if (val <= 5) { c.radius = 4; } else { c.radius = 5; } if(model.is3D()) { c.fill = RadialGradient { cx: c.cx cy: c.cy radius: c.radius focusX: c.cx focusY: c.cy spreadMethod: REPEAT:SpreadMethod stops: [Stop { color: model.customColor offset: 0 }, Stop { color: white offset: 1 }] }; } else if(not model.colorFlag) { c.fill = white; } else { c.fill = Color { red: Math.random() blue: Math.random() green: Math.random() }; } insert c into model.circles; } circleText = "No. of circles: {sizeof model.circles}"; } } else if (e.keyStroke == RIGHT:KeyStroke) { if (sizeof model.circles >= 15) { for (i in [1..10]) { delete model.circles[0]; } } circleText = "No. of circles: {sizeof model.circles}"; } else if (e.keyStroke == UP:KeyStroke) { model.modifyCircleRadius(2); } else if (e.keyStroke == DOWN:KeyStroke) { model.modifyCircleRadius(-2); } } } visible: true };