fabric-rails 1.0.12 → 1.0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +16 -0
- data/README.md +1 -1
- data/lib/fabric/rails/version.rb +2 -2
- data/vendor/assets/javascripts/event.js +1909 -0
- data/vendor/assets/javascripts/fabric.js +64 -16464
- data/vendor/assets/javascripts/fabric/HEADER.js +31 -0
- data/vendor/assets/javascripts/fabric/canvas.class.js +1007 -0
- data/vendor/assets/javascripts/fabric/canvas_animation.mixin.js +113 -0
- data/vendor/assets/javascripts/fabric/canvas_events.mixin.js +482 -0
- data/vendor/assets/javascripts/fabric/canvas_gestures.mixin.js +79 -0
- data/vendor/assets/javascripts/fabric/canvas_serialization.mixin.js +311 -0
- data/vendor/assets/javascripts/fabric/circle.class.js +182 -0
- data/vendor/assets/javascripts/fabric/color.class.js +284 -0
- data/vendor/assets/javascripts/fabric/ellipse.class.js +169 -0
- data/vendor/assets/javascripts/fabric/freedrawing.class.js +256 -0
- data/vendor/assets/javascripts/fabric/gradient.class.js +211 -0
- data/vendor/assets/javascripts/fabric/group.class.js +556 -0
- data/vendor/assets/javascripts/fabric/image.class.js +418 -0
- data/vendor/assets/javascripts/fabric/image_filters.js +809 -0
- data/vendor/assets/javascripts/fabric/intersection.class.js +178 -0
- data/vendor/assets/javascripts/fabric/line.class.js +188 -0
- data/vendor/assets/javascripts/fabric/log.js +26 -0
- data/vendor/assets/javascripts/fabric/node.js +149 -0
- data/vendor/assets/javascripts/fabric/object.class.js +1068 -0
- data/vendor/assets/javascripts/fabric/object_geometry.mixin.js +308 -0
- data/vendor/assets/javascripts/fabric/object_interactivity.mixin.js +496 -0
- data/vendor/assets/javascripts/fabric/object_origin.mixin.js +207 -0
- data/vendor/assets/javascripts/fabric/object_straightening.mixin.js +94 -0
- data/vendor/assets/javascripts/fabric/observable.mixin.js +91 -0
- data/vendor/assets/javascripts/fabric/parser.js +750 -0
- data/vendor/assets/javascripts/fabric/path.class.js +794 -0
- data/vendor/assets/javascripts/fabric/path_group.class.js +240 -0
- data/vendor/assets/javascripts/fabric/pattern.class.js +69 -0
- data/vendor/assets/javascripts/fabric/point.class.js +327 -0
- data/vendor/assets/javascripts/fabric/polygon.class.js +184 -0
- data/vendor/assets/javascripts/fabric/polyline.class.js +157 -0
- data/vendor/assets/javascripts/fabric/rect.class.js +298 -0
- data/vendor/assets/javascripts/fabric/scout.js +45 -0
- data/vendor/assets/javascripts/fabric/shadow.class.js +70 -0
- data/vendor/assets/javascripts/fabric/stateful.js +88 -0
- data/vendor/assets/javascripts/fabric/static_canvas.class.js +1298 -0
- data/vendor/assets/javascripts/fabric/text.class.js +934 -0
- data/vendor/assets/javascripts/fabric/triangle.class.js +108 -0
- data/vendor/assets/javascripts/fabric/util/anim_ease.js +360 -0
- data/vendor/assets/javascripts/fabric/util/dom_event.js +237 -0
- data/vendor/assets/javascripts/fabric/util/dom_misc.js +245 -0
- data/vendor/assets/javascripts/fabric/util/dom_request.js +72 -0
- data/vendor/assets/javascripts/fabric/util/dom_style.js +71 -0
- data/vendor/assets/javascripts/fabric/util/lang_array.js +250 -0
- data/vendor/assets/javascripts/fabric/util/lang_class.js +97 -0
- data/vendor/assets/javascripts/fabric/util/lang_function.js +35 -0
- data/vendor/assets/javascripts/fabric/util/lang_object.js +34 -0
- data/vendor/assets/javascripts/fabric/util/lang_string.js +60 -0
- data/vendor/assets/javascripts/fabric/util/misc.js +406 -0
- data/vendor/assets/javascripts/json2.js +491 -0
- metadata +53 -2
@@ -0,0 +1,794 @@
|
|
1
|
+
(function(global) {
|
2
|
+
|
3
|
+
var commandLengths = {
|
4
|
+
m: 2,
|
5
|
+
l: 2,
|
6
|
+
h: 1,
|
7
|
+
v: 1,
|
8
|
+
c: 6,
|
9
|
+
s: 4,
|
10
|
+
q: 4,
|
11
|
+
t: 2,
|
12
|
+
a: 7
|
13
|
+
};
|
14
|
+
|
15
|
+
function drawArc(ctx, x, y, coords) {
|
16
|
+
var rx = coords[0];
|
17
|
+
var ry = coords[1];
|
18
|
+
var rot = coords[2];
|
19
|
+
var large = coords[3];
|
20
|
+
var sweep = coords[4];
|
21
|
+
var ex = coords[5];
|
22
|
+
var ey = coords[6];
|
23
|
+
var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
|
24
|
+
for (var i=0; i<segs.length; i++) {
|
25
|
+
var bez = segmentToBezier.apply(this, segs[i]);
|
26
|
+
ctx.bezierCurveTo.apply(ctx, bez);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
var arcToSegmentsCache = { },
|
31
|
+
segmentToBezierCache = { },
|
32
|
+
_join = Array.prototype.join,
|
33
|
+
argsString;
|
34
|
+
|
35
|
+
// Generous contribution by Raph Levien, from libsvg-0.1.0.tar.gz
|
36
|
+
function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
37
|
+
argsString = _join.call(arguments);
|
38
|
+
if (arcToSegmentsCache[argsString]) {
|
39
|
+
return arcToSegmentsCache[argsString];
|
40
|
+
}
|
41
|
+
|
42
|
+
var th = rotateX * (Math.PI/180);
|
43
|
+
var sin_th = Math.sin(th);
|
44
|
+
var cos_th = Math.cos(th);
|
45
|
+
rx = Math.abs(rx);
|
46
|
+
ry = Math.abs(ry);
|
47
|
+
var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
|
48
|
+
var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
|
49
|
+
var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
|
50
|
+
if (pl > 1) {
|
51
|
+
pl = Math.sqrt(pl);
|
52
|
+
rx *= pl;
|
53
|
+
ry *= pl;
|
54
|
+
}
|
55
|
+
|
56
|
+
var a00 = cos_th / rx;
|
57
|
+
var a01 = sin_th / rx;
|
58
|
+
var a10 = (-sin_th) / ry;
|
59
|
+
var a11 = (cos_th) / ry;
|
60
|
+
var x0 = a00 * ox + a01 * oy;
|
61
|
+
var y0 = a10 * ox + a11 * oy;
|
62
|
+
var x1 = a00 * x + a01 * y;
|
63
|
+
var y1 = a10 * x + a11 * y;
|
64
|
+
|
65
|
+
var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0);
|
66
|
+
var sfactor_sq = 1 / d - 0.25;
|
67
|
+
if (sfactor_sq < 0) sfactor_sq = 0;
|
68
|
+
var sfactor = Math.sqrt(sfactor_sq);
|
69
|
+
if (sweep === large) sfactor = -sfactor;
|
70
|
+
var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0);
|
71
|
+
var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0);
|
72
|
+
|
73
|
+
var th0 = Math.atan2(y0-yc, x0-xc);
|
74
|
+
var th1 = Math.atan2(y1-yc, x1-xc);
|
75
|
+
|
76
|
+
var th_arc = th1-th0;
|
77
|
+
if (th_arc < 0 && sweep === 1){
|
78
|
+
th_arc += 2*Math.PI;
|
79
|
+
} else if (th_arc > 0 && sweep === 0) {
|
80
|
+
th_arc -= 2 * Math.PI;
|
81
|
+
}
|
82
|
+
|
83
|
+
var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
|
84
|
+
var result = [];
|
85
|
+
for (var i=0; i<segments; i++) {
|
86
|
+
var th2 = th0 + i * th_arc / segments;
|
87
|
+
var th3 = th0 + (i+1) * th_arc / segments;
|
88
|
+
result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
|
89
|
+
}
|
90
|
+
|
91
|
+
return (arcToSegmentsCache[argsString] = result);
|
92
|
+
}
|
93
|
+
|
94
|
+
function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
|
95
|
+
argsString = _join.call(arguments);
|
96
|
+
if (segmentToBezierCache[argsString]) {
|
97
|
+
return segmentToBezierCache[argsString];
|
98
|
+
}
|
99
|
+
|
100
|
+
var a00 = cos_th * rx;
|
101
|
+
var a01 = -sin_th * ry;
|
102
|
+
var a10 = sin_th * rx;
|
103
|
+
var a11 = cos_th * ry;
|
104
|
+
|
105
|
+
var th_half = 0.5 * (th1 - th0);
|
106
|
+
var t = (8/3) * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half);
|
107
|
+
var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
|
108
|
+
var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
|
109
|
+
var x3 = cx + Math.cos(th1);
|
110
|
+
var y3 = cy + Math.sin(th1);
|
111
|
+
var x2 = x3 + t * Math.sin(th1);
|
112
|
+
var y2 = y3 - t * Math.cos(th1);
|
113
|
+
|
114
|
+
return (segmentToBezierCache[argsString] = [
|
115
|
+
a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
|
116
|
+
a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
|
117
|
+
a00 * x3 + a01 * y3, a10 * x3 + a11 * y3
|
118
|
+
]);
|
119
|
+
}
|
120
|
+
|
121
|
+
"use strict";
|
122
|
+
|
123
|
+
var fabric = global.fabric || (global.fabric = { }),
|
124
|
+
min = fabric.util.array.min,
|
125
|
+
max = fabric.util.array.max,
|
126
|
+
extend = fabric.util.object.extend,
|
127
|
+
_toString = Object.prototype.toString;
|
128
|
+
|
129
|
+
if (fabric.Path) {
|
130
|
+
fabric.warn('fabric.Path is already defined');
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* @private
|
136
|
+
*/
|
137
|
+
function getX(item) {
|
138
|
+
if (item[0] === 'H') {
|
139
|
+
return item[1];
|
140
|
+
}
|
141
|
+
return item[item.length - 2];
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* @private
|
146
|
+
*/
|
147
|
+
function getY(item) {
|
148
|
+
if (item[0] === 'V') {
|
149
|
+
return item[1];
|
150
|
+
}
|
151
|
+
return item[item.length - 1];
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* Path class
|
156
|
+
* @class Path
|
157
|
+
* @extends fabric.Object
|
158
|
+
*/
|
159
|
+
fabric.Path = fabric.util.createClass(fabric.Object, /** @scope fabric.Path.prototype */ {
|
160
|
+
|
161
|
+
/**
|
162
|
+
* Type of an object
|
163
|
+
* @property
|
164
|
+
* @type String
|
165
|
+
*/
|
166
|
+
type: 'path',
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Constructor
|
170
|
+
* @method initialize
|
171
|
+
* @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
|
172
|
+
* @param {Object} [options] Options object
|
173
|
+
*/
|
174
|
+
initialize: function(path, options) {
|
175
|
+
options = options || { };
|
176
|
+
|
177
|
+
this.setOptions(options);
|
178
|
+
|
179
|
+
if (!path) {
|
180
|
+
throw new Error('`path` argument is required');
|
181
|
+
}
|
182
|
+
|
183
|
+
var fromArray = _toString.call(path) === '[object Array]';
|
184
|
+
|
185
|
+
this.path = fromArray
|
186
|
+
? path
|
187
|
+
// one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
|
188
|
+
: path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
|
189
|
+
|
190
|
+
if (!this.path) return;
|
191
|
+
|
192
|
+
if (!fromArray) {
|
193
|
+
this.path = this._parsePath();
|
194
|
+
}
|
195
|
+
this._initializePath(options);
|
196
|
+
|
197
|
+
if (options.sourcePath) {
|
198
|
+
this.setSourcePath(options.sourcePath);
|
199
|
+
}
|
200
|
+
},
|
201
|
+
|
202
|
+
/**
|
203
|
+
* @private
|
204
|
+
* @method _initializePath
|
205
|
+
*/
|
206
|
+
_initializePath: function (options) {
|
207
|
+
var isWidthSet = 'width' in options,
|
208
|
+
isHeightSet = 'height' in options,
|
209
|
+
isLeftSet = 'left' in options,
|
210
|
+
isTopSet = 'top' in options;
|
211
|
+
|
212
|
+
if (!isWidthSet || !isHeightSet) {
|
213
|
+
extend(this, this._parseDimensions());
|
214
|
+
if (isWidthSet) {
|
215
|
+
this.width = options.width;
|
216
|
+
}
|
217
|
+
if (isHeightSet) {
|
218
|
+
this.height = options.height;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
else { //Set center location relative to given height/width if not specified
|
222
|
+
if (!isTopSet) {
|
223
|
+
this.top = this.height / 2;
|
224
|
+
}
|
225
|
+
if (!isLeftSet) {
|
226
|
+
this.left = this.width / 2;
|
227
|
+
}
|
228
|
+
}
|
229
|
+
this.pathOffset = this._calculatePathOffset(isTopSet || isLeftSet); //Save top-left coords as offset
|
230
|
+
},
|
231
|
+
|
232
|
+
/**
|
233
|
+
* @private
|
234
|
+
* @method _calculatePathOffset
|
235
|
+
*/
|
236
|
+
_calculatePathOffset: function (positionSet) {
|
237
|
+
return {
|
238
|
+
x: positionSet ? 0 : this.left - (this.width / 2),
|
239
|
+
y: positionSet ? 0 : this.top - (this.height / 2)
|
240
|
+
};
|
241
|
+
},
|
242
|
+
|
243
|
+
/**
|
244
|
+
* @private
|
245
|
+
* @method _render
|
246
|
+
*/
|
247
|
+
_render: function(ctx) {
|
248
|
+
var current, // current instruction
|
249
|
+
previous = null,
|
250
|
+
x = 0, // current x
|
251
|
+
y = 0, // current y
|
252
|
+
controlX = 0, // current control point x
|
253
|
+
controlY = 0, // current control point y
|
254
|
+
tempX,
|
255
|
+
tempY,
|
256
|
+
tempControlX,
|
257
|
+
tempControlY,
|
258
|
+
l = -((this.width / 2) + this.pathOffset.x),
|
259
|
+
t = -((this.height / 2) + this.pathOffset.y);
|
260
|
+
|
261
|
+
for (var i = 0, len = this.path.length; i < len; ++i) {
|
262
|
+
|
263
|
+
current = this.path[i];
|
264
|
+
|
265
|
+
switch (current[0]) { // first letter
|
266
|
+
|
267
|
+
case 'l': // lineto, relative
|
268
|
+
x += current[1];
|
269
|
+
y += current[2];
|
270
|
+
ctx.lineTo(x + l, y + t);
|
271
|
+
break;
|
272
|
+
|
273
|
+
case 'L': // lineto, absolute
|
274
|
+
x = current[1];
|
275
|
+
y = current[2];
|
276
|
+
ctx.lineTo(x + l, y + t);
|
277
|
+
break;
|
278
|
+
|
279
|
+
case 'h': // horizontal lineto, relative
|
280
|
+
x += current[1];
|
281
|
+
ctx.lineTo(x + l, y + t);
|
282
|
+
break;
|
283
|
+
|
284
|
+
case 'H': // horizontal lineto, absolute
|
285
|
+
x = current[1];
|
286
|
+
ctx.lineTo(x + l, y + t);
|
287
|
+
break;
|
288
|
+
|
289
|
+
case 'v': // vertical lineto, relative
|
290
|
+
y += current[1];
|
291
|
+
ctx.lineTo(x + l, y + t);
|
292
|
+
break;
|
293
|
+
|
294
|
+
case 'V': // verical lineto, absolute
|
295
|
+
y = current[1];
|
296
|
+
ctx.lineTo(x + l, y + t);
|
297
|
+
break;
|
298
|
+
|
299
|
+
case 'm': // moveTo, relative
|
300
|
+
x += current[1];
|
301
|
+
y += current[2];
|
302
|
+
// draw a line if previous command was moveTo as well (otherwise, it will have no effect)
|
303
|
+
ctx[(previous && (previous[0] === 'm' || previous[0] === 'M')) ? 'lineTo' : 'moveTo'](x + l, y + t);
|
304
|
+
break;
|
305
|
+
|
306
|
+
case 'M': // moveTo, absolute
|
307
|
+
x = current[1];
|
308
|
+
y = current[2];
|
309
|
+
// draw a line if previous command was moveTo as well (otherwise, it will have no effect)
|
310
|
+
ctx[(previous && (previous[0] === 'm' || previous[0] === 'M')) ? 'lineTo' : 'moveTo'](x + l, y + t);
|
311
|
+
break;
|
312
|
+
|
313
|
+
case 'c': // bezierCurveTo, relative
|
314
|
+
tempX = x + current[5];
|
315
|
+
tempY = y + current[6];
|
316
|
+
controlX = x + current[3];
|
317
|
+
controlY = y + current[4];
|
318
|
+
ctx.bezierCurveTo(
|
319
|
+
x + current[1] + l, // x1
|
320
|
+
y + current[2] + t, // y1
|
321
|
+
controlX + l, // x2
|
322
|
+
controlY + t, // y2
|
323
|
+
tempX + l,
|
324
|
+
tempY + t
|
325
|
+
);
|
326
|
+
x = tempX;
|
327
|
+
y = tempY;
|
328
|
+
break;
|
329
|
+
|
330
|
+
case 'C': // bezierCurveTo, absolute
|
331
|
+
x = current[5];
|
332
|
+
y = current[6];
|
333
|
+
controlX = current[3];
|
334
|
+
controlY = current[4];
|
335
|
+
ctx.bezierCurveTo(
|
336
|
+
current[1] + l,
|
337
|
+
current[2] + t,
|
338
|
+
controlX + l,
|
339
|
+
controlY + t,
|
340
|
+
x + l,
|
341
|
+
y + t
|
342
|
+
);
|
343
|
+
break;
|
344
|
+
|
345
|
+
case 's': // shorthand cubic bezierCurveTo, relative
|
346
|
+
|
347
|
+
// transform to absolute x,y
|
348
|
+
tempX = x + current[3];
|
349
|
+
tempY = y + current[4];
|
350
|
+
|
351
|
+
// calculate reflection of previous control points
|
352
|
+
controlX = controlX ? (2 * x - controlX) : x;
|
353
|
+
controlY = controlY ? (2 * y - controlY) : y;
|
354
|
+
|
355
|
+
ctx.bezierCurveTo(
|
356
|
+
controlX + l,
|
357
|
+
controlY + t,
|
358
|
+
x + current[1] + l,
|
359
|
+
y + current[2] + t,
|
360
|
+
tempX + l,
|
361
|
+
tempY + t
|
362
|
+
);
|
363
|
+
// set control point to 2nd one of this command
|
364
|
+
// "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
|
365
|
+
controlX = x + current[1];
|
366
|
+
controlY = y + current[2];
|
367
|
+
|
368
|
+
x = tempX;
|
369
|
+
y = tempY;
|
370
|
+
break;
|
371
|
+
|
372
|
+
case 'S': // shorthand cubic bezierCurveTo, absolute
|
373
|
+
tempX = current[3];
|
374
|
+
tempY = current[4];
|
375
|
+
// calculate reflection of previous control points
|
376
|
+
controlX = 2*x - controlX;
|
377
|
+
controlY = 2*y - controlY;
|
378
|
+
ctx.bezierCurveTo(
|
379
|
+
controlX + l,
|
380
|
+
controlY + t,
|
381
|
+
current[1] + l,
|
382
|
+
current[2] + t,
|
383
|
+
tempX + l,
|
384
|
+
tempY + t
|
385
|
+
);
|
386
|
+
x = tempX;
|
387
|
+
y = tempY;
|
388
|
+
|
389
|
+
// set control point to 2nd one of this command
|
390
|
+
// "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
|
391
|
+
controlX = current[1];
|
392
|
+
controlY = current[2];
|
393
|
+
|
394
|
+
break;
|
395
|
+
|
396
|
+
case 'q': // quadraticCurveTo, relative
|
397
|
+
// transform to absolute x,y
|
398
|
+
tempX = x + current[3];
|
399
|
+
tempY = y + current[4];
|
400
|
+
|
401
|
+
controlX = x + current[1];
|
402
|
+
controlY = y + current[2];
|
403
|
+
|
404
|
+
ctx.quadraticCurveTo(
|
405
|
+
controlX + l,
|
406
|
+
controlY + t,
|
407
|
+
tempX + l,
|
408
|
+
tempY + t
|
409
|
+
);
|
410
|
+
x = tempX;
|
411
|
+
y = tempY;
|
412
|
+
break;
|
413
|
+
|
414
|
+
case 'Q': // quadraticCurveTo, absolute
|
415
|
+
tempX = current[3];
|
416
|
+
tempY = current[4];
|
417
|
+
|
418
|
+
ctx.quadraticCurveTo(
|
419
|
+
current[1] + l,
|
420
|
+
current[2] + t,
|
421
|
+
tempX + l,
|
422
|
+
tempY + t
|
423
|
+
);
|
424
|
+
x = tempX;
|
425
|
+
y = tempY;
|
426
|
+
controlX = current[1];
|
427
|
+
controlY = current[2];
|
428
|
+
break;
|
429
|
+
|
430
|
+
case 't': // shorthand quadraticCurveTo, relative
|
431
|
+
|
432
|
+
// transform to absolute x,y
|
433
|
+
tempX = x + current[1];
|
434
|
+
tempY = y + current[2];
|
435
|
+
|
436
|
+
|
437
|
+
if (previous[0].match(/[QqTt]/) === null) {
|
438
|
+
// If there is no previous command or if the previous command was not a Q, q, T or t,
|
439
|
+
// assume the control point is coincident with the current point
|
440
|
+
controlX = x;
|
441
|
+
controlY = y;
|
442
|
+
}
|
443
|
+
else if (previous[0] === 't') {
|
444
|
+
// calculate reflection of previous control points for t
|
445
|
+
controlX = 2 * x - tempControlX;
|
446
|
+
controlY = 2 * y - tempControlY;
|
447
|
+
}
|
448
|
+
else if (previous[0] === 'q') {
|
449
|
+
// calculate reflection of previous control points for q
|
450
|
+
controlX = 2 * x - controlX;
|
451
|
+
controlY = 2 * y - controlY;
|
452
|
+
}
|
453
|
+
|
454
|
+
tempControlX = controlX;
|
455
|
+
tempControlY = controlY;
|
456
|
+
|
457
|
+
ctx.quadraticCurveTo(
|
458
|
+
controlX + l,
|
459
|
+
controlY + t,
|
460
|
+
tempX + l,
|
461
|
+
tempY + t
|
462
|
+
);
|
463
|
+
x = tempX;
|
464
|
+
y = tempY;
|
465
|
+
controlX = x + current[1];
|
466
|
+
controlY = y + current[2];
|
467
|
+
break;
|
468
|
+
|
469
|
+
case 'T':
|
470
|
+
tempX = current[1];
|
471
|
+
tempY = current[2];
|
472
|
+
|
473
|
+
// calculate reflection of previous control points
|
474
|
+
controlX = 2 * x - controlX;
|
475
|
+
controlY = 2 * y - controlY;
|
476
|
+
ctx.quadraticCurveTo(
|
477
|
+
controlX + l,
|
478
|
+
controlY + t,
|
479
|
+
tempX + l,
|
480
|
+
tempY + t
|
481
|
+
);
|
482
|
+
x = tempX;
|
483
|
+
y = tempY;
|
484
|
+
break;
|
485
|
+
|
486
|
+
case 'a':
|
487
|
+
// TODO: optimize this
|
488
|
+
drawArc(ctx, x + l, y + t, [
|
489
|
+
current[1],
|
490
|
+
current[2],
|
491
|
+
current[3],
|
492
|
+
current[4],
|
493
|
+
current[5],
|
494
|
+
current[6] + x + l,
|
495
|
+
current[7] + y + t
|
496
|
+
]);
|
497
|
+
x += current[6];
|
498
|
+
y += current[7];
|
499
|
+
break;
|
500
|
+
|
501
|
+
case 'A':
|
502
|
+
// TODO: optimize this
|
503
|
+
drawArc(ctx, x + l, y + t, [
|
504
|
+
current[1],
|
505
|
+
current[2],
|
506
|
+
current[3],
|
507
|
+
current[4],
|
508
|
+
current[5],
|
509
|
+
current[6] + l,
|
510
|
+
current[7] + t
|
511
|
+
]);
|
512
|
+
x = current[6];
|
513
|
+
y = current[7];
|
514
|
+
break;
|
515
|
+
|
516
|
+
case 'z':
|
517
|
+
case 'Z':
|
518
|
+
ctx.closePath();
|
519
|
+
break;
|
520
|
+
}
|
521
|
+
previous = current;
|
522
|
+
}
|
523
|
+
},
|
524
|
+
|
525
|
+
/**
|
526
|
+
* Renders path on a specified context
|
527
|
+
* @method render
|
528
|
+
* @param {CanvasRenderingContext2D} ctx context to render path on
|
529
|
+
* @param {Boolean} noTransform When true, context is not transformed
|
530
|
+
*/
|
531
|
+
render: function(ctx, noTransform) {
|
532
|
+
ctx.save();
|
533
|
+
var m = this.transformMatrix;
|
534
|
+
if (m) {
|
535
|
+
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
536
|
+
}
|
537
|
+
if (!noTransform) {
|
538
|
+
this.transform(ctx);
|
539
|
+
}
|
540
|
+
// ctx.globalCompositeOperation = this.fillRule;
|
541
|
+
|
542
|
+
if (this.overlayFill) {
|
543
|
+
ctx.fillStyle = this.overlayFill;
|
544
|
+
}
|
545
|
+
else if (this.fill) {
|
546
|
+
ctx.fillStyle = this.fill.toLive
|
547
|
+
? this.fill.toLive(ctx)
|
548
|
+
: this.fill;
|
549
|
+
}
|
550
|
+
|
551
|
+
if (this.stroke) {
|
552
|
+
ctx.strokeStyle = this.stroke.toLive
|
553
|
+
? this.stroke.toLive(ctx)
|
554
|
+
: this.stroke;
|
555
|
+
}
|
556
|
+
ctx.beginPath();
|
557
|
+
|
558
|
+
this._setShadow(ctx);
|
559
|
+
this._render(ctx);
|
560
|
+
|
561
|
+
if (this.fill) {
|
562
|
+
ctx.fill();
|
563
|
+
}
|
564
|
+
this._removeShadow(ctx);
|
565
|
+
|
566
|
+
if (this.stroke) {
|
567
|
+
ctx.strokeStyle = this.stroke;
|
568
|
+
ctx.lineWidth = this.strokeWidth;
|
569
|
+
ctx.lineCap = ctx.lineJoin = 'round';
|
570
|
+
ctx.stroke();
|
571
|
+
}
|
572
|
+
if (!noTransform && this.active) {
|
573
|
+
this.drawBorders(ctx);
|
574
|
+
this.drawControls(ctx);
|
575
|
+
}
|
576
|
+
ctx.restore();
|
577
|
+
},
|
578
|
+
|
579
|
+
/**
|
580
|
+
* Returns string representation of an instance
|
581
|
+
* @method toString
|
582
|
+
* @return {String} string representation of an instance
|
583
|
+
*/
|
584
|
+
toString: function() {
|
585
|
+
return '#<fabric.Path (' + this.complexity() +
|
586
|
+
'): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
|
587
|
+
},
|
588
|
+
|
589
|
+
/**
|
590
|
+
* Returns object representation of an instance
|
591
|
+
* @method toObject
|
592
|
+
* @param {Array} propertiesToInclude
|
593
|
+
* @return {Object} object representation of an instance
|
594
|
+
*/
|
595
|
+
toObject: function(propertiesToInclude) {
|
596
|
+
var o = extend(this.callSuper('toObject', propertiesToInclude), {
|
597
|
+
path: this.path
|
598
|
+
});
|
599
|
+
if (this.sourcePath) {
|
600
|
+
o.sourcePath = this.sourcePath;
|
601
|
+
}
|
602
|
+
if (this.transformMatrix) {
|
603
|
+
o.transformMatrix = this.transformMatrix;
|
604
|
+
}
|
605
|
+
return o;
|
606
|
+
},
|
607
|
+
|
608
|
+
/**
|
609
|
+
* Returns dataless object representation of an instance
|
610
|
+
* @method toDatalessObject
|
611
|
+
* @param {Array} propertiesToInclude
|
612
|
+
* @return {Object} object representation of an instance
|
613
|
+
*/
|
614
|
+
toDatalessObject: function(propertiesToInclude) {
|
615
|
+
var o = this.toObject(propertiesToInclude);
|
616
|
+
if (this.sourcePath) {
|
617
|
+
o.path = this.sourcePath;
|
618
|
+
}
|
619
|
+
delete o.sourcePath;
|
620
|
+
return o;
|
621
|
+
},
|
622
|
+
|
623
|
+
/**
|
624
|
+
* Returns svg representation of an instance
|
625
|
+
* @method toSVG
|
626
|
+
* @return {String} svg representation of an instance
|
627
|
+
*/
|
628
|
+
toSVG: function() {
|
629
|
+
var chunks = [];
|
630
|
+
for (var i = 0, len = this.path.length; i < len; i++) {
|
631
|
+
chunks.push(this.path[i].join(' '));
|
632
|
+
}
|
633
|
+
var path = chunks.join(' ');
|
634
|
+
|
635
|
+
return [
|
636
|
+
'<g transform="', (this.group ? '' : this.getSvgTransform()), '">',
|
637
|
+
'<path ',
|
638
|
+
'd="', path, '" ',
|
639
|
+
'style="', this.getSvgStyles(), '" ',
|
640
|
+
'transform="translate(', (-this.width / 2), ' ', (-this.height/2), ')" />',
|
641
|
+
'</g>'
|
642
|
+
].join('');
|
643
|
+
},
|
644
|
+
|
645
|
+
/**
|
646
|
+
* Returns number representation of an instance complexity
|
647
|
+
* @method complexity
|
648
|
+
* @return {Number} complexity
|
649
|
+
*/
|
650
|
+
complexity: function() {
|
651
|
+
return this.path.length;
|
652
|
+
},
|
653
|
+
|
654
|
+
/**
|
655
|
+
* @private
|
656
|
+
* @method _parsePath
|
657
|
+
*/
|
658
|
+
_parsePath: function() {
|
659
|
+
var result = [ ],
|
660
|
+
currentPath,
|
661
|
+
chunks,
|
662
|
+
parsed;
|
663
|
+
|
664
|
+
for (var i = 0, chunksParsed, len = this.path.length; i < len; i++) {
|
665
|
+
currentPath = this.path[i];
|
666
|
+
chunks = currentPath.slice(1).trim().replace(/(\d)-/g, '$1###-').split(/\s|,|###/);
|
667
|
+
chunksParsed = [ currentPath.charAt(0) ];
|
668
|
+
|
669
|
+
for (var j = 0, jlen = chunks.length; j < jlen; j++) {
|
670
|
+
parsed = parseFloat(chunks[j]);
|
671
|
+
if (!isNaN(parsed)) {
|
672
|
+
chunksParsed.push(parsed);
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
var command = chunksParsed[0].toLowerCase(),
|
677
|
+
commandLength = commandLengths[command];
|
678
|
+
|
679
|
+
if (chunksParsed.length - 1 > commandLength) {
|
680
|
+
for (var k = 1, klen = chunksParsed.length; k < klen; k += commandLength) {
|
681
|
+
result.push([ chunksParsed[0] ].concat(chunksParsed.slice(k, k + commandLength)));
|
682
|
+
}
|
683
|
+
}
|
684
|
+
else {
|
685
|
+
result.push(chunksParsed);
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
return result;
|
690
|
+
},
|
691
|
+
|
692
|
+
/**
|
693
|
+
* @method _parseDimensions
|
694
|
+
*/
|
695
|
+
_parseDimensions: function() {
|
696
|
+
var aX = [],
|
697
|
+
aY = [],
|
698
|
+
previousX,
|
699
|
+
previousY,
|
700
|
+
isLowerCase = false,
|
701
|
+
x,
|
702
|
+
y;
|
703
|
+
|
704
|
+
this.path.forEach(function(item, i) {
|
705
|
+
if (item[0] !== 'H') {
|
706
|
+
previousX = (i === 0) ? getX(item) : getX(this.path[i-1]);
|
707
|
+
}
|
708
|
+
if (item[0] !== 'V') {
|
709
|
+
previousY = (i === 0) ? getY(item) : getY(this.path[i-1]);
|
710
|
+
}
|
711
|
+
|
712
|
+
// lowercased letter denotes relative position;
|
713
|
+
// transform to absolute
|
714
|
+
if (item[0] === item[0].toLowerCase()) {
|
715
|
+
isLowerCase = true;
|
716
|
+
}
|
717
|
+
|
718
|
+
// last 2 items in an array of coordinates are the actualy x/y (except H/V);
|
719
|
+
// collect them
|
720
|
+
|
721
|
+
// TODO (kangax): support relative h/v commands
|
722
|
+
|
723
|
+
x = isLowerCase
|
724
|
+
? previousX + getX(item)
|
725
|
+
: item[0] === 'V'
|
726
|
+
? previousX
|
727
|
+
: getX(item);
|
728
|
+
|
729
|
+
y = isLowerCase
|
730
|
+
? previousY + getY(item)
|
731
|
+
: item[0] === 'H'
|
732
|
+
? previousY
|
733
|
+
: getY(item);
|
734
|
+
|
735
|
+
var val = parseInt(x, 10);
|
736
|
+
if (!isNaN(val)) aX.push(val);
|
737
|
+
|
738
|
+
val = parseInt(y, 10);
|
739
|
+
if (!isNaN(val)) aY.push(val);
|
740
|
+
|
741
|
+
}, this);
|
742
|
+
|
743
|
+
var minX = min(aX),
|
744
|
+
minY = min(aY),
|
745
|
+
maxX = max(aX),
|
746
|
+
maxY = max(aY),
|
747
|
+
deltaX = maxX - minX,
|
748
|
+
deltaY = maxY - minY;
|
749
|
+
|
750
|
+
var o = {
|
751
|
+
top: minY + deltaY / 2,
|
752
|
+
left: minX + deltaX / 2,
|
753
|
+
bottom: max(aY) - deltaY,
|
754
|
+
right: max(aX) - deltaX
|
755
|
+
};
|
756
|
+
|
757
|
+
o.width = deltaX;
|
758
|
+
o.height = deltaY;
|
759
|
+
|
760
|
+
return o;
|
761
|
+
}
|
762
|
+
});
|
763
|
+
|
764
|
+
/**
|
765
|
+
* Creates an instance of fabric.Path from an object
|
766
|
+
* @static
|
767
|
+
* @method fabric.Path.fromObject
|
768
|
+
* @return {fabric.Path} Instance of fabric.Path
|
769
|
+
*/
|
770
|
+
fabric.Path.fromObject = function(object) {
|
771
|
+
return new fabric.Path(object.path, object);
|
772
|
+
};
|
773
|
+
|
774
|
+
/**
|
775
|
+
* List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
|
776
|
+
* @static
|
777
|
+
* @see http://www.w3.org/TR/SVG/paths.html#PathElement
|
778
|
+
*/
|
779
|
+
fabric.Path.ATTRIBUTE_NAMES = 'd fill fill-opacity opacity fill-rule stroke stroke-width transform'.split(' ');
|
780
|
+
|
781
|
+
/**
|
782
|
+
* Creates an instance of fabric.Path from an SVG <path> element
|
783
|
+
* @static
|
784
|
+
* @method fabric.Path.fromElement
|
785
|
+
* @param {SVGElement} element to parse
|
786
|
+
* @param {Object} [options] Options object
|
787
|
+
* @return {fabric.Path} Instance of fabric.Path
|
788
|
+
*/
|
789
|
+
fabric.Path.fromElement = function(element, options) {
|
790
|
+
var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
|
791
|
+
return new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options));
|
792
|
+
};
|
793
|
+
|
794
|
+
})(typeof exports !== 'undefined' ? exports : this);
|