aoqi@0: /* aoqi@0: * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /** aoqi@0: * Testing JavaFX canvas run by Nashorn. aoqi@0: * aoqi@0: * @test/nocompare aoqi@0: * @run aoqi@0: * @fork aoqi@0: */ aoqi@0: aoqi@0: TESTNAME = "spread"; aoqi@0: aoqi@0: var WIDTH = 800; aoqi@0: var HEIGHT = 600; aoqi@0: var canvas = new Canvas(WIDTH, HEIGHT); aoqi@0: var context = canvas.graphicsContext2D; aoqi@0: aoqi@0: /* "Spread" tech demo of canvas by Tom Theisen aoqi@0: * aoqi@0: * This will animate a sequence of branch structures in a canvas element. aoqi@0: * Each frame, a new direction is calculated, similar to the last frame. aoqi@0: */ aoqi@0: aoqi@0: var start_width = 20; // starting width of each branch aoqi@0: var frame_time = 30; // milliseconds per frame aoqi@0: var straighten_factor = 0.95; // value from 0 to 1, factor applied to direction_offset every frame aoqi@0: var curviness = 0.2; // amount of random direction change each frame aoqi@0: aoqi@0: var color_speed = 0.03; // speed at which colors change when cycling is enabled aoqi@0: var branch_shrink = 0.95; // factor by which branches shrink every frame aoqi@0: var min_width = 1; // minimum WIDTH for branch, after which they are discontinued aoqi@0: var branch_opacity = 0.4; // opacity of lines drawn aoqi@0: var branch_count = 3; // branch count per tree aoqi@0: var branch_bud_size = 0.5; // ratio of original branch size at which branch will split aoqi@0: var branch_bud_angle = 1; // angle offset for split branch; aoqi@0: aoqi@0: var paper; // reference to graphics context aoqi@0: var branches = Object(); // linked list of active branches aoqi@0: var color_styles = []; // pre-computed list of colors as styles. format: (r,g,b,a) aoqi@0: var direction_offset = 0; // current direction offset in radians. this is applied to all branches. aoqi@0: var frame = 0; // frame counter aoqi@0: var timespent = 0; // total time spent so far, used to calculate average frame render duration aoqi@0: var frameratespan; // html span element for updating performance number aoqi@0: aoqi@0: // preferences object, contains an attribute for each user setting aoqi@0: var prefs = { aoqi@0: wrap: true, // causes branches reaching edge of viewable area to appear on opposite side aoqi@0: fade: false, // fade existing graphics on each frame aoqi@0: cycle: true, // gradually change colors each frame aoqi@0: new_branch_frames: 20 // number of frames elapsed between each auto-generated tree aoqi@0: }; aoqi@0: aoqi@0: // create tree at the specified position with number of branches aoqi@0: function create_tree(branches, start_width, position, branch_count) { aoqi@0: var angle_offset = Math.PI * 2 / branch_count; aoqi@0: for (var i = 0; i < branch_count; ++i) { aoqi@0: branch_add(branches, new Branch(position, angle_offset * i, start_width)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // add branch to collection aoqi@0: function branch_add(branches, branch) { aoqi@0: branch.next = branches.next; aoqi@0: branches.next = branch; aoqi@0: } aoqi@0: aoqi@0: // get the coordinates for the position of a new tree aoqi@0: // use the center of the canvas aoqi@0: function get_new_tree_center(width, height) { aoqi@0: return { aoqi@0: x: 0.5 * width, aoqi@0: y: 0.5 * height aoqi@0: }; aoqi@0: } aoqi@0: aoqi@0: // Branch constructor aoqi@0: // position has x and y properties aoqi@0: // direction is in radians aoqi@0: function Branch(position, direction, width) { aoqi@0: this.x = position.x; aoqi@0: this.y = position.y; aoqi@0: this.width = width; aoqi@0: this.original_width = width; aoqi@0: this.direction = direction; aoqi@0: } aoqi@0: aoqi@0: // update position, direction and width of a particular branch aoqi@0: function branch_update(branches, branch, paper) { aoqi@0: paper.beginPath(); aoqi@0: paper.lineWidth = branch.width; aoqi@0: paper.moveTo(branch.x, branch.y); aoqi@0: aoqi@0: branch.width *= branch_shrink; aoqi@0: branch.direction += direction_offset; aoqi@0: branch.x += Math.cos(branch.direction) * branch.width; aoqi@0: branch.y += Math.sin(branch.direction) * branch.width; aoqi@0: aoqi@0: paper.lineTo(branch.x, branch.y); aoqi@0: paper.stroke(); aoqi@0: aoqi@0: if (prefs.wrap) wrap_branch(branch, WIDTH, HEIGHT); aoqi@0: aoqi@0: if (branch.width < branch.original_width * branch_bud_size) { aoqi@0: branch.original_width *= branch_bud_size; aoqi@0: branch_add(branches, new Branch(branch, branch.direction + 1, branch.original_width)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: function draw_frame() { aoqi@0: if (prefs.fade) { aoqi@0: paper.fillRect(0, 0, WIDTH, HEIGHT); aoqi@0: } aoqi@0: aoqi@0: if (prefs.cycle) { aoqi@0: paper.setStroke(Paint.valueOf(color_styles[frame % color_styles.length])); aoqi@0: } aoqi@0: aoqi@0: if (frame++ % prefs.new_branch_frames == 0) { aoqi@0: create_tree(branches, start_width, get_new_tree_center(WIDTH, HEIGHT), branch_count); aoqi@0: } aoqi@0: aoqi@0: direction_offset += (0.35 + (frame % 200) * 0.0015) * curviness - curviness / 2; aoqi@0: direction_offset *= straighten_factor; aoqi@0: aoqi@0: var branch = branches; aoqi@0: var prev_branch = branches; aoqi@0: while (branch = branch.next) { aoqi@0: branch_update(branches, branch, paper); aoqi@0: aoqi@0: if (branch.width < min_width) { aoqi@0: // remove branch from list aoqi@0: prev_branch.next = branch.next; aoqi@0: } aoqi@0: aoqi@0: prev_branch = branch; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // constrain branch position to visible area by "wrapping" from edge to edge aoqi@0: function wrap_branch(branch, WIDTH, HEIGHT) { aoqi@0: branch.x = positive_mod(branch.x, WIDTH); aoqi@0: branch.y = positive_mod(branch.y, HEIGHT); aoqi@0: } aoqi@0: aoqi@0: // for a < 0, b > 0, javascript returns a negative number for a % b aoqi@0: // this is a variant of the % operator that adds b to the result in this case aoqi@0: function positive_mod(a, b) { aoqi@0: // ECMA 262 11.5.3: Applying the % Operator aoqi@0: // remainder operator does not convert operands to integers, aoqi@0: // although negative results are possible aoqi@0: aoqi@0: return ((a % b) + b) % b; aoqi@0: } aoqi@0: aoqi@0: // pre-compute color styles that will be used for color cycling aoqi@0: function populate_colors(color_speed, color_styles, branch_opacity) { aoqi@0: // used in calculation of RGB values aoqi@0: var two_thirds_pi = Math.PI * 2 / 3; aoqi@0: var four_thirds_pi = Math.PI * 4 / 3; aoqi@0: var two_pi = Math.PI * 2; aoqi@0: aoqi@0: // hue does represent hue, but not in the conventional HSL scheme aoqi@0: for(var hue = 0; hue < two_pi; hue += color_speed) { aoqi@0: var r = Math.floor(Math.sin(hue) * 128 + 128); aoqi@0: var g = Math.floor(Math.sin(hue + two_thirds_pi) * 128 + 128); aoqi@0: var b = Math.floor(Math.sin(hue + four_thirds_pi) * 128 + 128); aoqi@0: color = "rgba(" + [r, g, b, branch_opacity].join() + ")"; aoqi@0: aoqi@0: color_styles.push(color); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // apply initial settings to canvas object aoqi@0: function setup_canvas() { aoqi@0: paper = canvas.graphicsContext2D; aoqi@0: paper.setFill(Paint.valueOf('rgb(0, 0, 0)')); aoqi@0: paper.fillRect(0, 0, WIDTH, HEIGHT); aoqi@0: paper.setFill(Paint.valueOf("rgba(0, 0, 0, 0.005)")); aoqi@0: paper.setStroke(Paint.valueOf("rgba(128, 128, 64, " + String(branch_opacity) + ")")); aoqi@0: } aoqi@0: aoqi@0: populate_colors(color_speed, color_styles, branch_opacity); aoqi@0: setup_canvas(); aoqi@0: aoqi@0: var stack = new StackPane(); aoqi@0: var pane = new BorderPane(); aoqi@0: pane.setCenter(canvas); aoqi@0: stack.getChildren().add(pane); aoqi@0: $STAGE.scene = new Scene(stack); aoqi@0: var timer = new AnimationTimerExtend() { aoqi@0: handle: function handle(now) { aoqi@0: if (frame < 200) { aoqi@0: draw_frame(); aoqi@0: } else { aoqi@0: checkImageAndExit(); aoqi@0: timer.stop(); aoqi@0: } aoqi@0: } aoqi@0: }; aoqi@0: timer.start(); aoqi@0: