Tue, 16 Aug 2016 21:41:28 -0700
Added tag jdk8u102-b33 for changeset 36e3c21b5fca
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) { |
attila@962 | 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 |