package bezier;
import javafx.animation.*;
import javafx.util.*;
import javafx.stage.*;
import javafx.ext.swing.*;
import javafx.scene.*;
import javafx.geometry.*;
import javafx.scene.shape.*;
import javafx.scene.paint.*;
import javafx.scene.input.*;
import javafx.scene.transform.*;
import javafx.scene.layout.*;
import javafx.lang.*;
import java.lang.Math;
def fullWidth:Integer = 600;
def w = fullWidth - 123;
def h:Integer = 400;
def wp:Integer = 120;
def vgap:Integer = 20;
def r = 10.0;
def maxV = 2.0;
function rnd(a: Number, b: Number) : Number {
return a + Math.random() * (b - a);
}
var nCoords : Integer = 8;
var v : Number [] = for (i in [0..7][n | indexof n < nCoords]) {
rnd(-maxV, maxV)}
var t : Number [] = for (i in [0..7][n | indexof n < nCoords]) {0.0}
var p : Number [] = for (i in [0..7][n | indexof n < nCoords]) {
if (i mod 2 == 0) {
rnd(r, w - r)
} else {
rnd(r, h - r)
}
}
def clip = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames:
KeyFrame {
time: 40ms
action: function () : Void {
for (i in [0..7][n | indexof n < nCoords]) {
var np = p[i] + v[i];
if (((i mod 2) == 0 and (np > w-r or np < r)) or
((i mod 2) == 1 and (np > h-r or np < r))) {
v[i] = -v[i]
}
p[i] += v[i];
}
}
}
}
var strokeColor: Color = Color.BLUE;
def strokeFld : SwingCheckBox = SwingCheckBox {
width: wp
text: "stroke"
foreground: Color.ALICEBLUE
selected:true
action:function() : Void {
strokeColor = if(strokeFld.selected) Color.BLUE else null;
}
};
var fillColor: Color = null;
def fillFld : SwingCheckBox = SwingCheckBox {
width: wp
text: "fill"
foreground: Color.ALICEBLUE
action:function() : Void {
fillColor = if(fillFld.selected) Color.GREEN else null;
}
};
var cpFillColor : Color;
var cpStrokeColor : Color;
var pathColor : Color;
def cpFld : SwingCheckBox = SwingCheckBox {
width: wp
text: "Control Points"
foreground: Color.ALICEBLUE
action:function() : Void {
if (cpFld.selected) {
cpFillColor = Color.MAROON;
cpStrokeColor = Color.web("0x808000");
pathColor = Color.web("0xff6688",0.5);
} else {
cpFillColor = cpStrokeColor = pathColor = null;
}
}
};
def strokeWidth = SwingSlider {
minimum: 1
maximum: 20
width:wp
height:20
}
var strokeDashArray: Number [];
def strokeDashFld = SwingComboBox {
width: wp
items:[
SwingComboBoxItem {
value: null
text: "null"
selected: true
},
SwingComboBoxItem {
value: [8 4] as Object
text: "[8 4]"
},
SwingComboBoxItem {
value: [32 16] as Object
text: "[32 16]"
},
SwingComboBoxItem {
value: [64 64] as Object
text: "[64 64]"
}
]
editable:false
}
def strokeLineCapFld = SwingComboBox {
width:wp
items:[
SwingComboBoxItem {
text: "BUTT"
value: StrokeLineCap.BUTT;
},
SwingComboBoxItem {
text: "ROUND"
value: StrokeLineCap.ROUND;
},
SwingComboBoxItem {
text: "SQUARE"
value: StrokeLineCap.SQUARE;
selected: true
}
]
}
def curveFld = SwingComboBox {
width:wp
items: [
SwingComboBoxItem {
text: "CubicCurve"
selected: true
value: CubicCurve {
startX : bind p[0] startY : bind p[1]
controlX1 : bind p[2] controlY1 : bind p[3]
controlX2 : bind p[4] controlY2 : bind p[5]
endX : bind p[6] endY : bind p[7]
stroke: bind strokeColor fill: bind fillColor
strokeWidth: bind strokeWidth.value
strokeDashArray: bind
strokeDashFld.selectedItem.value as Number []
strokeLineCap: bind
strokeLineCapFld.selectedItem.value as StrokeLineCap
};
},
SwingComboBoxItem {
text: "QuadCurve"
value: QuadCurve {
startX : bind p[0] startY : bind p[1]
controlX : bind p[2] controlY : bind p[3]
endX : bind p[4] endY : bind p[5]
stroke: bind strokeColor fill: bind fillColor
strokeWidth: bind strokeWidth.value
strokeDashArray: bind
strokeDashFld.selectedItem.value as Number []
strokeLineCap: bind
strokeLineCapFld.selectedItem.value as StrokeLineCap
};
}
]
}
var geomArea : Group = Group {}
var curve : Shape = bind curveFld.selectedItem.value as Shape on replace {
if (curve instanceof CubicCurve) {
nCoords = 8;
} else {
nCoords = 6;
}
geomArea.content = [
curve,
for (i in [0 2 4 6][n | n < nCoords])
Ellipse {
centerX: bind p[i]
centerY: bind p[i + 1]
radiusX:r radiusY:r
stroke: bind cpStrokeColor fill: bind cpFillColor
strokeWidth: 2
onMouseDragged: function(e:MouseEvent) : Void {
t[i] += e.dragX/50;
t[i+1] += e.dragY/50;
p[i] = if (e.sceneX < r) r else {
if (e.sceneX > w-r) w-r else e.sceneX
}
p[i+1] = if (e.sceneY < r) r else {
if (e.sceneY > h-r) h-r else e.sceneY
}
if (t[i] > maxV) t[i] = maxV;
if (t[i] < -maxV) t[i] = -maxV;
if (t[i+1] > maxV) t[i+1] = maxV;
if (t[i+1] < -maxV) t[i+1] = -maxV;
}
onMousePressed: function(e:MouseEvent) : Void {
t[i] = v[i]; t[i+1] = v[i+1];
v[i] = 0; v[i+1] = 0;
}
onMouseReleased: function(e:MouseEvent) : Void {
v[i] = t[i]; v[i+1] = t[i+1];
}
onMouseClicked: function(e:MouseEvent) : Void {
v[i] = 0; v[i+1] = 0;
}
},
Path {
stroke: bind pathColor
strokeWidth: 2
fill:null
elements: [
MoveTo {
x: bind p[0] y: bind p[1]
},
for (i in [2 4 6][n | n < nCoords]) {
LineTo {
x: bind p[i] y: bind p[i+1]
}
}
]
}
]
}
Stage {
title: "Bezier";
visible: true
resizable: false
scene: Scene {
content:[
Group {
content: [
Rectangle {
width: fullWidth height:h
fill: Color.BLACK},
Rectangle {
width: w height:h
fill: null
stroke: Color.WHITE},
Group { content: bind geomArea},
VBox {
translateX: w + 3
layoutBounds: Rectangle2D {
minX: 0
minY: 0
width: wp
height: h
}
content: [
curveFld,
SwingLabel {height: vgap},
strokeFld, fillFld, cpFld,
SwingLabel {height: vgap},
SwingLabel {
width: wp
text: " strokeWidth"
foreground: Color.ALICEBLUE},
strokeWidth,
SwingLabel {height: vgap},
SwingLabel {
width: wp
text: " strokeDashArray"
foreground: Color.ALICEBLUE},
strokeDashFld,
SwingLabel {height: vgap},
SwingLabel {
width: wp
text: " strokeLineCap"
foreground: Color.ALICEBLUE},
strokeLineCapFld
]
}
]
}
]
}
}
strokeWidth.value = 3;
clip.play();
cpFld.selected = true;
cpFld.getJComponent().setBackground(java.awt.Color.BLACK);
fillFld.getJComponent().setBackground(java.awt.Color.BLACK);
strokeFld.getJComponent().setBackground(java.awt.Color.BLACK);
strokeWidth.getJComponent().setBackground(java.awt.Color.BLACK);
cpFld.action();