summaryrefslogtreecommitdiffstats
path: root/src/types
diff options
context:
space:
mode:
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2020-05-19 20:06:29 +1000
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2020-05-19 20:06:29 +1000
commit4ea53725a9021a136f6d81122dd78dc97a3e7da0 (patch)
tree7cd4b2aaa2c1e834aa61a669066f87d9746e7ef6 /src/types
parent47cffd9d5d9628c1729be291ee3a2f9b5651bd2c (diff)
downloadsvg.js-4ea53725a9021a136f6d81122dd78dc97a3e7da0.tar.gz
svg.js-4ea53725a9021a136f6d81122dd78dc97a3e7da0.zip
sorted method names
Diffstat (limited to 'src/types')
-rw-r--r--src/types/Box.js37
-rw-r--r--src/types/Color.js387
-rw-r--r--src/types/Matrix.js419
-rw-r--r--src/types/PathArray.js107
-rw-r--r--src/types/Point.js13
-rw-r--r--src/types/PointArray.js123
-rw-r--r--src/types/SVGArray.js31
-rw-r--r--src/types/SVGNumber.js59
8 files changed, 591 insertions, 585 deletions
diff --git a/src/types/Box.js b/src/types/Box.js
index 3e91c35..9707b7f 100644
--- a/src/types/Box.js
+++ b/src/types/Box.js
@@ -26,6 +26,13 @@ export default class Box {
this.init(...args)
}
+ addOffset () {
+ // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+ this.x += globals.window.pageXOffset
+ this.y += globals.window.pageYOffset
+ return new Box(this)
+ }
+
init (source) {
var base = [ 0, 0, 0, 0 ]
source = typeof source === 'string' ? source.split(delimiter).map(parseFloat)
@@ -49,6 +56,10 @@ export default class Box {
return this
}
+ isNulled () {
+ return isNulledBox(this)
+ }
+
// Merge rect box with another, return a new instance
merge (box) {
const x = Math.min(this.x, box.x)
@@ -59,6 +70,14 @@ export default class Box {
return new Box(x, y, width, height)
}
+ toArray () {
+ return [ this.x, this.y, this.width, this.height ]
+ }
+
+ toString () {
+ return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height
+ }
+
transform (m) {
if (!(m instanceof Matrix)) {
m = new Matrix(m)
@@ -91,24 +110,6 @@ export default class Box {
)
}
- addOffset () {
- // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
- this.x += globals.window.pageXOffset
- this.y += globals.window.pageYOffset
- return new Box(this)
- }
-
- toString () {
- return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height
- }
-
- toArray () {
- return [ this.x, this.y, this.width, this.height ]
- }
-
- isNulled () {
- return isNulledBox(this)
- }
}
function getBox (el, getBBoxFn, retry) {
diff --git a/src/types/Color.js b/src/types/Color.js
index eb6168f..d5f0efa 100644
--- a/src/types/Color.js
+++ b/src/types/Color.js
@@ -62,6 +62,152 @@ export default class Color {
this.init(...inputs)
}
+ // Test if given value is a color
+ static isColor (color) {
+ return color && (
+ color instanceof Color
+ || this.isRgb(color)
+ || this.test(color)
+ )
+ }
+
+ // Test if given value is an rgb object
+ static isRgb (color) {
+ return color && typeof color.r === 'number'
+ && typeof color.g === 'number'
+ && typeof color.b === 'number'
+ }
+
+ /*
+ Generating random colors
+ */
+ static random (mode = 'vibrant', t, u) {
+
+ // Get the math modules
+ const { random, round, sin, PI: pi } = Math
+
+ // Run the correct generator
+ if (mode === 'vibrant') {
+
+ const l = (81 - 57) * random() + 57
+ const c = (83 - 45) * random() + 45
+ const h = 360 * random()
+ const color = new Color(l, c, h, 'lch')
+ return color
+
+ } else if (mode === 'sine') {
+
+ t = t == null ? random() : t
+ const r = round(80 * sin(2 * pi * t / 0.5 + 0.01) + 150)
+ const g = round(50 * sin(2 * pi * t / 0.5 + 4.6) + 200)
+ const b = round(100 * sin(2 * pi * t / 0.5 + 2.3) + 150)
+ const color = new Color(r, g, b)
+ return color
+
+ } else if (mode === 'pastel') {
+
+ const l = (94 - 86) * random() + 86
+ const c = (26 - 9) * random() + 9
+ const h = 360 * random()
+ const color = new Color(l, c, h, 'lch')
+ return color
+
+ } else if (mode === 'dark') {
+
+ const l = 10 + 10 * random()
+ const c = (125 - 75) * random() + 86
+ const h = 360 * random()
+ const color = new Color(l, c, h, 'lch')
+ return color
+
+ } else if (mode === 'rgb') {
+
+ const r = 255 * random()
+ const g = 255 * random()
+ const b = 255 * random()
+ const color = new Color(r, g, b)
+ return color
+
+ } else if (mode === 'lab') {
+
+ const l = 100 * random()
+ const a = 256 * random() - 128
+ const b = 256 * random() - 128
+ const color = new Color(l, a, b, 'lab')
+ return color
+
+ } else if (mode === 'grey') {
+
+ const grey = 255 * random()
+ const color = new Color(grey, grey, grey)
+ return color
+
+ } else {
+
+ throw new Error('Unsupported random color mode')
+
+ }
+ }
+
+ // Test if given value is a color string
+ static test (color) {
+ return (typeof color === 'string')
+ && (isHex.test(color) || isRgb.test(color))
+ }
+
+ cmyk () {
+
+ // Get the rgb values for the current color
+ const { _a, _b, _c } = this.rgb()
+ const [ r, g, b ] = [ _a, _b, _c ].map(v => v / 255)
+
+ // Get the cmyk values in an unbounded format
+ const k = Math.min(1 - r, 1 - g, 1 - b)
+
+ if (k === 1) {
+ // Catch the black case
+ return new Color(0, 0, 0, 1, 'cmyk')
+ }
+
+ const c = (1 - r - k) / (1 - k)
+ const m = (1 - g - k) / (1 - k)
+ const y = (1 - b - k) / (1 - k)
+
+ // Construct the new color
+ const color = new Color(c, m, y, k, 'cmyk')
+ return color
+ }
+
+ hsl () {
+
+ // Get the rgb values
+ const { _a, _b, _c } = this.rgb()
+ const [ r, g, b ] = [ _a, _b, _c ].map(v => v / 255)
+
+ // Find the maximum and minimum values to get the lightness
+ const max = Math.max(r, g, b)
+ const min = Math.min(r, g, b)
+ const l = (max + min) / 2
+
+ // If the r, g, v values are identical then we are grey
+ const isGrey = max === min
+
+ // Calculate the hue and saturation
+ const delta = max - min
+ const s = isGrey ? 0
+ : l > 0.5 ? delta / (2 - max - min)
+ : delta / (max + min)
+ const h = isGrey ? 0
+ : max === r ? ((g - b) / delta + (g < b ? 6 : 0)) / 6
+ : max === g ? ((b - r) / delta + 2) / 6
+ : max === b ? ((r - g) / delta + 4) / 6
+ : 0
+
+ // Construct and return the new color
+ const color = new Color(360 * h, 100 * s, 100 * l, 'hsl')
+ return color
+ }
+
init (a = 0, b = 0, c = 0, d = 0, space = 'rgb') {
// This catches the case when a falsy value is passed like ''
a = !a ? 0 : a
@@ -113,6 +259,37 @@ export default class Color {
Object.assign(this, components)
}
+ lab () {
+ // Get the xyz color
+ const { x, y, z } = this.xyz()
+
+ // Get the lab components
+ const l = (116 * y) - 16
+ const a = 500 * (x - y)
+ const b = 200 * (y - z)
+
+ // Construct and return a new color
+ const color = new Color(l, a, b, 'lab')
+ return color
+ }
+
+ lch () {
+
+ // Get the lab color directly
+ const { l, a, b } = this.lab()
+
+ // Get the chromaticity and the hue using polar coordinates
+ const c = Math.sqrt(a ** 2 + b ** 2)
+ let h = 180 * Math.atan2(b, a) / Math.PI
+ if (h < 0) {
+ h *= -1
+ h = 360 - h
+ }
+
+ // Make a new color and return it
+ const color = new Color(l, c, h, 'lch')
+ return color
+ }
/*
Conversion Methods
*/
@@ -207,18 +384,24 @@ export default class Color {
}
}
- lab () {
- // Get the xyz color
- const { x, y, z } = this.xyz()
+ toArray () {
+ const { _a, _b, _c, _d, space } = this
+ return [ _a, _b, _c, _d, space ]
+ }
- // Get the lab components
- const l = (116 * y) - 16
- const a = 500 * (x - y)
- const b = 200 * (y - z)
+ toHex () {
+ const [ r, g, b ] = this._clamped().map(componentHex)
+ return `#${r}${g}${b}`
+ }
- // Construct and return a new color
- const color = new Color(l, a, b, 'lab')
- return color
+ toRgb () {
+ const [ rV, gV, bV ] = this._clamped()
+ const string = `rgb(${rV},${gV},${bV})`
+ return string
+ }
+
+ toString () {
+ return this.toHex()
}
xyz () {
@@ -247,77 +430,6 @@ export default class Color {
return color
}
- lch () {
-
- // Get the lab color directly
- const { l, a, b } = this.lab()
-
- // Get the chromaticity and the hue using polar coordinates
- const c = Math.sqrt(a ** 2 + b ** 2)
- let h = 180 * Math.atan2(b, a) / Math.PI
- if (h < 0) {
- h *= -1
- h = 360 - h
- }
-
- // Make a new color and return it
- const color = new Color(l, c, h, 'lch')
- return color
- }
-
- hsl () {
-
- // Get the rgb values
- const { _a, _b, _c } = this.rgb()
- const [ r, g, b ] = [ _a, _b, _c ].map(v => v / 255)
-
- // Find the maximum and minimum values to get the lightness
- const max = Math.max(r, g, b)
- const min = Math.min(r, g, b)
- const l = (max + min) / 2
-
- // If the r, g, v values are identical then we are grey
- const isGrey = max === min
-
- // Calculate the hue and saturation
- const delta = max - min
- const s = isGrey ? 0
- : l > 0.5 ? delta / (2 - max - min)
- : delta / (max + min)
- const h = isGrey ? 0
- : max === r ? ((g - b) / delta + (g < b ? 6 : 0)) / 6
- : max === g ? ((b - r) / delta + 2) / 6
- : max === b ? ((r - g) / delta + 4) / 6
- : 0
-
- // Construct and return the new color
- const color = new Color(360 * h, 100 * s, 100 * l, 'hsl')
- return color
- }
-
- cmyk () {
-
- // Get the rgb values for the current color
- const { _a, _b, _c } = this.rgb()
- const [ r, g, b ] = [ _a, _b, _c ].map(v => v / 255)
-
- // Get the cmyk values in an unbounded format
- const k = Math.min(1 - r, 1 - g, 1 - b)
-
- if (k === 1) {
- // Catch the black case
- return new Color(0, 0, 0, 1, 'cmyk')
- }
-
- const c = (1 - r - k) / (1 - k)
- const m = (1 - g - k) / (1 - k)
- const y = (1 - b - k) / (1 - k)
-
- // Construct the new color
- const color = new Color(c, m, y, k, 'cmyk')
- return color
- }
-
/*
Input and Output methods
*/
@@ -329,121 +441,8 @@ export default class Color {
return [ _a, _b, _c ].map(format)
}
- toHex () {
- const [ r, g, b ] = this._clamped().map(componentHex)
- return `#${r}${g}${b}`
- }
-
- toString () {
- return this.toHex()
- }
-
- toRgb () {
- const [ rV, gV, bV ] = this._clamped()
- const string = `rgb(${rV},${gV},${bV})`
- return string
- }
-
- toArray () {
- const { _a, _b, _c, _d, space } = this
- return [ _a, _b, _c, _d, space ]
- }
-
- /*
- Generating random colors
- */
-
- static random (mode = 'vibrant', t, u) {
-
- // Get the math modules
- const { random, round, sin, PI: pi } = Math
-
- // Run the correct generator
- if (mode === 'vibrant') {
-
- const l = (81 - 57) * random() + 57
- const c = (83 - 45) * random() + 45
- const h = 360 * random()
- const color = new Color(l, c, h, 'lch')
- return color
-
- } else if (mode === 'sine') {
-
- t = t == null ? random() : t
- const r = round(80 * sin(2 * pi * t / 0.5 + 0.01) + 150)
- const g = round(50 * sin(2 * pi * t / 0.5 + 4.6) + 200)
- const b = round(100 * sin(2 * pi * t / 0.5 + 2.3) + 150)
- const color = new Color(r, g, b)
- return color
-
- } else if (mode === 'pastel') {
-
- const l = (94 - 86) * random() + 86
- const c = (26 - 9) * random() + 9
- const h = 360 * random()
- const color = new Color(l, c, h, 'lch')
- return color
-
- } else if (mode === 'dark') {
-
- const l = 10 + 10 * random()
- const c = (125 - 75) * random() + 86
- const h = 360 * random()
- const color = new Color(l, c, h, 'lch')
- return color
-
- } else if (mode === 'rgb') {
-
- const r = 255 * random()
- const g = 255 * random()
- const b = 255 * random()
- const color = new Color(r, g, b)
- return color
-
- } else if (mode === 'lab') {
-
- const l = 100 * random()
- const a = 256 * random() - 128
- const b = 256 * random() - 128
- const color = new Color(l, a, b, 'lab')
- return color
-
- } else if (mode === 'grey') {
-
- const grey = 255 * random()
- const color = new Color(grey, grey, grey)
- return color
-
- } else {
-
- throw new Error('Unsupported random color mode')
-
- }
- }
-
/*
Constructing colors
*/
- // Test if given value is a color string
- static test (color) {
- return (typeof color === 'string')
- && (isHex.test(color) || isRgb.test(color))
- }
-
- // Test if given value is an rgb object
- static isRgb (color) {
- return color && typeof color.r === 'number'
- && typeof color.g === 'number'
- && typeof color.b === 'number'
- }
-
- // Test if given value is a color
- static isColor (color) {
- return color && (
- color instanceof Color
- || this.isRgb(color)
- || this.test(color)
- )
- }
}
diff --git a/src/types/Matrix.js b/src/types/Matrix.js
index 9b783da..d4f516c 100644
--- a/src/types/Matrix.js
+++ b/src/types/Matrix.js
@@ -13,72 +13,99 @@ export default class Matrix {
this.init(...args)
}
- // Initialize
- init (source) {
- var base = Matrix.fromArray([ 1, 0, 0, 1, 0, 0 ])
-
- // ensure source as object
- source = source instanceof Element ? source.matrixify()
- : typeof source === 'string' ? Matrix.fromArray(source.split(delimiter).map(parseFloat))
- : Array.isArray(source) ? Matrix.fromArray(source)
- : (typeof source === 'object' && Matrix.isMatrixLike(source)) ? source
- : (typeof source === 'object') ? new Matrix().transform(source)
- : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments))
- : base
+ static formatTransforms (o) {
+ // Get all of the parameters required to form the matrix
+ var flipBoth = o.flip === 'both' || o.flip === true
+ var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1
+ var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1
+ var skewX = o.skew && o.skew.length ? o.skew[0]
+ : isFinite(o.skew) ? o.skew
+ : isFinite(o.skewX) ? o.skewX
+ : 0
+ var skewY = o.skew && o.skew.length ? o.skew[1]
+ : isFinite(o.skew) ? o.skew
+ : isFinite(o.skewY) ? o.skewY
+ : 0
+ var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX
+ : isFinite(o.scale) ? o.scale * flipX
+ : isFinite(o.scaleX) ? o.scaleX * flipX
+ : flipX
+ var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY
+ : isFinite(o.scale) ? o.scale * flipY
+ : isFinite(o.scaleY) ? o.scaleY * flipY
+ : flipY
+ var shear = o.shear || 0
+ var theta = o.rotate || o.theta || 0
+ var origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY)
+ var ox = origin.x
+ var oy = origin.y
+ // We need Point to be invalid if nothing was passed because we cannot default to 0 here. Thats why NaN
+ var position = new Point(o.position || o.px || o.positionX || NaN, o.py || o.positionY || NaN)
+ var px = position.x
+ var py = position.y
+ var translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY)
+ var tx = translate.x
+ var ty = translate.y
+ var relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY)
+ var rx = relative.x
+ var ry = relative.y
- // Merge the source matrix with the base matrix
- this.a = source.a != null ? source.a : base.a
- this.b = source.b != null ? source.b : base.b
- this.c = source.c != null ? source.c : base.c
- this.d = source.d != null ? source.d : base.d
- this.e = source.e != null ? source.e : base.e
- this.f = source.f != null ? source.f : base.f
+ // Populate all of the values
+ return {
+ scaleX, scaleY, skewX, skewY, shear, theta, rx, ry, tx, ty, ox, oy, px, py
+ }
+ }
- return this
+ static fromArray (a) {
+ return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] }
}
- // Clones this matrix
- clone () {
- return new Matrix(this)
+ static isMatrixLike (o) {
+ return (
+ o.a != null
+ || o.b != null
+ || o.c != null
+ || o.d != null
+ || o.e != null
+ || o.f != null
+ )
}
- // Transform a matrix into another matrix by manipulating the space
- transform (o) {
- // Check if o is a matrix and then left multiply it directly
- if (Matrix.isMatrixLike(o)) {
- var matrix = new Matrix(o)
- return matrix.multiplyO(this)
- }
+ // left matrix, right matrix, target matrix which is overwritten
+ static matrixMultiply (l, r, o) {
+ // Work out the product directly
+ var a = l.a * r.a + l.c * r.b
+ var b = l.b * r.a + l.d * r.b
+ var c = l.a * r.c + l.c * r.d
+ var d = l.b * r.c + l.d * r.d
+ var e = l.e + l.a * r.e + l.c * r.f
+ var f = l.f + l.b * r.e + l.d * r.f
- // Get the proposed transformations and the current transformations
- var t = Matrix.formatTransforms(o)
- var current = this
- const { x: ox, y: oy } = new Point(t.ox, t.oy).transform(current)
+ // make sure to use local variables because l/r and o could be the same
+ o.a = a
+ o.b = b
+ o.c = c
+ o.d = d
+ o.e = e
+ o.f = f
- // Construct the resulting matrix
- var transformer = new Matrix()
- .translateO(t.rx, t.ry)
- .lmultiplyO(current)
- .translateO(-ox, -oy)
- .scaleO(t.scaleX, t.scaleY)
- .skewO(t.skewX, t.skewY)
- .shearO(t.shear)
- .rotateO(t.theta)
- .translateO(ox, oy)
+ return o
+ }
- // If we want the origin at a particular place, we force it there
- if (isFinite(t.px) || isFinite(t.py)) {
- const origin = new Point(ox, oy).transform(transformer)
- // TODO: Replace t.px with isFinite(t.px)
- // Doesnt work because t.px is also 0 if it wasnt passed
- const dx = isFinite(t.px) ? t.px - origin.x : 0
- const dy = isFinite(t.py) ? t.py - origin.y : 0
- transformer.translateO(dx, dy)
- }
+ around (cx, cy, matrix) {
+ return this.clone().aroundO(cx, cy, matrix)
+ }
- // Translate now after positioning
- transformer.translateO(t.tx, t.ty)
- return transformer
+ // Transform around a center point
+ aroundO (cx, cy, matrix) {
+ var dx = cx || 0
+ var dy = cy || 0
+ return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy)
+ }
+
+ // Clones this matrix
+ clone () {
+ return new Matrix(this)
}
// Decomposes this matrix into its affine parameters
@@ -134,32 +161,52 @@ export default class Matrix {
}
}
- // Left multiplies by the given matrix
- multiply (matrix) {
- return this.clone().multiplyO(matrix)
+ // Check if two matrices are equal
+ equals (other) {
+ if (other === this) return true
+ var comp = new Matrix(other)
+ return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b)
+ && closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d)
+ && closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f)
}
- multiplyO (matrix) {
- // Get the matrices
- var l = this
- var r = matrix instanceof Matrix
- ? matrix
- : new Matrix(matrix)
-
- return Matrix.matrixMultiply(l, r, this)
+ // Flip matrix on x or y, at a given offset
+ flip (axis, around) {
+ return this.clone().flipO(axis, around)
}
- lmultiply (matrix) {
- return this.clone().lmultiplyO(matrix)
+ flipO (axis, around) {
+ return axis === 'x' ? this.scaleO(-1, 1, around, 0)
+ : axis === 'y' ? this.scaleO(1, -1, 0, around)
+ : this.scaleO(-1, -1, axis, around || axis) // Define an x, y flip point
}
- lmultiplyO (matrix) {
- var r = this
- var l = matrix instanceof Matrix
- ? matrix
- : new Matrix(matrix)
+ // Initialize
+ init (source) {
+ var base = Matrix.fromArray([ 1, 0, 0, 1, 0, 0 ])
- return Matrix.matrixMultiply(l, r, this)
+ // ensure source as object
+ source = source instanceof Element ? source.matrixify()
+ : typeof source === 'string' ? Matrix.fromArray(source.split(delimiter).map(parseFloat))
+ : Array.isArray(source) ? Matrix.fromArray(source)
+ : (typeof source === 'object' && Matrix.isMatrixLike(source)) ? source
+ : (typeof source === 'object') ? new Matrix().transform(source)
+ : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments))
+ : base
+
+ // Merge the source matrix with the base matrix
+ this.a = source.a != null ? source.a : base.a
+ this.b = source.b != null ? source.b : base.b
+ this.c = source.c != null ? source.c : base.c
+ this.d = source.d != null ? source.d : base.d
+ this.e = source.e != null ? source.e : base.e
+ this.f = source.f != null ? source.f : base.f
+
+ return this
+ }
+
+ inverse () {
+ return this.clone().inverseO()
}
// Inverses matrix
@@ -197,44 +244,32 @@ export default class Matrix {
return this
}
- inverse () {
- return this.clone().inverseO()
+ lmultiply (matrix) {
+ return this.clone().lmultiplyO(matrix)
}
- // Translate matrix
- translate (x, y) {
- return this.clone().translateO(x, y)
- }
+ lmultiplyO (matrix) {
+ var r = this
+ var l = matrix instanceof Matrix
+ ? matrix
+ : new Matrix(matrix)
- translateO (x, y) {
- this.e += x || 0
- this.f += y || 0
- return this
+ return Matrix.matrixMultiply(l, r, this)
}
- // Scale matrix
- scale (x, y, cx, cy) {
- return this.clone().scaleO(...arguments)
+ // Left multiplies by the given matrix
+ multiply (matrix) {
+ return this.clone().multiplyO(matrix)
}
- scaleO (x, y = x, cx = 0, cy = 0) {
- // Support uniform scaling
- if (arguments.length === 3) {
- cy = cx
- cx = y
- y = x
- }
-
- const { a, b, c, d, e, f } = this
-
- this.a = a * x
- this.b = b * y
- this.c = c * x
- this.d = d * y
- this.e = e * x - cx * x + cx
- this.f = f * y - cy * y + cy
+ multiplyO (matrix) {
+ // Get the matrices
+ var l = this
+ var r = matrix instanceof Matrix
+ ? matrix
+ : new Matrix(matrix)
- return this
+ return Matrix.matrixMultiply(l, r, this)
}
// Rotate matrix
@@ -261,15 +296,29 @@ export default class Matrix {
return this
}
- // Flip matrix on x or y, at a given offset
- flip (axis, around) {
- return this.clone().flipO(axis, around)
+ // Scale matrix
+ scale (x, y, cx, cy) {
+ return this.clone().scaleO(...arguments)
}
- flipO (axis, around) {
- return axis === 'x' ? this.scaleO(-1, 1, around, 0)
- : axis === 'y' ? this.scaleO(1, -1, 0, around)
- : this.scaleO(-1, -1, axis, around || axis) // Define an x, y flip point
+ scaleO (x, y = x, cx = 0, cy = 0) {
+ // Support uniform scaling
+ if (arguments.length === 3) {
+ cy = cx
+ cx = y
+ y = x
+ }
+
+ const { a, b, c, d, e, f } = this
+
+ this.a = a * x
+ this.b = b * y
+ this.c = c * x
+ this.d = d * y
+ this.e = e * x - cx * x + cx
+ this.f = f * y - cy * y + cy
+
+ return this
}
// Shear matrix
@@ -329,33 +378,63 @@ export default class Matrix {
return this.skew(0, y, cx, cy)
}
- // Transform around a center point
- aroundO (cx, cy, matrix) {
- var dx = cx || 0
- var dy = cy || 0
- return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy)
+ toArray () {
+ return [ this.a, this.b, this.c, this.d, this.e, this.f ]
}
- around (cx, cy, matrix) {
- return this.clone().aroundO(cx, cy, matrix)
+ // Convert matrix to string
+ toString () {
+ return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')'
}
- // Check if two matrices are equal
- equals (other) {
- if (other === this) return true
- var comp = new Matrix(other)
- return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b)
- && closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d)
- && closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f)
+ // Transform a matrix into another matrix by manipulating the space
+ transform (o) {
+ // Check if o is a matrix and then left multiply it directly
+ if (Matrix.isMatrixLike(o)) {
+ var matrix = new Matrix(o)
+ return matrix.multiplyO(this)
+ }
+
+ // Get the proposed transformations and the current transformations
+ var t = Matrix.formatTransforms(o)
+ var current = this
+ const { x: ox, y: oy } = new Point(t.ox, t.oy).transform(current)
+
+ // Construct the resulting matrix
+ var transformer = new Matrix()
+ .translateO(t.rx, t.ry)
+ .lmultiplyO(current)
+ .translateO(-ox, -oy)
+ .scaleO(t.scaleX, t.scaleY)
+ .skewO(t.skewX, t.skewY)
+ .shearO(t.shear)
+ .rotateO(t.theta)
+ .translateO(ox, oy)
+
+ // If we want the origin at a particular place, we force it there
+ if (isFinite(t.px) || isFinite(t.py)) {
+ const origin = new Point(ox, oy).transform(transformer)
+ // TODO: Replace t.px with isFinite(t.px)
+ // Doesnt work because t.px is also 0 if it wasnt passed
+ const dx = isFinite(t.px) ? t.px - origin.x : 0
+ const dy = isFinite(t.py) ? t.py - origin.y : 0
+ transformer.translateO(dx, dy)
+ }
+
+ // Translate now after positioning
+ transformer.translateO(t.tx, t.ty)
+ return transformer
}
- // Convert matrix to string
- toString () {
- return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')'
+ // Translate matrix
+ translate (x, y) {
+ return this.clone().translateO(x, y)
}
- toArray () {
- return [ this.a, this.b, this.c, this.d, this.e, this.f ]
+ translateO (x, y) {
+ this.e += x || 0
+ this.f += y || 0
+ return this
}
valueOf () {
@@ -369,84 +448,6 @@ export default class Matrix {
}
}
- static fromArray (a) {
- return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] }
- }
-
- static isMatrixLike (o) {
- return (
- o.a != null
- || o.b != null
- || o.c != null
- || o.d != null
- || o.e != null
- || o.f != null
- )
- }
-
- static formatTransforms (o) {
- // Get all of the parameters required to form the matrix
- var flipBoth = o.flip === 'both' || o.flip === true
- var flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1
- var flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1
- var skewX = o.skew && o.skew.length ? o.skew[0]
- : isFinite(o.skew) ? o.skew
- : isFinite(o.skewX) ? o.skewX
- : 0
- var skewY = o.skew && o.skew.length ? o.skew[1]
- : isFinite(o.skew) ? o.skew
- : isFinite(o.skewY) ? o.skewY
- : 0
- var scaleX = o.scale && o.scale.length ? o.scale[0] * flipX
- : isFinite(o.scale) ? o.scale * flipX
- : isFinite(o.scaleX) ? o.scaleX * flipX
- : flipX
- var scaleY = o.scale && o.scale.length ? o.scale[1] * flipY
- : isFinite(o.scale) ? o.scale * flipY
- : isFinite(o.scaleY) ? o.scaleY * flipY
- : flipY
- var shear = o.shear || 0
- var theta = o.rotate || o.theta || 0
- var origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY)
- var ox = origin.x
- var oy = origin.y
- // We need Point to be invalid if nothing was passed because we cannot default to 0 here. Thats why NaN
- var position = new Point(o.position || o.px || o.positionX || NaN, o.py || o.positionY || NaN)
- var px = position.x
- var py = position.y
- var translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY)
- var tx = translate.x
- var ty = translate.y
- var relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY)
- var rx = relative.x
- var ry = relative.y
-
- // Populate all of the values
- return {
- scaleX, scaleY, skewX, skewY, shear, theta, rx, ry, tx, ty, ox, oy, px, py
- }
- }
-
- // left matrix, right matrix, target matrix which is overwritten
- static matrixMultiply (l, r, o) {
- // Work out the product directly
- var a = l.a * r.a + l.c * r.b
- var b = l.b * r.a + l.d * r.b
- var c = l.a * r.c + l.c * r.d
- var d = l.b * r.c + l.d * r.d
- var e = l.e + l.a * r.e + l.c * r.f
- var f = l.f + l.b * r.e + l.d * r.f
-
- // make sure to use local variables because l/r and o could be the same
- o.a = a
- o.b = b
- o.c = c
- o.d = d
- o.e = e
- o.f = f
-
- return o
- }
}
export function ctm () {
diff --git a/src/types/PathArray.js b/src/types/PathArray.js
index d9c1eb2..b7c3c33 100644
--- a/src/types/PathArray.js
+++ b/src/types/PathArray.js
@@ -125,9 +125,10 @@ for (var i = 0, il = mlhvqtcsaz.length; i < il; ++i) {
}
export default class PathArray extends SVGArray {
- // Convert array to string
- toString () {
- return arrayToString(this)
+ // Get bounding box of path
+ bbox () {
+ parser().path.setAttribute('d', this.toString())
+ return new Box(parser.nodes.path.getBBox())
}
// Move path string
@@ -171,52 +172,6 @@ export default class PathArray extends SVGArray {
return this
}
- // Resize path string
- size (width, height) {
- // get bounding box of current situation
- var box = this.bbox()
- var i, l
-
- // If the box width or height is 0 then we ignore
- // transformations on the respective axis
- box.width = box.width === 0 ? 1 : box.width
- box.height = box.height === 0 ? 1 : box.height
-
- // recalculate position of all points according to new size
- for (i = this.length - 1; i >= 0; i--) {
- l = this[i][0]
-
- if (l === 'M' || l === 'L' || l === 'T') {
- this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
- this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y
- } else if (l === 'H') {
- this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
- } else if (l === 'V') {
- this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y
- } else if (l === 'C' || l === 'S' || l === 'Q') {
- this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
- this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y
- this[i][3] = ((this[i][3] - box.x) * width) / box.width + box.x
- this[i][4] = ((this[i][4] - box.y) * height) / box.height + box.y
-
- if (l === 'C') {
- this[i][5] = ((this[i][5] - box.x) * width) / box.width + box.x
- this[i][6] = ((this[i][6] - box.y) * height) / box.height + box.y
- }
- } else if (l === 'A') {
- // resize radii
- this[i][1] = (this[i][1] * width) / box.width
- this[i][2] = (this[i][2] * height) / box.height
-
- // move position values
- this[i][6] = ((this[i][6] - box.x) * width) / box.width + box.x
- this[i][7] = ((this[i][7] - box.y) * height) / box.height + box.y
- }
- }
-
- return this
- }
-
// Absolutize and parse path to array
parse (array = [ 'M', 0, 0 ]) {
// prepare for parsing
@@ -264,9 +219,55 @@ export default class PathArray extends SVGArray {
return result
}
- // Get bounding box of path
- bbox () {
- parser().path.setAttribute('d', this.toString())
- return new Box(parser.nodes.path.getBBox())
+ // Resize path string
+ size (width, height) {
+ // get bounding box of current situation
+ var box = this.bbox()
+ var i, l
+
+ // If the box width or height is 0 then we ignore
+ // transformations on the respective axis
+ box.width = box.width === 0 ? 1 : box.width
+ box.height = box.height === 0 ? 1 : box.height
+
+ // recalculate position of all points according to new size
+ for (i = this.length - 1; i >= 0; i--) {
+ l = this[i][0]
+
+ if (l === 'M' || l === 'L' || l === 'T') {
+ this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
+ this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y
+ } else if (l === 'H') {
+ this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
+ } else if (l === 'V') {
+ this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y
+ } else if (l === 'C' || l === 'S' || l === 'Q') {
+ this[i][1] = ((this[i][1] - box.x) * width) / box.width + box.x
+ this[i][2] = ((this[i][2] - box.y) * height) / box.height + box.y
+ this[i][3] = ((this[i][3] - box.x) * width) / box.width + box.x
+ this[i][4] = ((this[i][4] - box.y) * height) / box.height + box.y
+
+ if (l === 'C') {
+ this[i][5] = ((this[i][5] - box.x) * width) / box.width + box.x
+ this[i][6] = ((this[i][6] - box.y) * height) / box.height + box.y
+ }
+ } else if (l === 'A') {
+ // resize radii
+ this[i][1] = (this[i][1] * width) / box.width
+ this[i][2] = (this[i][2] * height) / box.height
+
+ // move position values
+ this[i][6] = ((this[i][6] - box.x) * width) / box.width + box.x
+ this[i][7] = ((this[i][7] - box.y) * height) / box.height + box.y
+ }
+ }
+
+ return this
}
+
+ // Convert array to string
+ toString () {
+ return arrayToString(this)
+ }
+
}
diff --git a/src/types/Point.js b/src/types/Point.js
index 634ffff..cb09bf3 100644
--- a/src/types/Point.js
+++ b/src/types/Point.js
@@ -6,6 +6,11 @@ export default class Point {
this.init(...args)
}
+ // Clone point
+ clone () {
+ return new Point(this)
+ }
+
init (x, y) {
const base = { x: 0, y: 0 }
@@ -21,9 +26,8 @@ export default class Point {
return this
}
- // Clone point
- clone () {
- return new Point(this)
+ toArray () {
+ return [ this.x, this.y ]
}
transform (m) {
@@ -45,9 +49,6 @@ export default class Point {
return this
}
- toArray () {
- return [ this.x, this.y ]
- }
}
export function point (x, y) {
diff --git a/src/types/PointArray.js b/src/types/PointArray.js
index 27a2076..28e649c 100644
--- a/src/types/PointArray.js
+++ b/src/types/PointArray.js
@@ -4,24 +4,37 @@ import Box from './Box.js'
import Matrix from './Matrix.js'
export default class PointArray extends SVGArray {
- // Convert array to string
- toString () {
- // convert to a poly point string
- for (var i = 0, il = this.length, array = []; i < il; i++) {
- array.push(this[i].join(','))
- }
-
- return array.join(' ')
+ // Get bounding box of points
+ bbox () {
+ var maxX = -Infinity
+ var maxY = -Infinity
+ var minX = Infinity
+ var minY = Infinity
+ this.forEach(function (el) {
+ maxX = Math.max(el[0], maxX)
+ maxY = Math.max(el[1], maxY)
+ minX = Math.min(el[0], minX)
+ minY = Math.min(el[1], minY)
+ })
+ return new Box(minX, minY, maxX - minX, maxY - minY)
}
- // Convert array to line object
- toLine () {
- return {
- x1: this[0][0],
- y1: this[0][1],
- x2: this[1][0],
- y2: this[1][1]
+ // Move point string
+ move (x, y) {
+ var box = this.bbox()
+
+ // get relative offset
+ x -= box.x
+ y -= box.y
+
+ // move every point
+ if (!isNaN(x) && !isNaN(y)) {
+ for (var i = this.length - 1; i >= 0; i--) {
+ this[i] = [ this[i][0] + x, this[i][1] + y ]
+ }
}
+
+ return this
}
// Parse point string and flat array
@@ -48,6 +61,40 @@ export default class PointArray extends SVGArray {
return points
}
+ // Resize poly string
+ size (width, height) {
+ var i
+ var box = this.bbox()
+
+ // recalculate position of all points according to new size
+ for (i = this.length - 1; i >= 0; i--) {
+ if (box.width) this[i][0] = ((this[i][0] - box.x) * width) / box.width + box.x
+ if (box.height) this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y
+ }
+
+ return this
+ }
+
+ // Convert array to line object
+ toLine () {
+ return {
+ x1: this[0][0],
+ y1: this[0][1],
+ x2: this[1][0],
+ y2: this[1][1]
+ }
+ }
+
+ // Convert array to string
+ toString () {
+ // convert to a poly point string
+ for (var i = 0, il = this.length, array = []; i < il; i++) {
+ array.push(this[i].join(','))
+ }
+
+ return array.join(' ')
+ }
+
transform (m) {
return this.clone().transformO(m)
}
@@ -68,50 +115,4 @@ export default class PointArray extends SVGArray {
return this
}
- // Move point string
- move (x, y) {
- var box = this.bbox()
-
- // get relative offset
- x -= box.x
- y -= box.y
-
- // move every point
- if (!isNaN(x) && !isNaN(y)) {
- for (var i = this.length - 1; i >= 0; i--) {
- this[i] = [ this[i][0] + x, this[i][1] + y ]
- }
- }
-
- return this
- }
-
- // Resize poly string
- size (width, height) {
- var i
- var box = this.bbox()
-
- // recalculate position of all points according to new size
- for (i = this.length - 1; i >= 0; i--) {
- if (box.width) this[i][0] = ((this[i][0] - box.x) * width) / box.width + box.x
- if (box.height) this[i][1] = ((this[i][1] - box.y) * height) / box.height + box.y
- }
-
- return this
- }
-
- // Get bounding box of points
- bbox () {
- var maxX = -Infinity
- var maxY = -Infinity
- var minX = Infinity
- var minY = Infinity
- this.forEach(function (el) {
- maxX = Math.max(el[0], maxX)
- maxY = Math.max(el[1], maxY)
- minX = Math.min(el[0], minX)
- minY = Math.min(el[1], minY)
- })
- return new Box(minX, minY, maxX - minX, maxY - minY)
- }
}
diff --git a/src/types/SVGArray.js b/src/types/SVGArray.js
index dafa2d4..6ce024a 100644
--- a/src/types/SVGArray.js
+++ b/src/types/SVGArray.js
@@ -6,6 +6,10 @@ export default class SVGArray extends Array {
this.init(...args)
}
+ clone () {
+ return new this.constructor(this)
+ }
+
init (arr) {
// This catches the case, that native map tries to create an array with new Array(1)
if (typeof arr === 'number') return this
@@ -14,10 +18,22 @@ export default class SVGArray extends Array {
return this
}
+ // Parse whitespace separated string
+ parse (array = []) {
+ // If already is an array, no need to parse it
+ if (array instanceof Array) return array
+
+ return array.trim().split(delimiter).map(parseFloat)
+ }
+
toArray () {
return Array.prototype.concat.apply([], this)
}
+ toSet () {
+ return new Set(this)
+ }
+
toString () {
return this.join(' ')
}
@@ -29,19 +45,4 @@ export default class SVGArray extends Array {
return ret
}
- // Parse whitespace separated string
- parse (array = []) {
- // If already is an array, no need to parse it
- if (array instanceof Array) return array
-
- return array.trim().split(delimiter).map(parseFloat)
- }
-
- clone () {
- return new this.constructor(this)
- }
-
- toSet () {
- return new Set(this)
- }
}
diff --git a/src/types/SVGNumber.js b/src/types/SVGNumber.js
index fedb00e..914919e 100644
--- a/src/types/SVGNumber.js
+++ b/src/types/SVGNumber.js
@@ -7,6 +7,16 @@ export default class SVGNumber {
this.init(...args)
}
+ convert (unit) {
+ return new SVGNumber(this.value, unit)
+ }
+
+ // Divide number
+ divide (number) {
+ number = new SVGNumber(number)
+ return new SVGNumber(this / number, this.unit || number.unit)
+ }
+
init (value, unit) {
unit = Array.isArray(value) ? value[1] : unit
value = Array.isArray(value) ? value[0] : value
@@ -46,23 +56,10 @@ export default class SVGNumber {
return this
}
- toString () {
- return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6
- : this.unit === 's' ? this.value / 1e3
- : this.value
- ) + this.unit
- }
-
- toJSON () {
- return this.toString()
- }
-
- toArray () {
- return [ this.value, this.unit ]
- }
-
- valueOf () {
- return this.value
+ // Subtract number
+ minus (number) {
+ number = new SVGNumber(number)
+ return new SVGNumber(this - number, this.unit || number.unit)
}
// Add number
@@ -71,25 +68,29 @@ export default class SVGNumber {
return new SVGNumber(this + number, this.unit || number.unit)
}
- // Subtract number
- minus (number) {
- number = new SVGNumber(number)
- return new SVGNumber(this - number, this.unit || number.unit)
- }
-
// Multiply number
times (number) {
number = new SVGNumber(number)
return new SVGNumber(this * number, this.unit || number.unit)
}
- // Divide number
- divide (number) {
- number = new SVGNumber(number)
- return new SVGNumber(this / number, this.unit || number.unit)
+ toArray () {
+ return [ this.value, this.unit ]
}
- convert (unit) {
- return new SVGNumber(this.value, unit)
+ toJSON () {
+ return this.toString()
+ }
+
+ toString () {
+ return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6
+ : this.unit === 's' ? this.value / 1e3
+ : this.value
+ ) + this.unit
}
+
+ valueOf () {
+ return this.value
+ }
+
}