Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(456)

Side by Side Diff: samples/openglui/src/openglui_canvas_tests.dart

Issue 24698003: Removed the openglui sample app. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 //
5 // Canvas API tests. Some of these are adapted from:
6 //
7 // http://www.html5canvastutorials.com/
8
9 library openglui_canvas_tests;
10
11 import 'gl.dart';
12 import 'dart:math' as Math;
13
14 var ctx;
15 var width, height;
16 bool isDirty = true;
17 var canvas;
18
19 void resize(int w, int h) {
20 width = w;
21 height = h;
22 }
23
24 void setup(canvasp, int w, int h, int f) {
25 if (canvasp == null) {
26 log("Allocating canvas");
27 canvas = new CanvasElement(width: w, height: h);
28 document.body.nodes.add(canvas);
29 } else {
30 log("Using parent canvas");
31 canvas = canvasp;
32 }
33 canvas.onMouseDown.listen((e) {
34 ++testnum;
35 isDirty = true;
36 });
37 canvas.onKeyDown.listen((e) {
38 ++testnum;
39 isDirty = true;
40 });
41 ctx = canvas.getContext("2d");
42 if (ctx == null) {
43 throw "Failed to get 2D context";
44 }
45 resize(w, h);
46 window.requestAnimationFrame(update);
47 log("Done setup");
48 }
49
50 resetCanvas() {
51 ctx.globalCompositeOperation = "source-over";
52 ctx.setTransform(1, 0, 0, 1, 0, 0);
53 ctx.fillStyle = "#FFFFFF";
54 ctx.clearRect(0, 0, width, height);
55 ctx.shadowOffsetX = ctx.shadowOffsetY = ctx.shadowBlur = 0.0;
56 ctx.beginPath();
57 }
58
59 initTest(title) {
60 resetCanvas();
61 ctx.font = "15px Courier";
62 ctx.textAlign = 'left';
63 ctx.fillStyle = "black";
64 ctx.fillText(title, 20, 20);
65 }
66
67 helloWorld() {
68 initTest("Hello world");
69 ctx.font = "30px Courier";
70 ctx.strokeStyle = "#7F7F7F";
71 ctx.fillStyle = "#7F7F7F";
72 ctx.textAlign = "center";
73 ctx.lineWidth = 2;
74 ctx.strokeText("Align Center", width / 2, height / 4);
75 ctx.textAlign = "left";
76 ctx.fillText("Align Left", width / 2, height / 2);
77 ctx.textAlign = "right";
78 ctx.fillText("Align Right", width / 2, 3 * height / 4);
79 }
80
81 blocks() {
82 initTest("fillRect/strokeRect");
83 ctx.fillStyle = "#FF0000";
84 ctx.fillRect(width / 10, height / 10, width / 2, height / 25);
85 ctx.fillStyle = "#00FF00";
86 ctx.fillRect(width / 4, height / 5, width / 20, height / 8);
87 //ctx.fillStyle = "rgba(0,0,255,0.8)";
88 ctx.strokeStyle = "rgba(128,128,128, 0.5)";
89 //ctx.fillStyle = "#7F7F7F";
90 ctx.strokeRect(width / 5, height / 10, width / 2, height / 8);
91 }
92
93 square(left, top, width, height) {
94 ctx.beginPath();
95 ctx.moveTo(left, top);
96 ctx.lineTo(left + width, top);
97 ctx.lineTo(left + width, top + height);
98 ctx.lineTo(left, top + height);
99 ctx.closePath(); //lineTo(left, top);
100 }
101
102 squares() {
103 initTest("fill/stroke paths");
104 ctx.strokeStyle = "black";
105 ctx.fillStyle = "#FF0000";
106 ctx.lineWidth = 4;
107 square(width / 10, height / 10, width / 2, height / 25);
108 ctx.fill();
109 ctx.stroke();
110 ctx.fillStyle = "#00FF00";
111 square(width / 4, height / 5, width / 20, height / 8);
112 ctx.fill();
113 ctx.stroke();
114 ctx.fillStyle = "rgba(128,128,128, 0.5)";
115 square(width / 5, height / 10, width / 2, height / 8);
116 ctx.fill();
117 ctx.stroke();
118 }
119
120 lineJoin() {
121 initTest("Line joins");
122 ctx.strokeStyle = "black";
123 ctx.fillStyle = "#FF0000";
124 ctx.lineWidth = 8;
125 ctx.lineJoin = "round";
126 square(width / 10, height / 10, width / 2, height / 25);
127 ctx.stroke();
128 ctx.fillStyle = "#00FF00";
129 ctx.lineJoin = "bevel";
130 square(width / 4, height / 5, width / 20, height / 8);
131 ctx.stroke();
132 ctx.fillStyle = "rgba(128,128,128, 0.5)";
133 ctx.lineJoin = "miter";
134 square(width / 5, height / 10, width / 2, height / 8);
135 ctx.stroke();
136 }
137
138 grid() {
139 initTest("line strokes");
140 ctx.lineWidth = 1;
141 for (var x = 0.5; x < width; x += 10) {
142 ctx.moveTo(x, 0);
143 ctx.lineTo(x, height);
144 }
145 for (var y = 0.5; y < height; y += 10) {
146 ctx.moveTo(0, y);
147 ctx.lineTo(width, y);
148 }
149 ctx.strokeStyle = "#eee";
150 ctx.stroke();
151 }
152
153 line(x1, y1, x2, y2) {
154 ctx.beginPath();
155 ctx.moveTo(x1, y1);
156 ctx.lineTo(x2, y2);
157 }
158
159 strokeLines() {
160 initTest("line caps");
161 ctx.lineWidth = height / 40;
162 ctx.strokeStyle = '#0000ff';
163 // butt line cap (top line)
164 line(width / 4, height / 4, 3 * width / 4, height / 4);
165 ctx.lineCap = 'butt';
166 ctx.stroke();
167
168 // round line cap (middle line)
169 line(width / 4, height / 2, 3 * width / 4, height / 2);
170 ctx.lineCap = 'round';
171 ctx.stroke();
172
173 // square line cap (bottom line)
174 line(width / 4, 3 * height / 4, 3 * width / 4, 3 * height / 4);
175 ctx.lineCap = 'square';
176 ctx.stroke();
177 }
178
179 colors() {
180 initTest("Colors");
181 var colors = [
182 "maroon",
183 "red",
184 "orange",
185 "yellow",
186 "olive",
187 "purple",
188 "fuschia",
189 "white",
190 "lime",
191 "green",
192 "navy",
193 "blue",
194 "aqua",
195 "teal",
196 "silver",
197 "gray",
198 "black"
199 ];
200
201 var i = 1;
202 var yinc = height / (2 * colors.length + 1);
203
204 ctx.textAlign = "center";
205 ctx.font = "${yinc}px Courier";
206
207 for (var color in colors) {
208 ctx.fillStyle = color;
209 ctx.fillRect(width / 4, i * 2 * yinc, width / 2, 3 * yinc / 2);
210 ctx.fillStyle = (color == "gray") ? "white" : "gray";
211 ctx.fillText(color, width / 2, i * 2 * yinc + 7 * yinc / 8);
212 ++i;
213 }
214 }
215
216 smiley() {
217 initTest("arcs");
218 ctx.translate(width / 2 - 80, height / 2 - 75);
219
220 ctx.beginPath();
221 ctx.arc(100,80,75,0,Math.PI*2,true);
222 ctx.fillStyle = "rgb(255,255,204)";
223 ctx.fill();
224 ctx.stroke();
225
226 ctx.fillStyle = "black";
227 ctx.beginPath();
228 ctx.arc(80,55,8,0,Math.PI*2,true);
229 ctx.fill();
230
231 ctx.beginPath();
232 ctx.arc(120,55,8,0,Math.PI*2,true);
233 ctx.fill();
234
235 ctx.beginPath();
236 ctx.arc(100,85,10,4,Math.PI*2,true);
237 ctx.stroke();
238
239 ctx.beginPath();
240 ctx.arc(100,95,30,0,Math.PI,false);
241 ctx.stroke();
242
243 ctx.setTransform(1, 0, 0, 1, 0, 0);
244 }
245
246 rot(txt1, [txt2, sx = 1.0, sy = 1.0]) {
247 if (txt2 == null) txt2 = txt1;
248 ctx.font = "50px sans serif";
249 ctx.translate(width / 2, height / 2);
250 ctx.textAlign = "right";
251 ctx.fillStyle = "red";
252 ctx.fillText(txt1, 0, 0);
253 ctx.rotate(Math.PI / 2);
254 ctx.scale(sx, sy);
255 ctx.fillStyle = "green";
256 ctx.fillText(txt2, 0, 0);
257 ctx.scale(sx, sy);
258 ctx.rotate(Math.PI / 2);
259 ctx.fillStyle = "blue";
260 ctx.fillText(txt1, 0, 0);
261 ctx.scale(sx, sy);
262 ctx.rotate(Math.PI / 2);
263 ctx.fillStyle = "yellow";
264 ctx.fillText(txt2, 0, 0);
265 ctx.setTransform(1, 0, 0, 1, 0, 0);
266 }
267
268 rotate() {
269 initTest("Rotation");
270 rot("Dart");
271 }
272
273 alpha() {
274 initTest("Global alpha");
275 ctx.fillStyle = "gray";
276 ctx.fillRect(0, 0, width, height);
277 grid();
278 ctx.globalAlpha = 0.5;
279 rot("Global", "Alpha");
280 ctx.globalAlpha = 1.0;
281 }
282
283 scale() {
284 initTest("Scale");
285 rot("Scale", "Test", 0.8, 0.5);
286 }
287
288 curves() {
289 initTest("Curves");
290 ctx.beginPath();
291 ctx.moveTo(188, 150);
292 ctx.quadraticCurveTo(288, 0, 388, 150);
293 ctx.lineWidth = 2;
294 ctx.strokeStyle = 'red';
295 ctx.stroke();
296 ctx.beginPath();
297 ctx.moveTo(188, 130);
298 ctx.bezierCurveTo(140, 10, 388, 10, 388, 170);
299 ctx.lineWidth = 5;
300 ctx.strokeStyle = 'blue';
301 ctx.stroke();
302 var x1, x2, y1, y2, ex, ey;
303 ctx.beginPath();
304 x1 = width / 4;
305 y1 = height / 2;
306 x2 = width / 2;
307 y2 = height / 4;
308 ex = 3 * width / 4;
309 ey = y1;
310 ctx.moveTo(x1, x2);
311 ctx.quadraticCurveTo(x2, y2, ex, ey);
312 ctx.lineWidth = 10;
313 ctx.strokeStyle = 'green';
314 ctx.stroke();
315
316 ctx.beginPath();
317 ctx.moveTo(188, 130);
318 x1 = width / 8;
319 x2 = 7 * width / 8;
320 y1 = height / 50;
321 y2 = y1;
322 ex = x2;
323 ey = height / 2;
324 ctx.bezierCurveTo(x1, y1, x2, y2, ex, ey);
325 ctx.lineWidth = 7;
326 ctx.strokeStyle = 'black';
327 ctx.stroke();
328
329 // Draw a cloud
330 ctx.beginPath();
331 var wscale = width / 578;
332 var hscale = height / 800;
333 ctx.translate(0, height / 2);
334 ctx.moveTo(170 * wscale, 80 * hscale);
335 ctx.bezierCurveTo(130 * wscale, 100 * hscale,
336 130 * wscale, 150 * hscale,
337 230 * wscale, 150 * hscale);
338 ctx.bezierCurveTo(250 * wscale, 180 * hscale,
339 320 * wscale, 180 * hscale,
340 340 * wscale, 150 * hscale);
341 ctx.bezierCurveTo(420 * wscale, 150 * hscale,
342 420 * wscale, 120 * hscale,
343 390 * wscale, 100 * hscale);
344 ctx.bezierCurveTo(430 * wscale, 40 * hscale,
345 370 * wscale, 30 * hscale,
346 340 * wscale, 50 * hscale);
347 ctx.bezierCurveTo(320 * wscale, 5 * hscale,
348 250 * wscale, 20 * hscale,
349 250 * wscale, 50 * hscale);
350 ctx.bezierCurveTo(200 * wscale, 5 * hscale,
351 150 * wscale, 20 * hscale,
352 170 * wscale, 80 * hscale);
353 ctx.closePath();
354 ctx.lineWidth = 5;
355 ctx.fillStyle = 'gray';
356 ctx.fill();
357 }
358
359 void shadows() {
360 initTest("Shadows");
361 ctx.shadowBlur=20;
362 ctx.shadowColor="black";
363 ctx.fillStyle="red";
364 var w = width / 2;
365 if (w > height / 2) w = height / 2;
366 ctx.fillRect(width / 2 - w / 2, height / 4 - w / 2, w, w);
367 ctx.shadowOffsetX = 10;
368 ctx.shadowOffsetY = 10;
369 ctx.shadowColor="green";
370 ctx.fillRect(width / 2 - w / 2, 3 * height / 4 - w / 2, w, w);
371 }
372
373 void lineJoins() {
374 initTest("Line joins");
375 ctx.lineWidth=10;
376 ctx.lineJoin="miter";
377 ctx.moveTo(width / 2 - 25, height / 4 - 10);
378 ctx.lineTo(width /2 + 25, height / 4);
379 ctx.lineTo(width / 2 - 25, height / 4 + 10);
380 ctx.stroke();
381 ctx.lineJoin="round";
382 ctx.moveTo(width / 2 - 25, height / 2 - 10);
383 ctx.lineTo(width /2 + 25, height / 2);
384 ctx.lineTo(width / 2 - 25, height / 2 + 10);
385 ctx.stroke();
386 ctx.lineJoin="bevel";
387 ctx.moveTo(width / 2 - 25, 3 * height / 4 - 10);
388 ctx.lineTo(width /2 + 25, 3 * height / 4);
389 ctx.lineTo(width / 2 - 25, 3 * height / 4 + 10);
390 ctx.stroke();
391 }
392
393 void saveRestore() {
394 initTest("Save/restore state");
395 ctx.font = "30px courier";
396 ctx.fillStyle = "red";
397 ctx.strokeStyle = "black";
398 ctx.shadowBlur = 5;
399 ctx.shadowColor = "green";
400 ctx.lineWidth = 1;
401 ctx.textAlign = "left";
402 ctx.rotate(Math.PI / 30);
403 ctx.fillText("State 1", width /2, height / 6);
404 ctx.strokeText("State 1", width /2, height / 6);
405 ctx.save();
406
407 ctx.font = "40px sans serif";
408 ctx.fillStyle = "blue";
409 ctx.strokeStyle = "orange";
410 ctx.shadowBlur = 8;
411 ctx.shadowOffsetX = 5;
412 ctx.shadowColor = "black";
413 ctx.lineWidth = 2;
414 ctx.textAlign = "right";
415 ctx.rotate(Math.PI / 30);
416 ctx.fillText("State 2", width /2, 2 * height / 6);
417 ctx.strokeText("State 2", width /2, 2 * height / 6);
418 ctx.save();
419
420 ctx.font = "50px times roman";
421 ctx.fillStyle = "yellow";
422 ctx.strokeStyle = "gray";
423 ctx.shadowBlur = 8;
424 ctx.shadowOffsetX = 5;
425 ctx.shadowColor = "red";
426 ctx.lineWidth = 3;
427 ctx.textAlign = "center";
428 ctx.rotate(-Math.PI / 15);
429 ctx.fillText("State 3", width /2, 3 * height / 6);
430 ctx.strokeText("State 3", width /2, 3 * height / 6);
431
432 ctx.restore();
433 ctx.fillText("State 2", width /2, 4 * height / 6);
434 ctx.strokeText("State 2", width /2, 4 * height / 6);
435
436 ctx.restore();
437 ctx.fillText("State 1", width /2, 5 * height / 6);
438 ctx.strokeText("State 1", width /2, 5 * height / 6);
439 }
440
441 void mirror() {
442 initTest("Mirror");
443 // translate context to center of canvas
444 ctx.translate(width / 2, height / 2);
445
446 // flip context horizontally
447 ctx.scale(-1, 1);
448
449 ctx.font = '30pt Calibri';
450 ctx.textAlign = 'center';
451 ctx.fillStyle = 'blue';
452 ctx.fillText('Magic Mirror', 0, 0);
453 }
454
455 void oval() {
456 initTest("Path - pop state - stroke");
457 var centerX = 0;
458 var centerY = 0;
459 var radius = 50;
460
461 // save state
462 ctx.save();
463
464 // translate context
465 ctx.translate(width / 2, height / 2);
466
467 // scale context horizontally
468 ctx.scale(2, 1);
469
470 // draw circle which will be stretched into an oval
471 ctx.beginPath();
472 ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
473
474 // restore to original state
475 ctx.restore();
476
477 // apply styling
478 ctx.fillStyle = '#8ED6FF';
479 ctx.fill();
480 ctx.lineWidth = 5;
481 ctx.strokeStyle = 'black';
482 ctx.stroke();
483 }
484
485 void lineDash() {
486 initTest("Line dash");
487 ctx.setLineDash([ 5, 8, 3 ]);
488 ctx.strokeStyle = "#FF0000";
489 ctx.strokeRect(width / 10, height / 10, width / 2, height / 25);
490 ctx.lineDashOffset = 1;
491 ctx.strokeStyle = "#00FF00";
492 ctx.strokeRect(width / 4, height / 5, width / 20, height / 8);
493 ctx.setLineDash([]);
494 ctx.strokeStyle = "#0000FF";
495 ctx.strokeStyle = "rgba(128,128,128, 0.5)";
496 ctx.strokeRect(width / 5, height / 10, width / 2, height / 8);
497 log("Width = $width");
498 }
499
500 void loadImage() {
501 initTest("Image loading");
502 var imageObj = new ImageElement();
503 // Setting src before onLoad is a more interesting test.
504 imageObj.src = 'chrome.png';
505 imageObj.onLoad.listen((e) {
506 ctx.drawImage(e.target, 0, 0, width, height, 0, 0, width, height);
507 });
508 }
509
510 void clip() {
511 initTest("Clipping");
512 var x = width / 2;
513 var y = height / 2;
514 var radius = height / 4;
515 var offset = 2 * radius / 3;
516
517 ctx.save();
518 ctx.beginPath();
519 ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
520 ctx.clip();
521
522 // draw blue circle inside clipping region
523 ctx.beginPath();
524 ctx.arc(x - offset, y - offset, radius, 0, 2 * Math.PI, false);
525 ctx.fillStyle = 'blue';
526 ctx.fill();
527
528 // draw yellow circle inside clipping region
529 ctx.beginPath();
530 ctx.arc(x + offset, y, radius, 0, 2 * Math.PI, false);
531 ctx.fillStyle = 'yellow';
532 ctx.fill();
533
534 // draw red circle inside clipping region
535 ctx.beginPath();
536 ctx.arc(x, y + offset, radius, 0, 2 * Math.PI, false);
537 ctx.fillStyle = 'red';
538 ctx.fill();
539
540 // Restore the canvas context to its original state
541 // before we defined the clipping region
542 ctx.restore();
543 ctx.beginPath();
544 ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
545 ctx.lineWidth = 10;
546 ctx.strokeStyle = 'blue';
547 ctx.stroke();
548 }
549
550 void shear() {
551 initTest("Transform");
552 var rectWidth = width / 4;
553 var rectHeight = height / 8;
554
555 // shear matrix:
556 // 1 sx 0
557 // sy 1 0
558 // 0 0 1
559
560 var sx = 0.75;
561 // .75 horizontal shear
562 var sy = 0;
563 // no vertical shear
564
565 // translate context to center of canvas
566 ctx.translate(width / 2, height / 2);
567
568 // apply custom transform
569 ctx.transform(1, sy, sx, 1, 0, 0);
570
571 ctx.fillStyle = 'blue';
572 ctx.fillRect(-rectWidth / 2, rectHeight / -2, rectWidth, rectHeight);
573 }
574
575 void composite() {
576 initTest("Composition");
577 var num = 0;
578 var numPerRow = width ~/ 150;
579 var tempCanvas = new CanvasElement(width: width, height:height);
580 var tempContext = tempCanvas.getContext("2d");
581 log("Width = $width, numPerRow = $numPerRow\n");
582 for (var mode in [ 'source-atop', 'source-in',
583 'source-out', 'source-over',
584 'destination-atop', 'destination-in',
585 'destination-out', 'destination-over',
586 'lighter', 'darker',
587 'xor', 'copy']) {
588 tempContext.save();
589 tempContext.clearRect(0, 0, width, height);
590 tempContext.beginPath();
591 tempContext.rect(0, 0, 55, 55);
592 tempContext.fillStyle = 'blue';
593 tempContext.fill();
594
595 tempContext.globalCompositeOperation = mode;
596 tempContext.beginPath();
597 tempContext.arc(50, 50, 35, 0, 2 * Math.PI, false);
598 tempContext.fillStyle = 'red';
599 tempContext.fill();
600 tempContext.restore();
601 tempContext.font = '10pt Verdana';
602 tempContext.fillStyle = 'black';
603 tempContext.fillText(mode, 0, 100);
604 if (num > 0) {
605 if ((num % numPerRow) == 0) {
606 ctx.translate(-150 * (numPerRow-1), 150);
607 } else {
608 ctx.translate(150, 0);
609 }
610 }
611 ctx.drawImage(tempCanvas, 0, 0);
612 ++num;
613 }
614 }
615
616 class Rectangle {
617 num x, y, width, height, borderWidth;
618 }
619
620 var startTime = 0;
621 var myRectangle = null;
622
623 void anim() {
624 if (myRectangle == null) {
625 myRectangle = new Rectangle();
626 myRectangle.x = 250;
627 myRectangle.y = 70;
628 myRectangle.width = 100;
629 myRectangle.height = 50;
630 myRectangle.borderWidth = 5;
631 startTime = (new DateTime.now()).millisecondsSinceEpoch;
632 }
633
634 var now = (new DateTime.now()).millisecondsSinceEpoch;
635 var time = now - startTime;
636 var amplitude = 150;
637
638 // in ms
639 var period = 2000;
640 var centerX = width / 2 - myRectangle.width / 2;
641 var nextX = amplitude * Math.sin(time * 2 * Math.PI / period) + centerX;
642 myRectangle.x = nextX;
643
644 // clear
645 ctx.clearRect(0, 0, width, height);
646
647 // draw
648
649 ctx.beginPath();
650 ctx.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
651 ctx.fillStyle = '#8ED6FF';
652 ctx.fill();
653 ctx.lineWidth = myRectangle.borderWidth;
654 ctx.strokeStyle = 'black';
655 ctx.stroke();
656 }
657
658 void linearGradient() {
659 initTest("Linear Gradient");
660 ctx.rect(0, 0, width, height);
661 var grd = ctx.createLinearGradient(0, 0, width, height);
662 // light blue
663 grd.addColorStop(0, '#8ED6FF');
664 // dark blue
665 grd.addColorStop(1, '#004CB3');
666 ctx.fillStyle = grd;
667 ctx.fill();
668 }
669
670 void radialGradient() {
671 initTest("Radial Gradient");
672 ctx.rect(0, 0, width, height);
673 var grd = ctx.createRadialGradient(238, 50, 10, 238, 50, 300);
674 // light blue
675 grd.addColorStop(0, '#8ED6FF');
676 // dark blue
677 grd.addColorStop(1, '#004CB3');
678 ctx.fillStyle = grd;
679 ctx.fill();
680 }
681
682 int testnum = 0; // Set this to -1 to start with last test.
683
684 double x, y, z;
685
686 onAccelerometer(double xx, double yy, double zz) {
687 x = xx;
688 y = yy;
689 z = zz;
690 }
691
692 void update(num when) {
693 window.requestAnimationFrame(update);
694 if (testnum == 0) {
695 anim();
696 return;
697 }
698 if (!isDirty) return;
699 switch(testnum) {
700 case 1:
701 helloWorld();
702 break;
703 case 2:
704 blocks();
705 break;
706 case 3:
707 squares();
708 break;
709 case 4:
710 grid();
711 break;
712 case 5:
713 strokeLines();
714 break;
715 case 6:
716 lineJoin();
717 break;
718 case 7:
719 colors();
720 break;
721 case 8:
722 rotate();
723 break;
724 case 9:
725 alpha();
726 break;
727 case 10:
728 scale();
729 break;
730 case 11:
731 curves();
732 break;
733 case 12:
734 shadows();
735 break;
736 case 13:
737 lineJoins();
738 break;
739 case 14:
740 saveRestore();
741 break;
742 case 15:
743 mirror();
744 break;
745 case 16:
746 oval();
747 break;
748 case 17:
749 lineDash();
750 break;
751 case 18:
752 loadImage();
753 break;
754 case 19:
755 clip();
756 break;
757 case 20:
758 shear();
759 break;
760 case 21:
761 composite();
762 break;
763 case 22:
764 smiley();
765 break;
766 case 23:
767 linearGradient();
768 break;
769 case 24:
770 radialGradient();
771 break;
772 case 25:
773 break; // Skip for now; this is really slow.
774 var rayTracer = new RayTracer();
775 rayTracer.render(defaultScene(), ctx, width, height);
776 break;
777 default:
778 if (testnum < 0) {
779 testnum = 24;
780 } else {
781 testnum = 0;
782 }
783 return;
784 }
785
786 isDirty = false;
787 }
788
789 // Raytracer adapted from https://gist.github.com/mythz/3817303.
790
791 Scene defaultScene() =>
792 new Scene(
793 [new Plane(new Vector(0.0, 1.0, 0.0), 0.0, Surfaces.checkerboard),
794 new Sphere(new Vector(0.0, 1.0, -0.25), 1.0, Surfaces.shiny),
795 new Sphere(new Vector(-1.0, 0.5, 1.5), 0.5, Surfaces.shiny)],
796 [new Light(new Vector(-2.0, 2.5, 0.0), new Color(0.49, 0.07, 0.07) ),
797 new Light(new Vector(1.5, 2.5, 1.5), new Color(0.07, 0.07, 0.49) ),
798 new Light(new Vector(1.5, 2.5, -1.5), new Color(0.07, 0.49, 0.071) ),
799 new Light(new Vector(0.0, 3.5, 0.0), new Color(0.21, 0.21, 0.35) )],
800 new Camera(new Vector(3.0, 2.0, 4.0), new Vector(-1.0, 0.5, 0.0))
801 );
802
803
804 class Vector {
805 num x, y, z;
806 Vector(this.x, this.y, this.z);
807
808 operator -(Vector v) => new Vector(x - v.x, y - v.y, z - v.z);
809 operator +(Vector v) => new Vector(x + v.x, y + v.y, z + v.z);
810 static times(num k, Vector v) => new Vector(k * v.x, k * v.y, k * v.z);
811 static num dot(Vector v1, Vector v2) =>
812 v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
813 static num mag(Vector v) => Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
814 static Vector norm(Vector v) {
815 var _mag = mag(v);
816 var div = _mag == 0 ? double.INFINITY : 1.0 / _mag;
817 return times(div, v);
818 }
819 static Vector cross(Vector v1, Vector v2) {
820 return new Vector(v1.y * v2.z - v1.z * v2.y,
821 v1.z * v2.x - v1.x * v2.z,
822 v1.x * v2.y - v1.y * v2.x);
823 }
824 }
825
826 class Color {
827 num r, g, b;
828 static final white = new Color(1.0, 1.0, 1.0);
829 static final grey = new Color(0.5, 0.5, 0.5);
830 static final black = new Color(0.0, 0.0, 0.0);
831 static final background = Color.black;
832 static final defaultColor = Color.black;
833
834 Color(this.r,this.g,this.b);
835 static scale(num k, Color v) => new Color(k * v.r, k * v.g, k * v.b);
836 operator +(Color v) => new Color(r + v.r, g + v.g, b + v.b);
837 operator *(Color v) => new Color(r * v.r, g * v.g, b * v.b);
838 static _intColor(num d) => ((d > 1 ? 1 : d) * 255).toInt();
839 static String toDrawingRGB(Color c) =>
840 "rgb(${_intColor(c.r)}, ${_intColor(c.g)}, ${_intColor(c.b)})";
841 }
842
843 class Camera {
844 Vector pos, forward, right, up;
845 Camera (this.pos, Vector lookAt) {
846 var down = new Vector(0.0, -1.0, 0.0);
847 forward = Vector.norm(lookAt - pos);
848 right = Vector.times(1.5, Vector.norm(Vector.cross(forward, down)));
849 up = Vector.times(1.5, Vector.norm(Vector.cross(forward, right)));
850 }
851 }
852
853 class Ray {
854 Vector start, dir;
855 Ray([this.start, this.dir]);
856 }
857
858 class Intersection {
859 Thing thing;
860 Ray ray;
861 num dist;
862 Intersection(this.thing, this.ray, this.dist);
863 }
864
865 class Light {
866 Vector pos;
867 Color color;
868 Light(this.pos, this.color);
869 }
870
871 abstract class Surface {
872 int roughness;
873 Color diffuse(Vector pos);
874 Color specular(Vector pos);
875 num reflect(Vector pos);
876 }
877
878 abstract class Thing {
879 Intersection intersect(Ray ray);
880 Vector normal(Vector pos);
881 Surface surface;
882 }
883
884 class Scene {
885 List<Thing> things;
886 List<Light> lights;
887 Camera camera;
888 Scene([this.things,this.lights,this.camera]);
889 }
890
891 class Sphere implements Thing {
892 num radius2, radius;
893 Vector center;
894 Surface surface;
895
896 Sphere (this.center, this.radius, this.surface) {
897 this.radius2 = radius * radius;
898 }
899 normal(Vector pos) => Vector.norm(pos - center);
900 intersect(Ray ray) {
901 var eo = this.center - ray.start;
902 var v = Vector.dot(eo, ray.dir);
903 var dist = 0;
904 if (v >= 0) {
905 var disc = this.radius2 - (Vector.dot(eo, eo) - v * v);
906 if (disc >= 0) {
907 dist = v - Math.sqrt(disc);
908 }
909 }
910 return dist == 0 ? null : new Intersection(this, ray, dist);
911 }
912 }
913
914 class Plane implements Thing {
915 Vector norm;
916 num offset;
917 Surface surface;
918 Plane(this.norm, this.offset, this.surface);
919 Vector normal(Vector pos) => norm;
920 Intersection intersect(Ray ray) {
921 var denom = Vector.dot(norm, ray.dir);
922 if (denom > 0) {
923 return null;
924 } else {
925 var dist = (Vector.dot(norm, ray.start) + offset) / (-denom);
926 return new Intersection(this, ray, dist);
927 }
928 }
929 }
930
931 class CustomSurface implements Surface {
932 Color diffuseColor, specularColor;
933 int roughness;
934 num reflectPos;
935 CustomSurface(this.diffuseColor, this.specularColor,
936 this.reflectPos, this.roughness);
937 diffuse(pos) => diffuseColor;
938 specular(pos) => specularColor;
939 reflect(pos) => reflectPos;
940 }
941
942 class CheckerBoardSurface implements Surface {
943 int roughness;
944 CheckerBoardSurface([this.roughness=150]);
945 diffuse(pos) => (pos.z.floor() + pos.x.floor()) % 2 != 0
946 ? Color.white
947 : Color.black;
948 specular(pos) => Color.white;
949 reflect(pos) => (pos.z.floor() + pos.x.floor()) % 2 != 0 ? 0.1 : 0.7;
950 }
951
952 class Surfaces {
953 static final shiny = new CustomSurface(Color.white, Color.grey, 0.7, 250);
954 static final checkerboard = new CheckerBoardSurface();
955 }
956
957 class RayTracer {
958 num _maxDepth = 5;
959
960 Intersection _intersections(Ray ray, Scene scene) {
961 var closest = double.INFINITY;
962 Intersection closestInter = null;
963 for (Thing thing in scene.things) {
964 var inter = thing.intersect(ray);
965 if (inter != null && inter.dist < closest) {
966 closestInter = inter;
967 closest = inter.dist;
968 }
969 }
970 return closestInter;
971 }
972
973 _testRay(Ray ray, Scene scene) {
974 var isect = _intersections(ray, scene);
975 return isect != null ? isect.dist : null;
976 }
977
978 _traceRay(Ray ray, Scene scene, num depth) {
979 var isect = _intersections(ray, scene);
980 return isect == null ? Color.background : _shade(isect, scene, depth);
981 }
982
983 _shade(Intersection isect, Scene scene, num depth) {
984 var d = isect.ray.dir;
985 var pos = Vector.times(isect.dist, d) + isect.ray.start;
986 var normal = isect.thing.normal(pos);
987 var reflectDir = d -
988 Vector.times(2, Vector.times(Vector.dot(normal, d), normal));
989 var naturalColor = Color.background +
990 _getNaturalColor(isect.thing, pos, normal, reflectDir, scene);
991 var reflectedColor = (depth >= _maxDepth) ? Color.grey :
992 _getReflectionColor(isect.thing, pos, normal, reflectDir,
993 scene, depth);
994 return naturalColor + reflectedColor;
995 }
996
997 _getReflectionColor(Thing thing, Vector pos, Vector normal, Vector rd,
998 Scene scene, num depth) =>
999 Color.scale(thing.surface.reflect(pos),
1000 _traceRay(new Ray(pos, rd), scene, depth + 1));
1001
1002 _getNaturalColor(Thing thing, Vector pos, Vector norm, Vector rd,
1003 Scene scene) {
1004 var addLight = (col, light) {
1005 var ldis = light.pos - pos;
1006 var livec = Vector.norm(ldis);
1007 var neatIsect = _testRay(new Ray(pos, livec), scene);
1008 var isInShadow = neatIsect == null ? false :
1009 (neatIsect <= Vector.mag(ldis));
1010 if (isInShadow) {
1011 return col;
1012 } else {
1013 var illum = Vector.dot(livec, norm);
1014 var lcolor = (illum > 0) ? Color.scale(illum, light.color)
1015 : Color.defaultColor;
1016 var specular = Vector.dot(livec, Vector.norm(rd));
1017 var scolor = (specular > 0)
1018 ? Color.scale(Math.pow(specular, thing.surface.roughness),
1019 light.color)
1020 : Color.defaultColor;
1021 return col + (thing.surface.diffuse(pos) * lcolor)
1022 + (thing.surface.specular(pos) * scolor);
1023 }
1024 };
1025 return scene.lights.fold(Color.defaultColor, addLight);
1026 }
1027
1028 render(Scene scene, CanvasRenderingContext2D ctx, num screenWidth,
1029 num screenHeight) {
1030 var getPoint = (x, y, camera) {
1031 var recenterX = (x) => (x - (screenWidth / 2.0)) / 2.0 / screenWidth;
1032 var recenterY = (y) => - (y - (screenHeight / 2.0)) / 2.0 / screenHeight;
1033 return Vector.norm(camera.forward
1034 + Vector.times(recenterX(x), camera.right)
1035 + Vector.times(recenterY(y), camera.up));
1036 };
1037 for (int y = 0; y < screenHeight; y++) {
1038 for (int x = 0; x < screenWidth; x++) {
1039 var color = _traceRay(new Ray(scene.camera.pos,
1040 getPoint(x, y, scene.camera) ), scene, 0);
1041 ctx.fillStyle = Color.toDrawingRGB(color);
1042 ctx.fillRect(x, y, x + 1, y + 1);
1043 }
1044 }
1045 }
1046 }
1047
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698