@mstone6969/graph 0.1.0 → 0.2.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.
Files changed (238) hide show
  1. package/README.md +111 -14
  2. package/dist/ExcelWriter-3ZSYUH4T.cjs +154 -0
  3. package/dist/GraphType.js +24 -38
  4. package/dist/PdfWriter-6HJFYMCO.cjs +205 -0
  5. package/dist/catalog-YWX6FHRM.cjs +8 -0
  6. package/dist/catalog.js +5 -1
  7. package/dist/charts/bars/index.js +2 -12
  8. package/dist/charts/comparison/EffectForestPlot.js +1 -1
  9. package/dist/charts/comparison/index.js +1 -8
  10. package/dist/charts/demography/Nomogram.js +3 -3
  11. package/dist/charts/density/HexbinChart.js +6 -4
  12. package/dist/charts/diagrams/ArchitectureCanvas.js +68 -0
  13. package/dist/charts/diagrams/ArchitectureDiagram.js +331 -0
  14. package/dist/charts/diagrams/ArchitectureLayout.js +106 -0
  15. package/dist/charts/diagrams/ArchitectureLinkLabels.js +78 -0
  16. package/dist/charts/diagrams/ArchitectureModel.js +108 -0
  17. package/dist/charts/diagrams/ArchitectureRouter.js +348 -0
  18. package/dist/charts/diagrams/ArchitectureSamples.js +312 -0
  19. package/dist/charts/diagrams/ArchitectureShapes.js +130 -0
  20. package/dist/charts/diagrams/ArchitectureText.js +25 -0
  21. package/dist/charts/diagrams/DiagramText.js +38 -0
  22. package/dist/charts/diagrams/EntityRelationshipDiagram.js +465 -0
  23. package/dist/charts/diagrams/ErCrowsFoot.js +33 -0
  24. package/dist/charts/diagrams/ErSamples.js +205 -0
  25. package/dist/charts/diagrams/ErSchema.js +122 -0
  26. package/dist/charts/diagrams/LogicCircuit.js +154 -0
  27. package/dist/charts/diagrams/LogicCircuitLayout.js +600 -0
  28. package/dist/charts/diagrams/LogicCircuitSamples.js +66 -0
  29. package/dist/charts/diagrams/LogicGateDiagram.js +216 -0
  30. package/dist/charts/diagrams/LogicGateSymbol.js +113 -0
  31. package/dist/charts/diagrams/NetworkTopologyDiagram.js +247 -0
  32. package/dist/charts/diagrams/SequenceDiagram.js +351 -0
  33. package/dist/charts/diagrams/SequenceLayout.js +422 -0
  34. package/dist/charts/diagrams/SequenceModel.js +221 -0
  35. package/dist/charts/diagrams/TopologyLayout.js +143 -0
  36. package/dist/charts/diagrams/architectureEnums.js +50 -0
  37. package/dist/charts/diagrams/index.js +26 -0
  38. package/dist/charts/diagrams/logicEnums.js +42 -0
  39. package/dist/charts/diagrams/sequenceEnums.js +40 -0
  40. package/dist/charts/distribution/HistogramChart.js +25 -3
  41. package/dist/charts/distribution/index.js +1 -3
  42. package/dist/charts/earthscience/HovmollerDiagram.js +477 -0
  43. package/dist/charts/earthscience/HovmollerEnums.js +42 -0
  44. package/dist/charts/earthscience/HovmollerField.js +180 -0
  45. package/dist/charts/earthscience/HovmollerSample.js +40 -0
  46. package/dist/charts/earthscience/HovmollerSpeed.js +71 -0
  47. package/dist/charts/earthscience/index.js +10 -2
  48. package/dist/charts/engineering/ConstellationDiagram.js +26 -6
  49. package/dist/charts/engineering/EyeDiagram.js +2 -1
  50. package/dist/charts/engineering/PoleZeroPlot.js +32 -8
  51. package/dist/charts/financial/RelativeStrengthRotation.js +434 -0
  52. package/dist/charts/financial/RelativeStrengthRotationMath.js +71 -0
  53. package/dist/charts/financial/RelativeStrengthRotationSamples.js +27 -0
  54. package/dist/charts/financial/index.js +5 -3
  55. package/dist/charts/flow/index.js +1 -5
  56. package/dist/charts/frameworks/AnsoffMatrix.js +523 -0
  57. package/dist/charts/frameworks/FrameworkText.js +98 -0
  58. package/dist/charts/frameworks/NpsGauge.js +279 -0
  59. package/dist/charts/frameworks/NpsSurvey.js +58 -0
  60. package/dist/charts/frameworks/PerceptualMap.js +419 -0
  61. package/dist/charts/frameworks/PerceptualProjection.js +142 -0
  62. package/dist/charts/frameworks/PositioningSamples.js +191 -0
  63. package/dist/charts/frameworks/RaciMatrix.js +458 -0
  64. package/dist/charts/frameworks/RaciPlan.js +81 -0
  65. package/dist/charts/frameworks/RaciSamples.js +76 -0
  66. package/dist/charts/frameworks/SwotMatrix.js +358 -0
  67. package/dist/charts/frameworks/SwotSamples.js +85 -0
  68. package/dist/charts/frameworks/VanWestendorpChart.js +187 -0
  69. package/dist/charts/frameworks/VanWestendorpSurvey.js +197 -0
  70. package/dist/charts/frameworks/index.js +26 -0
  71. package/dist/charts/frameworks/raciEnums.js +30 -0
  72. package/dist/charts/frameworks/swotEnums.js +34 -0
  73. package/dist/charts/genomics/CircosPlot.js +1 -1
  74. package/dist/charts/geoflows/GeoFlowChart.js +2 -2
  75. package/dist/charts/geoflows/GeoFlowSample.js +2 -1
  76. package/dist/charts/geomaps/FloorPlanMap.js +324 -0
  77. package/dist/charts/geomaps/FloorPlanModel.js +179 -0
  78. package/dist/charts/geomaps/FloorPlanSamples.js +416 -0
  79. package/dist/charts/geomaps/GeoLand.js +2 -2
  80. package/dist/charts/geomaps/GeoSamples.js +2 -1
  81. package/dist/charts/geomaps/GraticuleMap.js +2 -2
  82. package/dist/charts/geomaps/RoomLabelFit.js +200 -0
  83. package/dist/charts/geomaps/TissotMap.js +2 -2
  84. package/dist/charts/geomaps/index.js +8 -4
  85. package/dist/charts/hierarchy/SunburstChart.js +17 -10
  86. package/dist/charts/hierarchy/index.js +2 -11
  87. package/dist/charts/indicators/BulletChart.js +8 -2
  88. package/dist/charts/indicators/SpeedometerChart.js +1 -1
  89. package/dist/charts/indicators/ThermometerChart.js +1 -1
  90. package/dist/charts/lifescience/LoessFit.js +113 -0
  91. package/dist/charts/lifescience/MaPlot.js +337 -0
  92. package/dist/charts/lifescience/MaPlotSamples.js +35 -0
  93. package/dist/charts/lifescience/OncoGlyph.js +137 -0
  94. package/dist/charts/lifescience/OncoPrint.js +373 -0
  95. package/dist/charts/lifescience/OncoPrintMatrix.js +107 -0
  96. package/dist/charts/lifescience/OncoPrintSamples.js +142 -0
  97. package/dist/charts/lifescience/PedigreeChart.js +306 -0
  98. package/dist/charts/lifescience/PedigreeLayout.js +261 -0
  99. package/dist/charts/lifescience/PedigreeRows.js +70 -0
  100. package/dist/charts/lifescience/PedigreeSamples.js +161 -0
  101. package/dist/charts/lifescience/PedigreeSymbol.js +47 -0
  102. package/dist/charts/lifescience/RamachandranPlot.js +364 -0
  103. package/dist/charts/lifescience/RamachandranRegions.js +2712 -0
  104. package/dist/charts/lifescience/RamachandranSamples.js +235 -0
  105. package/dist/charts/lifescience/index.js +25 -0
  106. package/dist/charts/lifescience/pedigreeEnums.js +21 -0
  107. package/dist/charts/lifescience/ramachandranEnums.js +34 -0
  108. package/dist/charts/linesareas/DualAxisChart.js +1 -1
  109. package/dist/charts/linesareas/LineEnums.js +1 -15
  110. package/dist/charts/linesareas/MultiLineChart.js +1 -1
  111. package/dist/charts/linesareas/TrendChart.js +15 -5
  112. package/dist/charts/linesareas/index.js +2 -6
  113. package/dist/charts/massive/MassiveChart.js +3 -2
  114. package/dist/charts/mathematical/FourierSeriesChart.js +7 -1
  115. package/dist/charts/mathematical/index.js +2 -6
  116. package/dist/charts/matrix/GridPlot.js +27 -2
  117. package/dist/charts/matrix/TilegramChart.js +2 -2
  118. package/dist/charts/mlcharts/AttentionGrid.js +79 -0
  119. package/dist/charts/mlcharts/AttentionHeatmap.js +329 -0
  120. package/dist/charts/mlcharts/AttentionModel.js +132 -0
  121. package/dist/charts/mlcharts/AttentionSamples.js +82 -0
  122. package/dist/charts/mlcharts/CellNumber.js +8 -0
  123. package/dist/charts/mlcharts/CurveDiagnosticSamples.js +95 -0
  124. package/dist/charts/mlcharts/CurvePickMark.js +31 -0
  125. package/dist/charts/mlcharts/DecisionBoundaryChart.js +316 -0
  126. package/dist/charts/mlcharts/DecisionSamples.js +60 -0
  127. package/dist/charts/mlcharts/DecisionSurface.js +130 -0
  128. package/dist/charts/mlcharts/DistinctTicks.js +13 -0
  129. package/dist/charts/mlcharts/ElbowMath.js +153 -0
  130. package/dist/charts/mlcharts/ElbowPlot.js +273 -0
  131. package/dist/charts/mlcharts/ExplainabilitySamples.js +70 -0
  132. package/dist/charts/mlcharts/FeatureImportanceChart.js +150 -0
  133. package/dist/charts/mlcharts/ImportanceRanking.js +98 -0
  134. package/dist/charts/mlcharts/LearningCurveChart.js +276 -0
  135. package/dist/charts/mlcharts/LearningCurveMath.js +92 -0
  136. package/dist/charts/mlcharts/PartialDependence.js +92 -0
  137. package/dist/charts/mlcharts/PartialDependencePlot.js +225 -0
  138. package/dist/charts/mlcharts/WideLabel.js +28 -0
  139. package/dist/charts/mlcharts/curveDiagnosticEnums.js +37 -0
  140. package/dist/charts/mlcharts/index.js +32 -0
  141. package/dist/charts/proportion/index.js +5 -9
  142. package/dist/charts/scatter/CategoryScatterBase.js +1 -4
  143. package/dist/charts/scatter/ConnectedScatter.js +7 -4
  144. package/dist/charts/scatter/MarginalScatter.js +13 -8
  145. package/dist/charts/scatter/index.js +4 -17
  146. package/dist/charts/scientific/SpectrogramChart.js +9 -3
  147. package/dist/charts/scientific/index.js +2 -2
  148. package/dist/charts/signals/index.js +3 -13
  149. package/dist/charts/statistics/LorenzCurve.js +7 -4
  150. package/dist/charts/threebars/Candlestick3DChart.js +18 -2
  151. package/dist/charts/threeglobe/GlobeChart.js +2 -2
  152. package/dist/charts/threeglobe/GlobeSamples.js +2 -1
  153. package/dist/charts/threeglobe/RingGlobe.js +1 -1
  154. package/dist/charts/threenetwork/ConeTreeChart.js +2 -1
  155. package/dist/charts/threenetwork/Gantt3DChart.js +3 -2
  156. package/dist/charts/threesurfaces/Ridgeline3DChart.js +22 -4
  157. package/dist/charts/threevolume/LorenzAttractor.js +19 -0
  158. package/dist/charts/threevolume/VolumeRaycastChart.js +4 -3
  159. package/dist/charts/threevolume/volumeEnums.js +1 -0
  160. package/dist/charts/timeline/index.js +2 -10
  161. package/dist/chunk-753UBLV2.cjs +33 -0
  162. package/dist/chunk-VX54A7KA.cjs +17 -0
  163. package/dist/{chunk-W5GAAAZY.cjs → chunk-WAODDPYX.cjs} +24874 -17620
  164. package/dist/chunk-Z3JPIJSC.cjs +338 -0
  165. package/dist/core/CartesianChart.js +87 -11
  166. package/dist/core/Chart.js +63 -15
  167. package/dist/core/Chart2D.js +54 -17
  168. package/dist/core/Chart3D.js +56 -20
  169. package/dist/core/ChartExportError.js +2 -0
  170. package/dist/core/ChartExporter.js +50 -8
  171. package/dist/core/ColumnCheck.js +53 -0
  172. package/dist/core/Context2D.js +29 -15
  173. package/dist/core/Context3D.js +7 -2
  174. package/dist/core/ExcelWriter.js +152 -0
  175. package/dist/core/ExportText.js +28 -0
  176. package/dist/core/MarkOrigin.js +88 -0
  177. package/dist/core/MarkStyle.js +294 -0
  178. package/dist/core/Palette.js +27 -7
  179. package/dist/core/PdfPage.js +13 -0
  180. package/dist/core/PdfWriter.js +245 -0
  181. package/dist/core/Scales.js +52 -2
  182. package/dist/core/SceneOverlay.js +4 -4
  183. package/dist/core/TextFit.js +37 -0
  184. package/dist/core/Theme.js +28 -1
  185. package/dist/core/enums.js +33 -1
  186. package/dist/core/pathMorph.js +15 -0
  187. package/dist/data/SampleData.js +1 -17
  188. package/dist/data/WorldSample.js +23 -0
  189. package/dist/index.cjs +810 -691
  190. package/dist/index.d.cts +8390 -2925
  191. package/dist/index.d.ts +8392 -2927
  192. package/dist/index.js +106 -56
  193. package/dist/react/ChartView.js +18 -8
  194. package/dist/react/GraphHandle.js +2 -0
  195. package/dist/react/components.js +74 -116
  196. package/package.json +13 -3
  197. package/dist/catalog-4UP2YFNR.cjs +0 -7
  198. package/dist/charts/bars/BulletBar.js +0 -184
  199. package/dist/charts/bars/ErrorBarColumn.js +0 -147
  200. package/dist/charts/bars/HistogramBar.js +0 -217
  201. package/dist/charts/bars/MarimekkoBar.js +0 -191
  202. package/dist/charts/bars/ParetoBar.js +0 -143
  203. package/dist/charts/comparison/PanelChart.js +0 -70
  204. package/dist/charts/comparison/SmallMultipleBars.js +0 -134
  205. package/dist/charts/comparison/SpanChart.js +0 -118
  206. package/dist/charts/comparison/TrellisChart.js +0 -158
  207. package/dist/charts/distribution/RidgeSlabs3DChart.js +0 -169
  208. package/dist/charts/financial/Candlestick3D.js +0 -157
  209. package/dist/charts/flow/BumpFlow.js +0 -212
  210. package/dist/charts/flow/StreamFlow.js +0 -174
  211. package/dist/charts/geomaps/ThiessenMap.js +0 -106
  212. package/dist/charts/hierarchy/ConeTree3D.js +0 -135
  213. package/dist/charts/hierarchy/Hierarchy3DChart.js +0 -27
  214. package/dist/charts/hierarchy/SpherePack3D.js +0 -81
  215. package/dist/charts/hierarchy/SunburstTower3D.js +0 -93
  216. package/dist/charts/hierarchy/TreemapSkyline3D.js +0 -98
  217. package/dist/charts/linesareas/ConnectedScatterTime.js +0 -206
  218. package/dist/charts/linesareas/SmallMultipleLines.js +0 -153
  219. package/dist/charts/mathematical/ParametricSurface3D.js +0 -234
  220. package/dist/charts/mathematical/StrangeAttractor3D.js +0 -251
  221. package/dist/charts/proportion/ConeFunnel.js +0 -126
  222. package/dist/charts/proportion/ExtrudedPieChart.js +0 -123
  223. package/dist/charts/scatter/BeeswarmScatter.js +0 -151
  224. package/dist/charts/scatter/BinnedScatterBase.js +0 -51
  225. package/dist/charts/scatter/ContourScatter.js +0 -116
  226. package/dist/charts/scatter/GridBinScatter.js +0 -98
  227. package/dist/charts/scatter/HexbinScatter.js +0 -74
  228. package/dist/charts/scatter/StripScatter.js +0 -107
  229. package/dist/charts/scatter/SunflowerScatter.js +0 -109
  230. package/dist/charts/signals/ConstellationChart.js +0 -153
  231. package/dist/charts/signals/CorrelogramChart.js +0 -171
  232. package/dist/charts/signals/EyeDiagramChart.js +0 -155
  233. package/dist/charts/signals/PoleZeroChart.js +0 -164
  234. package/dist/charts/signals/SpectrogramChart.js +0 -79
  235. package/dist/charts/timeline/CalendarSkylineChart.js +0 -128
  236. package/dist/charts/timeline/ScheduleBlocksChart.js +0 -119
  237. package/dist/charts/timeline/SpiralTimeline.js +0 -154
  238. package/dist/charts/timeline/StreamTimeline.js +0 -141
