]> source.dussan.org Git - svg.js.git/commitdiff
Fix move and size of groups, removed setting of a default font so we dont act against...
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Sat, 12 Jan 2019 19:32:39 +0000 (20:32 +0100)
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Sat, 12 Jan 2019 19:32:39 +0000 (20:32 +0100)
CHANGELOG.md
spec/spec/elements/G.js
src/elements/Ellipse.js
src/elements/G.js
src/elements/Text.js
src/elements/Tspan.js
src/modules/optional/sugar.js
src/types/Box.js
src/types/Point.js
src/types/PointArray.js
src/utils/adopter.js

index 0ad260bca7f454af539d7ec067f54995f5289bf2..0e8f89d2f9d71b651dc9356d270382c3503607a2 100644 (file)
@@ -7,7 +7,16 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
 
 ====
 
+## [3.0.6]
+
+### Fixed
+ - fixed group move and size commands
+ - default font size is not set anymore because it mostly goes against what the user wants
+ - fix bug in `font()` which set wrong values
+
 ## [3.0.5] - 2018-12-12
+
+### Fixed
  - fixed `parser` which didnt have all required css rules and not focusable=false
  - group `x(), y(), width(), height(), dx(), dy()` now correctly change the bbox of the group by moving/resizing all children
  - fixed timeline which fired `finished` too early
index 2c2efc812faa8aab3c89a5fff6095beb29bcf0c7..385419e9b7517e5b25d157c6523e183d5eed7185 100644 (file)
@@ -1,4 +1,4 @@
-import { G, Rect, makeInstance } from '../../../src/main';
+import { Box, G, Rect, makeInstance } from '../../../src/main.js';
 
 const { any, createSpy, objectContaining } = jasmine
 
@@ -15,6 +15,68 @@ describe('G.js', () => {
     })
   })
 
