1 | const CAG = require('./CAG')
|
2 | const {parseOptionAs2DVector, parseOptionAsFloat, parseOptionAsInt} = require('./optionParsers')
|
3 | const {defaultResolution2D} = require('./constants')
|
4 | const Vector2D = require('./math/Vector2')
|
5 | const Path2D = require('./math/Path2')
|
6 | const {fromCompactBinary} = require('./CAGFactories')
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | const circle = function (options) {
|
16 | options = options || {}
|
17 | let center = parseOptionAs2DVector(options, 'center', [0, 0])
|
18 | let radius = parseOptionAsFloat(options, 'radius', 1)
|
19 | let resolution = parseOptionAsInt(options, 'resolution', defaultResolution2D)
|
20 | let points = []
|
21 | for (let i = 0; i < resolution; i++) {
|
22 | let radians = 2 * Math.PI * i / resolution
|
23 | let point = Vector2D.fromAngleRadians(radians).times(radius).plus(center)
|
24 | points.push(point)
|
25 | }
|
26 | return CAG.fromPoints(points)
|
27 | }
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | const ellipse = function (options) {
|
37 | options = options || {}
|
38 | let c = parseOptionAs2DVector(options, 'center', [0, 0])
|
39 | let r = parseOptionAs2DVector(options, 'radius', [1, 1])
|
40 | r = r.abs()
|
41 | let res = parseOptionAsInt(options, 'resolution', defaultResolution2D)
|
42 |
|
43 | let e2 = new Path2D([[c.x, c.y + r.y]])
|
44 | e2 = e2.appendArc([c.x, c.y - r.y], {
|
45 | xradius: r.x,
|
46 | yradius: r.y,
|
47 | xaxisrotation: 0,
|
48 | resolution: res,
|
49 | clockwise: true,
|
50 | large: false
|
51 | })
|
52 | e2 = e2.appendArc([c.x, c.y + r.y], {
|
53 | xradius: r.x,
|
54 | yradius: r.y,
|
55 | xaxisrotation: 0,
|
56 | resolution: res,
|
57 | clockwise: true,
|
58 | large: false
|
59 | })
|
60 | e2 = e2.close()
|
61 | return CAG.fromPath2(e2)
|
62 | }
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | const rectangle = function (options) {
|
73 | options = options || {}
|
74 | let c, r
|
75 | if (('corner1' in options) || ('corner2' in options)) {
|
76 | if (('center' in options) || ('radius' in options)) {
|
77 | throw new Error('rectangle: should either give a radius and center parameter, or a corner1 and corner2 parameter')
|
78 | }
|
79 | let corner1 = parseOptionAs2DVector(options, 'corner1', [0, 0])
|
80 | let corner2 = parseOptionAs2DVector(options, 'corner2', [1, 1])
|
81 | c = corner1.plus(corner2).times(0.5)
|
82 | r = corner2.minus(corner1).times(0.5)
|
83 | } else {
|
84 | c = parseOptionAs2DVector(options, 'center', [0, 0])
|
85 | r = parseOptionAs2DVector(options, 'radius', [1, 1])
|
86 | }
|
87 | r = r.abs()
|
88 | let rswap = new Vector2D(r.x, -r.y)
|
89 | let points = [
|
90 | c.plus(r), c.plus(rswap), c.minus(r), c.minus(rswap)
|
91 | ]
|
92 | return CAG.fromPoints(points)
|
93 | }
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | const roundedRectangle = function (options) {
|
114 | options = options || {}
|
115 | let center, radius
|
116 | if (('corner1' in options) || ('corner2' in options)) {
|
117 | if (('center' in options) || ('radius' in options)) {
|
118 | throw new Error('roundedRectangle: should either give a radius and center parameter, or a corner1 and corner2 parameter')
|
119 | }
|
120 | let corner1 = parseOptionAs2DVector(options, 'corner1', [0, 0])
|
121 | let corner2 = parseOptionAs2DVector(options, 'corner2', [1, 1])
|
122 | center = corner1.plus(corner2).times(0.5)
|
123 | radius = corner2.minus(corner1).times(0.5)
|
124 | } else {
|
125 | center = parseOptionAs2DVector(options, 'center', [0, 0])
|
126 | radius = parseOptionAs2DVector(options, 'radius', [1, 1])
|
127 | }
|
128 | radius = radius.abs()
|
129 | let roundradius = parseOptionAsFloat(options, 'roundradius', 0.2)
|
130 | let resolution = parseOptionAsInt(options, 'resolution', defaultResolution2D)
|
131 | let maxroundradius = Math.min(radius.x, radius.y)
|
132 | maxroundradius -= 0.1
|
133 | roundradius = Math.min(roundradius, maxroundradius)
|
134 | roundradius = Math.max(0, roundradius)
|
135 | radius = new Vector2D(radius.x - roundradius, radius.y - roundradius)
|
136 | let rect = CAG.rectangle({
|
137 | center: center,
|
138 | radius: radius
|
139 | })
|
140 | if (roundradius > 0) {
|
141 | rect = rect.expand(roundradius, resolution)
|
142 | }
|
143 | return rect
|
144 | }
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | CAG.fromCompactBinary = function (bin) {
|
151 | if (bin['class'] !== 'CAG') throw new Error('Not a CAG')
|
152 | let vertices = []
|
153 | let vertexData = bin.vertexData
|
154 | let numvertices = vertexData.length / 2
|
155 | let arrayindex = 0
|
156 | for (let vertexindex = 0; vertexindex < numvertices; vertexindex++) {
|
157 | let x = vertexData[arrayindex++]
|
158 | let y = vertexData[arrayindex++]
|
159 | let pos = new Vector2D(x, y)
|
160 | let vertex = new CAG.Vertex(pos)
|
161 | vertices.push(vertex)
|
162 | }
|
163 |
|
164 | let sides = []
|
165 | let numsides = bin.sideVertexIndices.length / 2
|
166 | arrayindex = 0
|
167 | for (let sideindex = 0; sideindex < numsides; sideindex++) {
|
168 | let vertexindex0 = bin.sideVertexIndices[arrayindex++]
|
169 | let vertexindex1 = bin.sideVertexIndices[arrayindex++]
|
170 | let side = new CAG.Side(vertices[vertexindex0], vertices[vertexindex1])
|
171 | sides.push(side)
|
172 | }
|
173 | let cag = CAG.fromSides(sides)
|
174 | cag.isCanonicalized = true
|
175 | return cag
|
176 | }
|
177 |
|
178 | module.exports = {
|
179 | circle,
|
180 | ellipse,
|
181 | rectangle,
|
182 | roundedRectangle,
|
183 | fromCompactBinary
|
184 | }
|