@@ -0,0 +1,600 @@
1
+ import { DiagramText } from "./DiagramText.js";
2
+ import { LogicGateSymbol } from "./LogicGateSymbol.js";
3
+ import { LogicGateKind, LogicLayering, LogicState } from "./logicEnums.js";
4
+ // src/charts/diagrams/LogicCircuitLayout.ts
5
+ var GAP = 10;
6
+ var TERMINAL = 22;
7
+ var THROUGH = 12;
8
+ var MAX_SCALE = 1.25;
9
+ var MIN_CHANNEL = 10;
10
+ /** Below this a gate is too small to read, so shrinking stops and the finished drawing is scaled to fit instead. */
11
+ var MIN_SCALE = .2;
12
+ var LABEL = 11;
13
+ var NARROW = 6.2 / LABEL;
14
+ var LABEL_GAP = 5;
15
+ var STUB = 10;
16
+ var MAX_STRETCH = 1.6;
17
+ var STRAIGHTEN = 3;
18
+ var JOG = 16;
19
+ /** Trunks are at least this far apart, so a junction dot never touches a neighbouring wire. */
20
+ var TRACK = 6;
21
+ var SWEEPS = 8;
22
+ var NUDGES = 6;
23
+ var CLEAR = 5;
24
+ var ALIGN = 3;
25
+ var key = (x, y) => `${x},${y}`;
26
+ /** Internal vertex and net ids start with a NUL, so no gate id can collide with them. */
27
+ var through = (net, way, column) => `\0${net}${way}${column}`;
28
+ var floating = (node, pin) => `\0floating:${node}:${pin}`;
29
+ var fix = (value) => +value.toFixed(2);
30
+ var mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
31
+ /**
32
+ * Layered drawing of a netlist after Sugiyama, Tagawa and Toda (1981): feedback edges are found by
33
+ * depth-first search and routed backwards, gates take the longest-path layer (or its as-late-as-
34
+ * possible mirror), long wires get a through-vertex in each layer they cross, and barycentre sweeps
35
+ * reduce crossings. Wires between layers are routed as a channel with left-edge track assignment
36
+ * (Hashimoto and Stevens, 1971), one vertical trunk per net, so a net's branches share copper.
37
+ */
38
+ var LogicCircuitLayout = class LogicCircuitLayout {
39
+ gates = [];
40
+ terminals = [];
41
+ nets = [];
42
+ junctions = [];
43
+ stateMarks = [];
44
+ scale = 1;
45
+ /** Transform that fits the drawing to the plot. It is the identity unless the circuit is too long or dense for the plot at the smallest scale. */
46
+ view = {
47
+ x: 0,
48
+ y: 0,
49
+ scale: 1
50
+ };
51
+ #columns = [];
52
+ #vertices = /* @__PURE__ */ new Map();
53
+ #nets = /* @__PURE__ */ new Map();
54
+ #places = /* @__PURE__ */ new Map();
55
+ #circuit;
56
+ #size;
57
+ #style;
58
+ constructor(circuit, size, style, layering) {
59
+ this.#circuit = circuit;
60
+ this.#size = size;
61
+ this.#style = style;
62
+ if (!circuit.nodes.size) return;
63
+ this.#assignLayers(layering);
64
+ this.#buildVertices();
65
+ this.#order();
66
+ this.#draw();
67
+ }
68
+ /** The layer of each node. Edges that close a cycle are left out, so feedback does not stretch the layering. */
69
+ #assignLayers(layering) {
70
+ const { nodes } = this.#circuit;
71
+ const drivenBy = /* @__PURE__ */ new Map();
72
+ const finished = [];
73
+ const active = /* @__PURE__ */ new Set(), seen = /* @__PURE__ */ new Set();
74
+ const loads = /* @__PURE__ */ new Map();
75
+ for (const node of nodes.values()) for (const id of node.inputs) if (id !== null) loads.set(id, [...loads.get(id) ?? [], node.id]);
76
+ const visit = (id) => {
77
+ seen.add(id);
78
+ active.add(id);
79
+ for (const load of loads.get(id) ?? []) {
80
+ if (active.has(load)) continue;
81
+ drivenBy.set(load, [...drivenBy.get(load) ?? [], id]);
82
+ if (!seen.has(load)) visit(load);
83
+ }
84
+ active.delete(id);
85
+ finished.push(id);
86
+ };
87
+ const all = [...nodes.values()];
88
+ for (const node of all) if (node.kind === LogicGateKind.Input) visit(node.id);
89
+ for (const node of all) if (!seen.has(node.id)) visit(node.id);
90
+ const isGate = (id) => !this.#isTerminal(nodes.get(id));
91
+ const order = finished.slice().reverse();
92
+ for (const id of order) if (nodes.get(id).kind === LogicGateKind.Input) this.#places.set(id, 0);
93
+ for (const id of order) {
94
+ if (!isGate(id)) continue;
95
+ this.#places.set(id, Math.max(0, ...(drivenBy.get(id) ?? []).map((driver) => this.#places.get(driver) ?? 0)) + 1);
96
+ }
97
+ const last = Math.max(0, ...this.#places.values());
98
+ if (layering === LogicLayering.Outputs) {
99
+ const forward = /* @__PURE__ */ new Map();
100
+ for (const [to, from] of drivenBy) for (const id of from) forward.set(id, [...forward.get(id) ?? [], to]);
101
+ for (const id of finished) {
102
+ if (!isGate(id)) continue;
103
+ const later = (forward.get(id) ?? []).filter(isGate).map((load) => this.#places.get(load) - 1);
104
+ this.#places.set(id, Math.min(last, ...later));
105
+ }
106
+ }
107
+ for (const node of all) if (node.kind === LogicGateKind.Output) this.#places.set(node.id, last + 1);
108
+ for (let column = 0; column <= last + 1; column++) this.#columns.push([]);
109
+ }
110
+ #isTerminal(node) {
111
+ return node.kind === LogicGateKind.Input || node.kind === LogicGateKind.Output;
112
+ }
113
+ #buildVertices() {
114
+ const { nodes } = this.#circuit;
115
+ const vertices = this.#vertices;
116
+ const add = (id, column, height, fields) => {
117
+ const vertex = {
118
+ key: id,
119
+ column,
120
+ height,
121
+ y: 0,
122
+ previous: [],
123
+ next: [],
124
+ ...fields
125
+ };
126
+ vertices.set(id, vertex);
127
+ this.#columns[column].push(vertex);
128
+ return vertex;
129
+ };
130
+ for (const node of nodes.values()) {
131
+ const height = this.#isTerminal(node) ? TERMINAL : LogicGateSymbol.height(node.inputs.length);
132
+ add(node.id, this.#places.get(node.id), height, { node });
133
+ }
134
+ const link = (from, to) => {
135
+ from.next.push(to);
136
+ to.previous.push(from);
137
+ };
138
+ for (const node of nodes.values()) node.inputs.forEach((id, pin) => {
139
+ const netId = id ?? floating(node.id, pin);
140
+ const driver = id === null ? null : nodes.get(id);
141
+ const net = this.#nets.get(netId) ?? {
142
+ id: netId,
143
+ driver,
144
+ sinks: [],
145
+ column: driver ? this.#places.get(driver.id) : -1
146
+ };
147
+ net.sinks.push(node);
148
+ this.#nets.set(netId, net);
149
+ });
150
+ for (const node of nodes.values()) if (node.kind !== LogicGateKind.Output && !this.#nets.has(node.id)) this.#nets.set(node.id, {
151
+ id: node.id,
152
+ driver: node,
153
+ sinks: [],
154
+ column: this.#places.get(node.id)
155
+ });
156
+ for (const net of this.#nets.values()) {
157
+ if (!net.driver) continue;
158
+ const home = vertices.get(net.driver.id);
159
+ const columns = net.sinks.map((sink) => this.#places.get(sink.id));
160
+ const far = Math.max(net.column, ...columns);
161
+ const ahead = /* @__PURE__ */ new Map([[net.column, home]]);
162
+ for (let column = net.column + 1; column < far; column++) {
163
+ const vertex = add(through(net.id, ">", column), column, THROUGH, { net });
164
+ link(ahead.get(column - 1), vertex);
165
+ ahead.set(column, vertex);
166
+ }
167
+ let behind = null;
168
+ const returning = columns.filter((column) => column <= net.column);
169
+ for (let column = Math.min(...returning); returning.length && column <= net.column; column++) {
170
+ const vertex = add(through(net.id, "<", column), column, THROUGH, { net });
171
+ if (behind) link(behind, vertex);
172
+ behind = vertex;
173
+ }
174
+ for (const sink of net.sinks) {
175
+ const column = this.#places.get(sink.id);
176
+ if (column > net.column) link(ahead.get(column - 1), vertices.get(sink.id));
177
+ }
178
+ }
179
+ }
180
+ /** Barycentre sweeps, keeping the arrangement with the fewest crossings. */
181
+ #order() {
182
+ const columns = this.#columns;
183
+ const position = /* @__PURE__ */ new Map();
184
+ const index = () => {
185
+ for (const column of columns) column.forEach((vertex, i) => position.set(vertex, i));
186
+ };
187
+ const crossings = () => {
188
+ index();
189
+ let total = 0;
190
+ for (let c = 0; c + 1 < columns.length; c++) {
191
+ const edges = columns[c].flatMap((vertex) => vertex.next.map((next) => [position.get(vertex), position.get(next)]));
192
+ for (let i = 0; i < edges.length; i++) for (let j = i + 1; j < edges.length; j++) if ((edges[i][0] - edges[j][0]) * (edges[i][1] - edges[j][1]) < 0) total++;
193
+ }
194
+ return total;
195
+ };
196
+ const sortBy = (column, side) => {
197
+ const weight = new Map(column.map((vertex, i) => [vertex, vertex[side].length ? mean(vertex[side].map((other) => position.get(other))) : i]));
198
+ column.sort((a, b) => weight.get(a) - weight.get(b));
199
+ column.forEach((vertex, i) => position.set(vertex, i));
200
+ };
201
+ let best = columns.map((column) => column.slice()), fewest = crossings();
202
+ for (let sweep = 0; sweep < SWEEPS && fewest; sweep++) {
203
+ index();
204
+ for (let c = 1; c < columns.length; c++) sortBy(columns[c], "previous");
205
+ for (let c = columns.length - 2; c >= 0; c--) sortBy(columns[c], "next");
206
+ const count = crossings();
207
+ if (count < fewest) {
208
+ fewest = count;
209
+ best = columns.map((column) => column.slice());
210
+ }
211
+ }
212
+ best.forEach((column, c) => {
213
+ columns[c] = column;
214
+ });
215
+ }
216
+ #fitScale() {
217
+ const tallest = Math.max(...this.#columns.map((column) => column.reduce((sum, vertex) => sum + vertex.height + GAP, 0)));
218
+ return Math.min(MAX_SCALE, this.#size.height / Math.max(1, tallest));
219
+ }
220
+ #place(scale) {
221
+ const { height } = this.#size;
222
+ const columns = this.#columns;
223
+ const settle = (column, want) => {
224
+ column.forEach((vertex, i) => {
225
+ const above = column[i - 1];
226
+ const least = above ? above.y + ((above.height + vertex.height) / 2 + GAP) * scale : vertex.height / 2 * scale;
227
+ vertex.y = Math.max(want(vertex), least);
228
+ });
229
+ for (let i = column.length - 1; i >= 0; i--) {
230
+ const vertex = column[i], below = column[i + 1];
231
+ vertex.y = Math.min(vertex.y, below ? below.y - ((below.height + vertex.height) / 2 + GAP) * scale : height - vertex.height / 2 * scale);
232
+ }
233
+ };
234
+ for (const column of columns) {
235
+ let at = (height - column.reduce((sum, vertex) => sum + (vertex.height + GAP) * scale, -10 * scale)) / 2;
236
+ for (const vertex of column) {
237
+ vertex.y = at + vertex.height / 2 * scale;
238
+ at += (vertex.height + GAP) * scale;
239
+ }
240
+ }
241
+ const toward = (others, vertex) => others.length ? mean(others.map((other) => other.y)) : vertex.y;
242
+ for (let round = 0; round < NUDGES; round++) {
243
+ for (let c = 1; c < columns.length; c++) settle(columns[c], (vertex) => toward(vertex.previous, vertex));
244
+ for (let c = columns.length - 2; c >= 0; c--) settle(columns[c], (vertex) => toward(vertex.next, vertex));
245
+ }
246
+ for (const column of columns) settle(column, (vertex) => toward([...vertex.previous, ...vertex.next], vertex));
247
+ this.#fillHeight(scale);
248
+ }
249
+ /** The sweeps let columns drift together, so spread what they left about the middle of the plot. */
250
+ #fillHeight(scale) {
251
+ const { height } = this.#size;
252
+ const all = this.#columns.flat();
253
+ const top = Math.min(...all.map((vertex) => vertex.y - vertex.height / 2 * scale));
254
+ const bottom = Math.max(...all.map((vertex) => vertex.y + vertex.height / 2 * scale));
255
+ const stretch = Math.min(MAX_STRETCH, height / Math.max(1, bottom - top));
256
+ const middle = (top + bottom) / 2;
257
+ for (const vertex of all) vertex.y = fix(height / 2 + (vertex.y - middle) * stretch);
258
+ }
259
+ /** Removes small jogs from point-to-point wires by sliding the end that is free to move, where its column has room. */
260
+ #straighten(scale, widths) {
261
+ const free = (vertex) => !vertex.node || this.#isTerminal(vertex.node) || vertex.node.inputs.length === 1;
262
+ const slide = (vertex, y) => {
263
+ const column = this.#columns[vertex.column], i = column.indexOf(vertex);
264
+ const clear = (other) => !other || Math.abs(other.y - y) >= ((other.height + vertex.height) / 2 + GAP) * scale;
265
+ const inside = y >= vertex.height / 2 * scale && y <= this.#size.height - vertex.height / 2 * scale;
266
+ if (!clear(column[i - 1]) || !clear(column[i + 1]) || !inside) return false;
267
+ vertex.y = y;
268
+ return true;
269
+ };
270
+ for (let round = 0; round < STRAIGHTEN; round++) for (const channel of this.#pins(scale, widths)) for (const pin of channel.left) {
271
+ const across = channel.right.filter((other) => other.net === pin.net);
272
+ if (channel.left.filter((other) => other.net === pin.net).length !== 1 || across.length !== 1) continue;
273
+ const jog = across[0].y - pin.y;
274
+ if (!jog || Math.abs(jog) > JOG * scale) continue;
275
+ if (!(free(pin.vertex) && slide(pin.vertex, fix(pin.vertex.y + jog))) && free(across[0].vertex)) slide(across[0].vertex, fix(across[0].vertex.y - jog));
276
+ }
277
+ }
278
+ /** Inputs of a gate are interchangeable, so wires take pins in the order they arrive, which avoids a crossing at the gate. */
279
+ #wireOrder(vertex) {
280
+ const node = vertex.node, place = this.#places.get(node.id);
281
+ const height = (id) => {
282
+ if (id === null) return vertex.y;
283
+ const near = this.#nets.get(id).column < place ? through(id, ">", place - 1) : through(id, "<", place);
284
+ return (this.#vertices.get(near) ?? this.#vertices.get(id))?.y ?? vertex.y;
285
+ };
286
+ return node.inputs.map((id, pin) => ({
287
+ net: this.#nets.get(id ?? floating(node.id, pin)),
288
+ y: height(id)
289
+ })).sort((a, b) => a.y - b.y).map((entry) => entry.net);
290
+ }
291
+ /** Largest scale at which the channels hold their tracks. Lowers it step by step; past `MIN_SCALE` the channels keep their room and `view` shrinks the result. */
292
+ #draw() {
293
+ let scale = this.#fitScale();
294
+ const room = this.#labelRoom();
295
+ for (;;) {
296
+ const widths = this.#columns.map((column, c) => this.#columnWidth(column, c, scale));
297
+ const spare = this.#size.width - room.left - room.right - widths.reduce((sum, width) => sum + width, 0);
298
+ this.#place(scale);
299
+ this.#straighten(scale, widths);
300
+ const channels = this.#pins(scale, widths);
301
+ for (const channel of channels) this.#assignTracks(channel);
302
+ const need = channels.reduce((sum, channel) => sum + Math.max(MIN_CHANNEL, (channel.tracks + 1) * TRACK), 0);
303
+ if (spare >= need || scale * .84 < MIN_SCALE) {
304
+ this.scale = scale;
305
+ this.#emit(channels, scale, widths, room, Math.max(need, spare));
306
+ return;
307
+ }
308
+ scale *= .84;
309
+ }
310
+ }
311
+ #labelRoom() {
312
+ const widest = (kind) => {
313
+ const lengths = [...this.#circuit.nodes.values()].filter((node) => node.kind === kind).map((node) => DiagramText.width(node.label, LABEL, NARROW));
314
+ return lengths.length ? Math.min(this.#size.width * .2, Math.ceil(lengths.reduce((most, length) => Math.max(most, length), 0)) + LABEL_GAP) : 0;
315
+ };
316
+ return {
317
+ left: widest(LogicGateKind.Input),
318
+ right: widest(LogicGateKind.Output)
319
+ };
320
+ }
321
+ #chip(scale) {
322
+ return Math.min(18, Math.max(8, 16 * scale));
323
+ }
324
+ #columnWidth(column, c, scale) {
325
+ if (!column.some((vertex) => vertex.node)) return 0;
326
+ return c === 0 || c === this.#columns.length - 1 ? this.#chip(scale) : LogicGateSymbol.width * scale;
327
+ }
328
+ /** Every pin on either side of each channel. Columns are placed later, so x here is relative to the column's left edge. */
329
+ #pins(scale, widths) {
330
+ const channels = this.#columns.slice(1).map(() => ({
331
+ left: [],
332
+ right: [],
333
+ routes: [],
334
+ tracks: 0
335
+ }));
336
+ this.#columns.forEach((column, c) => {
337
+ for (const vertex of column) {
338
+ const right = channels[c], left = channels[c - 1], width = widths[c];
339
+ if (vertex.net) {
340
+ right?.left.push({
341
+ x: width,
342
+ y: vertex.y,
343
+ net: vertex.net,
344
+ vertex
345
+ });
346
+ left?.right.push({
347
+ x: 0,
348
+ y: vertex.y,
349
+ net: vertex.net,
350
+ vertex
351
+ });
352
+ continue;
353
+ }
354
+ const node = vertex.node;
355
+ if (node.kind !== LogicGateKind.Output) right?.left.push({
356
+ x: width,
357
+ y: vertex.y,
358
+ net: this.#nets.get(node.id),
359
+ vertex
360
+ });
361
+ if (node.kind === LogicGateKind.Input) continue;
362
+ const wires = this.#wireOrder(vertex);
363
+ if (node.kind === LogicGateKind.Output) {
364
+ if (wires[0]) left?.right.push({
365
+ x: 0,
366
+ y: vertex.y,
367
+ net: wires[0],
368
+ vertex
369
+ });
370
+ continue;
371
+ }
372
+ const { pins } = LogicGateSymbol.shape(node.kind, node.inputs.length, this.#style, scale);
373
+ wires.forEach((net, i) => left?.right.push({
374
+ x: pins[i].x,
375
+ y: fix(vertex.y + pins[i].y),
376
+ net,
377
+ vertex
378
+ }));
379
+ }
380
+ });
381
+ return channels;
382
+ }
383
+ #assignTracks(channel) {
384
+ const byNet = /* @__PURE__ */ new Map();
385
+ const entry = (net) => {
386
+ let route = byNet.get(net);
387
+ if (!route) byNet.set(net, route = {
388
+ net,
389
+ left: [],
390
+ right: [],
391
+ low: Infinity,
392
+ high: -Infinity,
393
+ track: -1
394
+ });
395
+ return route;
396
+ };
397
+ for (const pin of channel.left) entry(pin.net).left.push(pin);
398
+ for (const pin of channel.right) entry(pin.net).right.push(pin);
399
+ const routed = [...byNet.values()].filter((route) => {
400
+ const ys = [...route.left, ...route.right].map((pin) => pin.y);
401
+ route.low = Math.min(...ys);
402
+ route.high = Math.max(...ys);
403
+ return ys.length > 1 && route.high > route.low;
404
+ });
405
+ channel.routes = [...byNet.values()];
406
+ const side = (route, pins) => mean((pins.length ? pins : [...route.left, ...route.right]).map((pin) => pin.y));
407
+ const falling = (route) => side(route, route.right) >= side(route, route.left);
408
+ const rank = (route) => falling(route) ? -side(route, route.left) : 1e6 + side(route, route.left);
409
+ routed.sort((a, b) => rank(a) - rank(b));
410
+ const shares = (a, b) => a.some((one) => b.some((two) => Math.abs(one.y - two.y) < ALIGN));
411
+ const waiting = new Set(routed);
412
+ const placed = [];
413
+ const mustFollow = (route, other) => other === route.after || shares(other.left, route.right);
414
+ const taken = [...channel.left, ...channel.right].map((pin) => pin.y);
415
+ while (waiting.size) {
416
+ const list = [...waiting];
417
+ const route = list.find((candidate) => !list.some((other) => other !== candidate && mustFollow(candidate, other)));
418
+ if (!route) {
419
+ const tail = LogicCircuitLayout.#dogleg(list[0], taken, this.#size.height);
420
+ taken.push(tail.bend);
421
+ channel.routes.push(tail);
422
+ waiting.add(tail);
423
+ continue;
424
+ }
425
+ let track = 1 + Math.max(-1, ...placed.filter((other) => mustFollow(route, other)).map((other) => other.track));
426
+ while (placed.some((other) => other.track === track && !(route.high + CLEAR < other.low || other.high + CLEAR < route.low))) track++;
427
+ route.track = track;
428
+ channel.tracks = Math.max(channel.tracks, track + 1);
429
+ placed.push(route);
430
+ waiting.delete(route);
431
+ }
432
+ }
433
+ /**
434
+ * Two nets can each block the other: A's left stub lines up with B's right stub and the reverse,
435
+ * so no order of trunks works. Cuts `route` in two across a clear row and returns the right half.
436
+ */
437
+ static #dogleg(route, taken, height) {
438
+ const rows = [...taken].sort((a, b) => a - b);
439
+ const middle = (route.low + route.high) / 2;
440
+ const clear = rows.slice(1).map((row, i) => (row + rows[i]) / 2).filter((row, i) => rows[i + 1] - rows[i] >= 6);
441
+ const above = rows[0] - 6, below = rows.at(-1) + 6;
442
+ const bend = fix(clear.sort((a, b) => Math.abs(a - middle) - Math.abs(b - middle))[0] ?? (above >= 0 ? above : below <= height ? below : middle));
443
+ const tail = {
444
+ ...route,
445
+ left: [],
446
+ bend,
447
+ after: route,
448
+ low: Math.min(bend, ...route.right.map((pin) => pin.y)),
449
+ high: Math.max(bend, ...route.right.map((pin) => pin.y))
450
+ };
451
+ route.right = [];
452
+ route.bend = bend;
453
+ route.low = Math.min(bend, ...route.left.map((pin) => pin.y));
454
+ route.high = Math.max(bend, ...route.left.map((pin) => pin.y));
455
+ return tail;
456
+ }
457
+ #emit(channels, scale, widths, room, spare) {
458
+ const weight = channels.reduce((sum, channel) => sum + channel.tracks + 1, 0);
459
+ const gaps = channels.map((channel) => spare * (channel.tracks + 1) / weight);
460
+ const starts = [];
461
+ let at = room.left;
462
+ this.#columns.forEach((_, c) => {
463
+ starts.push(at);
464
+ at += widths[c] + (gaps[c] ?? 0);
465
+ });
466
+ const segments = /* @__PURE__ */ new Map();
467
+ const add = (net, x1, y1, x2, y2) => {
468
+ if (x1 === x2 && y1 === y2) return;
469
+ const list = segments.get(net.id) ?? [];
470
+ list.push({
471
+ x1: fix(x1),
472
+ y1: fix(y1),
473
+ x2: fix(x2),
474
+ y2: fix(y2)
475
+ });
476
+ segments.set(net.id, list);
477
+ };
478
+ channels.forEach((channel, c) => {
479
+ const start = starts[c], inner = start + widths[c], next = starts[c + 1], gap = gaps[c];
480
+ const stub = Math.min(STUB, gap * .4);
481
+ const trunk = (route) => inner + (route.track + 1) * gap / (channel.tracks + 1);
482
+ /** A lone stub stops short of a trunk whose wire runs the other way along the same row. */
483
+ const stops = (y, side) => channel.routes.filter((route) => route.track >= 0 && route[side].some((pin) => Math.abs(pin.y - y) < ALIGN)).map(trunk);
484
+ for (const route of channel.routes) {
485
+ const left = route.left.map((pin) => start + pin.x), right = route.right.map((pin) => next + pin.x);
486
+ if (route.bend === void 0 && route.left.length + route.right.length === 1) {
487
+ const single = route.left[0] ?? route.right[0];
488
+ if (route.left.length) add(route.net, left[0], single.y, Math.min(left[0] + stub, ...stops(single.y, "right").map((x) => x - ALIGN)), single.y);
489
+ else add(route.net, Math.max(right[0] - stub, ...stops(single.y, "left").map((x) => x + ALIGN)), single.y, right[0], single.y);
490
+ continue;
491
+ }
492
+ if (route.track < 0) {
493
+ add(route.net, left[0], route.low, right[0], route.low);
494
+ continue;
495
+ }
496
+ const x = trunk(route);
497
+ route.left.forEach((pin, i) => add(route.net, left[i], pin.y, x, pin.y));
498
+ route.right.forEach((pin, i) => add(route.net, x, pin.y, right[i], pin.y));
499
+ add(route.net, x, route.low, x, route.high);
500
+ if (route.after) add(route.net, trunk(route.after), route.bend, x, route.bend);
501
+ }
502
+ });
503
+ const chip = this.#chip(scale);
504
+ this.#columns.forEach((column, c) => {
505
+ for (const vertex of column) {
506
+ const x = starts[c], node = vertex.node;
507
+ if (vertex.net) {
508
+ add(vertex.net, x, vertex.y, x + widths[c], vertex.y);
509
+ continue;
510
+ }
511
+ if (this.#isTerminal(node)) {
512
+ this.terminals.push({
513
+ node,
514
+ x: fix(x + chip / 2),
515
+ y: vertex.y,
516
+ size: chip,
517
+ text: this.#clip(node.label, node.kind === LogicGateKind.Input ? room.left : room.right)
518
+ });
519
+ continue;
520
+ }
521
+ this.gates.push({
522
+ node,
523
+ x: fix(x),
524
+ y: vertex.y,
525
+ shape: LogicGateSymbol.shape(node.kind, node.inputs.length, this.#style, scale)
526
+ });
527
+ if (segments.has(node.id)) this.stateMarks.push({
528
+ key: node.id,
529
+ x: fix(x + widths[c] + 3),
530
+ y: vertex.y,
531
+ state: node.state
532
+ });
533
+ }
534
+ });
535
+ this.#fit(segments, at + room.right, scale);
536
+ for (const net of this.#nets.values()) {
537
+ const list = segments.get(net.id) ?? [];
538
+ if (!list.length) continue;
539
+ const state = net.driver?.state ?? LogicState.Unknown;
540
+ this.nets.push({
541
+ id: net.id,
542
+ state,
543
+ driver: net.driver,
544
+ fanout: net.sinks.length,
545
+ segments: list,
546
+ path: list.map((s) => `M${s.x1},${s.y1}L${s.x2},${s.y2}`).join("")
547
+ });
548
+ for (const point of LogicCircuitLayout.junctions(list)) this.junctions.push({
549
+ key: `${net.id}@${key(point.x, point.y)}`,
550
+ ...point,
551
+ state
552
+ });
553
+ }
554
+ }
555
+ /** Everything is routed by now, so a drawing wider or taller than the plot can be scaled whole. */
556
+ #fit(segments, right, scale) {
557
+ const { width, height } = this.#size;
558
+ let left = 0, top = 0, wide = Math.max(width, right), low = height;
559
+ const take = (x1, y1, x2, y2) => {
560
+ left = Math.min(left, x1);
561
+ wide = Math.max(wide, x2);
562
+ top = Math.min(top, y1);
563
+ low = Math.max(low, y2);
564
+ };
565
+ for (const list of segments.values()) for (const s of list) take(Math.min(s.x1, s.x2), Math.min(s.y1, s.y2), Math.max(s.x1, s.x2), Math.max(s.y1, s.y2));
566
+ for (const gate of this.gates) take(gate.x, gate.y - gate.shape.height / 2, gate.x + gate.shape.width, gate.y + gate.shape.height / 2);
567
+ const chip = this.#chip(scale) / 2;
568
+ for (const terminal of this.terminals) take(terminal.x - chip, terminal.y - chip, terminal.x + chip, terminal.y + chip);
569
+ const ratio = Math.min(1, width / (wide - left), height / (low - top));
570
+ this.view = {
571
+ x: fix((width - (wide - left) * ratio) / 2 - left * ratio),
572
+ y: fix((height - (low - top) * ratio) / 2 - top * ratio),
573
+ scale: ratio
574
+ };
575
+ }
576
+ #clip(label, room) {
577
+ return DiagramText.fit(label, room - LABEL_GAP, LABEL, NARROW);
578
+ }
579
+ /** Points where one net's copper leaves in three or more directions: these are the connections a dot must mark. */
580
+ static junctions(segments) {
581
+ const points = /* @__PURE__ */ new Map();
582
+ for (const s of segments) for (const [x, y] of [[s.x1, s.y1], [s.x2, s.y2]]) points.set(key(x, y), {
583
+ x,
584
+ y
585
+ });
586
+ return [...points.values()].filter(({ x, y }) => {
587
+ const ways = /* @__PURE__ */ new Set();
588
+ for (const s of segments) {
589
+ const [x1, x2] = [Math.min(s.x1, s.x2), Math.max(s.x1, s.x2)], [y1, y2] = [Math.min(s.y1, s.y2), Math.max(s.y1, s.y2)];
590
+ if (x < x1 || x > x2 || y < y1 || y > y2) continue;
591
+ if (x > x1) ways.add("west");
592
+ if (x < x2) ways.add("east");
593
+ if (y > y1) ways.add("north");
594
+ if (y < y2) ways.add("south");
595
+ }
596
+ return ways.size >= 3;
597
+ });
598
+ }
599
+ };
600
+ export { LogicCircuitLayout };
@@ -0,0 +1,66 @@
1
+ // src/charts/diagrams/LogicCircuitSamples.ts
2
+ var WIRES = [
3
+ ["A", "U1"],
4
+ ["B", "U1"],
5
+ ["B", "U2"],
6
+ ["C", "U2"],
7
+ ["C", "U3"],
8
+ ["A", "U4"],
9
+ ["U1", "U5"],
10
+ ["U2", "U5"],
11
+ ["U2", "U6"],
12
+ ["U3", "U6"],
13
+ ["U5", "U7"],
14
+ ["U6", "U7"],
15
+ ["U6", "U8"],
16
+ ["U4", "U8"],
17
+ ["U7", "Y"],
18
+ ["U8", "Z"]
19
+ ];
20
+ /** Synthetic netlists for samples: invented for illustration, not taken from a real design. */
21
+ var LogicCircuitSamples = class {
22
+ /** A made-up three-input circuit using every primitive once. Bits of the seed set the inputs. */
23
+ static everyGate(seed) {
24
+ const inputs = [
25
+ "A",
26
+ "B",
27
+ "C"
28
+ ].map((id, bit) => ({
29
+ id,
30
+ type: "input",
31
+ value: seed >> bit & 1
32
+ }));
33
+ const gates = [
34
+ "nand",
35
+ "nor",
36
+ "not",
37
+ "buffer",
38
+ "xor",
39
+ "xnor",
40
+ "and",
41
+ "or"
42
+ ].map((type, i) => ({
43
+ id: `U${i + 1}`,
44
+ type
45
+ }));
46
+ return {
47
+ gates: [
48
+ ...inputs,
49
+ ...gates,
50
+ {
51
+ id: "Y",
52
+ type: "output"
53
+ },
54
+ {
55
+ id: "Z",
56
+ type: "output"
57
+ }
58
+ ],
59
+ wires: WIRES.map(([from, to]) => ({
60
+ from,
61
+ to
62
+ }))
63
+ };
64
+ }
65
+ };
66
+ export { LogicCircuitSamples };