altium-toolkit 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/docs/api.md +78 -16
- package/docs/model-format.md +229 -8
- package/docs/schemas/altium_toolkit/netlist_a1.schema.json +47 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +1661 -104
- package/docs/schemas/altium_toolkit/pcb_svg_semantics_a1.schema.json +59 -0
- package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +57 -0
- package/docs/schemas/altium_toolkit/schematic_svg_semantics_a1.schema.json +50 -0
- package/docs/testing.md +9 -3
- package/package.json +1 -1
- package/spec/library-scope.md +7 -1
- package/src/core/altium/AltiumLayoutParser.mjs +104 -8
- package/src/core/altium/AltiumParser.mjs +191 -45
- package/src/core/altium/EmbeddedFileInventoryBuilder.mjs +255 -0
- package/src/core/altium/IntLibModelParser.mjs +240 -0
- package/src/core/altium/IntLibStreamExtractor.mjs +366 -0
- package/src/core/altium/LibraryRenderManifestBuilder.mjs +417 -0
- package/src/core/altium/LibrarySearchIndex.mjs +215 -0
- package/src/core/altium/NormalizedModelSchema.mjs +36 -0
- package/src/core/altium/PcbCustomPadShapeParser.mjs +244 -0
- package/src/core/altium/PcbDefaultsParser.mjs +171 -0
- package/src/core/altium/PcbDimensionParser.mjs +229 -0
- package/src/core/altium/PcbEmbeddedModelExtractor.mjs +232 -6
- package/src/core/altium/PcbExtendedPrimitiveInformationParser.mjs +256 -0
- package/src/core/altium/PcbLibModelParser.mjs +235 -14
- package/src/core/altium/PcbLibStreamExtractor.mjs +62 -4
- package/src/core/altium/PcbMaskPasteResolver.mjs +354 -0
- package/src/core/altium/PcbMechanicalLayerPairParser.mjs +204 -0
- package/src/core/altium/PcbModelParser.mjs +466 -28
- package/src/core/altium/PcbOwnershipGraphBuilder.mjs +245 -0
- package/src/core/altium/PcbPadPrimitiveParser.mjs +78 -65
- package/src/core/altium/PcbPadStackParser.mjs +58 -0
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +217 -0
- package/src/core/altium/PcbPrimitiveParameterParser.mjs +3 -2
- package/src/core/altium/PcbRawRecordRegistry.mjs +121 -130
- package/src/core/altium/PcbRegionPrimitiveParser.mjs +5 -1
- package/src/core/altium/PcbRuleParser.mjs +354 -33
- package/src/core/altium/PcbSidecarRecordParser.mjs +177 -0
- package/src/core/altium/PcbSpecialStringResolver.mjs +220 -0
- package/src/core/altium/PcbStatisticsBuilder.mjs +532 -0
- package/src/core/altium/PcbStreamExtractor.mjs +111 -4
- package/src/core/altium/PcbTextPrimitiveParser.mjs +60 -0
- package/src/core/altium/PcbUnionParser.mjs +307 -0
- package/src/core/altium/PcbViaStackParser.mjs +98 -10
- package/src/core/altium/PcbViaStructureParser.mjs +335 -0
- package/src/core/altium/PrintableTextDecoder.mjs +53 -3
- package/src/core/altium/PrjPcbModelParser.mjs +257 -5
- package/src/core/altium/ProjectAnnotationParser.mjs +205 -0
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +477 -0
- package/src/core/altium/ProjectNetlistExporter.mjs +499 -0
- package/src/core/altium/ProjectOutJobDigestBuilder.mjs +109 -0
- package/src/core/altium/ProjectVariantViewBuilder.mjs +334 -0
- package/src/core/altium/SchematicBindingProvenanceParser.mjs +223 -0
- package/src/core/altium/SchematicComponentOwnerTextResolver.mjs +312 -0
- package/src/core/altium/SchematicComponentTextResolver.mjs +72 -19
- package/src/core/altium/SchematicConnectivityQaBuilder.mjs +271 -0
- package/src/core/altium/SchematicCrossSheetConnectorParser.mjs +140 -0
- package/src/core/altium/SchematicDirectiveParser.mjs +312 -0
- package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +231 -0
- package/src/core/altium/SchematicHarnessParser.mjs +302 -0
- package/src/core/altium/SchematicImageParser.mjs +474 -3
- package/src/core/altium/SchematicImplementationParser.mjs +518 -0
- package/src/core/altium/SchematicNetlistBuilder.mjs +15 -2
- package/src/core/altium/SchematicOwnershipGraphParser.mjs +195 -0
- package/src/core/altium/SchematicPinParser.mjs +84 -1
- package/src/core/altium/SchematicPrimitiveParser.mjs +301 -0
- package/src/core/altium/SchematicProjectParameterResolver.mjs +361 -0
- package/src/core/altium/SchematicQaReportBuilder.mjs +284 -0
- package/src/core/altium/SchematicRecordTypeRegistry.mjs +137 -0
- package/src/core/altium/SchematicRepeatedChannelParser.mjs +229 -0
- package/src/core/altium/SchematicStreamExtractor.mjs +10 -1
- package/src/core/altium/SchematicTemplateParser.mjs +256 -0
- package/src/core/altium/SchematicTextParser.mjs +123 -0
- package/src/core/ole/OleCompoundDocument.mjs +20 -0
- package/src/parser.mjs +29 -0
- package/src/renderers.mjs +3 -0
- package/src/styles/altium-renderers.css +25 -0
- package/src/ui/PcbBarcodeTextRenderer.mjs +436 -0
- package/src/ui/PcbInteractionGeometry.mjs +350 -0
- package/src/ui/PcbInteractionIndex.mjs +593 -0
- package/src/ui/PcbInteractionItemRegistry.mjs +66 -0
- package/src/ui/PcbInteractionLayerModel.mjs +99 -0
- package/src/ui/PcbScene3dBoardOutlineRefiner.mjs +74 -9
- package/src/ui/PcbScene3dBuilder.mjs +169 -7
- package/src/ui/PcbScene3dModelRegistry.mjs +74 -0
- package/src/ui/PcbSvgRenderer.mjs +1187 -34
- package/src/ui/PcbTextPrimitiveRenderer.mjs +193 -7
- package/src/ui/SchematicNoteRenderer.mjs +9 -2
- package/src/ui/SchematicOwnerPinLabelLayout.mjs +206 -0
- package/src/ui/SchematicShapeRenderer.mjs +362 -0
- package/src/ui/SchematicSvgRenderer.mjs +1442 -92
- package/src/ui/SchematicTypography.mjs +48 -5
- package/src/ui/TextGeometrySidecarBuilder.mjs +147 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provides lightweight hit-test geometry helpers for PCB mil coordinates.
|
|
7
|
+
*/
|
|
8
|
+
export class PcbInteractionGeometry {
|
|
9
|
+
/**
|
|
10
|
+
* Builds a circular geometry descriptor.
|
|
11
|
+
* @param {{ x?: unknown, y?: unknown }} center Center point.
|
|
12
|
+
* @param {unknown} radius Radius.
|
|
13
|
+
* @returns {object}
|
|
14
|
+
*/
|
|
15
|
+
static circle(center, radius) {
|
|
16
|
+
return {
|
|
17
|
+
kind: 'circle',
|
|
18
|
+
center: PcbInteractionGeometry.point(center),
|
|
19
|
+
radius: Math.max(0, Number(radius) || 0)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Builds a stroked segment geometry descriptor.
|
|
25
|
+
* @param {{ x?: unknown, y?: unknown }} start Start point.
|
|
26
|
+
* @param {{ x?: unknown, y?: unknown }} end End point.
|
|
27
|
+
* @param {unknown} radius Stroke radius.
|
|
28
|
+
* @returns {object}
|
|
29
|
+
*/
|
|
30
|
+
static segment(start, end, radius = 0) {
|
|
31
|
+
return {
|
|
32
|
+
kind: 'segment',
|
|
33
|
+
start: PcbInteractionGeometry.point(start),
|
|
34
|
+
end: PcbInteractionGeometry.point(end),
|
|
35
|
+
radius: Math.max(0, Number(radius) || 0)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Builds a polygon geometry descriptor.
|
|
41
|
+
* @param {{ x?: unknown, y?: unknown }[]} points Polygon points.
|
|
42
|
+
* @returns {object}
|
|
43
|
+
*/
|
|
44
|
+
static polygon(points) {
|
|
45
|
+
return {
|
|
46
|
+
kind: 'polygon',
|
|
47
|
+
points: (Array.isArray(points) ? points : []).map((point) =>
|
|
48
|
+
PcbInteractionGeometry.point(point)
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Builds an axis-aligned bounds geometry descriptor.
|
|
55
|
+
* @param {object} bounds Bounds.
|
|
56
|
+
* @returns {object}
|
|
57
|
+
*/
|
|
58
|
+
static bounds(bounds) {
|
|
59
|
+
return {
|
|
60
|
+
kind: 'bounds',
|
|
61
|
+
bounds: PcbInteractionGeometry.normalizeBounds(bounds)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Builds a rotated rectangle polygon geometry descriptor.
|
|
67
|
+
* @param {object} rectangle Rectangle.
|
|
68
|
+
* @returns {object}
|
|
69
|
+
*/
|
|
70
|
+
static rotatedRectangle(rectangle) {
|
|
71
|
+
const center = PcbInteractionGeometry.point(rectangle)
|
|
72
|
+
const width = Math.max(0, Number(rectangle?.width) || 0)
|
|
73
|
+
const height = Math.max(0, Number(rectangle?.height) || 0)
|
|
74
|
+
const rotation = (Number(rectangle?.rotation) || 0) * (Math.PI / 180)
|
|
75
|
+
const cos = Math.cos(rotation)
|
|
76
|
+
const sin = Math.sin(rotation)
|
|
77
|
+
const halfWidth = width / 2
|
|
78
|
+
const halfHeight = height / 2
|
|
79
|
+
|
|
80
|
+
return PcbInteractionGeometry.polygon(
|
|
81
|
+
[
|
|
82
|
+
{ x: -halfWidth, y: -halfHeight },
|
|
83
|
+
{ x: halfWidth, y: -halfHeight },
|
|
84
|
+
{ x: halfWidth, y: halfHeight },
|
|
85
|
+
{ x: -halfWidth, y: halfHeight }
|
|
86
|
+
].map((point) => ({
|
|
87
|
+
x: center.x + point.x * cos - point.y * sin,
|
|
88
|
+
y: center.y + point.x * sin + point.y * cos
|
|
89
|
+
}))
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Normalizes a point value.
|
|
95
|
+
* @param {{ x?: unknown, y?: unknown }} point Point-like value.
|
|
96
|
+
* @returns {{ x: number, y: number }}
|
|
97
|
+
*/
|
|
98
|
+
static point(point) {
|
|
99
|
+
return {
|
|
100
|
+
x: Number(point?.x) || 0,
|
|
101
|
+
y: Number(point?.y) || 0
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns bounds for a geometry descriptor.
|
|
107
|
+
* @param {object | null | undefined} geometry Geometry descriptor.
|
|
108
|
+
* @returns {{ minX: number, minY: number, maxX: number, maxY: number, width: number, height: number }}
|
|
109
|
+
*/
|
|
110
|
+
static boundsFor(geometry) {
|
|
111
|
+
if (!geometry || typeof geometry !== 'object') {
|
|
112
|
+
return PcbInteractionGeometry.normalizeBounds(null)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (geometry.kind === 'bounds') {
|
|
116
|
+
return PcbInteractionGeometry.normalizeBounds(geometry.bounds)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const points = PcbInteractionGeometry.#pointsForBounds(geometry)
|
|
120
|
+
if (!points.length) return PcbInteractionGeometry.normalizeBounds(null)
|
|
121
|
+
|
|
122
|
+
const xs = points.map((point) => point.x)
|
|
123
|
+
const ys = points.map((point) => point.y)
|
|
124
|
+
const minX = Math.min(...xs)
|
|
125
|
+
const minY = Math.min(...ys)
|
|
126
|
+
const maxX = Math.max(...xs)
|
|
127
|
+
const maxY = Math.max(...ys)
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
minX,
|
|
131
|
+
minY,
|
|
132
|
+
maxX,
|
|
133
|
+
maxY,
|
|
134
|
+
width: maxX - minX,
|
|
135
|
+
height: maxY - minY
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Normalizes an axis-aligned bounds value.
|
|
141
|
+
* @param {object | null | undefined} bounds Bounds-like value.
|
|
142
|
+
* @returns {{ minX: number, minY: number, maxX: number, maxY: number, width: number, height: number }}
|
|
143
|
+
*/
|
|
144
|
+
static normalizeBounds(bounds) {
|
|
145
|
+
const minX = Number(bounds?.minX) || 0
|
|
146
|
+
const minY = Number(bounds?.minY) || 0
|
|
147
|
+
const width = Number(bounds?.widthMil ?? bounds?.width) || 0
|
|
148
|
+
const height = Number(bounds?.heightMil ?? bounds?.height) || 0
|
|
149
|
+
const maxX = Number.isFinite(Number(bounds?.maxX))
|
|
150
|
+
? Number(bounds.maxX)
|
|
151
|
+
: minX + width
|
|
152
|
+
const maxY = Number.isFinite(Number(bounds?.maxY))
|
|
153
|
+
? Number(bounds.maxY)
|
|
154
|
+
: minY + height
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
minX: Math.min(minX, maxX),
|
|
158
|
+
minY: Math.min(minY, maxY),
|
|
159
|
+
maxX: Math.max(minX, maxX),
|
|
160
|
+
maxY: Math.max(minY, maxY),
|
|
161
|
+
width: Math.abs(maxX - minX),
|
|
162
|
+
height: Math.abs(maxY - minY)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns whether a point intersects a geometry descriptor.
|
|
168
|
+
* @param {object | null | undefined} geometry Geometry descriptor.
|
|
169
|
+
* @param {{ x?: unknown, y?: unknown }} point Point-like value.
|
|
170
|
+
* @param {number} [tolerance] Extra tolerance.
|
|
171
|
+
* @returns {boolean}
|
|
172
|
+
*/
|
|
173
|
+
static containsPoint(geometry, point, tolerance = 0) {
|
|
174
|
+
const normalizedPoint = PcbInteractionGeometry.point(point)
|
|
175
|
+
if (!geometry || typeof geometry !== 'object') return false
|
|
176
|
+
|
|
177
|
+
if (
|
|
178
|
+
!PcbInteractionGeometry.#boundsContainsPoint(
|
|
179
|
+
PcbInteractionGeometry.boundsFor(geometry),
|
|
180
|
+
normalizedPoint,
|
|
181
|
+
tolerance
|
|
182
|
+
)
|
|
183
|
+
) {
|
|
184
|
+
return false
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (geometry.kind === 'circle') {
|
|
188
|
+
return (
|
|
189
|
+
PcbInteractionGeometry.#distance(
|
|
190
|
+
normalizedPoint,
|
|
191
|
+
geometry.center
|
|
192
|
+
) <=
|
|
193
|
+
(Number(geometry.radius) || 0) + tolerance
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (geometry.kind === 'segment') {
|
|
198
|
+
return (
|
|
199
|
+
PcbInteractionGeometry.#pointToSegmentDistance(
|
|
200
|
+
normalizedPoint,
|
|
201
|
+
geometry.start,
|
|
202
|
+
geometry.end
|
|
203
|
+
) <=
|
|
204
|
+
(Number(geometry.radius) || 0) + tolerance
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (geometry.kind === 'polygon') {
|
|
209
|
+
return PcbInteractionGeometry.#pointInPolygon(
|
|
210
|
+
normalizedPoint,
|
|
211
|
+
geometry.points || []
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (geometry.kind === 'bounds') {
|
|
216
|
+
return true
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Returns geometry points used for bounds calculations.
|
|
224
|
+
* @param {object} geometry Geometry descriptor.
|
|
225
|
+
* @returns {{ x: number, y: number }[]}
|
|
226
|
+
*/
|
|
227
|
+
static #pointsForBounds(geometry) {
|
|
228
|
+
if (geometry.kind === 'circle') {
|
|
229
|
+
const center = PcbInteractionGeometry.point(geometry.center)
|
|
230
|
+
const radius = Math.max(0, Number(geometry.radius) || 0)
|
|
231
|
+
return [
|
|
232
|
+
{ x: center.x - radius, y: center.y - radius },
|
|
233
|
+
{ x: center.x + radius, y: center.y + radius }
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (geometry.kind === 'segment') {
|
|
238
|
+
const start = PcbInteractionGeometry.point(geometry.start)
|
|
239
|
+
const end = PcbInteractionGeometry.point(geometry.end)
|
|
240
|
+
const radius = Math.max(0, Number(geometry.radius) || 0)
|
|
241
|
+
return [
|
|
242
|
+
{
|
|
243
|
+
x: Math.min(start.x, end.x) - radius,
|
|
244
|
+
y: Math.min(start.y, end.y) - radius
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
x: Math.max(start.x, end.x) + radius,
|
|
248
|
+
y: Math.max(start.y, end.y) + radius
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (geometry.kind === 'polygon') {
|
|
254
|
+
return (Array.isArray(geometry.points) ? geometry.points : []).map(
|
|
255
|
+
(point) => PcbInteractionGeometry.point(point)
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return []
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Returns whether bounds contain a point.
|
|
264
|
+
* @param {object} bounds Bounds.
|
|
265
|
+
* @param {{ x: number, y: number }} point Point.
|
|
266
|
+
* @param {number} tolerance Tolerance.
|
|
267
|
+
* @returns {boolean}
|
|
268
|
+
*/
|
|
269
|
+
static #boundsContainsPoint(bounds, point, tolerance) {
|
|
270
|
+
return (
|
|
271
|
+
point.x >= bounds.minX - tolerance &&
|
|
272
|
+
point.x <= bounds.maxX + tolerance &&
|
|
273
|
+
point.y >= bounds.minY - tolerance &&
|
|
274
|
+
point.y <= bounds.maxY + tolerance
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Returns Euclidean distance between two points.
|
|
280
|
+
* @param {{ x?: unknown, y?: unknown }} first First point.
|
|
281
|
+
* @param {{ x?: unknown, y?: unknown }} second Second point.
|
|
282
|
+
* @returns {number}
|
|
283
|
+
*/
|
|
284
|
+
static #distance(first, second) {
|
|
285
|
+
const a = PcbInteractionGeometry.point(first)
|
|
286
|
+
const b = PcbInteractionGeometry.point(second)
|
|
287
|
+
return Math.hypot(a.x - b.x, a.y - b.y)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Computes point-to-segment distance.
|
|
292
|
+
* @param {{ x: number, y: number }} point Point.
|
|
293
|
+
* @param {{ x?: unknown, y?: unknown }} start Segment start.
|
|
294
|
+
* @param {{ x?: unknown, y?: unknown }} end Segment end.
|
|
295
|
+
* @returns {number}
|
|
296
|
+
*/
|
|
297
|
+
static #pointToSegmentDistance(point, start, end) {
|
|
298
|
+
const first = PcbInteractionGeometry.point(start)
|
|
299
|
+
const second = PcbInteractionGeometry.point(end)
|
|
300
|
+
const dx = second.x - first.x
|
|
301
|
+
const dy = second.y - first.y
|
|
302
|
+
const lengthSquared = dx * dx + dy * dy
|
|
303
|
+
if (lengthSquared === 0) {
|
|
304
|
+
return PcbInteractionGeometry.#distance(point, first)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const t = Math.max(
|
|
308
|
+
0,
|
|
309
|
+
Math.min(
|
|
310
|
+
1,
|
|
311
|
+
((point.x - first.x) * dx + (point.y - first.y) * dy) /
|
|
312
|
+
lengthSquared
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
return PcbInteractionGeometry.#distance(point, {
|
|
316
|
+
x: first.x + t * dx,
|
|
317
|
+
y: first.y + t * dy
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Returns whether a point is inside a polygon.
|
|
323
|
+
* @param {{ x: number, y: number }} point Point.
|
|
324
|
+
* @param {{ x?: unknown, y?: unknown }[]} polygon Polygon.
|
|
325
|
+
* @returns {boolean}
|
|
326
|
+
*/
|
|
327
|
+
static #pointInPolygon(point, polygon) {
|
|
328
|
+
const points = (Array.isArray(polygon) ? polygon : []).map((entry) =>
|
|
329
|
+
PcbInteractionGeometry.point(entry)
|
|
330
|
+
)
|
|
331
|
+
let inside = false
|
|
332
|
+
for (
|
|
333
|
+
let index = 0, previous = points.length - 1;
|
|
334
|
+
index < points.length;
|
|
335
|
+
previous = index++
|
|
336
|
+
) {
|
|
337
|
+
const current = points[index]
|
|
338
|
+
const before = points[previous]
|
|
339
|
+
const intersects =
|
|
340
|
+
current.y > point.y !== before.y > point.y &&
|
|
341
|
+
point.x <
|
|
342
|
+
((before.x - current.x) * (point.y - current.y)) /
|
|
343
|
+
(before.y - current.y || Number.EPSILON) +
|
|
344
|
+
current.x
|
|
345
|
+
if (intersects) inside = !inside
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return inside
|
|
349
|
+
}
|
|
350
|
+
}
|