circuitjson-toolkit 1.0.17 → 1.1.0
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/NOTICE.md +31 -0
- package/README.md +267 -107
- package/docs/api.md +501 -96
- package/docs/capabilities.md +70 -0
- package/docs/migration/behaviors.md +45 -0
- package/docs/migration/parser.md +60 -0
- package/docs/migration/renderers.md +515 -0
- package/docs/migration/root.md +740 -0
- package/docs/migration.md +120 -0
- package/docs/model-format.md +175 -57
- package/docs/provenance.md +206 -0
- package/docs/release-notes-v1.1.0.md +154 -0
- package/docs/testing.md +117 -7
- package/package.json +31 -5
- package/spec/api-baseline-v1.0.17.json +1 -0
- package/spec/baseline-provenance-v1.0.17.json +7 -0
- package/spec/circuitjson-schema-snapshot.json +321 -0
- package/spec/circuitjson-schema-source.json +28 -0
- package/spec/feature-preservation.json +1 -0
- package/spec/library-scope.md +27 -20
- package/src/capabilities.mjs +1 -0
- package/src/core/ArchiveEntryPath.mjs +93 -0
- package/src/core/ArchiveLimits.mjs +31 -0
- package/src/core/ArchiveLimitsValidator.mjs +107 -0
- package/src/core/AsyncInputOwnership.mjs +56 -0
- package/src/core/AttachedValueLimits.mjs +67 -0
- package/src/core/CircuitJsonDiagnosticIndexer.mjs +184 -0
- package/src/core/CircuitJsonDocument.mjs +19 -61
- package/src/core/CircuitJsonElementTypes.mjs +10 -0
- package/src/core/CircuitJsonElementValidator.mjs +98 -847
- package/src/core/CircuitJsonIndexer.mjs +274 -194
- package/src/core/CircuitJsonManufacturingBuilder.mjs +167 -164
- package/src/core/CircuitJsonParser.mjs +75 -13
- package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +12 -6
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +108 -10
- package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +1 -1
- package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +44 -38
- package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +146 -28
- package/src/core/CircuitJsonPcbPrimitiveFields.mjs +70 -4
- package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +18 -2
- package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +26 -9
- package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +7 -6
- package/src/core/CircuitJsonSerializedInputAudit.mjs +87 -0
- package/src/core/CircuitJsonSourceMetadata.mjs +5 -1
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +3 -1
- package/src/core/CircuitJsonToolkitElementSchema.mjs +218 -0
- package/src/core/CircuitJsonUnitParsers.mjs +101 -0
- package/src/core/CircuitJsonUnits.mjs +13 -87
- package/src/core/CircuitJsonUpstreamSchema.mjs +9 -0
- package/src/core/CircuitJsonUpstreamValidator.mjs +418 -0
- package/src/core/CircuitJsonValidationUnits.mjs +6 -0
- package/src/core/ManufacturingService.mjs +323 -0
- package/src/core/Parser.mjs +343 -0
- package/src/core/ParserOptions.mjs +333 -0
- package/src/core/PcbBoundsSelectionModel.mjs +55 -19
- package/src/core/PcbDiagnosticFocusModel.mjs +42 -11
- package/src/core/PcbInteractionIndex.mjs +368 -0
- package/src/core/PcbInteractionPrimitiveModel.mjs +393 -62
- package/src/core/ProjectAsyncInputOwner.mjs +70 -0
- package/src/core/ProjectLoader.mjs +975 -0
- package/src/core/SimulationService.mjs +790 -0
- package/src/core/ToolkitCapabilities.mjs +130 -0
- package/src/core/ZipArchiveInspector.mjs +649 -0
- package/src/core/context/BinaryDataSnapshot.mjs +217 -0
- package/src/core/context/CircuitJsonContextIndexes.mjs +96 -0
- package/src/core/context/CircuitJsonDerivedCache.mjs +114 -0
- package/src/core/context/CircuitJsonDocumentContext.mjs +353 -0
- package/src/core/context/CircuitJsonLegacyModel.mjs +147 -0
- package/src/core/context/CircuitJsonLegacyNormalizer.mjs +847 -0
- package/src/core/context/CircuitJsonMetadataBoundary.mjs +76 -0
- package/src/core/context/CircuitJsonModelFreezeTraversal.mjs +179 -0
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +920 -0
- package/src/core/context/CircuitJsonSchematicTableNormalizer.mjs +314 -0
- package/src/core/context/CircuitJsonValidationAuthority.mjs +39 -0
- package/src/core/context/CircuitJsonValidationProof.mjs +217 -0
- package/src/core/context/PcbPrimitivePreparation.mjs +198 -0
- package/src/core/context/PcbSpatialIndex.mjs +701 -0
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +128 -0
- package/src/core/context/StructuredDataSnapshot.mjs +683 -0
- package/src/core/contracts/DocumentResult.mjs +198 -0
- package/src/core/contracts/ProjectResult.mjs +96 -0
- package/src/core/contracts/RuntimeProxyBoundary.mjs +48 -0
- package/src/core/contracts/ToolkitAsset.mjs +493 -0
- package/src/core/contracts/ToolkitDiagnostic.mjs +38 -0
- package/src/core/contracts/ToolkitError.mjs +176 -0
- package/src/core/contracts/ToolkitProgress.mjs +89 -0
- package/src/core/interaction/CanonicalInteractionOptions.mjs +246 -0
- package/src/core/interaction/PcbInteractionBounds.mjs +167 -0
- package/src/core/query/CircuitTraversal.mjs +343 -0
- package/src/core/query/ComponentGrouping.mjs +275 -0
- package/src/core/query/QueryNetlistBuilder.mjs +306 -0
- package/src/core/query/QueryService.mjs +435 -0
- package/src/core/query/RegexPattern.mjs +75 -0
- package/src/core/rendering/CanonicalBomOrder.mjs +81 -0
- package/src/core/rendering/CanonicalBomRows.mjs +92 -0
- package/src/core/rendering/CanonicalRenderOptions.mjs +498 -0
- package/src/core/rendering/CanonicalSvgDocument.mjs +102 -0
- package/src/core/rendering/PcbRenderPlan.mjs +429 -0
- package/src/core/rendering/SchematicSheetSelector.mjs +335 -0
- package/src/core/scene3d/PcbScene3dBuilder.mjs +906 -0
- package/src/core/scene3d/PcbScene3dPreparator.mjs +47 -0
- package/src/core/scene3d/Scene3dAssetIndex.mjs +284 -0
- package/src/core/scene3d/Scene3dBoardModel.mjs +596 -0
- package/src/core/scene3d/Scene3dDocumentMetadata.mjs +167 -0
- package/src/core/scene3d/Scene3dFreeze.mjs +37 -0
- package/src/core/scene3d/Scene3dIdRegistry.mjs +34 -0
- package/src/core/scene3d/Scene3dInputPreflight.mjs +193 -0
- package/src/core/scene3d/Scene3dMaterials.mjs +58 -0
- package/src/core/scene3d/Scene3dModelReference.mjs +134 -0
- package/src/core/scene3d/Scene3dOptions.mjs +363 -0
- package/src/core/scene3d/SceneAssetResolver.mjs +441 -0
- package/src/core/simulation/SimulationParameterCloner.mjs +543 -0
- package/src/core/worker/ParserWorkerClient.mjs +997 -0
- package/src/core/worker/ToolkitWorkerProtocol.mjs +412 -0
- package/src/core/worker/WorkerRequestData.mjs +623 -0
- package/src/core/worker/WorkerResponseData.mjs +905 -0
- package/src/extensions.mjs +37 -0
- package/src/index.mjs +14 -9
- package/src/interaction.mjs +2 -0
- package/src/manufacturing.mjs +1 -0
- package/src/parser.mjs +12 -2
- package/src/project.mjs +5 -0
- package/src/query.mjs +1 -0
- package/src/renderers.mjs +3 -29
- package/src/scene3d.mjs +3 -0
- package/src/simulation.mjs +1 -0
- package/src/styles/renderers.css +24 -0
- package/src/testing/ToolkitContractFixtures.mjs +124 -0
- package/src/testing/ToolkitLoopbackWorker.mjs +174 -0
- package/src/testing/runToolkitContract.mjs +705 -0
- package/src/testing.mjs +3 -0
- package/src/ui/BomTableRenderer.mjs +304 -0
- package/src/ui/CircuitJsonPcbBoardSvgRenderer.mjs +80 -0
- package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +3 -5
- package/src/ui/CircuitJsonPcbSvgRenderer.mjs +63 -43
- package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +3 -5
- package/src/ui/CircuitJsonSchematicDebugRenderer.mjs +164 -0
- package/src/ui/CircuitJsonSchematicImageSvgRenderer.mjs +210 -0
- package/src/ui/CircuitJsonSchematicLineRenderer.mjs +86 -0
- package/src/ui/CircuitJsonSchematicSheetSymbolSvgRenderer.mjs +98 -0
- package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +117 -17
- package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +67 -20
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +45 -9
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +151 -148
- package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +4 -292
- package/src/ui/PcbSvgRenderer.mjs +41 -0
- package/src/ui/SafeSvgPaint.mjs +26 -0
- package/src/ui/SafeXmlText.mjs +60 -0
- package/src/ui/SchematicGeometryBounds.mjs +540 -0
- package/src/ui/SchematicSvgRenderer.mjs +110 -0
- package/src/ui/SchematicTableGeometry.mjs +319 -0
- package/src/ui/SchematicTextAnchor.mjs +55 -0
- package/src/ui/SchematicTextBounds.mjs +98 -0
- package/src/workers/parser.worker.mjs +59 -0
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { CircuitJsonIndexer } from '../core/CircuitJsonIndexer.mjs'
|
|
2
2
|
import { CircuitJsonUnits } from '../core/CircuitJsonUnits.mjs'
|
|
3
|
+
import { CircuitJsonLegacyNormalizer } from '../core/context/CircuitJsonLegacyNormalizer.mjs'
|
|
3
4
|
import { CircuitJsonSchematicSvgArcPath } from './CircuitJsonSchematicSvgArcPath.mjs'
|
|
5
|
+
import { CircuitJsonSchematicDebugRenderer } from './CircuitJsonSchematicDebugRenderer.mjs'
|
|
6
|
+
import { CircuitJsonSchematicLineRenderer } from './CircuitJsonSchematicLineRenderer.mjs'
|
|
7
|
+
import { CircuitJsonSchematicImageSvgRenderer } from './CircuitJsonSchematicImageSvgRenderer.mjs'
|
|
8
|
+
import { CircuitJsonSchematicSheetSymbolSvgRenderer } from './CircuitJsonSchematicSheetSymbolSvgRenderer.mjs'
|
|
4
9
|
import { CircuitJsonSchematicSvgPortMetadata } from './CircuitJsonSchematicSvgPortMetadata.mjs'
|
|
5
10
|
import { CircuitJsonSchematicSvgPrimitiveAttributes } from './CircuitJsonSchematicSvgPrimitiveAttributes.mjs'
|
|
6
11
|
import { CircuitJsonSchematicTableSvgRenderer } from './CircuitJsonSchematicTableSvgRenderer.mjs'
|
|
12
|
+
import { SchematicGeometryBounds } from './SchematicGeometryBounds.mjs'
|
|
13
|
+
import { SafeXmlText } from './SafeXmlText.mjs'
|
|
14
|
+
import { SchematicTextAnchor } from './SchematicTextAnchor.mjs'
|
|
7
15
|
|
|
8
16
|
/**
|
|
9
17
|
* Renders standards-shaped schematic element arrays into app-compatible SVG.
|
|
@@ -12,26 +20,53 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
12
20
|
/**
|
|
13
21
|
* Renders one schematic document into SVG markup.
|
|
14
22
|
* @param {object | object[]} documentModel Parsed document model.
|
|
23
|
+
* @param {{ assets?: object[] }} [options] Render resources.
|
|
15
24
|
* @returns {string}
|
|
16
25
|
*/
|
|
17
|
-
static render(documentModel) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
static render(documentModel, options = {}) {
|
|
27
|
+
const prepared = documentModel?.elementsByType instanceof Map
|
|
28
|
+
const index = prepared
|
|
29
|
+
? documentModel
|
|
30
|
+
: CircuitJsonIndexer.index(
|
|
31
|
+
CircuitJsonLegacyNormalizer.normalize(
|
|
32
|
+
CircuitJsonSchematicSvgRenderer.#elements(documentModel)
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
const sourcePorts = CircuitJsonSchematicSvgPortMetadata.sourcePorts(
|
|
36
|
+
CircuitJsonSchematicSvgRenderer.#all(index, 'source_port')
|
|
37
|
+
)
|
|
38
|
+
const portHintCache = new WeakMap()
|
|
39
|
+
const bounds = prepared
|
|
40
|
+
? SchematicGeometryBounds.resolve(index, sourcePorts, portHintCache)
|
|
41
|
+
: SchematicGeometryBounds.legacy(index)
|
|
22
42
|
const viewBox = CircuitJsonSchematicSvgRenderer.#viewBox(bounds)
|
|
23
|
-
|
|
24
43
|
return (
|
|
25
|
-
'<svg class="schematic-svg schematic-svg--circuitjson" xmlns="http://www.w3.org/2000/svg" role="img"
|
|
44
|
+
'<svg class="schematic-svg schematic-svg--circuitjson" xmlns="http://www.w3.org/2000/svg" role="img"' +
|
|
45
|
+
(prepared ? ' font-size="1"' : '') +
|
|
46
|
+
' viewBox="' +
|
|
26
47
|
CircuitJsonSchematicSvgRenderer.#formatViewBox(viewBox) +
|
|
27
48
|
'">' +
|
|
28
49
|
CircuitJsonSchematicSvgRenderer.#renderSheet(bounds) +
|
|
50
|
+
CircuitJsonSchematicImageSvgRenderer.render(
|
|
51
|
+
CircuitJsonSchematicSvgRenderer.#all(index, 'schematic_image'),
|
|
52
|
+
options.assets || documentModel?.assets || []
|
|
53
|
+
) +
|
|
29
54
|
CircuitJsonSchematicSvgRenderer.#renderComponents(index) +
|
|
30
55
|
CircuitJsonSchematicSvgRenderer.#renderSymbols(index) +
|
|
31
56
|
CircuitJsonSchematicSvgRenderer.#renderGroups(index) +
|
|
57
|
+
CircuitJsonSchematicSheetSymbolSvgRenderer.render(
|
|
58
|
+
CircuitJsonSchematicSvgRenderer.#all(
|
|
59
|
+
index,
|
|
60
|
+
'schematic_sheet_symbol'
|
|
61
|
+
)
|
|
62
|
+
) +
|
|
32
63
|
CircuitJsonSchematicSvgRenderer.#renderLines(index) +
|
|
33
64
|
CircuitJsonSchematicSvgRenderer.#renderShapes(index) +
|
|
34
|
-
CircuitJsonSchematicSvgRenderer.#renderPorts(
|
|
65
|
+
CircuitJsonSchematicSvgRenderer.#renderPorts(
|
|
66
|
+
index,
|
|
67
|
+
sourcePorts,
|
|
68
|
+
portHintCache
|
|
69
|
+
) +
|
|
35
70
|
CircuitJsonSchematicSvgRenderer.#renderTables(index) +
|
|
36
71
|
CircuitJsonSchematicSvgRenderer.#renderTexts(index) +
|
|
37
72
|
CircuitJsonSchematicSvgRenderer.#renderProbes(index) +
|
|
@@ -75,11 +110,7 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
75
110
|
)
|
|
76
111
|
}
|
|
77
112
|
|
|
78
|
-
/**
|
|
79
|
-
* Renders schematic component boxes.
|
|
80
|
-
* @param {{ elementsByType: Map<string, object[]> }} index Element index.
|
|
81
|
-
* @returns {string}
|
|
82
|
-
*/
|
|
113
|
+
/** @param {{ elementsByType: Map<string, object[]> }} index Element index. @returns {string} Schematic component markup. */
|
|
83
114
|
static #renderComponents(index) {
|
|
84
115
|
const sourceNames = CircuitJsonSchematicSvgRenderer.#sourceNames(index)
|
|
85
116
|
const components = CircuitJsonSchematicSvgRenderer.#all(
|
|
@@ -101,6 +132,16 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
101
132
|
String(component.name || 'U' + (componentIndex + 1))
|
|
102
133
|
const x = center.x - size.width / 2
|
|
103
134
|
const y = center.y - size.height / 2
|
|
135
|
+
const label =
|
|
136
|
+
component.show_label === false
|
|
137
|
+
? ''
|
|
138
|
+
: '<text class="schematic-component__label" x="' +
|
|
139
|
+
CircuitJsonSchematicSvgRenderer.#formatNumber(center.x) +
|
|
140
|
+
'" y="' +
|
|
141
|
+
CircuitJsonSchematicSvgRenderer.#formatNumber(center.y) +
|
|
142
|
+
'" text-anchor="middle" dominant-baseline="central">' +
|
|
143
|
+
CircuitJsonSchematicSvgRenderer.#escapeHtml(key) +
|
|
144
|
+
'</text>'
|
|
104
145
|
|
|
105
146
|
return (
|
|
106
147
|
'<g class="schematic-component" data-component-key="' +
|
|
@@ -118,13 +159,9 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
118
159
|
CircuitJsonSchematicSvgRenderer.#formatNumber(size.width) +
|
|
119
160
|
'" height="' +
|
|
120
161
|
CircuitJsonSchematicSvgRenderer.#formatNumber(size.height) +
|
|
121
|
-
'"></rect
|
|
122
|
-
|
|
123
|
-
'
|
|
124
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(center.y) +
|
|
125
|
-
'" text-anchor="middle" dominant-baseline="central">' +
|
|
126
|
-
CircuitJsonSchematicSvgRenderer.#escapeHtml(key) +
|
|
127
|
-
'</text></g>'
|
|
162
|
+
'"></rect>' +
|
|
163
|
+
label +
|
|
164
|
+
'</g>'
|
|
128
165
|
)
|
|
129
166
|
})
|
|
130
167
|
|
|
@@ -256,8 +293,8 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
256
293
|
.flatMap((type) =>
|
|
257
294
|
CircuitJsonSchematicSvgRenderer.#all(index, type)
|
|
258
295
|
)
|
|
259
|
-
.
|
|
260
|
-
|
|
296
|
+
.flatMap((element) =>
|
|
297
|
+
CircuitJsonSchematicLineRenderer.render(element)
|
|
261
298
|
)
|
|
262
299
|
.filter(Boolean)
|
|
263
300
|
return lines.length
|
|
@@ -265,38 +302,6 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
265
302
|
: ''
|
|
266
303
|
}
|
|
267
304
|
|
|
268
|
-
/**
|
|
269
|
-
* Renders one schematic line.
|
|
270
|
-
* @param {object} element Source element.
|
|
271
|
-
* @returns {string}
|
|
272
|
-
*/
|
|
273
|
-
static #lineElement(element) {
|
|
274
|
-
const start = CircuitJsonSchematicSvgRenderer.#point({
|
|
275
|
-
x: element.x1 ?? element.start?.x,
|
|
276
|
-
y: element.y1 ?? element.start?.y
|
|
277
|
-
})
|
|
278
|
-
const end = CircuitJsonSchematicSvgRenderer.#point({
|
|
279
|
-
x: element.x2 ?? element.end?.x,
|
|
280
|
-
y: element.y2 ?? element.end?.y
|
|
281
|
-
})
|
|
282
|
-
if (!start || !end) return ''
|
|
283
|
-
return (
|
|
284
|
-
'<line class="schematic-wire" x1="' +
|
|
285
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(start.x) +
|
|
286
|
-
'" y1="' +
|
|
287
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(start.y) +
|
|
288
|
-
'" x2="' +
|
|
289
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(end.x) +
|
|
290
|
-
'" y2="' +
|
|
291
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(end.y) +
|
|
292
|
-
'"' +
|
|
293
|
-
CircuitJsonSchematicSvgPrimitiveAttributes.attributes(element, {
|
|
294
|
-
fill: false
|
|
295
|
-
}) +
|
|
296
|
-
'></line>'
|
|
297
|
-
)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
305
|
/**
|
|
301
306
|
* Renders schematic rectangle and circle shapes.
|
|
302
307
|
* @param {{ elementsByType: Map<string, object[]> }} index Element index.
|
|
@@ -418,11 +423,13 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
418
423
|
const radius = CircuitJsonUnits.optionalLength(element.radius)
|
|
419
424
|
if (!center || radius === null) return ''
|
|
420
425
|
const startAngle = CircuitJsonUnits.angle(
|
|
421
|
-
element.
|
|
426
|
+
element.start_angle_degrees ??
|
|
427
|
+
element.start_angle ??
|
|
428
|
+
element.startAngle,
|
|
422
429
|
0
|
|
423
430
|
)
|
|
424
431
|
const endAngle = CircuitJsonUnits.angle(
|
|
425
|
-
element.end_angle ?? element.endAngle,
|
|
432
|
+
element.end_angle_degrees ?? element.end_angle ?? element.endAngle,
|
|
426
433
|
360
|
|
427
434
|
)
|
|
428
435
|
const start = CircuitJsonSchematicSvgRenderer.#polarPoint(
|
|
@@ -435,23 +442,56 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
435
442
|
radius,
|
|
436
443
|
endAngle
|
|
437
444
|
)
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
+
const standardAngles =
|
|
446
|
+
element.start_angle_degrees !== undefined ||
|
|
447
|
+
element.end_angle_degrees !== undefined
|
|
448
|
+
const sweep =
|
|
449
|
+
element.direction === 'clockwise'
|
|
450
|
+
? 1
|
|
451
|
+
: element.direction === 'counterclockwise' || standardAngles
|
|
452
|
+
? 0
|
|
453
|
+
: 1
|
|
454
|
+
const span = CircuitJsonSchematicSvgRenderer.#directedAngleSpan(
|
|
455
|
+
startAngle,
|
|
456
|
+
endAngle,
|
|
457
|
+
sweep
|
|
458
|
+
)
|
|
459
|
+
const largeArc = span > 180 ? 1 : 0
|
|
460
|
+
const midpoint = CircuitJsonSchematicSvgRenderer.#polarPoint(
|
|
461
|
+
center,
|
|
462
|
+
radius,
|
|
463
|
+
startAngle + (sweep ? 180 : -180)
|
|
464
|
+
)
|
|
465
|
+
/**
|
|
466
|
+
* Builds one SVG arc command for the resolved circle.
|
|
467
|
+
* @param {{ x: number, y: number }} point Command endpoint.
|
|
468
|
+
* @param {boolean} [fullTurn] Whether this is half of a full turn.
|
|
469
|
+
* @returns {string} SVG path command.
|
|
470
|
+
*/
|
|
471
|
+
const arcEnd = (point, fullTurn = false) =>
|
|
445
472
|
' A ' +
|
|
446
473
|
CircuitJsonSchematicSvgRenderer.#formatNumber(radius) +
|
|
447
474
|
' ' +
|
|
448
475
|
CircuitJsonSchematicSvgRenderer.#formatNumber(radius) +
|
|
449
476
|
' 0 ' +
|
|
450
|
-
largeArc +
|
|
451
|
-
' 1 ' +
|
|
452
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(end.x) +
|
|
477
|
+
(fullTurn ? 0 : largeArc) +
|
|
453
478
|
' ' +
|
|
454
|
-
|
|
479
|
+
sweep +
|
|
480
|
+
' ' +
|
|
481
|
+
CircuitJsonSchematicSvgRenderer.#formatNumber(point.x) +
|
|
482
|
+
' ' +
|
|
483
|
+
CircuitJsonSchematicSvgRenderer.#formatNumber(point.y)
|
|
484
|
+
const path =
|
|
485
|
+
span === 360
|
|
486
|
+
? arcEnd(midpoint, true) + arcEnd(start, true)
|
|
487
|
+
: arcEnd(end)
|
|
488
|
+
|
|
489
|
+
return (
|
|
490
|
+
'<path class="schematic-shape schematic-shape--arc" d="M ' +
|
|
491
|
+
CircuitJsonSchematicSvgRenderer.#formatNumber(start.x) +
|
|
492
|
+
' ' +
|
|
493
|
+
CircuitJsonSchematicSvgRenderer.#formatNumber(start.y) +
|
|
494
|
+
path +
|
|
455
495
|
'"' +
|
|
456
496
|
CircuitJsonSchematicSvgPrimitiveAttributes.attributes(element, {
|
|
457
497
|
fill: false
|
|
@@ -487,12 +527,11 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
487
527
|
/**
|
|
488
528
|
* Renders schematic ports.
|
|
489
529
|
* @param {{ elementsByType: Map<string, object[]> }} index Element index.
|
|
530
|
+
* @param {Map<string, object>} sourcePorts Source-port lookup.
|
|
531
|
+
* @param {WeakMap<object, string>} portHintCache Render-scoped hint cache.
|
|
490
532
|
* @returns {string}
|
|
491
533
|
*/
|
|
492
|
-
static #renderPorts(index) {
|
|
493
|
-
const sourcePorts = CircuitJsonSchematicSvgPortMetadata.sourcePorts(
|
|
494
|
-
CircuitJsonSchematicSvgRenderer.#all(index, 'source_port')
|
|
495
|
-
)
|
|
534
|
+
static #renderPorts(index, sourcePorts, portHintCache) {
|
|
496
535
|
const ports = CircuitJsonSchematicSvgRenderer.#all(
|
|
497
536
|
index,
|
|
498
537
|
'schematic_port'
|
|
@@ -500,7 +539,8 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
500
539
|
.map((element) =>
|
|
501
540
|
CircuitJsonSchematicSvgRenderer.#portElement(
|
|
502
541
|
element,
|
|
503
|
-
sourcePorts
|
|
542
|
+
sourcePorts,
|
|
543
|
+
portHintCache
|
|
504
544
|
)
|
|
505
545
|
)
|
|
506
546
|
.filter(Boolean)
|
|
@@ -513,16 +553,18 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
513
553
|
* Renders one schematic port marker.
|
|
514
554
|
* @param {object} element Port element.
|
|
515
555
|
* @param {Map<string, object>} sourcePorts Source port lookup.
|
|
556
|
+
* @param {WeakMap<object, string>} portHintCache Render-scoped hint cache.
|
|
516
557
|
* @returns {string}
|
|
517
558
|
*/
|
|
518
|
-
static #portElement(element, sourcePorts) {
|
|
559
|
+
static #portElement(element, sourcePorts, portHintCache) {
|
|
519
560
|
const center = CircuitJsonSchematicSvgRenderer.#center(element)
|
|
520
561
|
if (!center) return ''
|
|
521
562
|
const sourcePortId = String(element.source_port_id || '').trim()
|
|
522
563
|
const sourcePort = sourcePorts.get(sourcePortId) || null
|
|
523
564
|
const label = CircuitJsonSchematicSvgPortMetadata.label(
|
|
524
565
|
element,
|
|
525
|
-
sourcePort
|
|
566
|
+
sourcePort,
|
|
567
|
+
portHintCache
|
|
526
568
|
)
|
|
527
569
|
|
|
528
570
|
return (
|
|
@@ -592,6 +634,7 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
592
634
|
element.ccw_rotation ?? element.rotation,
|
|
593
635
|
0
|
|
594
636
|
)
|
|
637
|
+
const anchor = element.anchor || element.anchor_side
|
|
595
638
|
const attributes = [
|
|
596
639
|
'class="schematic-text"',
|
|
597
640
|
'x="' +
|
|
@@ -607,6 +650,15 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
607
650
|
'"'
|
|
608
651
|
)
|
|
609
652
|
}
|
|
653
|
+
if (anchor) {
|
|
654
|
+
const alignment = SchematicTextAnchor.resolve(anchor)
|
|
655
|
+
attributes.push('text-anchor="' + alignment.textAnchor + '"')
|
|
656
|
+
if (alignment.baseline) {
|
|
657
|
+
attributes.push(
|
|
658
|
+
'dominant-baseline="' + alignment.baseline + '"'
|
|
659
|
+
)
|
|
660
|
+
}
|
|
661
|
+
}
|
|
610
662
|
if (rotation) {
|
|
611
663
|
attributes.push(
|
|
612
664
|
'transform="rotate(' +
|
|
@@ -655,7 +707,9 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
655
707
|
* @returns {string}
|
|
656
708
|
*/
|
|
657
709
|
static #probeElement(element) {
|
|
658
|
-
const center =
|
|
710
|
+
const center =
|
|
711
|
+
CircuitJsonSchematicSvgRenderer.#point(element.position) ||
|
|
712
|
+
CircuitJsonSchematicSvgRenderer.#center(element)
|
|
659
713
|
if (!center) return ''
|
|
660
714
|
const label = String(element.name || element.label || '').trim()
|
|
661
715
|
return (
|
|
@@ -687,45 +741,13 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
687
741
|
index,
|
|
688
742
|
'schematic_debug_object'
|
|
689
743
|
)
|
|
690
|
-
.map((element) =>
|
|
691
|
-
CircuitJsonSchematicSvgRenderer.#debugObjectElement(element)
|
|
692
|
-
)
|
|
744
|
+
.map((element) => CircuitJsonSchematicDebugRenderer.render(element))
|
|
693
745
|
.filter(Boolean)
|
|
694
746
|
return objects.length
|
|
695
747
|
? '<g class="schematic-debug-objects">' + objects.join('') + '</g>'
|
|
696
748
|
: ''
|
|
697
749
|
}
|
|
698
750
|
|
|
699
|
-
/**
|
|
700
|
-
* Renders one schematic debug object.
|
|
701
|
-
* @param {object} element Debug element.
|
|
702
|
-
* @returns {string}
|
|
703
|
-
*/
|
|
704
|
-
static #debugObjectElement(element) {
|
|
705
|
-
const rect = CircuitJsonSchematicSvgRenderer.#rectAttributes(element)
|
|
706
|
-
const center = CircuitJsonSchematicSvgRenderer.#center(element)
|
|
707
|
-
if (!rect || !center) return ''
|
|
708
|
-
const message = String(element.message || '').trim()
|
|
709
|
-
|
|
710
|
-
return (
|
|
711
|
-
'<g class="schematic-debug-object" data-schematic-debug-object-id="' +
|
|
712
|
-
CircuitJsonSchematicSvgRenderer.#escapeHtml(
|
|
713
|
-
element.schematic_debug_object_id || ''
|
|
714
|
-
) +
|
|
715
|
-
'"><title>' +
|
|
716
|
-
CircuitJsonSchematicSvgRenderer.#escapeHtml(message) +
|
|
717
|
-
'</title><rect ' +
|
|
718
|
-
rect +
|
|
719
|
-
'></rect><text x="' +
|
|
720
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(center.x) +
|
|
721
|
-
'" y="' +
|
|
722
|
-
CircuitJsonSchematicSvgRenderer.#formatNumber(center.y) +
|
|
723
|
-
'" text-anchor="middle" dominant-baseline="central">' +
|
|
724
|
-
CircuitJsonSchematicSvgRenderer.#escapeHtml(message) +
|
|
725
|
-
'</text></g>'
|
|
726
|
-
)
|
|
727
|
-
}
|
|
728
|
-
|
|
729
751
|
/**
|
|
730
752
|
* Renders schematic warning and error markers.
|
|
731
753
|
* @param {{ elements: object[] }} index Element index.
|
|
@@ -733,9 +755,13 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
733
755
|
*/
|
|
734
756
|
static #renderDiagnostics(index) {
|
|
735
757
|
const diagnostics = (index.elements || [])
|
|
736
|
-
.filter((element) =>
|
|
737
|
-
|
|
738
|
-
|
|
758
|
+
.filter((element) => {
|
|
759
|
+
const type = String(element?.type || '')
|
|
760
|
+
return (
|
|
761
|
+
type.startsWith('schematic_') &&
|
|
762
|
+
/(?:error|warning)/iu.test(type)
|
|
763
|
+
)
|
|
764
|
+
})
|
|
739
765
|
.map((element) =>
|
|
740
766
|
CircuitJsonSchematicSvgRenderer.#diagnosticElement(element)
|
|
741
767
|
)
|
|
@@ -796,38 +822,6 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
796
822
|
)
|
|
797
823
|
}
|
|
798
824
|
|
|
799
|
-
/**
|
|
800
|
-
* Resolves schematic bounds.
|
|
801
|
-
* @param {{ elements: object[], elementsByType: Map<string, object[]> }} index Element index.
|
|
802
|
-
* @returns {{ minX: number, minY: number, maxX: number, maxY: number, width: number, height: number }}
|
|
803
|
-
*/
|
|
804
|
-
static #bounds(index) {
|
|
805
|
-
const sheet = CircuitJsonSchematicSvgRenderer.#all(
|
|
806
|
-
index,
|
|
807
|
-
'schematic_sheet'
|
|
808
|
-
)[0]
|
|
809
|
-
const width = CircuitJsonUnits.optionalLength(sheet?.width)
|
|
810
|
-
const height = CircuitJsonUnits.optionalLength(sheet?.height)
|
|
811
|
-
if (width !== null && height !== null) {
|
|
812
|
-
return {
|
|
813
|
-
minX: 0,
|
|
814
|
-
minY: 0,
|
|
815
|
-
maxX: width,
|
|
816
|
-
maxY: height,
|
|
817
|
-
width,
|
|
818
|
-
height
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return {
|
|
822
|
-
minX: -10,
|
|
823
|
-
minY: -10,
|
|
824
|
-
maxX: 10,
|
|
825
|
-
maxY: 10,
|
|
826
|
-
width: 20,
|
|
827
|
-
height: 20
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
825
|
/**
|
|
832
826
|
* Builds a padded SVG viewBox.
|
|
833
827
|
* @param {object} bounds Bounds.
|
|
@@ -939,6 +933,19 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
939
933
|
}
|
|
940
934
|
}
|
|
941
935
|
|
|
936
|
+
/**
|
|
937
|
+
* Resolves the positive span for an SVG sweep direction.
|
|
938
|
+
* @param {number} start Start angle in degrees.
|
|
939
|
+
* @param {number} end End angle in degrees.
|
|
940
|
+
* @param {0 | 1} sweep SVG sweep flag.
|
|
941
|
+
* @returns {number} Directed span in degrees.
|
|
942
|
+
*/
|
|
943
|
+
static #directedAngleSpan(start, end, sweep) {
|
|
944
|
+
const difference = sweep ? end - start : start - end
|
|
945
|
+
const span = ((difference % 360) + 360) % 360
|
|
946
|
+
return span === 0 && start !== end ? 360 : span
|
|
947
|
+
}
|
|
948
|
+
|
|
942
949
|
/**
|
|
943
950
|
* Builds a polyline point string.
|
|
944
951
|
* @param {{ x: number, y: number }[]} points Points.
|
|
@@ -985,10 +992,6 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
985
992
|
* @returns {string}
|
|
986
993
|
*/
|
|
987
994
|
static #escapeHtml(value) {
|
|
988
|
-
return
|
|
989
|
-
.replaceAll('&', '&')
|
|
990
|
-
.replaceAll('<', '<')
|
|
991
|
-
.replaceAll('>', '>')
|
|
992
|
-
.replaceAll('"', '"')
|
|
995
|
+
return SafeXmlText.escape(value)
|
|
993
996
|
}
|
|
994
997
|
}
|