+  describe('Container', () => {
+    describe('group()', () => {
+      it('creates a group in the container', () => {
+        const canvas = makeInstance().addTo('#canvas')
+        const g = canvas.group()
+        expect(g).toEqual(any(G))
+        expect(g.parent()).toBe(canvas)
+      })
+    })
+  })
+
+  describe('dmove()', () => {
+    it('moves the bbox of the group by a certain amount (1)', () => {
+      const canvas = makeInstance().addTo('#canvas')
+      const g = canvas.group()
+
+      g.add(new Rect({width:100, height:120, x:10, y:20}))
+      g.add(new Rect({width:70, height:100, x:50, y:60}))
+
+      g.dmove(10, 10)
+
+      const box = g.bbox()
+      expect(box).toEqual(objectContaining({
+        x: 20, y: 30, width: box.width, height: box.height
+      }))
+    })
+
+    it('moves the bbox of the group by a certain amount (2)', () => {
+      const canvas = makeInstance().addTo('#canvas')
+      const g = canvas.group()
+
+      g.rect(400, 200).move(123, 312).rotate(34).skew(12)
+      g.rect(100, 50).move(11, 43).translate(123, 32).skew(-12)
+      g.rect(400, 200).rotate(90)
+      g.group().rotate(23).group().skew(32).rect(100, 40).skew(11).rotate(12)
+
+      const oldBox = g.bbox()
+
+      g.dmove(10, 10)
+
+      const newBox = g.bbox()
+
+      expect(newBox.x).toBeCloseTo(oldBox.x + 10, 4)
+      expect(newBox.y).toBeCloseTo(oldBox.y + 10, 4)
+      expect(newBox.w).toBeCloseTo(oldBox.w, 4)
+      expect(newBox.h).toBeCloseTo(oldBox.h, 4)
+    })
+  })
+
+  describe('move()', () => {
+    it('calls dmove() with the correct difference', () => {
+      const canvas = makeInstance().addTo('#canvas')
+      const g = canvas.group()
+      g.rect(100, 200).move(111, 223)
+
+      spyOn(g, 'dmove')
+
+      g.move(100, 150)
+      expect(g.dmove).toHaveBeenCalledWith(-11, -73)
+    })
+  })
+
   describe('x()', () => {
     it('gets the x value of the bbox', () => {
       const canvas = makeInstance().addTo('#canvas')
@@ -28,19 +90,15 @@ describe('G.js', () => {
       expect(g.x()).toBe(g.bbox().x)
       expect(g.x()).toBe(10)
     })
-    it('sets the x value of the bbox by moving all children', () => {
+    it('calls move with the paramater as x', () => {
       const canvas = makeInstance().addTo('#canvas')
+      const g = canvas.group()
+      g.rect(100, 200).move(111, 223)
 
-      const g = new G()
-      g.add(new Rect({width:100, height:120, x:10, y:20}))
-      g.add(new Rect({width:70, height:100, x:50, y:60}))
-
-      g.addTo(canvas)
+      spyOn(g, 'move')
 
-      expect(g.x(0)).toBe(g)
-      expect(g.bbox().x).toBe(0)
-      expect(g.children()[0].x()).toBe(0)
-      expect(g.children()[1].x()).toBe(40)
+      g.x(100)
+      expect(g.move).toHaveBeenCalledWith(100, g.bbox().y, any(Box))
     })
   })
 
@@ -57,7 +115,20 @@ describe('G.js', () => {
       expect(g.y()).toBe(g.bbox().y)
       expect(g.y()).toBe(20)
     })
-    it('sets the y value of the bbox by moving all children', () => {
+    it('calls move with the paramater as y', () => {
+      const canvas = makeInstance().addTo('#canvas')
+      const g = canvas.group()
+      g.rect(100, 200).move(111, 223)
+
+      spyOn(g, 'move')
+
+      g.y(100)
+      expect(g.move).toHaveBeenCalledWith(g.bbox().x, 100, any(Box))
+    })
+  })
+
+  describe('size()', () => {
+    it('changes the dimensions of the bbox (1)', () => {
       const canvas = makeInstance().addTo('#canvas')
 
       const g = new G()
@@ -66,11 +137,50 @@ describe('G.js', () => {
 
       g.addTo(canvas)
 
-      expect(g.y(0)).toBe(g)
-      expect(g.bbox().y).toBe(0)
-      expect(g.children()[0].y()).toBe(0)
-      expect(g.children()[1].y()).toBe(40)
+      const oldBox = g.bbox()
+
+      expect(g.size(100, 100)).toBe(g)
+
+      const newBox = g.bbox()
+
+      expect(newBox.x).toBeCloseTo(oldBox.x, 4)
+      expect(newBox.y).toBeCloseTo(oldBox.y, 4)
+      expect(newBox.w).toBeCloseTo(100, 4)
+      expect(newBox.h).toBeCloseTo(100, 4)
+
+      expect(g.children()[0].width()).toBeCloseTo(90.909, 3)
+      expect(g.children()[1].width()).toBeCloseTo(63.636, 3)
+
+      expect(g.children()[0].x()).toBeCloseTo(10, 3)
+      expect(g.children()[1].x()).toBeCloseTo(46.364, 3)
+      expect(g.children()[0].height()).toBeCloseTo(85.714, 3)
+      expect(g.children()[1].height()).toBeCloseTo(71.429, 3)
+      expect(g.children()[0].y()).toBeCloseTo(20, 3)
+      expect(g.children()[1].y()).toBeCloseTo(48.571, 3)
+    })
+
+    it('changes the dimensions of the bbox (2)', () => {
+      const canvas = makeInstance().addTo('#canvas')
+      const g = canvas.group()
+
+      g.rect(400, 200).move(123, 312).rotate(34).skew(12)
+      g.rect(100, 50).move(11, 43).translate(123, 32).skew(-12)
+      g.rect(400, 200).rotate(90)
+      g.group().rotate(23).group().skew(32).rect(100, 40).skew(11).rotate(12)
+
+      const oldBox = g.bbox()
+
+      g.size(100, 100)
+
+      const newBox = g.bbox()
+
+      expect(newBox.x).toBeCloseTo(oldBox.x, 4)
+      expect(newBox.y).toBeCloseTo(oldBox.y, 4)
+      expect(newBox.w).toBeCloseTo(100, 4)
+      expect(newBox.h).toBeCloseTo(100, 4)
     })
+
+
   })
 
   describe('width()', () => {
index 0350f1f5ae175238d68a9ff10dc58152274bf908..beba2289e1ec01823bc33f178099d57a4f6b5585 100644 (file)
@@ -28,7 +28,7 @@ extend(Ellipse, circled)
 
 registerMethods('Container', {
   // Create an ellipse
-  ellipse: wrapWithAttrCheck(function (width, height) {
+  ellipse: wrapWithAttrCheck(function (width = 0, height = width) {
     return this.put(new Ellipse()).size(width, height).move(0, 0)
   })
 })
index 8171fed62a64786c5d0e00999323ebaf0ee7444e..a9e8b558eb64c76e8ef93c71190134c84813d4d1 100644 (file)
@@ -2,7 +2,8 @@ import { nodeOrNew, register, wrapWithAttrCheck } from '../utils/adopter.js'
 import { proportionalSize } from '../utils/utils.js'
 import { registerMethods } from '../utils/methods.js'
 import Container from './Container.js'
-import SVGNumber from '../types/SVGNumber.js'
+import Matrix from '../types/Matrix.js'
+import Point from '../types/Point.js'
 
 export default class G extends Container {
   constructor (node) {
@@ -11,70 +12,68 @@ export default class G extends Container {
 
   x (x, box = this.bbox()) {
     if (x == null) return box.x
-
-    this.children().dx(x - box.x)
-    return this
+    return this.move(x, box.y, box)
   }
 
   y (y, box = this.bbox()) {
     if (y == null) return box.y
-
-    this.children().dy(y - box.y)
-    return this
+    return this.move(box.x, y, box)
   }
 
-  move (x, y) {
-    const box = this.bbox()
-    return this.x(x, box).y(y, box)
+  move (x = 0, y = 0, box = this.bbox()) {
+    const dx = x - box.x
+    const dy = y - box.y
+
+    return this.dmove(dx, dy)
   }
 
   dx (dx) {
-    return this.children().dx(dx)
+    return this.dmove(dx, 0)
   }
 
   dy (dy) {
-    return this.children().dy(dy)
+    return this.dmove(0, dy)
   }
 
-  width (width, box = this.bbox()) {
-    if (width == null) return box.width
-
-    const scale = width / box.width
-
-    this.each(function () {
-      const _width = this.width()
-      const _x = this.x()
-
-      this.width(_width * scale)
-      this.x((_x - box.x) * scale + box.x)
+  dmove (dx, dy) {
+    this.children().forEach((child, i) => {
+      // Get the childs bbox
+      const bbox = child.bbox()
+      // Get childs matrix
+      const m = new Matrix(child)
+      // Translate childs matrix by amount and
+      // transform it back into parents space
+      const matrix = m.translate(dx, dy).transform(m.inverse())
+      // Calculate new x and y from old box
+      const p = new Point(bbox.x, bbox.y).transform(matrix)
+      // Move element
+      child.move(p.x, p.y)
     })
 
     return this
   }
 
+  width (width, box = this.bbox()) {
+    if (width == null) return box.width
+    return this.size(width, box.height, box)
+  }
+
   height (height, box = this.bbox()) {
     if (height == null) return box.height
-
-    const scale = height / box.height
-
-    this.each(function () {
-      const _height = this.height()
-      const _y = this.y()
-
-      this.height(_height * scale)
-      this.y((_y - box.y) * scale + box.y)
-    })
-
-    return this
+    return this.size(box.width, height, box)
   }
 
-  size (width, height) {
-    const box = this.bbox()
+  size (width, height, box = this.bbox()) {
     const p = proportionalSize(this, width, height, box)
+    const scaleX = p.width / box.width
+    const scaleY = p.height / box.height
+
+    this.children().forEach((child, i) => {
+      const o = new Point(box).transform(new Matrix(child).inverse())
+      child.scale(scaleX, scaleY, o.x, o.y)
+    })
 
     return this
-      .width(new SVGNumber(p.width), box)
-      .height(new SVGNumber(p.height), box)
   }
 }
 
index db9c2ee078b06dc89339589bb2ee2828a35e9603..cfa3f51499e9ebbb76b7a8d83c8c98629f1bd7fb 100644 (file)
@@ -5,7 +5,6 @@ import {
   register,
   wrapWithAttrCheck
 } from '../utils/adopter.js'
-import { attrs } from '../modules/core/defaults.js'
 import { registerMethods } from '../utils/methods.js'
 import SVGNumber from '../types/SVGNumber.js'
 import Shape from './Shape.js'
@@ -20,9 +19,6 @@ export default class Text extends Shape {
     this.dom.leading = new SVGNumber(1.3) // store leading value for rebuilding
     this._rebuild = true // enable automatic updating of dy values
     this._build = false // disable build mode for adding multiple lines
-
-    // set default font
-    this.attr('font-family', attrs['font-family'])
   }
 
   // Move over x-axis
index abd032fd40bd91d83bc809d739cc550791e5bcb9..64895fc4537eae58111908e2866d9f49bded9af4 100644 (file)
@@ -4,7 +4,9 @@ import {
   register,
   wrapWithAttrCheck
 } from '../utils/adopter.js'
+import { globals } from '../utils/window.js'
 import { registerMethods } from '../utils/methods.js'
+import SVGNumber from '../types/SVGNumber.js'
 import Text from './Text.js'
 import * as textable from '../modules/core/textable.js'
 
@@ -41,8 +43,12 @@ export default class Tspan extends Text {
     // mark new line
     this.dom.newLined = true
 
+    var fontSize = globals.window.getComputedStyle(this.node)
+      .getPropertyValue('font-size')
+    var dy = t.dom.leading * new SVGNumber(fontSize)
+
     // apply new position
-    return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x())
+    return this.dy(dy).attr('x', t.x())
   }
 }
 
index c05512d0aa7b8b196e7985318d163921ca548535..7aba0f7b2e89e1326d852cb4639030cf0e998e94 100644 (file)
@@ -136,6 +136,7 @@ registerMethods([ 'Element', 'Runner' ], {
   font: function (a, v) {
     if (typeof a === 'object') {
       for (v in a) this.font(v, a[v])
+      return this
     }
 
     return a === 'leading'
index eb43d07e73070e1aff369ec5e440abda879db8e4..8cbda30e5bb74ab1db34547552d38c2993014df2 100644 (file)
@@ -59,6 +59,10 @@ export default class Box {
   }
 
   transform (m) {
+    if (!(m instanceof Matrix)) {
+      m = new Matrix(m)
+    }
+
     let xMin = Infinity
     let xMax = -Infinity
     let yMin = Infinity
@@ -130,7 +134,7 @@ export function bbox () {
       clone.remove()
       return box
     } catch (e) {
-      throw new Error('Getting bbox of element "' + el.node.nodeName + '" is not possible')
+      throw new Error('Getting bbox of element "' + el.node.nodeName + '" is not possible. ' + e.toString())
     }
   }))
 }
index f1c85a1eae7d8997d6f96c340392075f3a9b4bb2..329b37d23f6768643bcb257bfab549d9710ed2af 100644 (file)
@@ -1,3 +1,5 @@
+import Matrix from './Matrix.js'
+
 export default class Point {
   // Initialize
   constructor (...args) {
@@ -25,14 +27,23 @@ export default class Point {
     return new Point(this)
   }
 
-  // transform point with matrix
   transform (m) {
+    return this.clone().transformO(m)
+  }
+
+  // Transform point with matrix
+  transformO (m) {
+    if (!Matrix.isMatrixLike(m)) {
+      m = new Matrix(m)
+    }
+
+    let { x, y } = this
+
     // Perform the matrix multiplication
-    var x = m.a * this.x + m.c * this.y + m.e
-    var y = m.b * this.x + m.d * this.y + m.f
+    this.x = m.a * x + m.c * y + m.e
+    this.y = m.b * x + m.d * y + m.f
 
-    // Return the required point
-    return new Point(x, y)
+    return this
   }
 
   toArray () {
index 2246bbd46f41053b04d7d84e7b2d4f8761c3132e..6a869d7700c40767eac960e6c6bb6e7e1ce1b475 100644 (file)
@@ -63,7 +63,7 @@ extend(PointArray, {
     // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
     if (array.length % 2 !== 0) array.pop()
 
-    // wrap points in two-tuples and parse points as floats
+    // wrap points in two-tuples
     for (var i = 0, len = array.length; i < len; i = i + 2) {
       points.push([ array[i], array[i + 1] ])
     }
index f091c96950c2283d7caf108afa7865ab53ee134e..45e9bd310a66f1f92a972230cb078617f91b7680 100644 (file)
@@ -138,3 +138,27 @@ export function wrapWithAttrCheck (fn) {
     }
   }
 }
+
+export function invent (config) {
+  // Create element initializer
+  var initializer = typeof config.create === 'function'
+    ? config.create
+    : function (node) {
+      this.constructor(node || create(config.create))
+    }
+
+  // Inherit prototype
+  if (config.inherit) {
+    /* eslint new-cap: off */
+    initializer.prototype = new config.inherit()
+    initializer.prototype.constructor = initializer
+  }
+
+  // Extend with methods
+  if (config.extend) { extend(initializer, config.extend) }
+
+  // Attach construct method to parent
+  if (config.construct) { extend(config.parent || elements['Container'], config.construct) }
+
+  return initializer
+}