UNPKG

6.82 kBJavaScriptView Raw
1const CAG = require('./CAG')
2const {parseOptionAs2DVector, parseOptionAsFloat, parseOptionAsInt} = require('./optionParsers')
3const {defaultResolution2D} = require('./constants')
4const Vector2D = require('./math/Vector2')
5const Path2D = require('./math/Path2')
6const {fromCompactBinary} = require('./CAGFactories')
7
8/** Construct a circle.
9 * @param {Object} [options] - options for construction
10 * @param {Vector2D} [options.center=[0,0]] - center of circle
11 * @param {Number} [options.radius=1] - radius of circle
12 * @param {Number} [options.resolution=defaultResolution2D] - number of sides per 360 rotation
13 * @returns {CAG} new CAG object
14 */
15const 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/** Construct an ellispe.
30 * @param {Object} [options] - options for construction
31 * @param {Vector2D} [options.center=[0,0]] - center of ellipse
32 * @param {Vector2D} [options.radius=[1,1]] - radius of ellipse, width and height
33 * @param {Number} [options.resolution=defaultResolution2D] - number of sides per 360 rotation
34 * @returns {CAG} new CAG object
35 */
36const 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() // negative radii make no sense
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/** Construct a rectangle.
65 * @param {Object} [options] - options for construction
66 * @param {Vector2D} [options.center=[0,0]] - center of rectangle
67 * @param {Vector2D} [options.radius=[1,1]] - radius of rectangle, width and height
68 * @param {Vector2D} [options.corner1=[0,0]] - bottom left corner of rectangle (alternate)
69 * @param {Vector2D} [options.corner2=[0,0]] - upper right corner of rectangle (alternate)
70 * @returns {CAG} new CAG object
71 */
72const 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() // negative radii make no sense
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/** Construct a rounded rectangle.
96 * @param {Object} [options] - options for construction
97 * @param {Vector2D} [options.center=[0,0]] - center of rounded rectangle
98 * @param {Vector2D} [options.radius=[1,1]] - radius of rounded rectangle, width and height
99 * @param {Vector2D} [options.corner1=[0,0]] - bottom left corner of rounded rectangle (alternate)
100 * @param {Vector2D} [options.corner2=[0,0]] - upper right corner of rounded rectangle (alternate)
101 * @param {Number} [options.roundradius=0.2] - round radius of corners
102 * @param {Number} [options.resolution=defaultResolution2D] - number of sides per 360 rotation
103 * @returns {CAG} new CAG object
104 *
105 * @example
106 * let r = roundedRectangle({
107 * center: [0, 0],
108 * radius: [5, 10],
109 * roundradius: 2,
110 * resolution: 36,
111 * });
112 */
113const 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() // negative radii make no sense
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/** Reconstruct a CAG from the output of toCompactBinary().
147 * @param {CompactBinary} bin - see toCompactBinary()
148 * @returns {CAG} new CAG object
149 */
150CAG.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
178module.exports = {
179 circle,
180 ellipse,
181 rectangle,
182 roundedRectangle,
183 fromCompactBinary
184}