Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / dy3 / js / dashed-canvas.js
1 /**
2  * @license
3  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
4  * MIT-licensed (http://opensource.org/licenses/MIT)
5  */
6
7 /**
8  * @fileoverview Adds support for dashed lines to the HTML5 canvas.
9  *
10  * Usage:
11  *   var ctx = canvas.getContext("2d");
12  *   ctx.installPattern([10, 5])  // draw 10 pixels, skip 5 pixels, repeat.
13  *   ctx.beginPath();
14  *   ctx.moveTo(100, 100);  // start the first line segment.
15  *   ctx.lineTo(150, 200);
16  *   ctx.lineTo(200, 100);
17  *   ctx.moveTo(300, 150);  // start a second, unconnected line
18  *   ctx.lineTo(400, 250);
19  *   ...
20  *   ctx.stroke();          // draw the dashed line.
21  *   ctx.uninstallPattern();
22  *
23  * This is designed to leave the canvas untouched when it's not used.
24  * If you never install a pattern, or call uninstallPattern(), then the canvas
25  * will be exactly as it would have if you'd never used this library. The only
26  * difference from the standard canvas will be the "installPattern" method of
27  * the drawing context.
28  */
29
30 /**
31  * Change the stroking style of the canvas drawing context from a solid line to
32  * a pattern (e.g. dashes, dash-dot-dash, etc.)
33  *
34  * Once you've installed the pattern, you can draw with it by using the
35  * beginPath(), moveTo(), lineTo() and stroke() method calls. Note that some
36  * more advanced methods (e.g. quadraticCurveTo() and bezierCurveTo()) are not
37  * supported. See file overview for a working example.
38  *
39  * Side effects of calling this method include adding an "isPatternInstalled"
40  * property and "uninstallPattern" method to this particular canvas context.
41  * You must call uninstallPattern() before calling installPattern() again.
42  *
43  * @param {Array.<number>} pattern A description of the stroke pattern. Even
44  * indices indicate a draw and odd indices indicate a gap (in pixels). The
45  * array should have a even length as any odd lengthed array could be expressed
46  * as a smaller even length array.
47  */
48 CanvasRenderingContext2D.prototype.installPattern = function(pattern) {
49   "use strict";
50
51   if (typeof(this.isPatternInstalled) !== 'undefined') {
52     throw "Must un-install old line pattern before installing a new one.";
53   }
54   this.isPatternInstalled = true;
55
56   var dashedLineToHistory = [0, 0];
57
58   // list of connected line segements:
59   // [ [x1, y1], ..., [xn, yn] ], [ [x1, y1], ..., [xn, yn] ]
60   var segments = [];
61
62   // Stash away copies of the unmodified line-drawing functions.
63   var realBeginPath = this.beginPath;
64   var realLineTo = this.lineTo;
65   var realMoveTo = this.moveTo;
66   var realStroke = this.stroke;
67
68   /** @type {function()|undefined} */
69   this.uninstallPattern = function() {
70     this.beginPath = realBeginPath;
71     this.lineTo = realLineTo;
72     this.moveTo = realMoveTo;
73     this.stroke = realStroke;
74     this.uninstallPattern = undefined;
75     this.isPatternInstalled = undefined;
76   };
77
78   // Keep our own copies of the line segments as they're drawn.
79   this.beginPath = function() {
80     segments = [];
81     realBeginPath.call(this);
82   };
83   this.moveTo = function(x, y) {
84     segments.push([[x, y]]);
85     realMoveTo.call(this, x, y);
86   };
87   this.lineTo = function(x, y) {
88     var last = segments[segments.length - 1];
89     last.push([x, y]);
90   };
91
92   this.stroke = function() {
93     if (segments.length === 0) {
94       // Maybe the user is drawing something other than a line.
95       // TODO(danvk): test this case.
96       realStroke.call(this);
97       return;
98     }
99
100     for (var i = 0; i < segments.length; i++) {
101       var seg = segments[i];
102       var x1 = seg[0][0], y1 = seg[0][1];
103       for (var j = 1; j < seg.length; j++) {
104         // Draw a dashed line from (x1, y1) - (x2, y2)
105         var x2 = seg[j][0], y2 = seg[j][1];
106         this.save();
107
108         // Calculate transformation parameters
109         var dx = (x2-x1);
110         var dy = (y2-y1);
111         var len = Math.sqrt(dx*dx + dy*dy);
112         var rot = Math.atan2(dy, dx);
113
114         // Set transformation
115         this.translate(x1, y1);
116         realMoveTo.call(this, 0, 0);
117         this.rotate(rot);
118
119         // Set last pattern index we used for this pattern.
120         var patternIndex = dashedLineToHistory[0];
121         var x = 0;
122         while (len > x) {
123           // Get the length of the pattern segment we are dealing with.
124           var segment = pattern[patternIndex];
125           // If our last draw didn't complete the pattern segment all the way
126           // we will try to finish it. Otherwise we will try to do the whole
127           // segment.
128           if (dashedLineToHistory[1]) {
129             x += dashedLineToHistory[1];
130           } else {
131             x += segment;
132           }
133
134           if (x > len) {
135             // We were unable to complete this pattern index all the way, keep
136             // where we are the history so our next draw continues where we
137             // left off in the pattern.
138             dashedLineToHistory = [patternIndex, x-len];
139             x = len;
140           } else {
141             // We completed this patternIndex, we put in the history that we
142             // are on the beginning of the next segment.
143             dashedLineToHistory = [(patternIndex+1)%pattern.length, 0];
144           }
145
146           // We do a line on a even pattern index and just move on a odd
147           // pattern index.  The move is the empty space in the dash.
148           if (patternIndex % 2 === 0) {
149             realLineTo.call(this, x, 0);
150           } else {
151             realMoveTo.call(this, x, 0);
152           }
153
154           // If we are not done, next loop process the next pattern segment, or
155           // the first segment again if we are at the end of the pattern.
156           patternIndex = (patternIndex+1) % pattern.length;
157         }
158
159         this.restore();
160         x1 = x2, y1 = y2;
161       }
162     }
163     realStroke.call(this);
164     segments = [];
165   };
166 };
167
168 /**
169  * Removes the previously-installed pattern.
170  * You must call installPattern() before calling this. You can install at most
171  * one pattern at a time--there is no pattern stack.
172  */
173 CanvasRenderingContext2D.prototype.uninstallPattern = function() {
174   // This will be replaced by a non-error version when a pattern is installed.
175   throw "Must install a line pattern before uninstalling it.";
176 }