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