/*
 * Main.fx
 *
 * Created on 18.09.2008, 13:02:31
 */

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;

/* Velocity of the control points */
var v : Number [] = for (i in [0..7][n | indexof n < nCoords]) {
    rnd(-maxV, maxV)}

/* New velocity of the control points */
var t : Number [] = for (i in [0..7][n | indexof n < nCoords]) {0.0}

/* Control points of cubic or quadratic curve */
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 {
                /* Iterating through all the control points */
                for (i in [0..7][n | indexof n < nCoords]) {
                    var np = p[i] + v[i];
                    /* Changing velocity direction if there is a collision */
                    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;

/* Explicit setting of background of some swing based controls
 * (for Mac OS X only).
 */
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();