2019-02-27 21:06:04 +08:00
|
|
|
import { isBrowser, document, glob, _getGlobalKonva } from './Global';
|
2019-01-02 04:59:27 +08:00
|
|
|
import { Node } from './Node';
|
|
|
|
|
|
|
|
export type Point = {
|
|
|
|
x: number;
|
|
|
|
y: number;
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface RectConf {
|
|
|
|
x: number;
|
|
|
|
y: number;
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-01-06 16:01:20 +08:00
|
|
|
* Collection constructor. Collection extends Array.
|
|
|
|
* This class is used in conjunction with {@link Konva.Container#find}
|
|
|
|
* The good thing about collection is that it has ALL methods of all Konva nodes. Take a look into examples.
|
2019-01-02 04:59:27 +08:00
|
|
|
* @constructor
|
|
|
|
* @memberof Konva
|
2019-01-06 16:01:20 +08:00
|
|
|
* @example
|
|
|
|
*
|
|
|
|
* // find all rectangles and return them as Collection
|
|
|
|
* const shapes = layer.find('Rect');
|
|
|
|
* // fill all rectangles with a single function
|
|
|
|
* shapes.fill('red');
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
export class Collection<Child extends Node> {
|
|
|
|
[index: number]: Child;
|
|
|
|
|
2019-02-23 01:46:46 +08:00
|
|
|
// @ts-ignore
|
2019-01-02 04:59:27 +08:00
|
|
|
length: number;
|
2019-02-23 01:46:46 +08:00
|
|
|
// @ts-ignore
|
2019-01-02 04:59:27 +08:00
|
|
|
each: (f: Function) => void;
|
2019-02-23 01:46:46 +08:00
|
|
|
// @ts-ignore
|
2019-01-02 04:59:27 +08:00
|
|
|
toArray: () => Array<any>;
|
2019-02-23 01:46:46 +08:00
|
|
|
// @ts-ignore
|
2019-01-02 04:59:27 +08:00
|
|
|
push: (item: Child) => void;
|
2019-02-23 01:46:46 +08:00
|
|
|
// @ts-ignore
|
2019-01-02 04:59:27 +08:00
|
|
|
unshift: (item: Child) => void;
|
2019-02-23 01:46:46 +08:00
|
|
|
// @ts-ignore
|
2019-01-02 04:59:27 +08:00
|
|
|
splice: (start: number, length: number, replace?: any) => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* convert array into a collection
|
|
|
|
* @method
|
|
|
|
* @memberof Konva.Collection
|
|
|
|
* @param {Array} arr
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
static toCollection(arr: Array<Node>) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var collection = new Collection(),
|
|
|
|
len = arr.length,
|
|
|
|
n;
|
|
|
|
|
|
|
|
for (n = 0; n < len; n++) {
|
|
|
|
collection.push(arr[n]);
|
|
|
|
}
|
|
|
|
return collection;
|
|
|
|
}
|
|
|
|
|
2019-02-23 01:46:46 +08:00
|
|
|
static _mapMethod(methodName: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
Collection.prototype[methodName] = function() {
|
|
|
|
var len = this.length,
|
|
|
|
i;
|
|
|
|
|
|
|
|
var args = [].slice.call(arguments);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
this[i][methodName].apply(this[i], args);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-02-23 01:46:46 +08:00
|
|
|
static mapMethods = function(constructor: Function) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var prot = constructor.prototype;
|
|
|
|
for (var methodName in prot) {
|
|
|
|
Collection._mapMethod(methodName);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Collection.prototype = [] as any;
|
|
|
|
/**
|
|
|
|
* iterate through node array and run a function for each node.
|
|
|
|
* The node and index is passed into the function
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Collection#each
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Function} func
|
|
|
|
* @example
|
|
|
|
* // get all nodes with name foo inside layer, and set x to 10 for each
|
2019-02-20 22:13:39 +08:00
|
|
|
* layer.find('.foo').each(function(shape, n) {
|
2019-01-02 04:59:27 +08:00
|
|
|
* shape.setX(10);
|
|
|
|
* });
|
|
|
|
*/
|
|
|
|
Collection.prototype.each = function(func) {
|
|
|
|
for (var n = 0; n < this.length; n++) {
|
|
|
|
func(this[n], n);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* convert collection into an array
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Collection#toArray
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
Collection.prototype.toArray = function() {
|
|
|
|
var arr = [],
|
|
|
|
len = this.length,
|
|
|
|
n;
|
|
|
|
|
|
|
|
for (n = 0; n < len; n++) {
|
|
|
|
arr.push(this[n]);
|
|
|
|
}
|
|
|
|
return arr;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Last updated November 2011
|
|
|
|
* By Simon Sarris
|
|
|
|
* www.simonsarris.com
|
|
|
|
* sarris@acm.org
|
|
|
|
*
|
|
|
|
* Free to use and distribute at will
|
|
|
|
* So long as you are nice to people, etc
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* The usage of this class was inspired by some of the work done by a forked
|
|
|
|
* project, KineticJS-Ext by Wappworks, which is based on Simon's Transform
|
|
|
|
* class. Modified by Eric Rowell
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2019-01-06 16:01:20 +08:00
|
|
|
* Transform constructor.
|
|
|
|
* In most of the cases you don't need to use it in your app. Because it is for internal usage in Konva core.
|
2019-01-02 04:59:27 +08:00
|
|
|
* But there is a documentation for that class in case you still want
|
|
|
|
* to make some manual calculations.
|
|
|
|
* @constructor
|
|
|
|
* @param {Array} [m] Optional six-element matrix
|
|
|
|
* @memberof Konva
|
|
|
|
*/
|
|
|
|
export class Transform {
|
|
|
|
m: Array<number>;
|
|
|
|
constructor(m = [1, 0, 0, 1, 0, 0]) {
|
|
|
|
this.m = (m && m.slice()) || [1, 0, 0, 1, 0, 0];
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Copy Konva.Transform object
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#copy
|
2019-01-02 04:59:27 +08:00
|
|
|
* @returns {Konva.Transform}
|
2019-01-06 16:01:20 +08:00
|
|
|
* @example
|
|
|
|
* const tr = shape.getTransform().copy()
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
copy() {
|
|
|
|
return new Transform(this.m);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Transform point
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#point
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Object} point 2D point(x, y)
|
|
|
|
* @returns {Object} 2D point(x, y)
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
point(point: Point) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var m = this.m;
|
|
|
|
return {
|
|
|
|
x: m[0] * point.x + m[2] * point.y + m[4],
|
|
|
|
y: m[1] * point.x + m[3] * point.y + m[5]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Apply translation
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#translate
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Number} x
|
|
|
|
* @param {Number} y
|
|
|
|
* @returns {Konva.Transform}
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
translate(x: number, y: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
this.m[4] += this.m[0] * x + this.m[2] * y;
|
|
|
|
this.m[5] += this.m[1] * x + this.m[3] * y;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Apply scale
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#scale
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Number} sx
|
|
|
|
* @param {Number} sy
|
|
|
|
* @returns {Konva.Transform}
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
scale(sx: number, sy: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
this.m[0] *= sx;
|
|
|
|
this.m[1] *= sx;
|
|
|
|
this.m[2] *= sy;
|
|
|
|
this.m[3] *= sy;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Apply rotation
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#rotate
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Number} rad Angle in radians
|
|
|
|
* @returns {Konva.Transform}
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
rotate(rad: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var c = Math.cos(rad);
|
|
|
|
var s = Math.sin(rad);
|
|
|
|
var m11 = this.m[0] * c + this.m[2] * s;
|
|
|
|
var m12 = this.m[1] * c + this.m[3] * s;
|
|
|
|
var m21 = this.m[0] * -s + this.m[2] * c;
|
|
|
|
var m22 = this.m[1] * -s + this.m[3] * c;
|
|
|
|
this.m[0] = m11;
|
|
|
|
this.m[1] = m12;
|
|
|
|
this.m[2] = m21;
|
|
|
|
this.m[3] = m22;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns the translation
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#getTranslation
|
2019-01-02 04:59:27 +08:00
|
|
|
* @returns {Object} 2D point(x, y)
|
|
|
|
*/
|
|
|
|
getTranslation() {
|
|
|
|
return {
|
|
|
|
x: this.m[4],
|
|
|
|
y: this.m[5]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Apply skew
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#skew
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Number} sx
|
|
|
|
* @param {Number} sy
|
|
|
|
* @returns {Konva.Transform}
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
skew(sx: number, sy: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var m11 = this.m[0] + this.m[2] * sy;
|
|
|
|
var m12 = this.m[1] + this.m[3] * sy;
|
|
|
|
var m21 = this.m[2] + this.m[0] * sx;
|
|
|
|
var m22 = this.m[3] + this.m[1] * sx;
|
|
|
|
this.m[0] = m11;
|
|
|
|
this.m[1] = m12;
|
|
|
|
this.m[2] = m21;
|
|
|
|
this.m[3] = m22;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Transform multiplication
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#multiply
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {Konva.Transform} matrix
|
|
|
|
* @returns {Konva.Transform}
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
multiply(matrix: Transform) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var m11 = this.m[0] * matrix.m[0] + this.m[2] * matrix.m[1];
|
|
|
|
var m12 = this.m[1] * matrix.m[0] + this.m[3] * matrix.m[1];
|
|
|
|
|
|
|
|
var m21 = this.m[0] * matrix.m[2] + this.m[2] * matrix.m[3];
|
|
|
|
var m22 = this.m[1] * matrix.m[2] + this.m[3] * matrix.m[3];
|
|
|
|
|
|
|
|
var dx = this.m[0] * matrix.m[4] + this.m[2] * matrix.m[5] + this.m[4];
|
|
|
|
var dy = this.m[1] * matrix.m[4] + this.m[3] * matrix.m[5] + this.m[5];
|
|
|
|
|
|
|
|
this.m[0] = m11;
|
|
|
|
this.m[1] = m12;
|
|
|
|
this.m[2] = m21;
|
|
|
|
this.m[3] = m22;
|
|
|
|
this.m[4] = dx;
|
|
|
|
this.m[5] = dy;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Invert the matrix
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#invert
|
2019-01-02 04:59:27 +08:00
|
|
|
* @returns {Konva.Transform}
|
|
|
|
*/
|
|
|
|
invert() {
|
|
|
|
var d = 1 / (this.m[0] * this.m[3] - this.m[1] * this.m[2]);
|
|
|
|
var m0 = this.m[3] * d;
|
|
|
|
var m1 = -this.m[1] * d;
|
|
|
|
var m2 = -this.m[2] * d;
|
|
|
|
var m3 = this.m[0] * d;
|
|
|
|
var m4 = d * (this.m[2] * this.m[5] - this.m[3] * this.m[4]);
|
|
|
|
var m5 = d * (this.m[1] * this.m[4] - this.m[0] * this.m[5]);
|
|
|
|
this.m[0] = m0;
|
|
|
|
this.m[1] = m1;
|
|
|
|
this.m[2] = m2;
|
|
|
|
this.m[3] = m3;
|
|
|
|
this.m[4] = m4;
|
|
|
|
this.m[5] = m5;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* return matrix
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#getMatrix
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
getMatrix() {
|
|
|
|
return this.m;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* set to absolute position via translation
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Transform#setAbsolutePosition
|
2019-01-02 04:59:27 +08:00
|
|
|
* @returns {Konva.Transform}
|
|
|
|
* @author ericdrowell
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
setAbsolutePosition(x: number, y: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var m0 = this.m[0],
|
|
|
|
m1 = this.m[1],
|
|
|
|
m2 = this.m[2],
|
|
|
|
m3 = this.m[3],
|
|
|
|
m4 = this.m[4],
|
|
|
|
m5 = this.m[5],
|
|
|
|
yt = (m0 * (y - m5) - m1 * (x - m4)) / (m0 * m3 - m1 * m2),
|
|
|
|
xt = (x - m4 - m2 * yt) / m0;
|
|
|
|
|
|
|
|
return this.translate(xt, yt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// CONSTANTS
|
2019-01-25 13:20:15 +08:00
|
|
|
var OBJECT_ARRAY = '[object Array]',
|
2019-01-02 04:59:27 +08:00
|
|
|
OBJECT_NUMBER = '[object Number]',
|
|
|
|
OBJECT_STRING = '[object String]',
|
|
|
|
OBJECT_BOOLEAN = '[object Boolean]',
|
|
|
|
PI_OVER_DEG180 = Math.PI / 180,
|
|
|
|
DEG180_OVER_PI = 180 / Math.PI,
|
|
|
|
HASH = '#',
|
|
|
|
EMPTY_STRING = '',
|
|
|
|
ZERO = '0',
|
|
|
|
KONVA_WARNING = 'Konva warning: ',
|
|
|
|
KONVA_ERROR = 'Konva error: ',
|
|
|
|
RGB_PAREN = 'rgb(',
|
|
|
|
COLORS = {
|
|
|
|
aliceblue: [240, 248, 255],
|
|
|
|
antiquewhite: [250, 235, 215],
|
|
|
|
aqua: [0, 255, 255],
|
|
|
|
aquamarine: [127, 255, 212],
|
|
|
|
azure: [240, 255, 255],
|
|
|
|
beige: [245, 245, 220],
|
|
|
|
bisque: [255, 228, 196],
|
|
|
|
black: [0, 0, 0],
|
|
|
|
blanchedalmond: [255, 235, 205],
|
|
|
|
blue: [0, 0, 255],
|
|
|
|
blueviolet: [138, 43, 226],
|
|
|
|
brown: [165, 42, 42],
|
|
|
|
burlywood: [222, 184, 135],
|
|
|
|
cadetblue: [95, 158, 160],
|
|
|
|
chartreuse: [127, 255, 0],
|
|
|
|
chocolate: [210, 105, 30],
|
|
|
|
coral: [255, 127, 80],
|
|
|
|
cornflowerblue: [100, 149, 237],
|
|
|
|
cornsilk: [255, 248, 220],
|
|
|
|
crimson: [220, 20, 60],
|
|
|
|
cyan: [0, 255, 255],
|
|
|
|
darkblue: [0, 0, 139],
|
|
|
|
darkcyan: [0, 139, 139],
|
|
|
|
darkgoldenrod: [184, 132, 11],
|
|
|
|
darkgray: [169, 169, 169],
|
|
|
|
darkgreen: [0, 100, 0],
|
|
|
|
darkgrey: [169, 169, 169],
|
|
|
|
darkkhaki: [189, 183, 107],
|
|
|
|
darkmagenta: [139, 0, 139],
|
|
|
|
darkolivegreen: [85, 107, 47],
|
|
|
|
darkorange: [255, 140, 0],
|
|
|
|
darkorchid: [153, 50, 204],
|
|
|
|
darkred: [139, 0, 0],
|
|
|
|
darksalmon: [233, 150, 122],
|
|
|
|
darkseagreen: [143, 188, 143],
|
|
|
|
darkslateblue: [72, 61, 139],
|
|
|
|
darkslategray: [47, 79, 79],
|
|
|
|
darkslategrey: [47, 79, 79],
|
|
|
|
darkturquoise: [0, 206, 209],
|
|
|
|
darkviolet: [148, 0, 211],
|
|
|
|
deeppink: [255, 20, 147],
|
|
|
|
deepskyblue: [0, 191, 255],
|
|
|
|
dimgray: [105, 105, 105],
|
|
|
|
dimgrey: [105, 105, 105],
|
|
|
|
dodgerblue: [30, 144, 255],
|
|
|
|
firebrick: [178, 34, 34],
|
|
|
|
floralwhite: [255, 255, 240],
|
|
|
|
forestgreen: [34, 139, 34],
|
|
|
|
fuchsia: [255, 0, 255],
|
|
|
|
gainsboro: [220, 220, 220],
|
|
|
|
ghostwhite: [248, 248, 255],
|
|
|
|
gold: [255, 215, 0],
|
|
|
|
goldenrod: [218, 165, 32],
|
|
|
|
gray: [128, 128, 128],
|
|
|
|
green: [0, 128, 0],
|
|
|
|
greenyellow: [173, 255, 47],
|
|
|
|
grey: [128, 128, 128],
|
|
|
|
honeydew: [240, 255, 240],
|
|
|
|
hotpink: [255, 105, 180],
|
|
|
|
indianred: [205, 92, 92],
|
|
|
|
indigo: [75, 0, 130],
|
|
|
|
ivory: [255, 255, 240],
|
|
|
|
khaki: [240, 230, 140],
|
|
|
|
lavender: [230, 230, 250],
|
|
|
|
lavenderblush: [255, 240, 245],
|
|
|
|
lawngreen: [124, 252, 0],
|
|
|
|
lemonchiffon: [255, 250, 205],
|
|
|
|
lightblue: [173, 216, 230],
|
|
|
|
lightcoral: [240, 128, 128],
|
|
|
|
lightcyan: [224, 255, 255],
|
|
|
|
lightgoldenrodyellow: [250, 250, 210],
|
|
|
|
lightgray: [211, 211, 211],
|
|
|
|
lightgreen: [144, 238, 144],
|
|
|
|
lightgrey: [211, 211, 211],
|
|
|
|
lightpink: [255, 182, 193],
|
|
|
|
lightsalmon: [255, 160, 122],
|
|
|
|
lightseagreen: [32, 178, 170],
|
|
|
|
lightskyblue: [135, 206, 250],
|
|
|
|
lightslategray: [119, 136, 153],
|
|
|
|
lightslategrey: [119, 136, 153],
|
|
|
|
lightsteelblue: [176, 196, 222],
|
|
|
|
lightyellow: [255, 255, 224],
|
|
|
|
lime: [0, 255, 0],
|
|
|
|
limegreen: [50, 205, 50],
|
|
|
|
linen: [250, 240, 230],
|
|
|
|
magenta: [255, 0, 255],
|
|
|
|
maroon: [128, 0, 0],
|
|
|
|
mediumaquamarine: [102, 205, 170],
|
|
|
|
mediumblue: [0, 0, 205],
|
|
|
|
mediumorchid: [186, 85, 211],
|
|
|
|
mediumpurple: [147, 112, 219],
|
|
|
|
mediumseagreen: [60, 179, 113],
|
|
|
|
mediumslateblue: [123, 104, 238],
|
|
|
|
mediumspringgreen: [0, 250, 154],
|
|
|
|
mediumturquoise: [72, 209, 204],
|
|
|
|
mediumvioletred: [199, 21, 133],
|
|
|
|
midnightblue: [25, 25, 112],
|
|
|
|
mintcream: [245, 255, 250],
|
|
|
|
mistyrose: [255, 228, 225],
|
|
|
|
moccasin: [255, 228, 181],
|
|
|
|
navajowhite: [255, 222, 173],
|
|
|
|
navy: [0, 0, 128],
|
|
|
|
oldlace: [253, 245, 230],
|
|
|
|
olive: [128, 128, 0],
|
|
|
|
olivedrab: [107, 142, 35],
|
|
|
|
orange: [255, 165, 0],
|
|
|
|
orangered: [255, 69, 0],
|
|
|
|
orchid: [218, 112, 214],
|
|
|
|
palegoldenrod: [238, 232, 170],
|
|
|
|
palegreen: [152, 251, 152],
|
|
|
|
paleturquoise: [175, 238, 238],
|
|
|
|
palevioletred: [219, 112, 147],
|
|
|
|
papayawhip: [255, 239, 213],
|
|
|
|
peachpuff: [255, 218, 185],
|
|
|
|
peru: [205, 133, 63],
|
|
|
|
pink: [255, 192, 203],
|
|
|
|
plum: [221, 160, 203],
|
|
|
|
powderblue: [176, 224, 230],
|
|
|
|
purple: [128, 0, 128],
|
|
|
|
rebeccapurple: [102, 51, 153],
|
|
|
|
red: [255, 0, 0],
|
|
|
|
rosybrown: [188, 143, 143],
|
|
|
|
royalblue: [65, 105, 225],
|
|
|
|
saddlebrown: [139, 69, 19],
|
|
|
|
salmon: [250, 128, 114],
|
|
|
|
sandybrown: [244, 164, 96],
|
|
|
|
seagreen: [46, 139, 87],
|
|
|
|
seashell: [255, 245, 238],
|
|
|
|
sienna: [160, 82, 45],
|
|
|
|
silver: [192, 192, 192],
|
|
|
|
skyblue: [135, 206, 235],
|
|
|
|
slateblue: [106, 90, 205],
|
|
|
|
slategray: [119, 128, 144],
|
|
|
|
slategrey: [119, 128, 144],
|
|
|
|
snow: [255, 255, 250],
|
|
|
|
springgreen: [0, 255, 127],
|
|
|
|
steelblue: [70, 130, 180],
|
|
|
|
tan: [210, 180, 140],
|
|
|
|
teal: [0, 128, 128],
|
|
|
|
thistle: [216, 191, 216],
|
|
|
|
transparent: [255, 255, 255, 0],
|
|
|
|
tomato: [255, 99, 71],
|
|
|
|
turquoise: [64, 224, 208],
|
|
|
|
violet: [238, 130, 238],
|
|
|
|
wheat: [245, 222, 179],
|
|
|
|
white: [255, 255, 255],
|
|
|
|
whitesmoke: [245, 245, 245],
|
|
|
|
yellow: [255, 255, 0],
|
|
|
|
yellowgreen: [154, 205, 5]
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
RGB_REGEX = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/,
|
|
|
|
animQueue: Array<Function> = [];
|
2019-01-02 04:59:27 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @namespace Util
|
|
|
|
* @memberof Konva
|
|
|
|
*/
|
|
|
|
export const Util = {
|
|
|
|
/*
|
|
|
|
* cherry-picked utilities from underscore.js
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
_isElement(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return !!(obj && obj.nodeType == 1);
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isFunction(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return !!(obj && obj.constructor && obj.call && obj.apply);
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isPlainObject(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return !!obj && obj.constructor === Object;
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isArray(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return Object.prototype.toString.call(obj) === OBJECT_ARRAY;
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isNumber(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return (
|
|
|
|
Object.prototype.toString.call(obj) === OBJECT_NUMBER &&
|
|
|
|
!isNaN(obj) &&
|
|
|
|
isFinite(obj)
|
|
|
|
);
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isString(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return Object.prototype.toString.call(obj) === OBJECT_STRING;
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isBoolean(obj: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return Object.prototype.toString.call(obj) === OBJECT_BOOLEAN;
|
|
|
|
},
|
|
|
|
// arrays are objects too
|
2019-02-23 01:46:46 +08:00
|
|
|
isObject(val: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return val instanceof Object;
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
isValidSelector(selector: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
if (typeof selector !== 'string') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
var firstChar = selector[0];
|
|
|
|
return (
|
|
|
|
firstChar === '#' ||
|
|
|
|
firstChar === '.' ||
|
|
|
|
firstChar === firstChar.toUpperCase()
|
|
|
|
);
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_sign(number: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
if (number === 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (number > 0) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
|
|
|
|
requestAnimFrame(callback: Function) {
|
|
|
|
animQueue.push(callback);
|
|
|
|
if (animQueue.length === 1) {
|
2019-02-14 12:41:32 +08:00
|
|
|
requestAnimationFrame(function() {
|
2019-02-23 01:46:46 +08:00
|
|
|
const queue = animQueue;
|
|
|
|
animQueue = [];
|
2019-02-14 12:41:32 +08:00
|
|
|
queue.forEach(function(cb) {
|
|
|
|
cb();
|
|
|
|
});
|
2019-01-25 13:20:15 +08:00
|
|
|
});
|
2019-02-03 01:17:27 +08:00
|
|
|
}
|
2019-01-25 13:20:15 +08:00
|
|
|
},
|
2019-01-02 04:59:27 +08:00
|
|
|
createCanvasElement() {
|
|
|
|
var canvas = isBrowser
|
|
|
|
? document.createElement('canvas')
|
2019-02-27 21:06:04 +08:00
|
|
|
: new (_getGlobalKonva()._nodeCanvas())();
|
2019-01-02 04:59:27 +08:00
|
|
|
// on some environments canvas.style is readonly
|
|
|
|
try {
|
|
|
|
canvas.style = canvas.style || {};
|
|
|
|
} catch (e) {}
|
|
|
|
return canvas;
|
|
|
|
},
|
|
|
|
createImageElement() {
|
|
|
|
return document.createElement('img');
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_isInDocument(el: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
while ((el = el.parentNode)) {
|
|
|
|
if (el == document) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_simplifyArray(arr: []) {
|
2019-01-02 04:59:27 +08:00
|
|
|
var retArr = [],
|
|
|
|
len = arr.length,
|
|
|
|
util = Util,
|
|
|
|
n,
|
|
|
|
val;
|
|
|
|
|
|
|
|
for (n = 0; n < len; n++) {
|
|
|
|
val = arr[n];
|
|
|
|
if (util._isNumber(val)) {
|
|
|
|
val = Math.round(val * 1000) / 1000;
|
|
|
|
} else if (!util._isString(val)) {
|
|
|
|
val = val.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
retArr.push(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retArr;
|
|
|
|
},
|
|
|
|
/*
|
|
|
|
* arg can be an image object or image data
|
|
|
|
*/
|
2019-02-23 01:46:46 +08:00
|
|
|
_urlToImage(url: string, callback: Function) {
|
2019-01-22 10:22:36 +08:00
|
|
|
// if arg is a string, then it's a data url
|
2019-02-23 01:46:46 +08:00
|
|
|
var imageObj = new glob.Image();
|
2019-01-22 10:22:36 +08:00
|
|
|
imageObj.onload = function() {
|
|
|
|
callback(imageObj);
|
|
|
|
};
|
|
|
|
imageObj.src = url;
|
2019-01-02 04:59:27 +08:00
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_rgbToHex(r: number, g: number, b: number) {
|
2019-01-02 04:59:27 +08:00
|
|
|
return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
|
|
|
},
|
2019-02-23 01:46:46 +08:00
|
|
|
_hexToRgb(hex: string) {
|
2019-01-02 04:59:27 +08:00
|
|
|
hex = hex.replace(HASH, EMPTY_STRING);
|
|
|
|
var bigint = parseInt(hex, 16);
|
|
|
|
return {
|
|
|
|
r: (bigint >> 16) & 255,
|
|
|
|
g: (bigint >> 8) & 255,
|
|
|
|
b: bigint & 255
|
|
|
|
};
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* return random hex color
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @memberof Konva.Util
|
|
|
|
* @example
|
|
|
|
* shape.fill(Konva.Util.getRandomColor());
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
getRandomColor() {
|
|
|
|
var randColor = ((Math.random() * 0xffffff) << 0).toString(16);
|
|
|
|
while (randColor.length < 6) {
|
|
|
|
randColor = ZERO + randColor;
|
|
|
|
}
|
|
|
|
return HASH + randColor;
|
|
|
|
},
|
2019-01-06 16:01:20 +08:00
|
|
|
|
2019-01-02 04:59:27 +08:00
|
|
|
get(val, def) {
|
|
|
|
if (val === undefined) {
|
|
|
|
return def;
|
|
|
|
} else {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* get RGB components of a color
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @memberof Konva.Util
|
2019-01-02 04:59:27 +08:00
|
|
|
* @param {String} color
|
|
|
|
* @example
|
|
|
|
* // each of the following examples return {r:0, g:0, b:255}
|
2019-01-06 16:01:20 +08:00
|
|
|
* var rgb = Konva.Util.getRGB('blue');
|
|
|
|
* var rgb = Konva.Util.getRGB('#0000ff');
|
|
|
|
* var rgb = Konva.Util.getRGB('rgb(0,0,255)');
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
getRGB(color) {
|
|
|
|
var rgb;
|
|
|
|
// color string
|
|
|
|
if (color in COLORS) {
|
|
|
|
rgb = COLORS[color];
|
|
|
|
return {
|
|
|
|
r: rgb[0],
|
|
|
|
g: rgb[1],
|
|
|
|
b: rgb[2]
|
|
|
|
};
|
|
|
|
} else if (color[0] === HASH) {
|
|
|
|
// hex
|
|
|
|
return this._hexToRgb(color.substring(1));
|
|
|
|
} else if (color.substr(0, 4) === RGB_PAREN) {
|
|
|
|
// rgb string
|
|
|
|
rgb = RGB_REGEX.exec(color.replace(/ /g, ''));
|
|
|
|
return {
|
|
|
|
r: parseInt(rgb[1], 10),
|
|
|
|
g: parseInt(rgb[2], 10),
|
|
|
|
b: parseInt(rgb[3], 10)
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// default
|
|
|
|
return {
|
|
|
|
r: 0,
|
|
|
|
g: 0,
|
|
|
|
b: 0
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// convert any color string to RGBA object
|
|
|
|
// from https://github.com/component/color-parser
|
|
|
|
colorToRGBA(str) {
|
|
|
|
str = str || 'black';
|
|
|
|
return (
|
|
|
|
Util._namedColorToRBA(str) ||
|
|
|
|
Util._hex3ColorToRGBA(str) ||
|
|
|
|
Util._hex6ColorToRGBA(str) ||
|
|
|
|
Util._rgbColorToRGBA(str) ||
|
|
|
|
Util._rgbaColorToRGBA(str)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
// Parse named css color. Like "green"
|
|
|
|
_namedColorToRBA(str) {
|
|
|
|
var c = COLORS[str.toLowerCase()];
|
|
|
|
if (!c) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
r: c[0],
|
|
|
|
g: c[1],
|
|
|
|
b: c[2],
|
|
|
|
a: 1
|
|
|
|
};
|
|
|
|
},
|
|
|
|
// Parse rgb(n, n, n)
|
|
|
|
_rgbColorToRGBA(str) {
|
|
|
|
if (str.indexOf('rgb(') === 0) {
|
|
|
|
str = str.match(/rgb\(([^)]+)\)/)[1];
|
|
|
|
var parts = str.split(/ *, */).map(Number);
|
|
|
|
return {
|
|
|
|
r: parts[0],
|
|
|
|
g: parts[1],
|
|
|
|
b: parts[2],
|
|
|
|
a: 1
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Parse rgba(n, n, n, n)
|
|
|
|
_rgbaColorToRGBA(str) {
|
|
|
|
if (str.indexOf('rgba(') === 0) {
|
|
|
|
str = str.match(/rgba\(([^)]+)\)/)[1];
|
|
|
|
var parts = str.split(/ *, */).map(Number);
|
|
|
|
return {
|
|
|
|
r: parts[0],
|
|
|
|
g: parts[1],
|
|
|
|
b: parts[2],
|
|
|
|
a: parts[3]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Parse #nnnnnn
|
|
|
|
_hex6ColorToRGBA(str) {
|
|
|
|
if (str[0] === '#' && str.length === 7) {
|
|
|
|
return {
|
|
|
|
r: parseInt(str.slice(1, 3), 16),
|
|
|
|
g: parseInt(str.slice(3, 5), 16),
|
|
|
|
b: parseInt(str.slice(5, 7), 16),
|
|
|
|
a: 1
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Parse #nnn
|
|
|
|
_hex3ColorToRGBA(str) {
|
|
|
|
if (str[0] === '#' && str.length === 4) {
|
|
|
|
return {
|
|
|
|
r: parseInt(str[1] + str[1], 16),
|
|
|
|
g: parseInt(str[2] + str[2], 16),
|
|
|
|
b: parseInt(str[3] + str[3], 16),
|
|
|
|
a: 1
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* check intersection of two client rectangles
|
|
|
|
* @method
|
2019-01-06 16:01:20 +08:00
|
|
|
* @memberof Konva.Util
|
|
|
|
* @param {Object} r1 - { x, y, width, height } client rectangle
|
|
|
|
* @param {Object} r2 - { x, y, width, height } client rectangle
|
|
|
|
* @example
|
|
|
|
* const overlapping = Konva.Util.haveIntersection(shape1.getClientRect(), shape2.getClientRect());
|
2019-01-02 04:59:27 +08:00
|
|
|
*/
|
|
|
|
haveIntersection(r1, r2) {
|
|
|
|
return !(
|
|
|
|
r2.x > r1.x + r1.width ||
|
|
|
|
r2.x + r2.width < r1.x ||
|
|
|
|
r2.y > r1.y + r1.height ||
|
|
|
|
r2.y + r2.height < r1.y
|
|
|
|
);
|
|
|
|
},
|
|
|
|
cloneObject<Any>(obj: Any): Any {
|
|
|
|
var retObj: any = {};
|
|
|
|
for (var key in obj) {
|
2019-01-22 06:42:02 +08:00
|
|
|
if (this._isPlainObject(obj[key])) {
|
2019-01-02 04:59:27 +08:00
|
|
|
retObj[key] = this.cloneObject(obj[key]);
|
|
|
|
} else if (this._isArray(obj[key])) {
|
|
|
|
retObj[key] = this.cloneArray(obj[key]);
|
|
|
|
} else {
|
|
|
|
retObj[key] = obj[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retObj;
|
|
|
|
},
|
|
|
|
cloneArray(arr) {
|
|
|
|
return arr.slice(0);
|
|
|
|
},
|
|
|
|
_degToRad(deg) {
|
|
|
|
return deg * PI_OVER_DEG180;
|
|
|
|
},
|
|
|
|
_radToDeg(rad) {
|
|
|
|
return rad * DEG180_OVER_PI;
|
|
|
|
},
|
|
|
|
_capitalize(str) {
|
|
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
|
|
},
|
|
|
|
throw(str) {
|
|
|
|
throw new Error(KONVA_ERROR + str);
|
|
|
|
},
|
|
|
|
error(str) {
|
|
|
|
console.error(KONVA_ERROR + str);
|
|
|
|
},
|
|
|
|
warn(str) {
|
2019-02-27 21:06:04 +08:00
|
|
|
if (!_getGlobalKonva().showWarnings) {
|
2019-02-20 22:13:39 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-01-02 04:59:27 +08:00
|
|
|
console.warn(KONVA_WARNING + str);
|
|
|
|
},
|
|
|
|
extend(child, parent) {
|
|
|
|
function Ctor() {
|
|
|
|
this.constructor = child;
|
|
|
|
}
|
|
|
|
Ctor.prototype = parent.prototype;
|
|
|
|
var oldProto = child.prototype;
|
|
|
|
child.prototype = new Ctor();
|
|
|
|
for (var key in oldProto) {
|
|
|
|
if (oldProto.hasOwnProperty(key)) {
|
|
|
|
child.prototype[key] = oldProto[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
child.__super__ = parent.prototype;
|
|
|
|
// create reference to parent
|
|
|
|
child.super = parent;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getControlPoints(x0, y0, x1, y1, x2, y2, t) {
|
|
|
|
var d01 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)),
|
|
|
|
d12 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)),
|
|
|
|
fa = (t * d01) / (d01 + d12),
|
|
|
|
fb = (t * d12) / (d01 + d12),
|
|
|
|
p1x = x1 - fa * (x2 - x0),
|
|
|
|
p1y = y1 - fa * (y2 - y0),
|
|
|
|
p2x = x1 + fb * (x2 - x0),
|
|
|
|
p2y = y1 + fb * (y2 - y0);
|
|
|
|
|
|
|
|
return [p1x, p1y, p2x, p2y];
|
|
|
|
},
|
|
|
|
_expandPoints(p, tension) {
|
|
|
|
var len = p.length,
|
|
|
|
allPoints = [],
|
|
|
|
n,
|
|
|
|
cp;
|
|
|
|
|
|
|
|
for (n = 2; n < len - 2; n += 2) {
|
|
|
|
cp = Util._getControlPoints(
|
|
|
|
p[n - 2],
|
|
|
|
p[n - 1],
|
|
|
|
p[n],
|
|
|
|
p[n + 1],
|
|
|
|
p[n + 2],
|
|
|
|
p[n + 3],
|
|
|
|
tension
|
|
|
|
);
|
|
|
|
allPoints.push(cp[0]);
|
|
|
|
allPoints.push(cp[1]);
|
|
|
|
allPoints.push(p[n]);
|
|
|
|
allPoints.push(p[n + 1]);
|
|
|
|
allPoints.push(cp[2]);
|
|
|
|
allPoints.push(cp[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return allPoints;
|
|
|
|
},
|
|
|
|
each(obj, func) {
|
|
|
|
for (var key in obj) {
|
|
|
|
func(key, obj[key]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_inRange(val, left, right) {
|
|
|
|
return left <= val && val < right;
|
|
|
|
},
|
|
|
|
_getProjectionToSegment(x1, y1, x2, y2, x3, y3) {
|
|
|
|
var x, y, dist;
|
|
|
|
|
|
|
|
var pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
|
|
|
|
if (pd2 == 0) {
|
|
|
|
x = x1;
|
|
|
|
y = y1;
|
|
|
|
dist = (x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2);
|
|
|
|
} else {
|
|
|
|
var u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / pd2;
|
|
|
|
if (u < 0) {
|
|
|
|
x = x1;
|
|
|
|
y = y1;
|
|
|
|
dist = (x1 - x3) * (x1 - x3) + (y1 - y3) * (y1 - y3);
|
|
|
|
} else if (u > 1.0) {
|
|
|
|
x = x2;
|
|
|
|
y = y2;
|
|
|
|
dist = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3);
|
|
|
|
} else {
|
|
|
|
x = x1 + u * (x2 - x1);
|
|
|
|
y = y1 + u * (y2 - y1);
|
|
|
|
dist = (x - x3) * (x - x3) + (y - y3) * (y - y3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [x, y, dist];
|
|
|
|
},
|
|
|
|
// line as array of points.
|
|
|
|
// line might be closed
|
|
|
|
_getProjectionToLine(pt: Point, line, isClosed) {
|
|
|
|
var pc = Util.cloneObject(pt);
|
|
|
|
var dist = Number.MAX_VALUE;
|
|
|
|
line.forEach(function(p1, i) {
|
|
|
|
if (!isClosed && i === line.length - 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var p2 = line[(i + 1) % line.length];
|
|
|
|
var proj = Util._getProjectionToSegment(
|
|
|
|
p1.x,
|
|
|
|
p1.y,
|
|
|
|
p2.x,
|
|
|
|
p2.y,
|
|
|
|
pt.x,
|
|
|
|
pt.y
|
|
|
|
);
|
|
|
|
var px = proj[0],
|
|
|
|
py = proj[1],
|
|
|
|
pdist = proj[2];
|
|
|
|
if (pdist < dist) {
|
|
|
|
pc.x = px;
|
|
|
|
pc.y = py;
|
|
|
|
dist = pdist;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return pc;
|
|
|
|
},
|
|
|
|
_prepareArrayForTween(startArray, endArray, isClosed) {
|
|
|
|
var n,
|
|
|
|
start = [],
|
|
|
|
end = [];
|
|
|
|
if (startArray.length > endArray.length) {
|
|
|
|
var temp = endArray;
|
|
|
|
endArray = startArray;
|
|
|
|
startArray = temp;
|
|
|
|
}
|
|
|
|
for (n = 0; n < startArray.length; n += 2) {
|
|
|
|
start.push({
|
|
|
|
x: startArray[n],
|
|
|
|
y: startArray[n + 1]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
for (n = 0; n < endArray.length; n += 2) {
|
|
|
|
end.push({
|
|
|
|
x: endArray[n],
|
|
|
|
y: endArray[n + 1]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var newStart = [];
|
|
|
|
end.forEach(function(point) {
|
|
|
|
var pr = Util._getProjectionToLine(point, start, isClosed);
|
|
|
|
newStart.push(pr.x);
|
|
|
|
newStart.push(pr.y);
|
|
|
|
});
|
|
|
|
return newStart;
|
|
|
|
},
|
|
|
|
_prepareToStringify(obj) {
|
|
|
|
var desc;
|
|
|
|
|
|
|
|
obj.visitedByCircularReferenceRemoval = true;
|
|
|
|
|
|
|
|
for (var key in obj) {
|
|
|
|
if (
|
|
|
|
!(obj.hasOwnProperty(key) && obj[key] && typeof obj[key] == 'object')
|
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
|
|
if (
|
|
|
|
obj[key].visitedByCircularReferenceRemoval ||
|
|
|
|
Util._isElement(obj[key])
|
|
|
|
) {
|
|
|
|
if (desc.configurable) {
|
|
|
|
delete obj[key];
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
} else if (Util._prepareToStringify(obj[key]) === null) {
|
|
|
|
if (desc.configurable) {
|
|
|
|
delete obj[key];
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete obj.visitedByCircularReferenceRemoval;
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
};
|