From 747f8e810eea3bb4836b50f411a7de6f5d67cbfa Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ulrich-Matthias=20Sch=C3=A4fer?= Date: Wed, 29 Apr 2020 16:25:47 +1000 Subject: [PATCH] finished tests for Point, PointArray, PathArray and SVGArray --- CHANGELOG.md | 4 + spec/spec/types/PathArray.js | 108 +++++++++++++++++++ spec/spec/types/Point.js | 31 +++++- spec/spec/types/PointArray.js | 112 +++++++++++++++++++- spec/spec/types/SVGArray.js | 89 ++++++++++++++++ spec/spec/types/{Number.js => SVGNumber.js} | 0 src/types/PathArray.js | 74 +------------ src/types/PointArray.js | 52 ++++----- 8 files changed, 362 insertions(+), 108 deletions(-) create mode 100644 spec/spec/types/PathArray.js create mode 100644 spec/spec/types/SVGArray.js rename spec/spec/types/{Number.js => SVGNumber.js} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d89ea91..6292c29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http: - fixed `flatten()` which correctly flattens now but doesnt accept parameters anymore (makes no sense) - fixed `ungroup()` which now inserts the elements at the correct position in the correct order and has position as second argument now - fixed `position` for `transform()` to also allow a position of 0 + - fixed `bbox()` of `PathArray` and `PointArray` which returns an instance of `Box` now + - fixed bug in creation of PointArray which had still references to source arrays in it ### Added - added second Parameter to `SVG(el, isHTML)` which allows to explicitely create elements in the HTML namespace (#1058) @@ -41,10 +43,12 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http: - added position argument for `toRoot()` - added attr syntax for `data()` method - added index and array parameter when passing a function to `List.each()` so that it mostly behaves like map + - added possibility to pass transform object to `PointArray.transform()` ----- to Point - added lots of tests in es6 format ### Deleted - deleted undocumented `Matrix.compose()` method which did the same as `new Matrix()` or `Matrix.transform()` + - deleted undocumented `Path.morph()` and `Path.at()` which was replaced with Morphables in v3 ## [3.0.16] - 2019-11-12 diff --git a/spec/spec/types/PathArray.js b/spec/spec/types/PathArray.js new file mode 100644 index 0000000..fd43480 --- /dev/null +++ b/spec/spec/types/PathArray.js @@ -0,0 +1,108 @@ +/* globals describe, expect, it, beforeEach */ + +import { PathArray, Box } from '../../../src/main.js' + +describe('PathArray.js', () => { + let p1, p2, p3, p4, p5, p6, p7 + + beforeEach(() => { + p1 = new PathArray('m10 10 h 80 v 80 h -80 l 300 400 z') + p2 = new PathArray('m10 80 c 40 10 65 10 95 80 s 150 150 180 80 t 300 300 q 52 10 95 80 z') + p3 = new PathArray('m80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 z') + p4 = new PathArray('M215.458,245.23c0,0,77.403,0,94.274,0S405,216.451,405,138.054S329.581,15,287.9,15c-41.68,0-139.924,0-170.688,0C86.45,15,15,60.65,15,134.084c0,73.434,96.259,112.137,114.122,112.137C146.984,246.221,215.458,245.23,215.458,245.23z') + p5 = new PathArray('M10 10-45-30.5.5 .89L2e-2.5.5.5-.5C.5.5.5.5.5.5L-3-4z') + p6 = new PathArray('m 0,0 0,3189 2209,0 0,-3189 -2209,0 z m 154,154 1901,0 0,2881 -1901,0 0,-2881 z') + p7 = new PathArray('m 0,0 a 45 45, 0, 0, 0, 125 125') + }) + + it('converts to absolute values', () => { + expect(p1.toString()).toBe('M10 10H90V90H10L310 490Z ') + expect(p2.toString()).toBe('M10 80C50 90 75 90 105 160S255 310 285 240T585 540Q637 550 680 620Z ') + expect(p3.toString()).toBe('M80 80A45 45 0 0 0 125 125L125 80Z ') + expect(p4.toString()).toBe('M215.458 245.23C215.458 245.23 292.861 245.23 309.73199999999997 245.23S405 216.451 405 138.054S329.581 15 287.9 15C246.21999999999997 15 147.97599999999997 15 117.21199999999999 15C86.45 15 15 60.65 15 134.084C15 207.518 111.259 246.221 129.122 246.221C146.984 246.221 215.458 245.23 215.458 245.23Z ') + expect(p6.toString()).toBe('M0 0L0 3189L2209 3189L2209 0L0 0ZM154 154L2055 154L2055 3035L154 3035L154 154Z ') + expect(p7.toString()).toBe('M0 0A45 45 0 0 0 125 125 ') + }) + + it('parses difficult syntax correctly', () => { + expect(p5.toString()).toBe('M10 10L-45 -30.5L0.5 0.89L0.02 0.5L0.5 -0.5C0.5 0.5 0.5 0.5 0.5 0.5L-3 -4Z ') + }) + + it('parses flat arrays correctly', () => { + const arr = new PathArray([ 'M', 0, 0, 'L', 100, 100, 'z' ]) + expect(arr.toString()).toBe('M0 0L100 100Z ') + }) + + it('parses nested arrays correctly', () => { + const arr = new PathArray([ [ 'M', 0, 0 ], [ 'L', 100, 100 ], [ 'z' ] ]) + expect(arr.toString()).toBe('M0 0L100 100Z ') + }) + + // this test is designed to cover a certain line but it doesnt work because of #608 + it('returns the valueOf when PathArray is given', () => { + const p = new PathArray('m10 10 h 80 v 80 h -80 l 300 400 z') + + expect((new PathArray(p))).toEqual(p) + }) + + it('can handle all formats which can be used', () => { + // when no command is specified after move, line is used automatically (specs say so) + expect(new PathArray('M10 10 80 80 30 30 Z').toString()).toBe('M10 10L80 80L30 30Z ') + + // parsing can handle 0.5.3.3.2 stuff + expect(new PathArray('M10 10L.5.5.3.3Z').toString()).toBe('M10 10L0.5 0.5L0.3 0.3Z ') + }) + + describe('move()', () => { + it('moves all points in a straight path', () => { + expect(p1.move(100, 200).toString()).toBe('M100 200H180V280H100L400 680Z ') + }) + + it('moves all points in a curved path', () => { + expect(p2.move(100, 200).toString()).toBe('M100 200C140 210 165 210 195 280S345 430 375 360T675 660Q727 670 770 740Z ') + }) + + it('moves all points in a arc path', () => { + expect(p3.move(100, 200).toString()).toBe('M100 200A45 45 0 0 0 145 245L145 200Z ') + }) + + it('does nothing if passed number is not a number', () => { + expect(p3.move()).toEqual(p3) + }) + }) + + describe('size()', () => { + it('resizes all points in a straight path', () => { + expect(p1.size(600, 200).toString()).toBe('M10 10H170V43.333333333333336H10L610 210Z ') + }) + + it('resizes all points in a curved path', () => { + expect(p2.size(600, 200).toString()).toBe('M10 80C45.82089552238806 83.70370370370371 68.2089552238806 83.70370370370371 95.07462686567165 109.62962962962963S229.40298507462686 165.1851851851852 256.2686567164179 139.25925925925927T524.9253731343283 250.37037037037038Q571.4925373134329 254.07407407407408 610 280Z ') + }) + + it('resizes all points in a arc path', () => { + const expected = [ + [ 'M', 80, 80 ], + [ 'A', 600, 200, 0, 0, 0, 680, 280 ], + [ 'L', 680, 80 ], + [ 'Z' ] + ] + + const toBeTested = p3.size(600, 200) + + for (let i = toBeTested.length; i--;) { + expect(toBeTested[i].shift().toUpperCase()).toBe(expected[i].shift().toUpperCase()) + for (let j = toBeTested[i].length; j--;) { + expect(toBeTested[i][j]).toBeCloseTo(expected[i][j]) + } + } + }) + }) + + describe('bbox()', () => { + it('calculates the bounding box of the PathArray', () => { + const box = new PathArray('M0 0 L 10 10').bbox() + expect(box).toEqual(new Box(0, 0, 10, 10)) + }) + }) +}) diff --git a/spec/spec/types/Point.js b/spec/spec/types/Point.js index ee0bbd4..ef0b6f6 100644 --- a/spec/spec/types/Point.js +++ b/spec/spec/types/Point.js @@ -1,6 +1,6 @@ -/* globals describe, expect, it, beforeEach */ +/* globals describe, expect, it, beforeEach, spyOn */ -import { Point } from '../../../src/main.js' +import { Point, Rect, Matrix } from '../../../src/main.js' describe('Point.js', () => { var point @@ -66,6 +66,16 @@ describe('Point.js', () => { }) }) + describe('transform()', () => { + it('transforms a point with a matrix', () => { + expect(new Point().transform(new Matrix({ translate: [ 10, 10 ] }))).toEqual(new Point(10, 10)) + }) + + it('transforms a point with a transformation object', () => { + expect(new Point().transform({ translate: [ 10, 10 ] })).toEqual(new Point(10, 10)) + }) + }) + describe('clone()', () => { it('returns cloned point', () => { var point1 = new Point(1, 1) @@ -75,4 +85,21 @@ describe('Point.js', () => { expect(point1).not.toBe(point2) }) }) + + describe('toArray()', () => { + it('creates an array representation of Point', () => { + const p = new Point(1, 2) + expect(p.toArray()).toEqual([ 1, 2 ]) + }) + }) + + describe('Element', () => { + describe('point()', () => { + it('transforms a screen point into the coordinate system of the element', () => { + const rect = new Rect() + spyOn(rect, 'screenCTM').and.callFake(() => new Matrix(1, 0, 0, 1, 20, 20)) + expect(rect.point({ x: 10, y: 10 })).toEqual(new Point(-10, -10)) + }) + }) + }) }) diff --git a/spec/spec/types/PointArray.js b/spec/spec/types/PointArray.js index da8675e..cbcc3c1 100644 --- a/spec/spec/types/PointArray.js +++ b/spec/spec/types/PointArray.js @@ -2,24 +2,122 @@ import { PointArray, Matrix, Point } from '../../../src/main.js' -describe('PointArray', () => { +describe('PointArray.js', () => { const squareString = '0,0 1,0 1,1 0,1' - const square = new PointArray(squareString) + + describe('()', () => { + + it('parses a string to a point array', () => { + var array = new PointArray('0,1 -.05,7.95 1000.0001,-200.222') + expect(array.valueOf()).toEqual([ [ 0, 1 ], [ -0.05, 7.95 ], [ 1000.0001, -200.222 ] ]) + }) + + it('parses a points array correctly to string', () => { + var array = new PointArray([ [ 0, 0.15 ], [ -100, -3.141592654 ], [ 50, 100 ] ]) + expect(array + '').toBe('0,0.15 -100,-3.141592654 50,100') + }) + + it('parses a flat array of x/y coordinates to a point array', () => { + var array = new PointArray([ 1, 4, 5, 68, 12, 24 ]) + expect(array.valueOf()).toEqual([ [ 1, 4 ], [ 5, 68 ], [ 12, 24 ] ]) + }) + + it('parses points with space delimitered x/y coordinates', () => { + var array = new PointArray('221.08 191.79 0.46 191.79 0.46 63.92 63.8 0.46 284.46 0.46 284.46 128.37 221.08 191.79') + expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79') + }) + + it('parses points with comma delimitered x/y coordinates', () => { + var array = new PointArray('221.08,191.79,0.46,191.79,0.46,63.92,63.8,0.46,284.46,0.46,284.46,128.37,221.08,191.79') + expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79') + }) + + it('parses points with comma and space delimitered x/y coordinates', () => { + var array = new PointArray('221.08, 191.79, 0.46, 191.79, 0.46, 63.92, 63.8, 0.46, 284.46, 0.46, 284.46, 128.37, 221.08, 191.79') + expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79') + }) + + it('parses points with space and comma delimitered x/y coordinates', () => { + var array = new PointArray('221.08 ,191.79 ,0.46 ,191.79 ,0.46 ,63.92 ,63.8 ,0.46 ,284.46 ,0.46 ,284.46 ,128.37 ,221.08 ,191.79') + expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79') + }) + + it('parses points with redundant spaces at the end', () => { + var array = new PointArray('2176.6,1708.8 2176.4,1755.8 2245.8,1801.5 2297,1787.8 ') + expect(array + '').toBe('2176.6,1708.8 2176.4,1755.8 2245.8,1801.5 2297,1787.8') + }) + + it('parses points with space delimitered x/y coordinates - even with leading or trailing space', () => { + var array = new PointArray(' 1 2 3 4 ') + expect(array + '').toBe('1,2 3,4') + }) + + it('parses odd number of points with space delimitered x/y coordinates and silently remove the odd point', () => { + // this is according to spec: https://svgwg.org/svg2-draft/shapes.html#DataTypePoints + var array = new PointArray('1 2 3') + expect(array + '').toBe('1,2') + }) + + it('parses odd number of points in a flat array of x/y coordinates and silently remove the odd point', () => { + // this is according to spec: https://svgwg.org/svg2-draft/shapes.html#DataTypePoints + var array = new PointArray([ 1, 2, 3 ]) + expect(array.valueOf()).toEqual([ [ 1, 2 ] ]) + }) + + }) + + describe('move()', () => { + it('moves the whole array by the passed value', () => { + const arr = new PointArray([ 1, 2, 3, 4 ]).move(10, 10) + expect(arr.toArray()).toEqual([ 10, 10, 12, 12 ]) + }) + + it('does nothing if values not numbers', () => { + const arr = new PointArray([ 1, 2, 3, 4 ]).move() + expect(arr.toArray()).toEqual([ 1, 2, 3, 4 ]) + }) + }) + + describe('size()', () => { + it('correctly sizes the points over the whole area', () => { + var array = new PointArray([ 10, 10, 20, 20, 30, 30 ]) + expect(array.size(60, 60).valueOf()).toEqual([ [ 10, 10 ], [ 40, 40 ], [ 70, 70 ] ]) + }) + + it('let coordinates untouched when width/height is zero', () => { + var array = new PointArray([ 10, 10, 10, 20, 10, 30 ]) + expect(array.size(60, 60).valueOf()).toEqual([ [ 10, 10 ], [ 10, 40 ], [ 10, 70 ] ]) + + array = new PointArray([ 10, 10, 20, 10, 30, 10 ]) + expect(array.size(60, 60).valueOf()).toEqual([ [ 10, 10 ], [ 40, 10 ], [ 70, 10 ] ]) + }) + + }) describe('toString()', () => { - it('round trips with string', () => { + it('converts to comma sepereated list', () => { + const square = new PointArray(squareString) expect(square.toString()).toEqual(squareString) }) }) + describe('toLine', () => { + it('returns an object which can be passed to a line as point attributes', () => { + const arr = new PointArray([ 1, 2, 3, 4 ]) + expect(arr.toLine()).toEqual({ x1: 1, y1: 2, x2: 3, y2: 4 }) + }) + }) + describe('transform()', () => { it('translates correctly', () => { - const translation = new Matrix().translate(2, 1) + const square = new PointArray(squareString) + const translation = new Matrix({ translate: [ 2, 1 ] }) const newSquare = square.transform(translation) expect(newSquare.toString()).toEqual('2,1 3,1 3,2 2,2') }) it('transforms like Point', () => { + const square = new PointArray(squareString) const matrix = new Matrix(1, 2, 3, 4, 5, 6) const newSquare = square.transform(matrix) for (let i = 0; i < square.length; i++) { @@ -28,5 +126,11 @@ describe('PointArray', () => { expect(squarePoint.transform(matrix)).toEqual(newSquarePoint) } }) + + it('works with transform object instead of matrix', () => { + const square = new PointArray(squareString) + const newSquare = square.transform({ translate: [ 2, 1 ] }) + expect(newSquare.toString()).toEqual('2,1 3,1 3,2 2,2') + }) }) }) diff --git a/spec/spec/types/SVGArray.js b/spec/spec/types/SVGArray.js new file mode 100644 index 0000000..cefc54e --- /dev/null +++ b/spec/spec/types/SVGArray.js @@ -0,0 +1,89 @@ +/* globals describe, expect, it, jasmine */ + +import { Array as SVGArray, PointArray, PathArray } from '../../../src/main.js' + +const { any } = jasmine + +describe('SVGArray.js', () => { + describe('()', () => { + + it('preallocates memory if only number is passed', () => { + const arr = new SVGArray(1) + expect(arr.length).toBe(1) + }) + + it('parses a matrix array correctly to string', () => { + const array = new SVGArray([ + 0.343, 0.669, 0.119, 0, 0, + 0.249, -0.626, 0.130, 0, 0, + 0.172, 0.334, 0.111, 0, 0, + 0.000, 0.000, 0.000, 1, -0 + ]) + + expect(array + '').toBe('0.343 0.669 0.119 0 0 0.249 -0.626 0.13 0 0 0.172 0.334 0.111 0 0 0 0 0 1 0') + }) + + it('parses space seperated string and converts it to array', () => { + expect((new SVGArray('1 2 3 4')).valueOf()).toEqual([ 1, 2, 3, 4 ]) + }) + + it('parses comma seperated string and converts it to array', () => { + expect((new SVGArray('1,2,3,4')).valueOf()).toEqual([ 1, 2, 3, 4 ]) + }) + + }) + + describe('reverse()', () => { + it('reverses the array', () => { + const array = new SVGArray([ 1, 2, 3, 4, 5 ]).reverse() + expect(array.valueOf()).toEqual([ 5, 4, 3, 2, 1 ]) + }) + + it('returns itself', () => { + const array = new SVGArray() + expect(array.reverse()).toBe(array) + }) + }) + + describe('clone()', () => { + it('creates a shallow clone of the array', () => { + const array = new SVGArray([ 1, 2, 3, 4, 5 ]) + const clone = array.clone() + + expect(array).toEqual(clone) + expect(array).not.toBe(clone) + }) + + it('also works with PointArray (one depths clone)', () => { + const array = new PointArray([ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]) + const clone = array.clone() + + expect(array).toEqual(clone) + expect(array).not.toBe(clone) + + for (let i = array.length; i--;) { + expect(array[i]).not.toBe(clone[i]) + } + }) + + it('also works with PathArray (one depths clone)', () => { + const array = new PathArray([ [ 'M', 1, 2 ], [ 'L', 3, 4 ], [ 'L', 5, 6 ] ]) + const clone = array.clone() + + expect(array).toEqual(clone) + expect(array).not.toBe(clone) + + for (let i = array.length; i--;) { + expect(array[i]).not.toBe(clone[i]) + } + }) + }) + + describe('toSet()', () => { + it('creates a Set from the Array', () => { + const set = new SVGArray([ 1, 1, 2, 3 ]).toSet() + expect(set).toEqual(any(Set)) + expect(set).toEqual(new Set([ 1, 2, 3 ])) + }) + }) +}) diff --git a/spec/spec/types/Number.js b/spec/spec/types/SVGNumber.js similarity index 100% rename from spec/spec/types/Number.js rename to spec/spec/types/SVGNumber.js diff --git a/src/types/PathArray.js b/src/types/PathArray.js index f124b1b..03fdee3 100644 --- a/src/types/PathArray.js +++ b/src/types/PathArray.js @@ -11,6 +11,7 @@ import { subClassArray } from './ArrayPolyfill.js' import Point from './Point.js' import SVGArray from './SVGArray.js' import parser from '../modules/core/parser.js' +import Box from './Box.js' const PathArray = subClassArray('PathArray', SVGArray) @@ -222,72 +223,8 @@ extend(PathArray, { return this }, - // Test if the passed path array use the same path data commands as this path array - equalCommands (pathArray) { - var i, il, equalCommands - - pathArray = new PathArray(pathArray) - - equalCommands = this.length === pathArray.length - for (i = 0, il = this.length; equalCommands && i < il; i++) { - equalCommands = this[i][0] === pathArray[i][0] - } - - return equalCommands - }, - - // Make path array morphable - morph (pathArray) { - pathArray = new PathArray(pathArray) - - if (this.equalCommands(pathArray)) { - this.destination = pathArray - } else { - this.destination = null - } - - return this - }, - - // Get morphed path array at given position - at (pos) { - // make sure a destination is defined - if (!this.destination) return this - - var sourceArray = this - var destinationArray = this.destination.value - var array = [] - var pathArray = new PathArray() - var i, il, j, jl - - // Animate has specified in the SVG spec - // See: https://www.w3.org/TR/SVG11/paths.html#PathElement - for (i = 0, il = sourceArray.length; i < il; i++) { - array[i] = [ sourceArray[i][0] ] - for (j = 1, jl = sourceArray[i].length; j < jl; j++) { - array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos - } - // For the two flags of the elliptical arc command, the SVG spec say: - // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true - // Elliptical arc command as an array followed by corresponding indexes: - // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y] - // 0 1 2 3 4 5 6 7 - if (array[i][0] === 'A') { - array[i][4] = +(array[i][4] !== 0) - array[i][5] = +(array[i][5] !== 0) - } - } - - // Directly modify the value of a path array, this is done this way for performance - pathArray.value = array - return pathArray - }, - // Absolutize and parse path to array - parse (array = [ [ 'M', 0, 0 ] ]) { - // if it's already a patharray, no need to parse it - if (array instanceof PathArray) return array - + parse (array = [ 'M', 0, 0 ]) { // prepare for parsing var s var paramCnt = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7, Z: 0 } @@ -300,9 +237,8 @@ extend(PathArray, { .trim() // trim .split(delimiter) // split into array } else { - array = array.reduce(function (prev, curr) { - return [].concat.call(prev, curr) - }, []) + // Flatten array + array = Array.prototype.concat.apply([], array) } // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...] @@ -337,6 +273,6 @@ extend(PathArray, { // Get bounding box of path bbox () { parser().path.setAttribute('d', this.toString()) - return parser.nodes.path.getBBox() + return new Box(parser.nodes.path.getBBox()) } }) diff --git a/src/types/PointArray.js b/src/types/PointArray.js index 6a869d7..54df08f 100644 --- a/src/types/PointArray.js +++ b/src/types/PointArray.js @@ -2,6 +2,8 @@ import { delimiter } from '../modules/core/regex.js' import { extend } from '../utils/adopter.js' import { subClassArray } from './ArrayPolyfill.js' import SVGArray from './SVGArray.js' +import { Matrix } from '../main.js' +import Box from './Box.js' const PointArray = subClassArray('PointArray', SVGArray) @@ -28,32 +30,13 @@ extend(PointArray, { } }, - // Get morphed array at given position - at (pos) { - // make sure a destination is defined - if (!this.destination) return this - - // generate morphed point string - for (var i = 0, il = this.length, array = []; i < il; i++) { - array.push([ - this[i][0] + (this.destination[i][0] - this[i][0]) * pos, - this[i][1] + (this.destination[i][1] - this[i][1]) * pos - ]) - } - - return new PointArray(array) - }, - // Parse point string and flat array - parse (array = [ [ 0, 0 ] ]) { + parse (array = [ 0, 0 ]) { var points = [] - // if it is an array + // if it is an array, we flatten it and therefore clone it to 1 depths if (array instanceof Array) { - // and it is not flat, there is no need to parse it - if (array[0] instanceof Array) { - return array - } + array = Array.prototype.concat.apply([], array) } else { // Else, it is considered as a string // parse points array = array.trim().split(delimiter).map(parseFloat) @@ -71,21 +54,24 @@ extend(PointArray, { return points }, - // transform points with matrix (similar to Point.transform) transform (m) { - const points = [] + return this.clone().transformO(m) + }, + + // transform points with matrix (similar to Point.transform) + transformO (m) { + if (!Matrix.isMatrixLike(m)) { + m = new Matrix(m) + } - for (let i = 0; i < this.length; i++) { - const point = this[i] + for (let i = this.length; i--;) { // Perform the matrix multiplication - points.push([ - m.a * point[0] + m.c * point[1] + m.e, - m.b * point[0] + m.d * point[1] + m.f - ]) + const [ x, y ] = this[i] + this[i][0] = m.a * x + m.c * y + m.e + this[i][1] = m.b * x + m.d * y + m.f } - // Return the required point - return new PointArray(points) + return this }, // Move point string @@ -132,6 +118,6 @@ extend(PointArray, { minX = Math.min(el[0], minX) minY = Math.min(el[1], minY) }) - return { x: minX, y: minY, width: maxX - minX, height: maxY - minY } + return new Box(minX, minY, maxX - minX, maxY - minY) } }) -- 2.39.5