@tscircuit/3d-viewer 0.0.599 → 0.0.601
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 +100 -0
- package/dist/index.js +1011 -30
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -146,6 +146,106 @@ Each component has specific props for defining its characteristics and position
|
|
|
146
146
|
|
|
147
147
|
## Advanced Usage
|
|
148
148
|
|
|
149
|
+
### Non-blocking renderer comparison diagnostics
|
|
150
|
+
|
|
151
|
+
The `Diagnostics/Renderer Parity` stories compare the actual viewer with
|
|
152
|
+
`circuit-json-to-gltf` using the same Circuit JSON and local model assets:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
bun run storybook:comparisons
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This opens `Diagnostics/Renderer Parity / X Rotation` directly instead of
|
|
159
|
+
restoring an unrelated story. The ordinary `bun run storybook` entry point is
|
|
160
|
+
unchanged.
|
|
161
|
+
|
|
162
|
+
Each comparison story automatically displays **both oblique and side geometry**
|
|
163
|
+
once its model geometry loads. Both panels remain visible, with the exact
|
|
164
|
+
unlit/no-texture PNGs, edge maps, red/cyan overlays, and metrics used by the
|
|
165
|
+
browser tests. There are no comparison buttons. Click an image to download its
|
|
166
|
+
full-resolution PNG. Bottom-layer fixtures use cameras below the PCB.
|
|
167
|
+
|
|
168
|
+
circuit-json-to-gltf is a development-only dependency. A Bun preparation step generates
|
|
169
|
+
GLBs and records circuit-json-to-gltf failures; it is not imported into the viewer's
|
|
170
|
+
production bundle or the browser story. Model files are local, not fetched from
|
|
171
|
+
ModelCDN during the tests.
|
|
172
|
+
|
|
173
|
+
`build-storybook` also prepares the fixture data, so the existing `vercel-build`
|
|
174
|
+
entry point publishes working comparison stories. Vercel hosts those static
|
|
175
|
+
stories; Playwright diagnostics run separately in GitHub Actions.
|
|
176
|
+
|
|
177
|
+
The browser suite has two separate roles:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Blocking tests of the matcher itself and deliberate browser mutations
|
|
181
|
+
bun test ./tests/geometry-edge-*.test.ts ./tests/renderer-diagnostic-result.test.ts ./tests/renderer-comparison-*.test.ts ./tests/renderer-usb-mounting.test.ts
|
|
182
|
+
bun run test:renderer-calibration
|
|
183
|
+
|
|
184
|
+
# Diagnostic comparisons: failures are reported but do not gate a completed run
|
|
185
|
+
bun run test:renderer-comparisons
|
|
186
|
+
bunx --no-install playwright show-report
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Install the test browser with `bunx playwright install chromium` if Playwright
|
|
190
|
+
reports that its Chromium executable is missing.
|
|
191
|
+
|
|
192
|
+
CI uses Ubuntu, Node 22, Bun 1.3.14 and the Chromium version paired with the
|
|
193
|
+
pinned Playwright dependency. `playwright install --with-deps chromium`
|
|
194
|
+
installs the Linux browser libraries; captures use software WebGL and require
|
|
195
|
+
no physical GPU. Playwright executes under Node even when launched by a Bun
|
|
196
|
+
package script.
|
|
197
|
+
|
|
198
|
+
A local Node 26 `module.register()` deprecation warning is not a test failure;
|
|
199
|
+
Node 22 is the CI runtime. Vite messages about missing dependency source maps
|
|
200
|
+
(for example `manifold-3d/lib/wasm.js.map`) concern debugger metadata, not a
|
|
201
|
+
missing JavaScript or WebAssembly module. A run ending in `passed` completed
|
|
202
|
+
successfully. Runtime/model-loading errors are still surfaced normally; the
|
|
203
|
+
harness does not blanket-suppress logs.
|
|
204
|
+
|
|
205
|
+
Actual comparisons are ordinary failing Playwright assertions, not skipped or
|
|
206
|
+
automatically approved baselines. The runner retains those failures and their
|
|
207
|
+
artifacts but returns success after a completed diagnostic report, making it
|
|
208
|
+
non-blocking for local gate runners too. Missing reports, global runner errors,
|
|
209
|
+
and interrupted runs still fail. For strict exit behavior, invoke
|
|
210
|
+
`bunx playwright test --project=diagnostics` directly.
|
|
211
|
+
|
|
212
|
+
The CI comparison step additionally has `continue-on-error: true`.
|
|
213
|
+
Dependency setup, matcher/runner unit tests, and browser calibration remain blocking.
|
|
214
|
+
The workflow always uploads the HTML report, geometry images, edge maps, diff
|
|
215
|
+
overlays, metadata, and failure traces.
|
|
216
|
+
|
|
217
|
+
The geometry pass isolates the studied CAD component, preserves its actual
|
|
218
|
+
world placement, and uses neutral unlit surfaces rather than textures/shadows.
|
|
219
|
+
The exported GLB receives only the fixed frame conversion back to project
|
|
220
|
+
coordinates: `P = (-G.x, G.z, G.y)`. Both sides use the same orthographic camera
|
|
221
|
+
and viewport. There is no recentering, image registration, or per-renderer fit.
|
|
222
|
+
|
|
223
|
+
The matcher checks edge coverage in both directions with a 1.5-pixel tolerance
|
|
224
|
+
and at most 1% unmatched edges on each side. It does not average differences
|
|
225
|
+
over the whole PCB image. Browser calibration requires identical geometry to
|
|
226
|
+
match and deliberate X-sign, rotation-order, and 0.2248885 mm origin errors to
|
|
227
|
+
be rejected. Pixel agreement is not a substitute for the existing numerical
|
|
228
|
+
placement tests, and it is not a test of material or normal appearance.
|
|
229
|
+
The calibration case has no exported GLB and compares viewer geometry only, so
|
|
230
|
+
browser-side exporter failures remain in the non-blocking diagnostic stage.
|
|
231
|
+
Diff overlays show unmatched viewer edges in red, unmatched exporter edges in
|
|
232
|
+
cyan, and covered edges in gray.
|
|
233
|
+
|
|
234
|
+
Cases keep rotation, origin inference, format dispatch, and the physical USB
|
|
235
|
+
mounting example separate. Explicit-origin zero/Z cases are controls; missing-origin
|
|
236
|
+
cases retain their original alignment tags. Generated output lives in ignored
|
|
237
|
+
directories, never committed PNG baselines. See
|
|
238
|
+
[`tests/fixtures/renderer-parity/README.md`](tests/fixtures/renderer-parity/README.md)
|
|
239
|
+
for source provenance and the common-board adaptation.
|
|
240
|
+
|
|
241
|
+
Nonzero angle probes use oblique values (for example X=37, Y=30, Z=47, and
|
|
242
|
+
mixed 23/31/47), not quarter turns that can conceal symmetry and axis mistakes.
|
|
243
|
+
Zero angles and the renderer's implicit bottom-layer fallback remain deliberate
|
|
244
|
+
controls. The physical USB mounting fixture is a documented exception: its
|
|
245
|
+
native Z-up mesh requires Z=270 degrees to fit the footprint. Actual tab/hole
|
|
246
|
+
and contact/pad geometry is checked separately. The original USB missing-origin
|
|
247
|
+
input remains a distinct diagnostic, not the mounted pose's zero-angle control.
|
|
248
|
+
|
|
149
249
|
### Custom Component Models
|
|
150
250
|
|
|
151
251
|
You can define custom 3D models for components using the `cadModel` prop:
|
package/dist/index.js
CHANGED
|
@@ -26343,6 +26343,292 @@ var FootprintPlatedHole = ({
|
|
|
26343
26343
|
}
|
|
26344
26344
|
throw new Error("Shape not supported: " + hole.shape);
|
|
26345
26345
|
};
|
|
26346
|
+
function LGA(p) {
|
|
26347
|
+
for (const k of [
|
|
26348
|
+
"bodyWidth",
|
|
26349
|
+
"bodyLength",
|
|
26350
|
+
"bodyHeight",
|
|
26351
|
+
"pitch",
|
|
26352
|
+
"landWidth",
|
|
26353
|
+
"landLength",
|
|
26354
|
+
"landThickness"
|
|
26355
|
+
])
|
|
26356
|
+
if (!Number.isFinite(p[k]) || p[k] <= 0)
|
|
26357
|
+
throw new Error(`${k} must be finite and positive`);
|
|
26358
|
+
for (const k of ["landsPerSideX", "landsPerSideY"])
|
|
26359
|
+
if (!Number.isInteger(p[k]) || p[k] < 0)
|
|
26360
|
+
throw new Error(`${k} must be a nonnegative integer`);
|
|
26361
|
+
if (!Number.isFinite(p.edgeInset) || p.edgeInset < 0 || p.landThickness >= p.bodyHeight || p.landWidth >= p.pitch || p.landsPerSideX + p.landsPerSideY < 1)
|
|
26362
|
+
throw new Error("Invalid land layout or thickness");
|
|
26363
|
+
const layouts = [
|
|
26364
|
+
{
|
|
26365
|
+
count: p.landsPerSideX,
|
|
26366
|
+
across: p.bodyWidth,
|
|
26367
|
+
along: p.bodyLength,
|
|
26368
|
+
horizontal: true
|
|
26369
|
+
},
|
|
26370
|
+
{
|
|
26371
|
+
count: p.landsPerSideY,
|
|
26372
|
+
across: p.bodyLength,
|
|
26373
|
+
along: p.bodyWidth,
|
|
26374
|
+
horizontal: false
|
|
26375
|
+
}
|
|
26376
|
+
];
|
|
26377
|
+
for (const l of layouts) {
|
|
26378
|
+
if (l.count === 0) continue;
|
|
26379
|
+
const row = (l.count - 1) * p.pitch + p.landWidth;
|
|
26380
|
+
if (row >= l.along - 2 * p.edgeInset || 2 * (p.edgeInset + p.landLength) >= l.across)
|
|
26381
|
+
throw new Error(
|
|
26382
|
+
"Lands must fit below the body without opposite-row overlap"
|
|
26383
|
+
);
|
|
26384
|
+
const other = l.horizontal ? p.landsPerSideY : p.landsPerSideX;
|
|
26385
|
+
if (other > 0 && row >= l.along - 2 * (p.edgeInset + p.landLength) && (other - 1) * p.pitch + p.landWidth >= l.across - 2 * (p.edgeInset + p.landLength))
|
|
26386
|
+
throw new Error("Adjacent land rows overlap at corners");
|
|
26387
|
+
}
|
|
26388
|
+
const r = Math.min(p.bodyWidth, p.bodyLength) * 0.035;
|
|
26389
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
26390
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#353535", children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
26391
|
+
/* @__PURE__ */ jsx2(Translate, { z: (p.bodyHeight + p.landThickness / 2) / 2, children: /* @__PURE__ */ jsx2(
|
|
26392
|
+
Cuboid,
|
|
26393
|
+
{
|
|
26394
|
+
size: [
|
|
26395
|
+
p.bodyWidth,
|
|
26396
|
+
p.bodyLength,
|
|
26397
|
+
p.bodyHeight - p.landThickness / 2
|
|
26398
|
+
]
|
|
26399
|
+
}
|
|
26400
|
+
) }),
|
|
26401
|
+
/* @__PURE__ */ jsx2(
|
|
26402
|
+
Translate,
|
|
26403
|
+
{
|
|
26404
|
+
offset: [
|
|
26405
|
+
-p.bodyWidth / 2 + 2 * r,
|
|
26406
|
+
p.bodyLength / 2 - 2 * r,
|
|
26407
|
+
p.bodyHeight
|
|
26408
|
+
],
|
|
26409
|
+
children: /* @__PURE__ */ jsx2(Cylinder, { radius: r, height: Math.min(0.04, p.bodyHeight / 10) })
|
|
26410
|
+
}
|
|
26411
|
+
)
|
|
26412
|
+
] }) }),
|
|
26413
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#c5b987", children: layouts.flatMap(
|
|
26414
|
+
(l) => [-1, 1].flatMap(
|
|
26415
|
+
(sign) => Array.from({ length: l.count }, (_, i) => {
|
|
26416
|
+
const across = sign * (l.across / 2 - p.edgeInset - p.landLength / 2), along = ((l.count - 1) / 2 - i) * p.pitch;
|
|
26417
|
+
return /* @__PURE__ */ jsx2(
|
|
26418
|
+
Translate,
|
|
26419
|
+
{
|
|
26420
|
+
offset: [
|
|
26421
|
+
l.horizontal ? across : along,
|
|
26422
|
+
l.horizontal ? along : across,
|
|
26423
|
+
p.landThickness / 2
|
|
26424
|
+
],
|
|
26425
|
+
children: /* @__PURE__ */ jsx2(
|
|
26426
|
+
Cuboid,
|
|
26427
|
+
{
|
|
26428
|
+
size: l.horizontal ? [p.landLength, p.landWidth, p.landThickness] : [p.landWidth, p.landLength, p.landThickness]
|
|
26429
|
+
}
|
|
26430
|
+
)
|
|
26431
|
+
},
|
|
26432
|
+
`${l.horizontal}-${sign}-${i}`
|
|
26433
|
+
);
|
|
26434
|
+
})
|
|
26435
|
+
)
|
|
26436
|
+
) })
|
|
26437
|
+
] });
|
|
26438
|
+
}
|
|
26439
|
+
function createDualRowGullWing(p) {
|
|
26440
|
+
const {
|
|
26441
|
+
pinCount,
|
|
26442
|
+
pitch,
|
|
26443
|
+
bodyWidth,
|
|
26444
|
+
bodyLength: bodyLength10,
|
|
26445
|
+
bodyHeight,
|
|
26446
|
+
standoff,
|
|
26447
|
+
leadSpan,
|
|
26448
|
+
leadWidth,
|
|
26449
|
+
leadThickness,
|
|
26450
|
+
contactLength,
|
|
26451
|
+
taperInset,
|
|
26452
|
+
exposedPadWidth = 0,
|
|
26453
|
+
exposedPadLength = 0
|
|
26454
|
+
} = p;
|
|
26455
|
+
for (const key of [
|
|
26456
|
+
"pitch",
|
|
26457
|
+
"bodyWidth",
|
|
26458
|
+
"bodyLength",
|
|
26459
|
+
"bodyHeight",
|
|
26460
|
+
"leadSpan",
|
|
26461
|
+
"leadWidth",
|
|
26462
|
+
"leadThickness",
|
|
26463
|
+
"contactLength"
|
|
26464
|
+
])
|
|
26465
|
+
if (!Number.isFinite(p[key]) || p[key] <= 0)
|
|
26466
|
+
throw new Error(`${key} must be finite and positive`);
|
|
26467
|
+
for (const [name, value] of Object.entries({
|
|
26468
|
+
standoff,
|
|
26469
|
+
taperInset,
|
|
26470
|
+
exposedPadWidth,
|
|
26471
|
+
exposedPadLength
|
|
26472
|
+
}))
|
|
26473
|
+
if (!Number.isFinite(value) || value < 0)
|
|
26474
|
+
throw new Error(`${name} must be finite and nonnegative`);
|
|
26475
|
+
if (!Number.isInteger(pinCount) || pinCount < 4 || pinCount % 2)
|
|
26476
|
+
throw new Error("pinCount must be even and at least 4");
|
|
26477
|
+
const moldHeight = bodyHeight - standoff;
|
|
26478
|
+
const reach = (leadSpan - bodyWidth) / 2;
|
|
26479
|
+
const rowLength = (pinCount / 2 - 1) * pitch + leadWidth;
|
|
26480
|
+
if (moldHeight <= leadThickness * 2 || leadWidth >= pitch || rowLength >= bodyLength10 - 2 * taperInset || taperInset * 2 >= Math.min(bodyWidth, bodyLength10) || contactLength >= reach)
|
|
26481
|
+
throw new Error("Invalid mold, lead spacing, or terminal reach");
|
|
26482
|
+
if (exposedPadWidth === 0 !== (exposedPadLength === 0) || exposedPadWidth >= bodyWidth - 2 * taperInset || exposedPadLength >= bodyLength10 - 2 * taperInset)
|
|
26483
|
+
throw new Error("Exposed pad must fit inside the bottom mold face");
|
|
26484
|
+
const midZ = standoff + moldHeight / 2;
|
|
26485
|
+
const slice = Math.min(2e-3, moldHeight / 100);
|
|
26486
|
+
const body = /* @__PURE__ */ jsxs(Hull, { children: [
|
|
26487
|
+
/* @__PURE__ */ jsx2(Translate, { z: standoff + slice / 2, children: /* @__PURE__ */ jsx2(
|
|
26488
|
+
Cuboid,
|
|
26489
|
+
{
|
|
26490
|
+
size: [
|
|
26491
|
+
bodyWidth - 2 * taperInset,
|
|
26492
|
+
bodyLength10 - 2 * taperInset,
|
|
26493
|
+
slice
|
|
26494
|
+
]
|
|
26495
|
+
}
|
|
26496
|
+
) }),
|
|
26497
|
+
/* @__PURE__ */ jsx2(Translate, { z: midZ, children: /* @__PURE__ */ jsx2(Cuboid, { size: [bodyWidth, bodyLength10, slice] }) }),
|
|
26498
|
+
/* @__PURE__ */ jsx2(Translate, { z: bodyHeight - slice / 2, children: /* @__PURE__ */ jsx2(
|
|
26499
|
+
Cuboid,
|
|
26500
|
+
{
|
|
26501
|
+
size: [
|
|
26502
|
+
bodyWidth - 2 * taperInset,
|
|
26503
|
+
bodyLength10 - 2 * taperInset,
|
|
26504
|
+
slice
|
|
26505
|
+
]
|
|
26506
|
+
}
|
|
26507
|
+
) })
|
|
26508
|
+
] });
|
|
26509
|
+
const dimpleRadius = Math.min(bodyWidth, bodyLength10) * 0.045;
|
|
26510
|
+
const dimpleDepth = Math.min(0.04, moldHeight / 10);
|
|
26511
|
+
const lead = (side, y) => {
|
|
26512
|
+
const outer = leadSpan / 2;
|
|
26513
|
+
const root = bodyWidth / 2 - Math.min(bodyWidth / 8, 0.15);
|
|
26514
|
+
const bendStart = outer - contactLength;
|
|
26515
|
+
const bendEnd = bodyWidth / 2 + Math.min(0.08, (reach - contactLength) / 4);
|
|
26516
|
+
const points = [
|
|
26517
|
+
[outer - slice / 2, leadThickness / 2],
|
|
26518
|
+
[bendStart, leadThickness / 2]
|
|
26519
|
+
];
|
|
26520
|
+
for (let i = 1; i <= 12; i++) {
|
|
26521
|
+
const t = i / 12;
|
|
26522
|
+
points.push([
|
|
26523
|
+
bendStart + (bendEnd - bendStart) * t,
|
|
26524
|
+
leadThickness / 2 + (midZ - leadThickness / 2) * t * t * (3 - 2 * t)
|
|
26525
|
+
]);
|
|
26526
|
+
}
|
|
26527
|
+
points.push([root, midZ]);
|
|
26528
|
+
const section = ([x, z22]) => /* @__PURE__ */ jsx2(Translate, { offset: { x: side * x, y, z: z22 }, children: /* @__PURE__ */ jsx2(Cuboid, { size: [slice, leadWidth, leadThickness] }) });
|
|
26529
|
+
return /* @__PURE__ */ jsx2(Union, { children: points.slice(1).map((point, i) => /* @__PURE__ */ jsxs(Hull, { children: [
|
|
26530
|
+
section(points[i]),
|
|
26531
|
+
section(point)
|
|
26532
|
+
] }, i)) });
|
|
26533
|
+
};
|
|
26534
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
26535
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#353535", children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
26536
|
+
body,
|
|
26537
|
+
/* @__PURE__ */ jsx2(
|
|
26538
|
+
Translate,
|
|
26539
|
+
{
|
|
26540
|
+
offset: {
|
|
26541
|
+
x: -bodyWidth / 2 + taperInset + dimpleRadius * 2,
|
|
26542
|
+
y: bodyLength10 / 2 - taperInset - dimpleRadius * 2,
|
|
26543
|
+
z: bodyHeight
|
|
26544
|
+
},
|
|
26545
|
+
children: /* @__PURE__ */ jsx2(Cylinder, { radius: dimpleRadius, height: dimpleDepth * 2 })
|
|
26546
|
+
}
|
|
26547
|
+
)
|
|
26548
|
+
] }) }),
|
|
26549
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#bfc1c4", children: Array.from(
|
|
26550
|
+
{ length: pinCount },
|
|
26551
|
+
(_, i) => lead(
|
|
26552
|
+
i < pinCount / 2 ? -1 : 1,
|
|
26553
|
+
((pinCount / 2 - 1) / 2 - i % (pinCount / 2)) * pitch
|
|
26554
|
+
)
|
|
26555
|
+
) }),
|
|
26556
|
+
exposedPadWidth > 0 && /* @__PURE__ */ jsx2(Colorize, { color: "#bfc1c4", children: /* @__PURE__ */ jsx2(Translate, { z: (standoff + leadThickness) / 2, children: /* @__PURE__ */ jsx2(
|
|
26557
|
+
Cuboid,
|
|
26558
|
+
{
|
|
26559
|
+
size: [
|
|
26560
|
+
exposedPadWidth,
|
|
26561
|
+
exposedPadLength,
|
|
26562
|
+
standoff + leadThickness
|
|
26563
|
+
]
|
|
26564
|
+
}
|
|
26565
|
+
) }) })
|
|
26566
|
+
] });
|
|
26567
|
+
}
|
|
26568
|
+
var SSOP = (props) => createDualRowGullWing(props);
|
|
26569
|
+
function renderFootprinterBodyModel(p) {
|
|
26570
|
+
if (p.bodywidth === void 0 && p.bodyheight === void 0 && p.bodythickness === void 0)
|
|
26571
|
+
return null;
|
|
26572
|
+
if (p.fn === "ssop") {
|
|
26573
|
+
const bodyWidth = p.bodywidth ?? p.w * 0.55;
|
|
26574
|
+
const bodyLength10 = p.bodyheight ?? (p.num_pins / 2 - 1) * p.p + p.pw + 0.4;
|
|
26575
|
+
const thickness = p.bodythickness ?? 1;
|
|
26576
|
+
const leadWidth = Math.min(p.pw, p.p * 0.5);
|
|
26577
|
+
const rowLength = (p.num_pins / 2 - 1) * p.p + leadWidth;
|
|
26578
|
+
if (bodyLength10 <= rowLength)
|
|
26579
|
+
throw new Error("SSOP bodyheight must contain the lead row");
|
|
26580
|
+
const leadSpan = Math.max(p.w + 0.2 + p.pl / 2, bodyWidth + p.p);
|
|
26581
|
+
const standoff = Math.min(0.1, thickness / 10);
|
|
26582
|
+
return /* @__PURE__ */ jsx2(
|
|
26583
|
+
SSOP,
|
|
26584
|
+
{
|
|
26585
|
+
pinCount: p.num_pins,
|
|
26586
|
+
pitch: p.p,
|
|
26587
|
+
bodyWidth,
|
|
26588
|
+
bodyLength: bodyLength10,
|
|
26589
|
+
bodyHeight: thickness + standoff,
|
|
26590
|
+
standoff,
|
|
26591
|
+
leadSpan,
|
|
26592
|
+
leadWidth,
|
|
26593
|
+
leadThickness: Math.min(0.15, thickness / 4),
|
|
26594
|
+
contactLength: Math.min(p.pl / 2, (leadSpan - bodyWidth) / 4),
|
|
26595
|
+
taperInset: Math.min(0.12, (bodyLength10 - rowLength) / 4, bodyWidth / 8)
|
|
26596
|
+
}
|
|
26597
|
+
);
|
|
26598
|
+
}
|
|
26599
|
+
if (p.fn === "lga") {
|
|
26600
|
+
const bodyWidth = p.bodywidth ?? p.w;
|
|
26601
|
+
const bodyLength10 = p.bodyheight ?? p.h;
|
|
26602
|
+
const thickness = p.bodythickness ?? 1;
|
|
26603
|
+
const grid2 = p.grid;
|
|
26604
|
+
const landWidth = Math.min(p.pw, p.p / 2);
|
|
26605
|
+
const roomX = bodyWidth - ((grid2.y - 1) * p.p + landWidth);
|
|
26606
|
+
const roomY = bodyLength10 - ((grid2.x - 1) * p.p + landWidth);
|
|
26607
|
+
const landLength = Math.min(
|
|
26608
|
+
p.pl / 2,
|
|
26609
|
+
bodyWidth / 8,
|
|
26610
|
+
bodyLength10 / 8,
|
|
26611
|
+
roomX / 4,
|
|
26612
|
+
roomY / 4
|
|
26613
|
+
);
|
|
26614
|
+
return /* @__PURE__ */ jsx2(
|
|
26615
|
+
LGA,
|
|
26616
|
+
{
|
|
26617
|
+
bodyWidth,
|
|
26618
|
+
bodyLength: bodyLength10,
|
|
26619
|
+
bodyHeight: thickness,
|
|
26620
|
+
landsPerSideX: grid2.x,
|
|
26621
|
+
landsPerSideY: grid2.y,
|
|
26622
|
+
pitch: p.p,
|
|
26623
|
+
landWidth,
|
|
26624
|
+
landLength,
|
|
26625
|
+
edgeInset: 0,
|
|
26626
|
+
landThickness: Math.min(0.015, thickness / 10)
|
|
26627
|
+
}
|
|
26628
|
+
);
|
|
26629
|
+
}
|
|
26630
|
+
return null;
|
|
26631
|
+
}
|
|
26346
26632
|
var ChipBody = ({
|
|
26347
26633
|
center,
|
|
26348
26634
|
width: width10,
|
|
@@ -26591,7 +26877,7 @@ var SmdChipLead = (props) => {
|
|
|
26591
26877
|
const polygon2 = getExpandedStroke(points, thickness);
|
|
26592
26878
|
return /* @__PURE__ */ jsx2(Colorize, { color: "#fff", children: /* @__PURE__ */ jsx2(Translate, { offset: { z: 0, y: 0, x: 0, ...props.position }, children: /* @__PURE__ */ jsx2(Rotate, { rotation: ["90deg", 0, rotation ?? 0], children: /* @__PURE__ */ jsx2(Translate, { offset: { x: 0, y: 0, z: -width10 / 2 }, children: /* @__PURE__ */ jsx2(ExtrudeLinear, { height: width10, children: /* @__PURE__ */ jsx2(Polygon, { points: polygon2.map((p) => [p.x, p.y]) }) }) }) }) }) });
|
|
26593
26879
|
};
|
|
26594
|
-
var
|
|
26880
|
+
var LegacyTssop = ({
|
|
26595
26881
|
pinCount,
|
|
26596
26882
|
leadLength,
|
|
26597
26883
|
leadWidth,
|
|
@@ -26669,7 +26955,8 @@ var Tssop = ({
|
|
|
26669
26955
|
)
|
|
26670
26956
|
] });
|
|
26671
26957
|
};
|
|
26672
|
-
var
|
|
26958
|
+
var Tssop = (props) => "leadSpan" in props ? createDualRowGullWing(props) : LegacyTssop(props);
|
|
26959
|
+
var LegacyMSOP = ({
|
|
26673
26960
|
pinCount,
|
|
26674
26961
|
padContactLength = 0.4,
|
|
26675
26962
|
leadWidth = 0.2,
|
|
@@ -26727,6 +27014,7 @@ var MSOP = ({
|
|
|
26727
27014
|
)
|
|
26728
27015
|
] });
|
|
26729
27016
|
};
|
|
27017
|
+
var MSOP = (props) => "leadSpan" in props ? createDualRowGullWing(props) : LegacyMSOP(props);
|
|
26730
27018
|
var fullLength = 1;
|
|
26731
27019
|
var width = 0.5;
|
|
26732
27020
|
var height = 0.5;
|
|
@@ -27105,6 +27393,103 @@ var PinRow = ({
|
|
|
27105
27393
|
);
|
|
27106
27394
|
}) });
|
|
27107
27395
|
};
|
|
27396
|
+
function createPhysicalQfn(p) {
|
|
27397
|
+
const {
|
|
27398
|
+
num_pins,
|
|
27399
|
+
bodyWidth,
|
|
27400
|
+
bodyLength: bodyLength10,
|
|
27401
|
+
bodyHeight,
|
|
27402
|
+
standoff,
|
|
27403
|
+
terminalSpanX,
|
|
27404
|
+
terminalSpanY,
|
|
27405
|
+
terminalThickness,
|
|
27406
|
+
padWidth,
|
|
27407
|
+
padLength,
|
|
27408
|
+
pitch,
|
|
27409
|
+
exposedPadWidth,
|
|
27410
|
+
exposedPadLength
|
|
27411
|
+
} = p;
|
|
27412
|
+
for (const key of [
|
|
27413
|
+
"bodyWidth",
|
|
27414
|
+
"bodyLength",
|
|
27415
|
+
"bodyHeight",
|
|
27416
|
+
"terminalSpanX",
|
|
27417
|
+
"terminalSpanY",
|
|
27418
|
+
"terminalThickness",
|
|
27419
|
+
"padWidth",
|
|
27420
|
+
"padLength",
|
|
27421
|
+
"pitch",
|
|
27422
|
+
"exposedPadWidth",
|
|
27423
|
+
"exposedPadLength"
|
|
27424
|
+
])
|
|
27425
|
+
if (!Number.isFinite(p[key]) || p[key] <= 0)
|
|
27426
|
+
throw new Error(`${key} must be finite and positive`);
|
|
27427
|
+
if (!Number.isInteger(num_pins) || num_pins < 8 || num_pins % 4)
|
|
27428
|
+
throw new Error("num_pins must be divisible by four and at least eight");
|
|
27429
|
+
if (!Number.isFinite(standoff) || standoff < 0)
|
|
27430
|
+
throw new Error("standoff must be finite and nonnegative");
|
|
27431
|
+
const rowLength = (num_pins / 4 - 1) * pitch + padWidth;
|
|
27432
|
+
if (terminalThickness <= standoff || terminalThickness >= bodyHeight || padWidth >= pitch || rowLength >= Math.min(bodyWidth, bodyLength10))
|
|
27433
|
+
throw new Error("Invalid mold, terminal thickness or spacing");
|
|
27434
|
+
if (terminalSpanX < bodyWidth || terminalSpanY < bodyLength10 || terminalSpanX / 2 - padLength >= bodyWidth / 2 || terminalSpanY / 2 - padLength >= bodyLength10 / 2 || exposedPadWidth >= Math.min(bodyWidth, terminalSpanX - 2 * padLength) || exposedPadLength >= Math.min(bodyLength10, terminalSpanY - 2 * padLength) || rowLength >= Math.min(terminalSpanX, terminalSpanY) - 2 * padLength)
|
|
27435
|
+
throw new Error(
|
|
27436
|
+
"Terminals must enter the mold and clear the exposed pad and corners"
|
|
27437
|
+
);
|
|
27438
|
+
const body = /* @__PURE__ */ jsx2(Translate, { z: (bodyHeight + standoff) / 2, children: /* @__PURE__ */ jsx2(Cuboid, { size: [bodyWidth, bodyLength10, bodyHeight - standoff] }) });
|
|
27439
|
+
const radius = Math.min(bodyWidth, bodyLength10) * 0.035;
|
|
27440
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
27441
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#353535", children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
27442
|
+
body,
|
|
27443
|
+
/* @__PURE__ */ jsx2(
|
|
27444
|
+
Translate,
|
|
27445
|
+
{
|
|
27446
|
+
offset: {
|
|
27447
|
+
x: -bodyWidth / 2 + radius * 2,
|
|
27448
|
+
y: bodyLength10 / 2 - radius * 2,
|
|
27449
|
+
z: bodyHeight
|
|
27450
|
+
},
|
|
27451
|
+
children: /* @__PURE__ */ jsx2(
|
|
27452
|
+
Cylinder,
|
|
27453
|
+
{
|
|
27454
|
+
radius,
|
|
27455
|
+
height: Math.min(0.06, (bodyHeight - standoff) / 5)
|
|
27456
|
+
}
|
|
27457
|
+
)
|
|
27458
|
+
}
|
|
27459
|
+
)
|
|
27460
|
+
] }) }),
|
|
27461
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#bfc1c4", children: Array.from({ length: num_pins }, (_, i) => {
|
|
27462
|
+
const side = Math.floor(i / (num_pins / 4));
|
|
27463
|
+
const along = ((num_pins / 4 - 1) / 2 - i % (num_pins / 4)) * pitch;
|
|
27464
|
+
const horizontal = side % 2 === 0;
|
|
27465
|
+
const sign = side < 2 ? -1 : 1;
|
|
27466
|
+
const across = ((horizontal ? terminalSpanX : terminalSpanY) - padLength) / 2 * sign;
|
|
27467
|
+
return /* @__PURE__ */ jsx2(
|
|
27468
|
+
Translate,
|
|
27469
|
+
{
|
|
27470
|
+
offset: {
|
|
27471
|
+
x: horizontal ? across : along,
|
|
27472
|
+
y: horizontal ? along : across,
|
|
27473
|
+
z: terminalThickness / 2
|
|
27474
|
+
},
|
|
27475
|
+
children: /* @__PURE__ */ jsx2(
|
|
27476
|
+
Cuboid,
|
|
27477
|
+
{
|
|
27478
|
+
size: horizontal ? [padLength, padWidth, terminalThickness] : [padWidth, padLength, terminalThickness]
|
|
27479
|
+
}
|
|
27480
|
+
)
|
|
27481
|
+
},
|
|
27482
|
+
i
|
|
27483
|
+
);
|
|
27484
|
+
}) }),
|
|
27485
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#bfc1c4", children: /* @__PURE__ */ jsx2(Translate, { z: terminalThickness / 2, children: /* @__PURE__ */ jsx2(
|
|
27486
|
+
Cuboid,
|
|
27487
|
+
{
|
|
27488
|
+
size: [exposedPadWidth, exposedPadLength, terminalThickness]
|
|
27489
|
+
}
|
|
27490
|
+
) }) })
|
|
27491
|
+
] });
|
|
27492
|
+
}
|
|
27108
27493
|
var getQuadCoords2 = (params) => {
|
|
27109
27494
|
const SIDES_CCW2 = ["left", "bottom", "right", "top"];
|
|
27110
27495
|
const { pin_count, pn, w, h: h2, p, pl, legsoutside } = params;
|
|
@@ -27188,7 +27573,7 @@ var getQuadPinMap2 = ({
|
|
|
27188
27573
|
}
|
|
27189
27574
|
return pin_map;
|
|
27190
27575
|
};
|
|
27191
|
-
var
|
|
27576
|
+
var LegacyQFN = ({
|
|
27192
27577
|
num_pins = 16,
|
|
27193
27578
|
bodyWidth = 9,
|
|
27194
27579
|
bodyLength: bodyLength10 = 9,
|
|
@@ -27258,6 +27643,7 @@ var QFN = ({
|
|
|
27258
27643
|
)
|
|
27259
27644
|
] });
|
|
27260
27645
|
};
|
|
27646
|
+
var QFN = (props) => "bodyHeight" in props ? createPhysicalQfn(props) : LegacyQFN(props);
|
|
27261
27647
|
var qfn_default = QFN;
|
|
27262
27648
|
var SOT235 = () => {
|
|
27263
27649
|
const fullWidth = 2.8;
|
|
@@ -28002,7 +28388,7 @@ var PushButtonLeg = (props) => {
|
|
|
28002
28388
|
}
|
|
28003
28389
|
) });
|
|
28004
28390
|
};
|
|
28005
|
-
var
|
|
28391
|
+
var LegacySOIC = ({
|
|
28006
28392
|
pinCount,
|
|
28007
28393
|
leadLength,
|
|
28008
28394
|
leadWidth,
|
|
@@ -28062,6 +28448,7 @@ var SOIC = ({
|
|
|
28062
28448
|
)
|
|
28063
28449
|
] });
|
|
28064
28450
|
};
|
|
28451
|
+
var SOIC = (props) => "leadSpan" in props ? createDualRowGullWing(props) : LegacySOIC(props);
|
|
28065
28452
|
var VSSOP = ({
|
|
28066
28453
|
pinCount,
|
|
28067
28454
|
pitch,
|
|
@@ -29110,7 +29497,140 @@ var SOT323 = () => {
|
|
|
29110
29497
|
)
|
|
29111
29498
|
] });
|
|
29112
29499
|
};
|
|
29113
|
-
|
|
29500
|
+
function createPhysicalLqfp(p) {
|
|
29501
|
+
const {
|
|
29502
|
+
pinCount,
|
|
29503
|
+
pitch,
|
|
29504
|
+
bodyWidth,
|
|
29505
|
+
bodyLength: bodyLength10,
|
|
29506
|
+
bodyHeight,
|
|
29507
|
+
standoff,
|
|
29508
|
+
leadSpanX,
|
|
29509
|
+
leadSpanY,
|
|
29510
|
+
leadWidth,
|
|
29511
|
+
leadThickness,
|
|
29512
|
+
contactLength,
|
|
29513
|
+
taperInset
|
|
29514
|
+
} = p;
|
|
29515
|
+
for (const key of [
|
|
29516
|
+
"pitch",
|
|
29517
|
+
"bodyWidth",
|
|
29518
|
+
"bodyLength",
|
|
29519
|
+
"bodyHeight",
|
|
29520
|
+
"leadSpanX",
|
|
29521
|
+
"leadSpanY",
|
|
29522
|
+
"leadWidth",
|
|
29523
|
+
"leadThickness",
|
|
29524
|
+
"contactLength"
|
|
29525
|
+
])
|
|
29526
|
+
if (!Number.isFinite(p[key]) || p[key] <= 0)
|
|
29527
|
+
throw new Error(`${key} must be finite and positive`);
|
|
29528
|
+
for (const [name, value] of Object.entries({
|
|
29529
|
+
standoff,
|
|
29530
|
+
taperInset
|
|
29531
|
+
}))
|
|
29532
|
+
if (!Number.isFinite(value) || value < 0)
|
|
29533
|
+
throw new Error(`${name} must be finite and nonnegative`);
|
|
29534
|
+
if (!Number.isInteger(pinCount) || pinCount < 8 || pinCount % 4)
|
|
29535
|
+
throw new Error("pinCount must be divisible by four and at least eight");
|
|
29536
|
+
const moldHeight = bodyHeight - standoff;
|
|
29537
|
+
const reach = Math.min(leadSpanX - bodyWidth, leadSpanY - bodyLength10) / 2;
|
|
29538
|
+
const rowLength = (pinCount / 4 - 1) * pitch + leadWidth;
|
|
29539
|
+
if (moldHeight <= leadThickness * 2 || leadWidth >= pitch || rowLength >= Math.min(bodyWidth, bodyLength10) - 2 * taperInset || taperInset * 2 >= Math.min(bodyWidth, bodyLength10) || contactLength >= reach)
|
|
29540
|
+
throw new Error("Invalid mold, lead spacing, or terminal reach");
|
|
29541
|
+
const midZ = standoff + moldHeight / 2;
|
|
29542
|
+
const slice = Math.min(2e-3, moldHeight / 100);
|
|
29543
|
+
const body = /* @__PURE__ */ jsxs(Hull, { children: [
|
|
29544
|
+
/* @__PURE__ */ jsx2(Translate, { z: standoff + slice / 2, children: /* @__PURE__ */ jsx2(
|
|
29545
|
+
Cuboid,
|
|
29546
|
+
{
|
|
29547
|
+
size: [
|
|
29548
|
+
bodyWidth - 2 * taperInset,
|
|
29549
|
+
bodyLength10 - 2 * taperInset,
|
|
29550
|
+
slice
|
|
29551
|
+
]
|
|
29552
|
+
}
|
|
29553
|
+
) }),
|
|
29554
|
+
/* @__PURE__ */ jsx2(Translate, { z: midZ, children: /* @__PURE__ */ jsx2(Cuboid, { size: [bodyWidth, bodyLength10, slice] }) }),
|
|
29555
|
+
/* @__PURE__ */ jsx2(Translate, { z: bodyHeight - slice / 2, children: /* @__PURE__ */ jsx2(
|
|
29556
|
+
Cuboid,
|
|
29557
|
+
{
|
|
29558
|
+
size: [
|
|
29559
|
+
bodyWidth - 2 * taperInset,
|
|
29560
|
+
bodyLength10 - 2 * taperInset,
|
|
29561
|
+
slice
|
|
29562
|
+
]
|
|
29563
|
+
}
|
|
29564
|
+
) })
|
|
29565
|
+
] });
|
|
29566
|
+
const dimpleRadius = Math.min(bodyWidth, bodyLength10) * 0.045;
|
|
29567
|
+
const dimpleDepth = Math.min(0.04, moldHeight / 10);
|
|
29568
|
+
const lead = (side, along) => {
|
|
29569
|
+
const horizontal = side % 2 === 0;
|
|
29570
|
+
const sign = side < 2 ? -1 : 1;
|
|
29571
|
+
const width10 = horizontal ? bodyWidth : bodyLength10;
|
|
29572
|
+
const outer = (horizontal ? leadSpanX : leadSpanY) / 2;
|
|
29573
|
+
const root = width10 / 2 - Math.min(width10 / 8, 0.15);
|
|
29574
|
+
const bendStart = outer - contactLength;
|
|
29575
|
+
const bendEnd = width10 / 2 + Math.min(0.08, (reach - contactLength) / 4);
|
|
29576
|
+
const points = [
|
|
29577
|
+
[outer - slice / 2, leadThickness / 2],
|
|
29578
|
+
[bendStart, leadThickness / 2]
|
|
29579
|
+
];
|
|
29580
|
+
for (let i = 1; i <= 12; i++) {
|
|
29581
|
+
const t = i / 12;
|
|
29582
|
+
points.push([
|
|
29583
|
+
bendStart + (bendEnd - bendStart) * t,
|
|
29584
|
+
leadThickness / 2 + (midZ - leadThickness / 2) * t * t * (3 - 2 * t)
|
|
29585
|
+
]);
|
|
29586
|
+
}
|
|
29587
|
+
points.push([root, midZ]);
|
|
29588
|
+
const section = ([x, z22]) => /* @__PURE__ */ jsx2(
|
|
29589
|
+
Translate,
|
|
29590
|
+
{
|
|
29591
|
+
offset: {
|
|
29592
|
+
x: horizontal ? sign * x : along,
|
|
29593
|
+
y: horizontal ? along : sign * x,
|
|
29594
|
+
z: z22
|
|
29595
|
+
},
|
|
29596
|
+
children: /* @__PURE__ */ jsx2(
|
|
29597
|
+
Cuboid,
|
|
29598
|
+
{
|
|
29599
|
+
size: horizontal ? [slice, leadWidth, leadThickness] : [leadWidth, slice, leadThickness]
|
|
29600
|
+
}
|
|
29601
|
+
)
|
|
29602
|
+
}
|
|
29603
|
+
);
|
|
29604
|
+
return /* @__PURE__ */ jsx2(Union, { children: points.slice(1).map((point, i) => /* @__PURE__ */ jsxs(Hull, { children: [
|
|
29605
|
+
section(points[i]),
|
|
29606
|
+
section(point)
|
|
29607
|
+
] }, i)) });
|
|
29608
|
+
};
|
|
29609
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
29610
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#353535", children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
29611
|
+
body,
|
|
29612
|
+
/* @__PURE__ */ jsx2(
|
|
29613
|
+
Translate,
|
|
29614
|
+
{
|
|
29615
|
+
offset: {
|
|
29616
|
+
x: -bodyWidth / 2 + taperInset + dimpleRadius * 2,
|
|
29617
|
+
y: bodyLength10 / 2 - taperInset - dimpleRadius * 2,
|
|
29618
|
+
z: bodyHeight
|
|
29619
|
+
},
|
|
29620
|
+
children: /* @__PURE__ */ jsx2(Cylinder, { radius: dimpleRadius, height: dimpleDepth * 2 })
|
|
29621
|
+
}
|
|
29622
|
+
)
|
|
29623
|
+
] }) }),
|
|
29624
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#bfc1c4", children: Array.from(
|
|
29625
|
+
{ length: pinCount },
|
|
29626
|
+
(_, i) => lead(
|
|
29627
|
+
Math.floor(i / (pinCount / 4)),
|
|
29628
|
+
((pinCount / 4 - 1) / 2 - i % (pinCount / 4)) * pitch
|
|
29629
|
+
)
|
|
29630
|
+
) })
|
|
29631
|
+
] });
|
|
29632
|
+
}
|
|
29633
|
+
var LegacyLQFP = ({
|
|
29114
29634
|
pinCount,
|
|
29115
29635
|
pitch,
|
|
29116
29636
|
leadWidth,
|
|
@@ -29220,6 +29740,7 @@ var LQFP = ({
|
|
|
29220
29740
|
)
|
|
29221
29741
|
] });
|
|
29222
29742
|
};
|
|
29743
|
+
var LQFP = (props) => "leadSpanX" in props ? createPhysicalLqfp(props) : LegacyLQFP(props);
|
|
29223
29744
|
var SOT723 = () => {
|
|
29224
29745
|
const bodyWidth = 0.85;
|
|
29225
29746
|
const bodyLength10 = 1.2;
|
|
@@ -29292,8 +29813,41 @@ var DFN = ({
|
|
|
29292
29813
|
padWidth = bodyLength10 / 5.3 * 0.6,
|
|
29293
29814
|
padLength = bodyLength10 / 5.3 * 1,
|
|
29294
29815
|
pitch = 0.5,
|
|
29295
|
-
thermalPadThickness = 0.2
|
|
29816
|
+
thermalPadThickness = 0.2,
|
|
29817
|
+
bodyStyle = "chamfered",
|
|
29818
|
+
standoff = 0,
|
|
29819
|
+
terminalInset = 0,
|
|
29820
|
+
terminalThickness = thermalPadThickness,
|
|
29821
|
+
pin1TerminalChamfer = 0,
|
|
29822
|
+
pin1MarkWidth = 0
|
|
29296
29823
|
}) => {
|
|
29824
|
+
if (bodyStyle === "rectangular") {
|
|
29825
|
+
if (!Number.isInteger(num_pins) || num_pins < 2 || num_pins % 2 !== 0)
|
|
29826
|
+
throw new Error("DFN requires an even pin count of at least two");
|
|
29827
|
+
for (const value of [
|
|
29828
|
+
bodyWidth,
|
|
29829
|
+
bodyLength10,
|
|
29830
|
+
bodyThickness,
|
|
29831
|
+
padWidth,
|
|
29832
|
+
padLength,
|
|
29833
|
+
pitch,
|
|
29834
|
+
terminalThickness
|
|
29835
|
+
])
|
|
29836
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
29837
|
+
throw new Error("DFN dimensions must be finite and positive");
|
|
29838
|
+
for (const value of [
|
|
29839
|
+
standoff,
|
|
29840
|
+
terminalInset,
|
|
29841
|
+
pin1TerminalChamfer,
|
|
29842
|
+
pin1MarkWidth
|
|
29843
|
+
])
|
|
29844
|
+
if (!Number.isFinite(value) || value < 0)
|
|
29845
|
+
throw new Error("DFN offsets must be finite and nonnegative");
|
|
29846
|
+
if (terminalThickness <= standoff || terminalThickness > standoff + bodyThickness || terminalInset + padLength >= bodyWidth / 2 || (num_pins / 2 - 1) * pitch + padWidth > bodyLength10 || num_pins > 2 && padWidth >= pitch || pin1TerminalChamfer >= Math.min(padLength, padWidth / 2) || pin1MarkWidth >= bodyWidth / 4)
|
|
29847
|
+
throw new Error(
|
|
29848
|
+
"DFN dimensions leave disconnected or overlapping terminals"
|
|
29849
|
+
);
|
|
29850
|
+
}
|
|
29297
29851
|
const pinPositions = [];
|
|
29298
29852
|
const pinsPerSide = Math.floor(num_pins / 2);
|
|
29299
29853
|
const pinSpan = pitch * (pinsPerSide - 1);
|
|
@@ -29303,10 +29857,88 @@ var DFN = ({
|
|
|
29303
29857
|
const y = pinSpan / 2 - indexOnSide * pitch;
|
|
29304
29858
|
const padSizeX = padLength;
|
|
29305
29859
|
const padSizeY = padWidth;
|
|
29306
|
-
const x = side === "left" ? -bodyWidth / 2 + padSizeX / 2 : bodyWidth / 2 - padSizeX / 2;
|
|
29860
|
+
const x = side === "left" ? -bodyWidth / 2 + padSizeX / 2 + terminalInset : bodyWidth / 2 - padSizeX / 2 - terminalInset;
|
|
29307
29861
|
const pinNumber = i + 1;
|
|
29308
29862
|
pinPositions.push({ pinNumber, x, y, padSizeX, padSizeY });
|
|
29309
29863
|
}
|
|
29864
|
+
if (bodyStyle === "rectangular") {
|
|
29865
|
+
const top = standoff + bodyThickness;
|
|
29866
|
+
const inkThickness = Math.min(bodyThickness, bodyLength10) / 100;
|
|
29867
|
+
const stripe = /* @__PURE__ */ jsx2(
|
|
29868
|
+
Cuboid,
|
|
29869
|
+
{
|
|
29870
|
+
size: [pin1MarkWidth, bodyLength10 * 0.8, inkThickness],
|
|
29871
|
+
center: [
|
|
29872
|
+
-bodyWidth / 2 + pin1MarkWidth * 1.5,
|
|
29873
|
+
0,
|
|
29874
|
+
top - inkThickness / 2
|
|
29875
|
+
]
|
|
29876
|
+
}
|
|
29877
|
+
);
|
|
29878
|
+
const body = /* @__PURE__ */ jsx2(
|
|
29879
|
+
Cuboid,
|
|
29880
|
+
{
|
|
29881
|
+
size: [bodyWidth, bodyLength10, bodyThickness],
|
|
29882
|
+
center: [0, 0, standoff + bodyThickness / 2]
|
|
29883
|
+
}
|
|
29884
|
+
);
|
|
29885
|
+
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
29886
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#25272a", children: pin1MarkWidth > 0 ? /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
29887
|
+
body,
|
|
29888
|
+
stripe
|
|
29889
|
+
] }) : body }),
|
|
29890
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#c7c9cd", children: pinPositions.map(
|
|
29891
|
+
(p) => p.pinNumber === 1 && pin1TerminalChamfer > 0 ? /* @__PURE__ */ jsxs(Hull, { children: [
|
|
29892
|
+
/* @__PURE__ */ jsx2(
|
|
29893
|
+
Cuboid,
|
|
29894
|
+
{
|
|
29895
|
+
size: [
|
|
29896
|
+
padLength - pin1TerminalChamfer,
|
|
29897
|
+
padWidth,
|
|
29898
|
+
terminalThickness
|
|
29899
|
+
],
|
|
29900
|
+
center: [
|
|
29901
|
+
p.x + pin1TerminalChamfer / 2,
|
|
29902
|
+
p.y,
|
|
29903
|
+
terminalThickness / 2
|
|
29904
|
+
]
|
|
29905
|
+
}
|
|
29906
|
+
),
|
|
29907
|
+
/* @__PURE__ */ jsx2(
|
|
29908
|
+
Cuboid,
|
|
29909
|
+
{
|
|
29910
|
+
size: [
|
|
29911
|
+
padLength,
|
|
29912
|
+
padWidth - 2 * pin1TerminalChamfer,
|
|
29913
|
+
terminalThickness
|
|
29914
|
+
],
|
|
29915
|
+
center: [p.x, p.y, terminalThickness / 2]
|
|
29916
|
+
}
|
|
29917
|
+
)
|
|
29918
|
+
] }, p.pinNumber) : /* @__PURE__ */ jsx2(
|
|
29919
|
+
Cuboid,
|
|
29920
|
+
{
|
|
29921
|
+
size: [p.padSizeX, p.padSizeY, terminalThickness],
|
|
29922
|
+
center: [p.x, p.y, terminalThickness / 2]
|
|
29923
|
+
},
|
|
29924
|
+
p.pinNumber
|
|
29925
|
+
)
|
|
29926
|
+
) }),
|
|
29927
|
+
thermalPadSize && /* @__PURE__ */ jsx2(
|
|
29928
|
+
Cuboid,
|
|
29929
|
+
{
|
|
29930
|
+
color: "#c7c9cd",
|
|
29931
|
+
size: [
|
|
29932
|
+
thermalPadSize.width,
|
|
29933
|
+
thermalPadSize.length,
|
|
29934
|
+
thermalPadThickness
|
|
29935
|
+
],
|
|
29936
|
+
center: [0, 0, thermalPadThickness / 2]
|
|
29937
|
+
}
|
|
29938
|
+
),
|
|
29939
|
+
pin1MarkWidth > 0 && /* @__PURE__ */ jsx2(Colorize, { color: "#969997", children: stripe })
|
|
29940
|
+
] });
|
|
29941
|
+
}
|
|
29310
29942
|
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
29311
29943
|
/* @__PURE__ */ jsx2(
|
|
29312
29944
|
ChipBody,
|
|
@@ -29483,36 +30115,50 @@ var MicroMELF = ({
|
|
|
29483
30115
|
var MINIMELF = ({
|
|
29484
30116
|
bodyLength: bodyLength10 = 3.5,
|
|
29485
30117
|
bodyDiameter = 1.5,
|
|
29486
|
-
color = "#
|
|
29487
|
-
contactColor = "#c6c6c6"
|
|
30118
|
+
color = "#202020",
|
|
30119
|
+
contactColor = "#c6c6c6",
|
|
30120
|
+
bandColor = "#f2f2f2"
|
|
29488
30121
|
}) => {
|
|
29489
|
-
const
|
|
30122
|
+
const contactLength = 0.4;
|
|
30123
|
+
const glassLength = bodyLength10 - 2 * contactLength + 0.04;
|
|
30124
|
+
const contactOffset = (bodyLength10 - contactLength) / 2;
|
|
30125
|
+
const contactRadius = bodyDiameter / 2 + 0.02;
|
|
30126
|
+
const bandLength = 0.14;
|
|
30127
|
+
const bandOffset = -bodyLength10 / 2 + contactLength + 0.35;
|
|
29490
30128
|
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
29491
30129
|
/* @__PURE__ */ jsx2(Colorize, { color, children: /* @__PURE__ */ jsx2(Rotate, { rotation: [0, "90deg", 0], children: /* @__PURE__ */ jsx2(
|
|
29492
30130
|
RoundedCylinder,
|
|
29493
30131
|
{
|
|
29494
|
-
height:
|
|
30132
|
+
height: glassLength,
|
|
29495
30133
|
radius: bodyDiameter / 2,
|
|
29496
|
-
roundRadius: 0.
|
|
30134
|
+
roundRadius: 0.05,
|
|
29497
30135
|
center: [-bodyDiameter / 2, 0, 0]
|
|
29498
30136
|
}
|
|
29499
30137
|
) }) }),
|
|
30138
|
+
/* @__PURE__ */ jsx2(Colorize, { color: bandColor, children: /* @__PURE__ */ jsx2(Rotate, { rotation: [0, "90deg", 0], children: /* @__PURE__ */ jsx2(
|
|
30139
|
+
Cylinder,
|
|
30140
|
+
{
|
|
30141
|
+
height: bandLength,
|
|
30142
|
+
radius: bodyDiameter / 2 + 2e-3,
|
|
30143
|
+
center: [-bodyDiameter / 2, 0, bandOffset]
|
|
30144
|
+
}
|
|
30145
|
+
) }) }),
|
|
29500
30146
|
/* @__PURE__ */ jsx2(Colorize, { color: contactColor, children: /* @__PURE__ */ jsx2(Rotate, { rotation: [0, "90deg", 0], children: /* @__PURE__ */ jsx2(
|
|
29501
30147
|
RoundedCylinder,
|
|
29502
30148
|
{
|
|
29503
|
-
height:
|
|
29504
|
-
radius:
|
|
29505
|
-
roundRadius: 0.
|
|
29506
|
-
center: [-bodyDiameter / 2, 0, -
|
|
30149
|
+
height: contactLength,
|
|
30150
|
+
radius: contactRadius,
|
|
30151
|
+
roundRadius: 0.04,
|
|
30152
|
+
center: [-bodyDiameter / 2, 0, -contactOffset]
|
|
29507
30153
|
}
|
|
29508
30154
|
) }) }),
|
|
29509
30155
|
/* @__PURE__ */ jsx2(Colorize, { color: contactColor, children: /* @__PURE__ */ jsx2(Rotate, { rotation: [0, "90deg", 0], children: /* @__PURE__ */ jsx2(
|
|
29510
30156
|
RoundedCylinder,
|
|
29511
30157
|
{
|
|
29512
|
-
height:
|
|
29513
|
-
radius:
|
|
29514
|
-
roundRadius: 0.
|
|
29515
|
-
center: [-bodyDiameter / 2, 0,
|
|
30158
|
+
height: contactLength,
|
|
30159
|
+
radius: contactRadius,
|
|
30160
|
+
roundRadius: 0.04,
|
|
30161
|
+
center: [-bodyDiameter / 2, 0, contactOffset]
|
|
29516
30162
|
}
|
|
29517
30163
|
) }) })
|
|
29518
30164
|
] });
|
|
@@ -31311,6 +31957,103 @@ var JSTXH2_5mm = ({
|
|
|
31311
31957
|
})()
|
|
31312
31958
|
] });
|
|
31313
31959
|
};
|
|
31960
|
+
function isJstShFootprint(footprint) {
|
|
31961
|
+
const p = fp.string(footprint).json();
|
|
31962
|
+
if (p.fn !== "jst") return false;
|
|
31963
|
+
return Boolean(
|
|
31964
|
+
p.sh || p.smd && Math.abs(mm(p.p) - 1) < 0.01 && Math.abs(mm(p.mpx ?? 0) - (p.num_pins - 1 + 2.6)) < 0.15 && Math.abs(mm(p.mpy ?? 0) - 2.525) < 0.15
|
|
31965
|
+
);
|
|
31966
|
+
}
|
|
31967
|
+
var JstSh = ({ footprint = "jst6_sh" }) => {
|
|
31968
|
+
if (!isJstShFootprint(footprint))
|
|
31969
|
+
throw new Error(
|
|
31970
|
+
"JstSh requires a named SH or matching 1mm SH top-entry footprint"
|
|
31971
|
+
);
|
|
31972
|
+
const canonical = footprint.replace(/_pin1location\([^)]*\)/g, "");
|
|
31973
|
+
const params = fp.string(canonical).json();
|
|
31974
|
+
const pads = fp.string(canonical).circuitJson().filter((e) => e.type === "pcb_smtpad" && e.shape === "rect");
|
|
31975
|
+
const target = fp.string(footprint).circuitJson().filter(
|
|
31976
|
+
(e) => e.type === "pcb_smtpad" && (e.shape === "rect" || e.shape === "rotated_rect")
|
|
31977
|
+
);
|
|
31978
|
+
const n = params.num_pins;
|
|
31979
|
+
if (!Number.isInteger(n) || n < 2 || n > 20 || pads.length !== n + 2)
|
|
31980
|
+
throw new Error("JstSh requires 2\u201320 contacts and two hold-down pads");
|
|
31981
|
+
const signals = pads.slice(0, n), mounts = pads.slice(n);
|
|
31982
|
+
const angle = Math.atan2(target[1].y - target[0].y, target[1].x - target[0].x) - Math.atan2(pads[1].y - pads[0].y, pads[1].x - pads[0].x);
|
|
31983
|
+
const x = signals.reduce((sum, p) => sum + p.x, 0) / n, rowY = signals[0].y;
|
|
31984
|
+
const direction = mounts[0].y > rowY ? 1 : -1;
|
|
31985
|
+
const y = rowY + direction * 1.8, width10 = n + 2, depth = 2.9, height10 = 4.2;
|
|
31986
|
+
const pinY = y - direction * 0.25;
|
|
31987
|
+
return /* @__PURE__ */ jsxs(Rotate, { rotation: [0, 0, angle], children: [
|
|
31988
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#e4e1d6", children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
31989
|
+
/* @__PURE__ */ jsx2(
|
|
31990
|
+
Cuboid,
|
|
31991
|
+
{
|
|
31992
|
+
size: [width10, depth, height10],
|
|
31993
|
+
center: [x, y, height10 / 2 + 0.05]
|
|
31994
|
+
}
|
|
31995
|
+
),
|
|
31996
|
+
/* @__PURE__ */ jsx2(
|
|
31997
|
+
Cuboid,
|
|
31998
|
+
{
|
|
31999
|
+
size: [width10 - 1.3, 1.8, height10],
|
|
32000
|
+
center: [x, y, height10 / 2 + 0.8]
|
|
32001
|
+
}
|
|
32002
|
+
),
|
|
32003
|
+
mounts.map((pad2, i) => /* @__PURE__ */ jsx2(
|
|
32004
|
+
Cuboid,
|
|
32005
|
+
{
|
|
32006
|
+
size: [0.7, pad2.height * 0.8, 1.5],
|
|
32007
|
+
center: [pad2.x, pad2.y, 0.75]
|
|
32008
|
+
},
|
|
32009
|
+
`recess-${i}`
|
|
32010
|
+
)),
|
|
32011
|
+
[-1, 1].map((side) => /* @__PURE__ */ jsx2(
|
|
32012
|
+
Cuboid,
|
|
32013
|
+
{
|
|
32014
|
+
size: [0.55, 0.6, 1],
|
|
32015
|
+
center: [
|
|
32016
|
+
x + side * (width10 / 2 - 0.9),
|
|
32017
|
+
y + direction * depth / 2,
|
|
32018
|
+
4
|
|
32019
|
+
]
|
|
32020
|
+
},
|
|
32021
|
+
side
|
|
32022
|
+
))
|
|
32023
|
+
] }) }),
|
|
32024
|
+
/* @__PURE__ */ jsxs(Colorize, { color: "#c6c7c9", children: [
|
|
32025
|
+
signals.map((pad2, i) => {
|
|
32026
|
+
const tailY = pad2.y - direction * pad2.height * 0.4;
|
|
32027
|
+
return /* @__PURE__ */ jsxs(Union, { children: [
|
|
32028
|
+
/* @__PURE__ */ jsx2(
|
|
32029
|
+
Cuboid,
|
|
32030
|
+
{
|
|
32031
|
+
size: [0.2, Math.abs(pinY - tailY) + 0.1, 0.2],
|
|
32032
|
+
center: [pad2.x, (pinY + tailY) / 2, 0.1]
|
|
32033
|
+
}
|
|
32034
|
+
),
|
|
32035
|
+
/* @__PURE__ */ jsx2(Cuboid, { size: [0.2, 0.2, 3.45], center: [pad2.x, pinY, 1.725] })
|
|
32036
|
+
] }, i);
|
|
32037
|
+
}),
|
|
32038
|
+
mounts.map((pad2, i) => /* @__PURE__ */ jsxs(Union, { children: [
|
|
32039
|
+
/* @__PURE__ */ jsx2(
|
|
32040
|
+
Cuboid,
|
|
32041
|
+
{
|
|
32042
|
+
size: [0.7, pad2.height * 0.75, 0.2],
|
|
32043
|
+
center: [pad2.x, pad2.y, 0.1]
|
|
32044
|
+
}
|
|
32045
|
+
),
|
|
32046
|
+
/* @__PURE__ */ jsx2(
|
|
32047
|
+
Cuboid,
|
|
32048
|
+
{
|
|
32049
|
+
size: [0.2, pad2.height * 0.6, 1.35],
|
|
32050
|
+
center: [pad2.x, pad2.y, 0.675]
|
|
32051
|
+
}
|
|
32052
|
+
)
|
|
32053
|
+
] }, `mount-${i}`))
|
|
32054
|
+
] })
|
|
32055
|
+
] });
|
|
32056
|
+
};
|
|
31314
32057
|
var getTerminalPoints = ([centerX, centerY], width10, length64, chamfer, isPinOne) => {
|
|
31315
32058
|
const xDirection = Math.sign(centerX);
|
|
31316
32059
|
const yDirection = Math.sign(centerY);
|
|
@@ -32486,6 +33229,81 @@ var SmdPushButton = ({
|
|
|
32486
33229
|
)
|
|
32487
33230
|
] });
|
|
32488
33231
|
};
|
|
33232
|
+
var SmdSlideSwitch = ({
|
|
33233
|
+
footprint = "smdslideswitch7",
|
|
33234
|
+
position = 0
|
|
33235
|
+
}) => {
|
|
33236
|
+
const canonical = footprint.replace(/_pin1location\([^)]*\)/g, "");
|
|
33237
|
+
const params = fp.string(canonical).json();
|
|
33238
|
+
const elements = fp.string(canonical).circuitJson();
|
|
33239
|
+
const pads = elements.filter(
|
|
33240
|
+
(e) => e.type === "pcb_smtpad" && e.shape === "rect"
|
|
33241
|
+
);
|
|
33242
|
+
const target = fp.string(footprint).circuitJson().filter(
|
|
33243
|
+
(e) => e.type === "pcb_smtpad" && (e.shape === "rect" || e.shape === "rotated_rect")
|
|
33244
|
+
);
|
|
33245
|
+
if (pads.length < 7 || !Number.isFinite(params.mpx) || params.mpx < 3)
|
|
33246
|
+
throw new Error("SmdSlideSwitch needs signal pads and four mounting pads");
|
|
33247
|
+
const angle = Math.atan2(target[1].y - target[0].y, target[1].x - target[0].x) - Math.atan2(pads[1].y - pads[0].y, pads[1].x - pads[0].x);
|
|
33248
|
+
const width10 = params.mpx - 0.5, depth = 2.7, height10 = params.mpx > 6 ? 1.5 : 1.4, y = params.mounty;
|
|
33249
|
+
return /* @__PURE__ */ jsxs(Rotate, { rotation: [0, 0, angle], children: [
|
|
33250
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#aeb4ba", children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
33251
|
+
/* @__PURE__ */ jsx2(Cuboid, { size: [width10, depth, height10], center: [0, y, height10 / 2] }),
|
|
33252
|
+
/* @__PURE__ */ jsx2(
|
|
33253
|
+
Cuboid,
|
|
33254
|
+
{
|
|
33255
|
+
size: [width10 - 0.3, depth - 0.3, height10],
|
|
33256
|
+
center: [0, y, height10 / 2 - 0.15]
|
|
33257
|
+
}
|
|
33258
|
+
),
|
|
33259
|
+
/* @__PURE__ */ jsx2(Cuboid, { size: [2.7, 0.5, 0.9], center: [0, y - depth / 2, 0.65] })
|
|
33260
|
+
] }) }),
|
|
33261
|
+
/* @__PURE__ */ jsxs(Colorize, { color: "#28292c", children: [
|
|
33262
|
+
/* @__PURE__ */ jsx2(
|
|
33263
|
+
Cuboid,
|
|
33264
|
+
{
|
|
33265
|
+
size: [width10 - 0.32, depth - 0.32, 0.35],
|
|
33266
|
+
center: [0, y, 0.175]
|
|
33267
|
+
}
|
|
33268
|
+
),
|
|
33269
|
+
/* @__PURE__ */ jsx2(
|
|
33270
|
+
Cuboid,
|
|
33271
|
+
{
|
|
33272
|
+
size: [1, 2.25, 0.65],
|
|
33273
|
+
center: [position === 0 ? -0.5 : 0.5, y - 1.5, 0.65]
|
|
33274
|
+
}
|
|
33275
|
+
),
|
|
33276
|
+
elements.filter((e) => e.type === "pcb_hole").map(
|
|
33277
|
+
(hole, i) => "hole_diameter" in hole && "x" in hole ? /* @__PURE__ */ jsx2(
|
|
33278
|
+
Cylinder,
|
|
33279
|
+
{
|
|
33280
|
+
radius: hole.hole_diameter * 0.42,
|
|
33281
|
+
height: 0.6,
|
|
33282
|
+
center: [hole.x, hole.y, -0.2]
|
|
33283
|
+
},
|
|
33284
|
+
i
|
|
33285
|
+
) : null
|
|
33286
|
+
)
|
|
33287
|
+
] }),
|
|
33288
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#c6c7c9", children: pads.map((pad2, i) => {
|
|
33289
|
+
const signal = i < pads.length - 4;
|
|
33290
|
+
const outerY = pad2.y + pad2.height * 0.4;
|
|
33291
|
+
const innerY = Math.min(pad2.y - pad2.height * 0.4, y + depth / 2 - 0.2);
|
|
33292
|
+
return /* @__PURE__ */ jsx2(
|
|
33293
|
+
Cuboid,
|
|
33294
|
+
{
|
|
33295
|
+
size: [
|
|
33296
|
+
pad2.width * 0.8,
|
|
33297
|
+
signal ? outerY - innerY : pad2.height * 0.8,
|
|
33298
|
+
0.18
|
|
33299
|
+
],
|
|
33300
|
+
center: [pad2.x, signal ? (outerY + innerY) / 2 : pad2.y, 0.09]
|
|
33301
|
+
},
|
|
33302
|
+
i
|
|
33303
|
+
);
|
|
33304
|
+
}) })
|
|
33305
|
+
] });
|
|
33306
|
+
};
|
|
32489
33307
|
var SOT563 = ({ fullWidth = 1.94, fullLength: fullLength10 = 1.6 }) => {
|
|
32490
33308
|
const bodyWidth = 1.2;
|
|
32491
33309
|
const bodyLength10 = 1.6;
|
|
@@ -32566,6 +33384,154 @@ var BGA = ({
|
|
|
32566
33384
|
})
|
|
32567
33385
|
] });
|
|
32568
33386
|
};
|
|
33387
|
+
var Capsule = ({
|
|
33388
|
+
width: width10,
|
|
33389
|
+
height: height10,
|
|
33390
|
+
depth
|
|
33391
|
+
}) => /* @__PURE__ */ jsx2(Hull, { children: [-1, 1].map((side) => /* @__PURE__ */ jsx2(Translate, { offset: [side * (width10 - height10) / 2, 0, 0], children: /* @__PURE__ */ jsx2(Rotate, { rotation: [Math.PI / 2, 0, 0], children: /* @__PURE__ */ jsx2(Cylinder, { radius: height10 / 2, height: depth }) }) }, side)) });
|
|
33392
|
+
var UsbCMidmount = ({
|
|
33393
|
+
footprint = "usbcmidmount16"
|
|
33394
|
+
}) => {
|
|
33395
|
+
const canonical = footprint.replace(/_pin1location\([^)]*\)/g, "");
|
|
33396
|
+
const params = fp.string(canonical).json();
|
|
33397
|
+
const elements = fp.string(canonical).circuitJson();
|
|
33398
|
+
const pads = elements.filter(
|
|
33399
|
+
(e) => e.type === "pcb_smtpad" && e.shape === "rect"
|
|
33400
|
+
);
|
|
33401
|
+
const slots = elements.filter((e) => e.type === "pcb_plated_hole");
|
|
33402
|
+
const holes = elements.filter((e) => e.type === "pcb_hole");
|
|
33403
|
+
const target = fp.string(footprint).circuitJson().find((e) => e.type === "pcb_plated_hole");
|
|
33404
|
+
const first = slots[0];
|
|
33405
|
+
const angle = first && target && "x" in target ? Math.atan2(target.y, target.x) - Math.atan2(first.y, first.x) : 0;
|
|
33406
|
+
const width10 = 8.94, height10 = 3.2, wall = 0.22;
|
|
33407
|
+
const back = params.rowy + 0.23, front = -params.bodybottom;
|
|
33408
|
+
const depth = back - front, centerY = (back + front) / 2, centerZ = 1.65;
|
|
33409
|
+
if (!Number.isFinite(depth) || depth < 3 || slots.length !== 4)
|
|
33410
|
+
throw new Error(
|
|
33411
|
+
"UsbCMidmount requires four mounting slots and a positive body depth"
|
|
33412
|
+
);
|
|
33413
|
+
const tabBottom = -0.9, tabTop = centerZ + 0.1;
|
|
33414
|
+
return /* @__PURE__ */ jsxs(Rotate, { rotation: [0, 0, angle], children: [
|
|
33415
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#b8bdc4", children: /* @__PURE__ */ jsx2(Translate, { offset: [0, centerY, centerZ], children: /* @__PURE__ */ jsxs(Subtract, { children: [
|
|
33416
|
+
/* @__PURE__ */ jsx2(Capsule, { width: width10, height: height10, depth }),
|
|
33417
|
+
/* @__PURE__ */ jsx2(
|
|
33418
|
+
Capsule,
|
|
33419
|
+
{
|
|
33420
|
+
width: width10 - wall * 2,
|
|
33421
|
+
height: height10 - wall * 2,
|
|
33422
|
+
depth: depth + 0.2
|
|
33423
|
+
}
|
|
33424
|
+
)
|
|
33425
|
+
] }) }) }),
|
|
33426
|
+
/* @__PURE__ */ jsxs(Colorize, { color: "#242529", children: [
|
|
33427
|
+
/* @__PURE__ */ jsx2(
|
|
33428
|
+
Cuboid,
|
|
33429
|
+
{
|
|
33430
|
+
size: [6.65, depth - 1.1, 0.7],
|
|
33431
|
+
center: [0, centerY + 0.3, centerZ]
|
|
33432
|
+
}
|
|
33433
|
+
),
|
|
33434
|
+
/* @__PURE__ */ jsx2(Cuboid, { size: [7.6, 1, 2.45], center: [0, back - 0.65, centerZ] }),
|
|
33435
|
+
holes.map(
|
|
33436
|
+
(hole, i) => "x" in hole && "hole_diameter" in hole ? /* @__PURE__ */ jsx2(
|
|
33437
|
+
Cylinder,
|
|
33438
|
+
{
|
|
33439
|
+
radius: hole.hole_diameter * 0.42,
|
|
33440
|
+
height: 0.8,
|
|
33441
|
+
center: [hole.x, hole.y, -0.3]
|
|
33442
|
+
},
|
|
33443
|
+
i
|
|
33444
|
+
) : null
|
|
33445
|
+
)
|
|
33446
|
+
] }),
|
|
33447
|
+
/* @__PURE__ */ jsxs(Colorize, { color: "#d3ad57", children: [
|
|
33448
|
+
[-1, 1].flatMap(
|
|
33449
|
+
(side) => [-2.75, -2.25, -1.25, -0.25, 0.25, 1.25, 2.25, 2.75].map((x) => /* @__PURE__ */ jsx2(
|
|
33450
|
+
Cuboid,
|
|
33451
|
+
{
|
|
33452
|
+
size: [0.24, depth - 2, 0.04],
|
|
33453
|
+
center: [x, centerY + 0.1, centerZ + side * 0.365]
|
|
33454
|
+
},
|
|
33455
|
+
`${side}-${x}`
|
|
33456
|
+
))
|
|
33457
|
+
),
|
|
33458
|
+
pads.map((pad2, i) => /* @__PURE__ */ jsx2(
|
|
33459
|
+
Cuboid,
|
|
33460
|
+
{
|
|
33461
|
+
size: [
|
|
33462
|
+
Math.min(pad2.width * 0.7, 0.35),
|
|
33463
|
+
pad2.height * 0.8 + 0.35,
|
|
33464
|
+
0.15
|
|
33465
|
+
],
|
|
33466
|
+
center: [pad2.x, pad2.y - 0.175, 0.075]
|
|
33467
|
+
},
|
|
33468
|
+
i
|
|
33469
|
+
))
|
|
33470
|
+
] }),
|
|
33471
|
+
/* @__PURE__ */ jsx2(Colorize, { color: "#b8bdc4", children: slots.map((slot, i) => /* @__PURE__ */ jsx2(
|
|
33472
|
+
Cuboid,
|
|
33473
|
+
{
|
|
33474
|
+
size: [
|
|
33475
|
+
0.4,
|
|
33476
|
+
"hole_height" in slot ? Math.max(0.3, slot.hole_height - 0.2) : 1,
|
|
33477
|
+
tabTop - tabBottom
|
|
33478
|
+
],
|
|
33479
|
+
center: [slot.x, slot.y, (tabTop + tabBottom) / 2]
|
|
33480
|
+
},
|
|
33481
|
+
i
|
|
33482
|
+
)) })
|
|
33483
|
+
] });
|
|
33484
|
+
};
|
|
33485
|
+
var getPads = (footprint) => fp.string(footprint).circuitJson().filter(
|
|
33486
|
+
(element) => element.type === "pcb_smtpad" && (element.shape === "rect" || element.shape === "rotated_rect")
|
|
33487
|
+
).map((pad2) => ({ x: pad2.x, y: pad2.y, pin: pad2.port_hints?.[0] }));
|
|
33488
|
+
var SOT143 = ({ footprint = "sot143" }) => {
|
|
33489
|
+
const pads = getPads(footprint);
|
|
33490
|
+
if (pads.length !== 4) throw new Error("SOT143 requires four signal pads");
|
|
33491
|
+
const canonical = getPads("sot143");
|
|
33492
|
+
const options = fp.string(footprint).json();
|
|
33493
|
+
const origin = canonical[1];
|
|
33494
|
+
const target = pads.find((pad2) => pad2.pin === "2");
|
|
33495
|
+
const angle = options.pin1location ? Math.round(
|
|
33496
|
+
(Math.atan2(target.y, target.x) - Math.atan2(origin.y, origin.x)) / (Math.PI / 2)
|
|
33497
|
+
) * (Math.PI / 2) : 0;
|
|
33498
|
+
const local = pads.map((pad2) => ({
|
|
33499
|
+
...pad2,
|
|
33500
|
+
x: pad2.x * Math.cos(angle) + pad2.y * Math.sin(angle),
|
|
33501
|
+
y: -pad2.x * Math.sin(angle) + pad2.y * Math.cos(angle)
|
|
33502
|
+
}));
|
|
33503
|
+
return /* @__PURE__ */ jsxs(Rotate, { rotation: [0, 0, angle], children: [
|
|
33504
|
+
/* @__PURE__ */ jsx2(
|
|
33505
|
+
ChipBody,
|
|
33506
|
+
{
|
|
33507
|
+
width: 2.9,
|
|
33508
|
+
length: 1.3,
|
|
33509
|
+
height: 0.95,
|
|
33510
|
+
heightAboveSurface: 0.05,
|
|
33511
|
+
center: { x: 0, y: 0, z: 0 },
|
|
33512
|
+
includeNotch: false,
|
|
33513
|
+
taperRatio: 0.06
|
|
33514
|
+
}
|
|
33515
|
+
),
|
|
33516
|
+
local.map((pad2) => {
|
|
33517
|
+
const side = pad2.y < 0 ? -1 : 1;
|
|
33518
|
+
const leadWidth = pad2.pin === "1" ? 0.83 : 0.43;
|
|
33519
|
+
return /* @__PURE__ */ jsx2(
|
|
33520
|
+
SmdChipLead,
|
|
33521
|
+
{
|
|
33522
|
+
width: leadWidth,
|
|
33523
|
+
thickness: 0.12,
|
|
33524
|
+
height: 0.45,
|
|
33525
|
+
padContactLength: 0.3,
|
|
33526
|
+
bodyDistance: 0.6,
|
|
33527
|
+
rotation: side < 0 ? Math.PI / 2 : -Math.PI / 2,
|
|
33528
|
+
position: { x: pad2.x, y: side * 1.15, z: 0.06 }
|
|
33529
|
+
},
|
|
33530
|
+
pad2.pin
|
|
33531
|
+
);
|
|
33532
|
+
})
|
|
33533
|
+
] });
|
|
33534
|
+
};
|
|
32569
33535
|
var DEFAULT_ASPECT_RATIO = 16 / 9;
|
|
32570
33536
|
var DEFAULT_DIAGONAL = 40;
|
|
32571
33537
|
var EPSILON = 1e-6;
|
|
@@ -33138,6 +34104,8 @@ var Footprinter3d = ({ footprint }) => {
|
|
|
33138
34104
|
const parsed = mm(value);
|
|
33139
34105
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
33140
34106
|
};
|
|
34107
|
+
const bodyModel = renderFootprinterBodyModel(fpJson);
|
|
34108
|
+
if (bodyModel) return bodyModel;
|
|
33141
34109
|
switch (fpJson.fn) {
|
|
33142
34110
|
case "crystal":
|
|
33143
34111
|
return /* @__PURE__ */ jsx2(
|
|
@@ -33340,6 +34308,12 @@ var Footprinter3d = ({ footprint }) => {
|
|
|
33340
34308
|
}
|
|
33341
34309
|
case "sot457":
|
|
33342
34310
|
return /* @__PURE__ */ jsx2(SOT457, {});
|
|
34311
|
+
case "smdslideswitch":
|
|
34312
|
+
return /* @__PURE__ */ jsx2(SmdSlideSwitch, { footprint: normalizedFootprint });
|
|
34313
|
+
case "usbcmidmount":
|
|
34314
|
+
return /* @__PURE__ */ jsx2(UsbCMidmount, { footprint: normalizedFootprint });
|
|
34315
|
+
case "sot143":
|
|
34316
|
+
return /* @__PURE__ */ jsx2(SOT143, { footprint: normalizedFootprint });
|
|
33343
34317
|
case "sot223":
|
|
33344
34318
|
return /* @__PURE__ */ jsx2(SOT223, {});
|
|
33345
34319
|
case "sot23w":
|
|
@@ -33366,6 +34340,9 @@ var Footprinter3d = ({ footprint }) => {
|
|
|
33366
34340
|
}
|
|
33367
34341
|
);
|
|
33368
34342
|
case "jst":
|
|
34343
|
+
if (isJstShFootprint(normalizedFootprint)) {
|
|
34344
|
+
return /* @__PURE__ */ jsx2(JstSh, { footprint: normalizedFootprint });
|
|
34345
|
+
}
|
|
33369
34346
|
if (fpJson.zh) {
|
|
33370
34347
|
return /* @__PURE__ */ jsx2(JSTZH1_5mm, { numPins: fpJson.num_pins });
|
|
33371
34348
|
}
|
|
@@ -33806,11 +34783,8 @@ function renderNode(node, colorCtx, renderCtx) {
|
|
|
33806
34783
|
}));
|
|
33807
34784
|
}
|
|
33808
34785
|
if (type === Rotate) {
|
|
33809
|
-
const
|
|
33810
|
-
|
|
33811
|
-
degToRad(props.rotation[1]),
|
|
33812
|
-
degToRad(props.rotation[2])
|
|
33813
|
-
] : [
|
|
34786
|
+
const angles = props?.angles ?? props?.rotation;
|
|
34787
|
+
const rot = Array.isArray(angles) ? [degToRad(angles[0]), degToRad(angles[1]), degToRad(angles[2])] : [
|
|
33814
34788
|
degToRad(props?.x ?? 0),
|
|
33815
34789
|
degToRad(props?.y ?? 0),
|
|
33816
34790
|
degToRad(props?.z ?? 0)
|
|
@@ -35765,7 +36739,7 @@ import * as THREE23 from "three";
|
|
|
35765
36739
|
// package.json
|
|
35766
36740
|
var package_default = {
|
|
35767
36741
|
name: "@tscircuit/3d-viewer",
|
|
35768
|
-
version: "0.0.
|
|
36742
|
+
version: "0.0.600",
|
|
35769
36743
|
repository: {
|
|
35770
36744
|
type: "git",
|
|
35771
36745
|
url: "https://github.com/tscircuit/3d-viewer"
|
|
@@ -35789,11 +36763,16 @@ var package_default = {
|
|
|
35789
36763
|
prepublish: "npm run build",
|
|
35790
36764
|
preview: "vite preview",
|
|
35791
36765
|
storybook: "storybook dev -p 6006",
|
|
35792
|
-
"build-storybook": "storybook build",
|
|
36766
|
+
"build-storybook": "bun run prepare:renderer-comparisons && storybook build",
|
|
35793
36767
|
"vercel-build": "bun run build-storybook",
|
|
35794
36768
|
format: "biome format . --write",
|
|
35795
36769
|
"format:check": "biome format .",
|
|
35796
|
-
"test:node-bundle": "node ./scripts/load-bundle-in-node.js"
|
|
36770
|
+
"test:node-bundle": "node ./scripts/load-bundle-in-node.js",
|
|
36771
|
+
"prepare:renderer-comparisons": "bun scripts/prepare-renderer-comparisons.ts",
|
|
36772
|
+
"storybook:comparisons": "bun run prepare:renderer-comparisons && bun run storybook --initial-path '/story/diagnostics-renderer-parity--x-rotation'",
|
|
36773
|
+
"test:renderer-comparisons:serve": "bun run prepare:renderer-comparisons && vite --config playwright/vite.config.ts",
|
|
36774
|
+
"test:renderer-calibration": "playwright test --project=calibration",
|
|
36775
|
+
"test:renderer-comparisons": "bun scripts/run-renderer-comparisons.ts"
|
|
35797
36776
|
},
|
|
35798
36777
|
dependencies: {
|
|
35799
36778
|
"@jscad/regl-renderer": "^2.6.12",
|
|
@@ -35817,6 +36796,8 @@ var package_default = {
|
|
|
35817
36796
|
"@chromatic-com/storybook": "^1.9.0",
|
|
35818
36797
|
"@jscad/modeling": "^2.12.5",
|
|
35819
36798
|
"@napi-rs/canvas": "^1.0.9",
|
|
36799
|
+
"@playwright/test": "1.58.2",
|
|
36800
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
35820
36801
|
"@storybook/blocks": "9.0.0-alpha.17",
|
|
35821
36802
|
"@storybook/react-vite": "^9.1.5",
|
|
35822
36803
|
"@tscircuit/alphabet": "^0.0.25",
|
|
@@ -35826,12 +36807,12 @@ var package_default = {
|
|
|
35826
36807
|
"@types/react": "19",
|
|
35827
36808
|
"@types/react-dom": "19",
|
|
35828
36809
|
"@types/three": "^0.165.0",
|
|
35829
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
35830
36810
|
"@vitejs/plugin-react": "^4.3.4",
|
|
35831
36811
|
"bun-match-svg": "^0.0.9",
|
|
35832
36812
|
"bun-types": "1.2.1",
|
|
36813
|
+
"circuit-json-to-gltf": "0.0.129",
|
|
35833
36814
|
debug: "^4.4.0",
|
|
35834
|
-
"jscad-electronics": "^0.0.
|
|
36815
|
+
"jscad-electronics": "^0.0.178",
|
|
35835
36816
|
"jscad-planner": "^0.0.13",
|
|
35836
36817
|
jsdom: "^26.0.0",
|
|
35837
36818
|
"manifold-3d": "^3.2.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/3d-viewer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.601",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/tscircuit/3d-viewer"
|
|
@@ -24,11 +24,16 @@
|
|
|
24
24
|
"prepublish": "npm run build",
|
|
25
25
|
"preview": "vite preview",
|
|
26
26
|
"storybook": "storybook dev -p 6006",
|
|
27
|
-
"build-storybook": "storybook build",
|
|
27
|
+
"build-storybook": "bun run prepare:renderer-comparisons && storybook build",
|
|
28
28
|
"vercel-build": "bun run build-storybook",
|
|
29
29
|
"format": "biome format . --write",
|
|
30
30
|
"format:check": "biome format .",
|
|
31
|
-
"test:node-bundle": "node ./scripts/load-bundle-in-node.js"
|
|
31
|
+
"test:node-bundle": "node ./scripts/load-bundle-in-node.js",
|
|
32
|
+
"prepare:renderer-comparisons": "bun scripts/prepare-renderer-comparisons.ts",
|
|
33
|
+
"storybook:comparisons": "bun run prepare:renderer-comparisons && bun run storybook --initial-path '/story/diagnostics-renderer-parity--x-rotation'",
|
|
34
|
+
"test:renderer-comparisons:serve": "bun run prepare:renderer-comparisons && vite --config playwright/vite.config.ts",
|
|
35
|
+
"test:renderer-calibration": "playwright test --project=calibration",
|
|
36
|
+
"test:renderer-comparisons": "bun scripts/run-renderer-comparisons.ts"
|
|
32
37
|
},
|
|
33
38
|
"dependencies": {
|
|
34
39
|
"@jscad/regl-renderer": "^2.6.12",
|
|
@@ -52,6 +57,8 @@
|
|
|
52
57
|
"@chromatic-com/storybook": "^1.9.0",
|
|
53
58
|
"@jscad/modeling": "^2.12.5",
|
|
54
59
|
"@napi-rs/canvas": "^1.0.9",
|
|
60
|
+
"@playwright/test": "1.58.2",
|
|
61
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
55
62
|
"@storybook/blocks": "9.0.0-alpha.17",
|
|
56
63
|
"@storybook/react-vite": "^9.1.5",
|
|
57
64
|
"@tscircuit/alphabet": "^0.0.25",
|
|
@@ -61,12 +68,12 @@
|
|
|
61
68
|
"@types/react": "19",
|
|
62
69
|
"@types/react-dom": "19",
|
|
63
70
|
"@types/three": "^0.165.0",
|
|
64
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
65
71
|
"@vitejs/plugin-react": "^4.3.4",
|
|
66
72
|
"bun-match-svg": "^0.0.9",
|
|
67
73
|
"bun-types": "1.2.1",
|
|
74
|
+
"circuit-json-to-gltf": "0.0.129",
|
|
68
75
|
"debug": "^4.4.0",
|
|
69
|
-
"jscad-electronics": "^0.0.
|
|
76
|
+
"jscad-electronics": "^0.0.178",
|
|
70
77
|
"jscad-planner": "^0.0.13",
|
|
71
78
|
"jsdom": "^26.0.0",
|
|
72
79
|
"manifold-3d": "^3.2.1",
|