test/script/jfx/spread.js

Tue, 05 Nov 2013 13:09:40 +0400

author
kshefov
date
Tue, 05 Nov 2013 13:09:40 +0400
changeset 667
bda654c6d59c
parent 0
b1a7da25b547
child 962
ac62e33a99b0
permissions
-rw-r--r--

8027708: NASHORN TEST: Create Nashorn test that draws image step-by-step using JavaFX canvas.
Reviewed-by: jlaskey, lagergren

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation.
aoqi@0 8 *
aoqi@0 9 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 12 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 13 * accompanied this code).
aoqi@0 14 *
aoqi@0 15 * You should have received a copy of the GNU General Public License version
aoqi@0 16 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 18 *
aoqi@0 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 20 * or visit www.oracle.com if you need additional information or have any
aoqi@0 21 * questions.
aoqi@0 22 */
aoqi@0 23
aoqi@0 24 /**
aoqi@0 25 * Testing JavaFX canvas run by Nashorn.
aoqi@0 26 *
aoqi@0 27 * @test/nocompare
aoqi@0 28 * @run
aoqi@0 29 * @fork
aoqi@0 30 */
aoqi@0 31
aoqi@0 32 TESTNAME = "spread";
aoqi@0 33
aoqi@0 34 var WIDTH = 800;
aoqi@0 35 var HEIGHT = 600;
aoqi@0 36 var canvas = new Canvas(WIDTH, HEIGHT);
aoqi@0 37 var context = canvas.graphicsContext2D;
aoqi@0 38
aoqi@0 39 /* "Spread" tech demo of canvas by Tom Theisen
aoqi@0 40 *
aoqi@0 41 * This will animate a sequence of branch structures in a canvas element.
aoqi@0 42 * Each frame, a new direction is calculated, similar to the last frame.
aoqi@0 43 */
aoqi@0 44
aoqi@0 45 var start_width = 20; // starting width of each branch
aoqi@0 46 var frame_time = 30; // milliseconds per frame
aoqi@0 47 var straighten_factor = 0.95; // value from 0 to 1, factor applied to direction_offset every frame
aoqi@0 48 var curviness = 0.2; // amount of random direction change each frame
aoqi@0 49
aoqi@0 50 var color_speed = 0.03; // speed at which colors change when cycling is enabled
aoqi@0 51 var branch_shrink = 0.95; // factor by which branches shrink every frame
aoqi@0 52 var min_width = 1; // minimum WIDTH for branch, after which they are discontinued
aoqi@0 53 var branch_opacity = 0.4; // opacity of lines drawn
aoqi@0 54 var branch_count = 3; // branch count per tree
aoqi@0 55 var branch_bud_size = 0.5; // ratio of original branch size at which branch will split
aoqi@0 56 var branch_bud_angle = 1; // angle offset for split branch;
aoqi@0 57
aoqi@0 58 var paper; // reference to graphics context
aoqi@0 59 var branches = Object(); // linked list of active branches
aoqi@0 60 var color_styles = []; // pre-computed list of colors as styles. format: (r,g,b,a)
aoqi@0 61 var direction_offset = 0; // current direction offset in radians. this is applied to all branches.
aoqi@0 62 var frame = 0; // frame counter
aoqi@0 63 var timespent = 0; // total time spent so far, used to calculate average frame render duration
aoqi@0 64 var frameratespan; // html span element for updating performance number
aoqi@0 65
aoqi@0 66 // preferences object, contains an attribute for each user setting
aoqi@0 67 var prefs = {
aoqi@0 68 wrap: true, // causes branches reaching edge of viewable area to appear on opposite side
aoqi@0 69 fade: false, // fade existing graphics on each frame
aoqi@0 70 cycle: true, // gradually change colors each frame
aoqi@0 71 new_branch_frames: 20 // number of frames elapsed between each auto-generated tree
aoqi@0 72 };
aoqi@0 73
aoqi@0 74 // create tree at the specified position with number of branches
aoqi@0 75 function create_tree(branches, start_width, position, branch_count) {
aoqi@0 76 var angle_offset = Math.PI * 2 / branch_count;
aoqi@0 77 for (var i = 0; i < branch_count; ++i) {
aoqi@0 78 branch_add(branches, new Branch(position, angle_offset * i, start_width));
aoqi@0 79 }
aoqi@0 80 }
aoqi@0 81
aoqi@0 82 // add branch to collection
aoqi@0 83 function branch_add(branches, branch) {
aoqi@0 84 branch.next = branches.next;
aoqi@0 85 branches.next = branch;
aoqi@0 86 }
aoqi@0 87
aoqi@0 88 // get the coordinates for the position of a new tree
aoqi@0 89 // use the center of the canvas
aoqi@0 90 function get_new_tree_center(width, height) {
aoqi@0 91 return {
aoqi@0 92 x: 0.5 * width,
aoqi@0 93 y: 0.5 * height
aoqi@0 94 };
aoqi@0 95 }
aoqi@0 96
aoqi@0 97 // Branch constructor
aoqi@0 98 // position has x and y properties
aoqi@0 99 // direction is in radians
aoqi@0 100 function Branch(position, direction, width) {
aoqi@0 101 this.x = position.x;
aoqi@0 102 this.y = position.y;
aoqi@0 103 this.width = width;
aoqi@0 104 this.original_width = width;
aoqi@0 105 this.direction = direction;
aoqi@0 106 }
aoqi@0 107
aoqi@0 108 // update position, direction and width of a particular branch
aoqi@0 109 function branch_update(branches, branch, paper) {
aoqi@0 110 paper.beginPath();
aoqi@0 111 paper.lineWidth = branch.width;
aoqi@0 112 paper.moveTo(branch.x, branch.y);
aoqi@0 113
aoqi@0 114 branch.width *= branch_shrink;
aoqi@0 115 branch.direction += direction_offset;
aoqi@0 116 branch.x += Math.cos(branch.direction) * branch.width;
aoqi@0 117 branch.y += Math.sin(branch.direction) * branch.width;
aoqi@0 118
aoqi@0 119 paper.lineTo(branch.x, branch.y);
aoqi@0 120 paper.stroke();
aoqi@0 121
aoqi@0 122 if (prefs.wrap) wrap_branch(branch, WIDTH, HEIGHT);
aoqi@0 123
aoqi@0 124 if (branch.width < branch.original_width * branch_bud_size) {
aoqi@0 125 branch.original_width *= branch_bud_size;
aoqi@0 126 branch_add(branches, new Branch(branch, branch.direction + 1, branch.original_width));
aoqi@0 127 }
aoqi@0 128 }
aoqi@0 129
aoqi@0 130 function draw_frame() {
aoqi@0 131 if (prefs.fade) {
aoqi@0 132 paper.fillRect(0, 0, WIDTH, HEIGHT);
aoqi@0 133 }
aoqi@0 134
aoqi@0 135 if (prefs.cycle) {
aoqi@0 136 paper.setStroke(Paint.valueOf(color_styles[frame % color_styles.length]));
aoqi@0 137 }
aoqi@0 138
aoqi@0 139 if (frame++ % prefs.new_branch_frames == 0) {
aoqi@0 140 create_tree(branches, start_width, get_new_tree_center(WIDTH, HEIGHT), branch_count);
aoqi@0 141 }
aoqi@0 142
aoqi@0 143 direction_offset += (0.35 + (frame % 200) * 0.0015) * curviness - curviness / 2;
aoqi@0 144 direction_offset *= straighten_factor;
aoqi@0 145
aoqi@0 146 var branch = branches;
aoqi@0 147 var prev_branch = branches;
aoqi@0 148 while (branch = branch.next) {
aoqi@0 149 branch_update(branches, branch, paper);
aoqi@0 150
aoqi@0 151 if (branch.width < min_width) {
aoqi@0 152 // remove branch from list
aoqi@0 153 prev_branch.next = branch.next;
aoqi@0 154 }
aoqi@0 155
aoqi@0 156 prev_branch = branch;
aoqi@0 157 }
aoqi@0 158 }
aoqi@0 159
aoqi@0 160 // constrain branch position to visible area by "wrapping" from edge to edge
aoqi@0 161 function wrap_branch(branch, WIDTH, HEIGHT) {
aoqi@0 162 branch.x = positive_mod(branch.x, WIDTH);
aoqi@0 163 branch.y = positive_mod(branch.y, HEIGHT);
aoqi@0 164 }
aoqi@0 165
aoqi@0 166 // for a < 0, b > 0, javascript returns a negative number for a % b
aoqi@0 167 // this is a variant of the % operator that adds b to the result in this case
aoqi@0 168 function positive_mod(a, b) {
aoqi@0 169 // ECMA 262 11.5.3: Applying the % Operator
aoqi@0 170 // remainder operator does not convert operands to integers,
aoqi@0 171 // although negative results are possible
aoqi@0 172
aoqi@0 173 return ((a % b) + b) % b;
aoqi@0 174 }
aoqi@0 175
aoqi@0 176 // pre-compute color styles that will be used for color cycling
aoqi@0 177 function populate_colors(color_speed, color_styles, branch_opacity) {
aoqi@0 178 // used in calculation of RGB values
aoqi@0 179 var two_thirds_pi = Math.PI * 2 / 3;
aoqi@0 180 var four_thirds_pi = Math.PI * 4 / 3;
aoqi@0 181 var two_pi = Math.PI * 2;
aoqi@0 182
aoqi@0 183 // hue does represent hue, but not in the conventional HSL scheme
aoqi@0 184 for(var hue = 0; hue < two_pi; hue += color_speed) {
aoqi@0 185 var r = Math.floor(Math.sin(hue) * 128 + 128);
aoqi@0 186 var g = Math.floor(Math.sin(hue + two_thirds_pi) * 128 + 128);
aoqi@0 187 var b = Math.floor(Math.sin(hue + four_thirds_pi) * 128 + 128);
aoqi@0 188 color = "rgba(" + [r, g, b, branch_opacity].join() + ")";
aoqi@0 189
aoqi@0 190 color_styles.push(color);
aoqi@0 191 }
aoqi@0 192 }
aoqi@0 193
aoqi@0 194 // apply initial settings to canvas object
aoqi@0 195 function setup_canvas() {
aoqi@0 196 paper = canvas.graphicsContext2D;
aoqi@0 197 paper.setFill(Paint.valueOf('rgb(0, 0, 0)'));
aoqi@0 198 paper.fillRect(0, 0, WIDTH, HEIGHT);
aoqi@0 199 paper.setFill(Paint.valueOf("rgba(0, 0, 0, 0.005)"));
aoqi@0 200 paper.setStroke(Paint.valueOf("rgba(128, 128, 64, " + String(branch_opacity) + ")"));
aoqi@0 201 }
aoqi@0 202
aoqi@0 203 populate_colors(color_speed, color_styles, branch_opacity);
aoqi@0 204 setup_canvas();
aoqi@0 205
aoqi@0 206 var stack = new StackPane();
aoqi@0 207 var pane = new BorderPane();
aoqi@0 208 pane.setCenter(canvas);
aoqi@0 209 stack.getChildren().add(pane);
aoqi@0 210 $STAGE.scene = new Scene(stack);
aoqi@0 211 var timer = new AnimationTimerExtend() {
aoqi@0 212 handle: function handle(now) {
aoqi@0 213 if (frame < 200) {
aoqi@0 214 draw_frame();
aoqi@0 215 } else {
aoqi@0 216 checkImageAndExit();
aoqi@0 217 timer.stop();
aoqi@0 218 }
aoqi@0 219 }
aoqi@0 220 };
aoqi@0 221 timer.start();
aoqi@0 222

mercurial