aboutsummaryrefslogtreecommitdiffstats
path: root/src/Box.js
blob: 478f971198f69367c56130b49925b199dbd21b72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//import {Parent, Doc, Symbol, Image, Pattern, Marker, Point} from './classes.js'
import Point from './Point.js'
import parser from './parser.js'
import {fullBox, domContains, isNulledBox} from './helpers.js'
import {extend} from './tools.js'
import {delimiter} from './regex.js'
import {registerMethods} from './methods.js'

export default class Box {
  constructor (...args) {
    this.init(...args)
  }

  init (source) {
    var base = [0, 0, 0, 0]
    source = typeof source === 'string' ? source.split(delimiter).map(parseFloat)
      : Array.isArray(source) ? source
      : typeof source === 'object' ? [source.left != null ? source.left
      : source.x, source.top != null ? source.top : source.y, source.width, source.height]
      : arguments.length === 4 ? [].slice.call(arguments)
      : base

    this.x = source[0]
    this.y = source[1]
    this.width = source[2]
    this.height = source[3]

    // add center, right, bottom...
    fullBox(this)
  }

  // Merge rect box with another, return a new instance
  merge (box) {
    let x = Math.min(this.x, box.x)
    let y = Math.min(this.y, box.y)
    let width = Math.max(this.x + this.width, box.x + box.width) - x
    let height = Math.max(this.y + this.height, box.y + box.height) - y

    return new Box(x, y, width, height)
  }

  transform (m) {
    let xMin = Infinity
    let xMax = -Infinity
    let yMin = Infinity
    let yMax = -Infinity

    let pts = [
      new Point(this.x, this.y),
      new Point(this.x2, this.y),
      new Point(this.x, this.y2),
      new Point(this.x2, this.y2)
    ]

    pts.forEach(function (p) {
      p = p.transform(m)
      xMin = Math.min(xMin, p.x)
      xMax = Math.max(xMax, p.x)
      yMin = Math.min(yMin, p.y)
      yMax = Math.max(yMax, p.y)
    })

    return new Box(
      xMin, yMin,
      xMax - xMin,
      yMax - yMin
    )
  }

  addOffset () {
    // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
    this.x += window.pageXOffset
    this.y += window.pageYOffset
    return this
  }

  toString () {
    return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height
  }

  toArray () {
    return [this.x, this.y, this.width, this.height]
  }
}

function getBox(cb) {
  let box

  try {
    box = cb(this.node)

    if (isNulledBox(box) && !domContains(this.node)) {
      throw new Error('Element not in the dom')
    }
  } catch (e) {
    try {
      let clone = this.clone(parser().svg).show()
      box = cb(clone.node)
      clone.remove()
    } catch (e) {
      throw (e)
      console.warn('Getting a bounding box of this element is not possible')
    }
  }
  return box
}

registerMethods({
  Element: {
    // Get bounding box
    bbox () {
      return new Box(getBox.call(this, (node) => node.getBBox()))
    },

    rbox (el) {
      let box = new Box(getBox.call(this, (node) => node.getBoundingClientRect()))
      if (el) return box.transform(el.screenCTM().inverse())
      return box.addOffset()
    }
  },
  viewbox: function (x, y, width, height) {
    // act as getter
    if (x == null) return new Box(this.attr('viewBox'))

    // act as setter
    return this.attr('viewBox', new Box(x, y, width, height))
  }
})