|
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 |
|
32 TESTNAME = "spread"; |
|
33 |
|
34 var WIDTH = 800; |
|
35 var HEIGHT = 600; |
|
36 var canvas = new Canvas(WIDTH, HEIGHT); |
|
37 var 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 |
|
45 var start_width = 20; // starting width of each branch |
|
46 var frame_time = 30; // milliseconds per frame |
|
47 var straighten_factor = 0.95; // value from 0 to 1, factor applied to direction_offset every frame |
|
48 var curviness = 0.2; // amount of random direction change each frame |
|
49 |
|
50 var color_speed = 0.03; // speed at which colors change when cycling is enabled |
|
51 var branch_shrink = 0.95; // factor by which branches shrink every frame |
|
52 var min_width = 1; // minimum WIDTH for branch, after which they are discontinued |
|
53 var branch_opacity = 0.4; // opacity of lines drawn |
|
54 var branch_count = 3; // branch count per tree |
|
55 var branch_bud_size = 0.5; // ratio of original branch size at which branch will split |
|
56 var branch_bud_angle = 1; // angle offset for split branch; |
|
57 |
|
58 var paper; // reference to graphics context |
|
59 var branches = Object(); // linked list of active branches |
|
60 var color_styles = []; // pre-computed list of colors as styles. format: (r,g,b,a) |
|
61 var direction_offset = 0; // current direction offset in radians. this is applied to all branches. |
|
62 var frame = 0; // frame counter |
|
63 var timespent = 0; // total time spent so far, used to calculate average frame render duration |
|
64 var frameratespan; // html span element for updating performance number |
|
65 |
|
66 // preferences object, contains an attribute for each user setting |
|
67 var 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 |
|
75 function 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 |
|
83 function 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 |
|
90 function 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 |
|
100 function 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 |
|
109 function 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 |
|
130 function 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 |
|
161 function 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 |
|
168 function 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 |
|
177 function 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 |
|
195 function 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 |
|
203 populate_colors(color_speed, color_styles, branch_opacity); |
|
204 setup_canvas(); |
|
205 |
|
206 var stack = new StackPane(); |
|
207 var pane = new BorderPane(); |
|
208 pane.setCenter(canvas); |
|
209 stack.getChildren().add(pane); |
|
210 $STAGE.scene = new Scene(stack); |
|
211 var 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 }; |
|
221 timer.start(); |
|
222 |