License text

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER 
 * Copyright  2008, 2010 Oracle and/or its affiliates.  All rights reserved. 
 * Use is subject to license terms.
 * 
 * This file is available and licensed under the following license:
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 
 *   * Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer. 
 *
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *   * Neither the name of Oracle Corporation nor the names of its contributors 
 *     may be used to endorse or promote products derived from this software 
 *     without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

    

package fishsim;

import javafx.animation.*;
import javafx.scene.*;
import javafx.scene.shape.*;
import javafx.scene.image.*;
import javafx.scene.paint.*;
import javafx.scene.transform.*;

import java.lang.Math;

/**
 * This class defines the fish itself, it's wagging animation, and how to move
 * to wherever the user clicked.
 *
 */

public class Fish extends CustomNode {
    var ang:Number = 0.0;                       // controls the angle of each segment, used for the wag animation
    var heading:Number = 0.0;                   // the heading of the entire fish

//make angle loop from -10 to +10 degrees
    public var wag = Timeline {
        keyFrames: [
            at(0s) { ang=> -10.0 tween Interpolator.EASEBOTH },
            at(1s) { ang=>  10.0 tween Interpolator.EASEBOTH },
        ]
        autoReverse: true
        repeatCount: Timeline.INDEFINITE
    };
    

    /* The fish is made up of segments, each decreasing in size,
     * except for the tail, which is hard coded to be scale=0.7.
     * each segment is bound to the 'angle' attribute, which is animated
     * by the 'wag' timeline.
     */
    override public function create():Node {
        var g:Group = Group {
            transforms: bind [Transform.rotate(heading+90,0,0)]
        };

        var orig = g;

        for(i in [0..4]) {
            var scale = 0.6 * (5 - i) / 3;
            if(i==4) {
                scale = 0.7;
            }
            var newg = Group {
                transforms: bind [
                    Transform.translate(20,0),
                    Transform.rotate(ang,20,0)
                ]
                content: [
                    ImageView {
                        scaleX: scale
                        scaleY: scale
                        translateY: -50;
                        image: Image { url: "{__DIR__}scale2.png" }
                        smooth: true
                    },
                ]
            }
            insert newg into g.content;
            // add eyeballs to the first one
            if(i == 0) {
                var eyeSpacing = 10;
                var eyeOffset = 20;
                insert Circle { 
			centerX: eyeOffset 
			centerY: eyeSpacing 
			radius: 10 
			fill: Color.WHITE 
		} into newg.content;
                insert Circle { 
			centerX: eyeOffset 
			centerY: -eyeSpacing 
			radius: 10 
			fill: Color.WHITE 
		} into newg.content;
                insert Circle { 
			centerX: eyeOffset-4 
			centerY: eyeSpacing 
			radius: 5 
			fill: Color.BLACK 
		} into newg.content;
                insert Circle { 
			centerX: eyeOffset-4 
			centerY: -eyeSpacing 
			radius: 5 
			fill: Color.BLACK 
		} into newg.content;
            }
            g = newg;
        }

        return orig;
    }
    
    
    var move:Timeline = Timeline { };
    public function goTo(x:Number, y:Number):Void {
        move.stop();
        var xoff = x - translateX;
        var yoff = y - translateY;
        if(xoff == 0 and yoff == 0) {
            return;
        }
        var angg = calcAngle(translateX, translateY, x, y);
        var dist = Math.sqrt(xoff * xoff + yoff * yoff);
        
        var t = 1s * dist / 100.0; 		//speed = 100px / sec
        move = Timeline {
            keyFrames: [
                KeyFrame {
                    time: t
                    values: [
                        heading => angg tween Interpolator.LINEAR,
                        translateX => x tween Interpolator.LINEAR,
                        translateY => y tween Interpolator.LINEAR,
                    ]
                }
            ]
        }
        move.play();
    }

    function calcAngle(x1:Number, y1:Number, x2:Number, y2:Number):Number {
        var xoff = x2-x1;
        var yoff = y2-y1;
        var hyp = Math.sqrt(xoff*xoff+yoff*yoff);
        var angle = Math.asin(yoff/hyp);
        return Math.toDegrees(angle);
    }
}