jscad-electronics 0.0.236 → 0.0.238

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/dist/vanilla.js CHANGED
@@ -24,7 +24,7 @@ function h(type, props, ...restChildren) {
24
24
  };
25
25
  }
26
26
 
27
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/external.js
27
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/external.js
28
28
  var external_exports = {};
29
29
  __export(external_exports, {
30
30
  BRAND: () => BRAND,
@@ -136,7 +136,7 @@ __export(external_exports, {
136
136
  void: () => voidType
137
137
  });
138
138
 
139
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/helpers/util.js
139
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/helpers/util.js
140
140
  var util;
141
141
  (function(util2) {
142
142
  util2.assertEqual = (_) => {
@@ -270,7 +270,7 @@ var getParsedType = (data) => {
270
270
  }
271
271
  };
272
272
 
273
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/ZodError.js
273
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/ZodError.js
274
274
  var ZodIssueCode = util.arrayToEnum([
275
275
  "invalid_type",
276
276
  "invalid_literal",
@@ -388,7 +388,7 @@ ZodError.create = (issues) => {
388
388
  return error;
389
389
  };
390
390
 
391
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/locales/en.js
391
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/locales/en.js
392
392
  var errorMap = (issue, _ctx) => {
393
393
  let message;
394
394
  switch (issue.code) {
@@ -491,7 +491,7 @@ var errorMap = (issue, _ctx) => {
491
491
  };
492
492
  var en_default = errorMap;
493
493
 
494
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/errors.js
494
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/errors.js
495
495
  var overrideErrorMap = en_default;
496
496
  function setErrorMap(map) {
497
497
  overrideErrorMap = map;
@@ -500,7 +500,7 @@ function getErrorMap() {
500
500
  return overrideErrorMap;
501
501
  }
502
502
 
503
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/helpers/parseUtil.js
503
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/helpers/parseUtil.js
504
504
  var makeIssue = (params) => {
505
505
  const { data, path, errorMaps, issueData } = params;
506
506
  const fullPath = [...path, ...issueData.path || []];
@@ -610,14 +610,14 @@ var isDirty = (x) => x.status === "dirty";
610
610
  var isValid = (x) => x.status === "valid";
611
611
  var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
612
612
 
613
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/helpers/errorUtil.js
613
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/helpers/errorUtil.js
614
614
  var errorUtil;
615
615
  (function(errorUtil2) {
616
616
  errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
617
617
  errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
618
618
  })(errorUtil || (errorUtil = {}));
619
619
 
620
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/types.js
620
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/types.js
621
621
  var ParseInputLazyPath = class {
622
622
  constructor(parent, value, path, key) {
623
623
  this._cachedPath = [];
@@ -4065,7 +4065,7 @@ var coerce = {
4065
4065
  };
4066
4066
  var NEVER = INVALID;
4067
4067
 
4068
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/@tscircuit/mm/dist/index.js
4068
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/@tscircuit/mm/dist/index.js
4069
4069
  var unitToMm = {
4070
4070
  in: 25.4,
4071
4071
  inch: 25.4,
@@ -4092,7 +4092,7 @@ var mm = (n) => {
4092
4092
  throw new Error(`Unsupported unit: ${unit}`);
4093
4093
  };
4094
4094
 
4095
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/index.js
4095
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/index.js
4096
4096
  function normalizeNemaMotorModelString(value) {
4097
4097
  if (!/^nema(?:8|17|23|34)(?:_|$)/i.test(value)) return value;
4098
4098
  return value.replace(/(^|_)jst([0-9]+)_(ph|sh)(?=_|$)/gi, "$1jst$3$2").replace(/(^|_)jst[_-](ph|sh)[_-]([0-9]+)(?=_|$)/gi, "$1jst$2$3").replace(/(^|_)none(?=_|$)/gi, "$1nowires").replace(/(^|_)stubs(?=_|$)/gi, "$1wirestubs");
@@ -4148,36 +4148,6 @@ var parseModelStringParams = (definition) => {
4148
4148
  params.string = normalizedDefinition;
4149
4149
  return params;
4150
4150
  };
4151
- function expandModelStringFlags(value, flags10) {
4152
- return splitModelStringTokens(value).map(
4153
- (token, index) => index > 0 && Object.hasOwn(flags10, token.toLowerCase()) ? flags10[token.toLowerCase()] : token
4154
- ).join("_");
4155
- }
4156
- function normalizeModelStringFlags(value, flags10, parse, omittedFlags) {
4157
- const tokens = splitModelStringTokens(value);
4158
- if (!tokens.slice(1).some((token) => Object.hasOwn(flags10, token.toLowerCase())))
4159
- return value;
4160
- const expanded = expandModelStringFlags(value, flags10);
4161
- parse(parseModelStringParams(expanded));
4162
- const preferred = new Map(
4163
- Object.entries(flags10).map(([flag, selector]) => [selector, flag])
4164
- );
4165
- const omitted = new Set(omittedFlags.map((flag) => flags10[flag]));
4166
- return splitModelStringTokens(expanded).flatMap((token, index) => {
4167
- if (index === 0) return [token.toLowerCase()];
4168
- const lower = token.toLowerCase();
4169
- if (omitted.has(lower)) return [];
4170
- return [preferred.get(lower) ?? token];
4171
- }).join("_");
4172
- }
4173
- var stringFlags = {
4174
- hex: "drive(hex)",
4175
- fullthread: "thread(full)",
4176
- lefthanded: "threadhand(left)",
4177
- righthanded: "threadhand(right)",
4178
- male: "threadgender(male)"
4179
- };
4180
- var omittedStringFlags = ["righthanded", "male"];
4181
4151
  function defineModel(model552) {
4182
4152
  return model552;
4183
4153
  }
@@ -4324,11 +4294,11 @@ var hexBoltModelPropsShape = {
4324
4294
  iso4017: external_exports.literal(true).default(true),
4325
4295
  metricSize: hexBoltMetricSizeSchema,
4326
4296
  length: positiveModelLengthSchema2,
4327
- thread: external_exports.literal("full").default("full"),
4328
- drive: external_exports.literal("hex").default("hex"),
4329
- threadHand: external_exports.enum(["right", "left"]).default("right"),
4297
+ fullThread: external_exports.literal(true).default(true),
4298
+ hex: external_exports.literal(true).default(true),
4299
+ leftHanded: external_exports.boolean().default(false),
4330
4300
  threadClass: external_exports.literal("6g").default("6g"),
4331
- threadGender: external_exports.literal("male").default("male"),
4301
+ male: external_exports.literal(true).default(true),
4332
4302
  showThreads: external_exports.boolean().default(true),
4333
4303
  diameter: positiveModelLengthSchema2.optional(),
4334
4304
  threadPitch: positiveModelLengthSchema2.optional(),
@@ -4373,11 +4343,7 @@ var aliases = {
4373
4343
  metricsize: "metricSize",
4374
4344
  l: "length",
4375
4345
  length: "length",
4376
- thread: "thread",
4377
- drive: "drive",
4378
- threadhand: "threadHand",
4379
4346
  threadclass: "threadClass",
4380
- threadgender: "threadGender",
4381
4347
  diameter: "diameter",
4382
4348
  threadpitch: "threadPitch",
4383
4349
  headacrossflats: "headAcrossFlats",
@@ -4395,19 +4361,18 @@ var aliases = {
4395
4361
  headh: "headHeight",
4396
4362
  af: "headAcrossFlats"
4397
4363
  };
4398
- var selectors = /* @__PURE__ */ new Set([
4399
- "thread",
4400
- "drive",
4401
- "threadHand",
4402
- "threadClass",
4403
- "threadGender"
4404
- ]);
4364
+ var selectors = /* @__PURE__ */ new Set(["threadClass"]);
4365
+ var booleanFlags = {
4366
+ hex: { property: "hex", value: true },
4367
+ fullthread: { property: "fullThread", value: true },
4368
+ male: { property: "male", value: true },
4369
+ lefthanded: { property: "leftHanded", value: true },
4370
+ righthanded: { property: "leftHanded", value: false }
4371
+ };
4405
4372
  var parseHexBoltModelParams = (raw) => {
4406
4373
  if (raw.fn !== "hexbolt")
4407
4374
  throw new Error(`Expected hexbolt params, got "${raw.fn}"`);
4408
- const [root, ...tokens] = splitModelStringTokens(
4409
- expandModelStringFlags(raw.string, stringFlags)
4410
- );
4375
+ const [root, ...tokens] = splitModelStringTokens(raw.string);
4411
4376
  if (root?.toLowerCase() !== "hexbolt")
4412
4377
  throw new Error("The hexbolt function does not accept an inline value");
4413
4378
  const props = {};
@@ -4423,13 +4388,17 @@ var parseHexBoltModelParams = (raw) => {
4423
4388
  const value = match?.[2];
4424
4389
  let property;
4425
4390
  let parsed;
4426
- if (key === "threads" || key === "nothreads") {
4391
+ if (key && Object.hasOwn(booleanFlags, key)) {
4392
+ if (value !== "") throw new Error(`Flag "${key}" does not accept a value`);
4393
+ property = booleanFlags[key].property;
4394
+ parsed = booleanFlags[key].value;
4395
+ } else if (key === "threads" || key === "nothreads") {
4427
4396
  if (value !== "")
4428
4397
  throw new Error(`Token "${key}" does not accept a value`);
4429
4398
  property = "showThreads";
4430
4399
  parsed = key === "threads";
4431
4400
  } else {
4432
- property = key ? aliases[key] ?? "" : "";
4401
+ property = key && Object.hasOwn(aliases, key) ? aliases[key] : "";
4433
4402
  if (!property || !value)
4434
4403
  throw new Error(`Unknown or missing hexbolt token "${token}"`);
4435
4404
  if (selectors.has(property)) {
@@ -4461,13 +4430,7 @@ var parseHexBoltModelParams = (raw) => {
4461
4430
  var model = defineModel({
4462
4431
  name: "hexbolt",
4463
4432
  schema: hexBoltModelDefinitionSchema,
4464
- parse: parseHexBoltModelParams,
4465
- normalizeString: (value) => normalizeModelStringFlags(
4466
- value,
4467
- stringFlags,
4468
- parseHexBoltModelParams,
4469
- omittedStringFlags
4470
- )
4433
+ parse: parseHexBoltModelParams
4471
4434
  });
4472
4435
  function register(registry2) {
4473
4436
  registry2.register(model);
@@ -4574,11 +4537,11 @@ var shape2 = {
4574
4537
  /** Unsigned pitch-cylinder helix angle to +Z, in degrees. */
4575
4538
  helixAngle: external_exports.number().finite().min(0).lt(90).default(20),
4576
4539
  /** Right-hand teeth advance counterclockwise as Z increases. */
4577
- handedness: external_exports.enum(["right", "left"]).default("right"),
4540
+ leftHanded: external_exports.boolean().default(false),
4578
4541
  segmentsPerTurn: external_exports.number().int().min(12).max(128).default(32)
4579
4542
  };
4580
4543
  function dimensions2(props) {
4581
- const { helixAngle, handedness, segmentsPerTurn, ...spur } = props;
4544
+ const { helixAngle, leftHanded, segmentsPerTurn, ...spur } = props;
4582
4545
  const d = getSpurGearDimensions(spur);
4583
4546
  const beta = helixAngle * Math.PI / 180;
4584
4547
  return {
@@ -4589,18 +4552,18 @@ function dimensions2(props) {
4589
4552
  ) * 180 / Math.PI,
4590
4553
  normalPitch: d.circularPitch * Math.cos(beta),
4591
4554
  /** Signed angular advance between the lower and upper faces, in degrees. */
4592
- twistAngle: helixAngle === 0 ? 0 : (handedness === "right" ? 1 : -1) * (2 * props.faceWidth / d.pitchDiameter * Math.tan(beta) * 180) / Math.PI
4555
+ twistAngle: helixAngle === 0 ? 0 : (leftHanded ? -1 : 1) * (2 * props.faceWidth / d.pitchDiameter * Math.tan(beta) * 180) / Math.PI
4593
4556
  };
4594
4557
  }
4595
4558
  function validate2(props, context) {
4596
- const { fn, helixAngle, handedness, segmentsPerTurn, ...spur } = props;
4559
+ const { fn, helixAngle, leftHanded, segmentsPerTurn, ...spur } = props;
4597
4560
  const result = spurGearModelPropsSchema.safeParse(spur);
4598
4561
  if (!result.success) {
4599
4562
  for (const issue of result.error.issues) context.addIssue(issue);
4600
4563
  return;
4601
4564
  }
4602
4565
  if (Object.values(
4603
- dimensions2({ ...result.data, helixAngle, handedness, segmentsPerTurn })
4566
+ dimensions2({ ...result.data, helixAngle, leftHanded, segmentsPerTurn })
4604
4567
  ).some((value) => !Number.isFinite(value)))
4605
4568
  context.addIssue({
4606
4569
  code: "custom",
@@ -4634,7 +4597,7 @@ function parseHelicalGearModelParams(raw) {
4634
4597
  const [name, value] = parseGearToken(token);
4635
4598
  let property;
4636
4599
  let parsed;
4637
- if (name in lengths) {
4600
+ if (Object.hasOwn(lengths, name)) {
4638
4601
  property = lengths[name];
4639
4602
  parsed = value;
4640
4603
  } else if (name === "teeth" || name === "segments" || name === "turnsegments") {
@@ -4646,8 +4609,8 @@ function parseHelicalGearModelParams(raw) {
4646
4609
  } else if (name === "right" || name === "left") {
4647
4610
  if (value)
4648
4611
  throw new Error(`Helical flag "${name}" does not accept a value`);
4649
- property = "handedness";
4650
- parsed = name;
4612
+ property = "leftHanded";
4613
+ parsed = name === "left";
4651
4614
  } else throw new Error(`Unknown helical gear token "${token}"`);
4652
4615
  if (property in props)
4653
4616
  throw new Error(
@@ -4684,6 +4647,76 @@ var jstMotorWireConnectionSchema = external_exports.string().refine((value) => {
4684
4647
  const connector = getJstMotorConnector(value);
4685
4648
  return connector !== void 0 && connector.pinCount >= 2 && connector.pinCount <= connector.maxPinCount;
4686
4649
  }, "Expected jst-ph-N (2\u201316 pins) or jst-sh-N (2\u201315 pins)");
4650
+ function isInsideNemaFace(x, y, p) {
4651
+ const h2 = p.width / 2;
4652
+ x = Math.abs(x);
4653
+ y = Math.abs(y);
4654
+ if (x > h2 || y > h2) return false;
4655
+ const chamfer = p.chamfer;
4656
+ if (!p.stepped) return x + y <= 2 * h2 - chamfer;
4657
+ const c = chamfer / Math.SQRT2;
4658
+ if (c === 0) return true;
4659
+ const recess = p.recessRadius;
4660
+ const fillet = p.filletRadius;
4661
+ const k = h2 - c + recess;
4662
+ if (x > h2 - c && y > h2 - c)
4663
+ return x <= k && y <= k && Math.hypot(x - k, y - k) >= recess;
4664
+ if (y > h2 - fillet && x > h2 - c - fillet)
4665
+ return Math.hypot(x - (h2 - c - fillet), y - (h2 - fillet)) <= fillet;
4666
+ if (x > h2 - fillet && y > h2 - c - fillet)
4667
+ return Math.hypot(x - (h2 - fillet), y - (h2 - c - fillet)) <= fillet;
4668
+ return true;
4669
+ }
4670
+ var segmentDistance = (x, y, ax, ay, bx, by) => {
4671
+ const dx = bx - ax;
4672
+ const dy = by - ay;
4673
+ const t = Math.max(
4674
+ 0,
4675
+ Math.min(1, ((x - ax) * dx + (y - ay) * dy) / (dx * dx + dy * dy || 1))
4676
+ );
4677
+ return Math.hypot(x - ax - t * dx, y - ay - t * dy);
4678
+ };
4679
+ var arcDistance = (x, y, cx, cy, r, start, end) => {
4680
+ const angle = (Math.atan2(y - cy, x - cx) + 2 * Math.PI) % (2 * Math.PI);
4681
+ if (angle >= start && angle <= end)
4682
+ return Math.abs(Math.hypot(x - cx, y - cy) - r);
4683
+ return Math.min(
4684
+ Math.hypot(x - cx - r * Math.cos(start), y - cy - r * Math.sin(start)),
4685
+ Math.hypot(x - cx - r * Math.cos(end), y - cy - r * Math.sin(end))
4686
+ );
4687
+ };
4688
+ var boundaryDistance = (x, y, p) => {
4689
+ const h2 = p.width / 2;
4690
+ x = Math.abs(x);
4691
+ y = Math.abs(y);
4692
+ const chamfer = p.chamfer;
4693
+ if (!p.stepped || chamfer === 0)
4694
+ return Math.min(
4695
+ Math.abs(h2 - x),
4696
+ Math.abs(h2 - y),
4697
+ Math.abs(2 * h2 - chamfer - x - y) / Math.SQRT2
4698
+ );
4699
+ const c = chamfer / Math.SQRT2;
4700
+ const recess = p.recessRadius;
4701
+ const fillet = p.filletRadius;
4702
+ const a = h2 - c - fillet;
4703
+ const k = h2 - c + recess;
4704
+ return Math.min(
4705
+ segmentDistance(x, y, -a, h2, a, h2),
4706
+ segmentDistance(x, y, h2, -a, h2, a),
4707
+ segmentDistance(x, y, h2 - c, h2 - fillet, h2 - c, k),
4708
+ segmentDistance(x, y, k, h2 - c, h2 - fillet, h2 - c),
4709
+ arcDistance(x, y, a, h2 - fillet, fillet, 0, Math.PI / 2),
4710
+ arcDistance(x, y, k, k, recess, Math.PI, 3 * Math.PI / 2),
4711
+ arcDistance(x, y, h2 - fillet, a, fillet, 0, Math.PI / 2)
4712
+ );
4713
+ };
4714
+ function fitsNemaCircle(x, y, radius, p) {
4715
+ return isInsideNemaFace(x, y, p) && boundaryDistance(x, y, p) > radius;
4716
+ }
4717
+ function clearsNemaCircle(x, y, radius, p) {
4718
+ return !isInsideNemaFace(x, y, p) && boundaryDistance(x, y, p) > radius;
4719
+ }
4687
4720
  var hexSocketBoltDimensions = {
4688
4721
  M2: {
4689
4722
  diameter: 2,
@@ -4800,7 +4833,8 @@ var nemaMotorDimensions = {
4800
4833
  pilotLength: 1.5,
4801
4834
  shaftDiameter: 4,
4802
4835
  shaftLength: 15,
4803
- shaftShape: "round",
4836
+ roundShaft: true,
4837
+ dShaft: false,
4804
4838
  shaftFlatDepth: 0.5,
4805
4839
  shaftFlatLength: 10,
4806
4840
  shaftFlatAngle: 0,
@@ -4822,7 +4856,8 @@ var nemaMotorDimensions = {
4822
4856
  pilotLength: 2,
4823
4857
  shaftDiameter: 5,
4824
4858
  shaftLength: 24,
4825
- shaftShape: "d",
4859
+ roundShaft: false,
4860
+ dShaft: true,
4826
4861
  shaftFlatDepth: 0.5,
4827
4862
  shaftFlatLength: 15,
4828
4863
  shaftFlatAngle: 0,
@@ -4832,7 +4867,15 @@ var nemaMotorDimensions = {
4832
4867
  bodyCornerChamfer: 6
4833
4868
  },
4834
4869
  23: {
4835
- backFaceScrewSize: "M4",
4870
+ // Representative side-tab rear pattern from STEPPERONLINE's ST-M10 adapter.
4871
+ // The adapter's R5/R2.5 profile is a visual reference, not a frame guarantee:
4872
+ // https://www.omc-stepperonline.com/image/catalog/dimensions/ST-M10_Dimension.svg
4873
+ backFaceHoleSpacing: 50,
4874
+ backFaceHoleSpacingY: 32,
4875
+ rearCornerChamfer: 9 * Math.SQRT2,
4876
+ rearCornerRecessRadius: 5,
4877
+ rearCornerFilletRadius: 2.5,
4878
+ backFaceScrewSize: "M3",
4836
4879
  backFaceHoleDepth: 4.5,
4837
4880
  bodyWidth: 56.4,
4838
4881
  bodyLength: 51,
@@ -4844,14 +4887,18 @@ var nemaMotorDimensions = {
4844
4887
  pilotLength: 1.6,
4845
4888
  shaftDiameter: 6.35,
4846
4889
  shaftLength: 20.6,
4847
- shaftShape: "d",
4890
+ roundShaft: false,
4891
+ dShaft: true,
4848
4892
  shaftFlatDepth: 0.5,
4849
4893
  shaftFlatLength: 15,
4850
4894
  shaftFlatAngle: 0,
4851
4895
  frontCapLength: 5,
4852
4896
  rearCapLength: 5,
4853
4897
  faceCornerChamfer: 2,
4854
- bodyCornerChamfer: 15
4898
+ bodyCornerChamfer: 9 * Math.SQRT2,
4899
+ bodyCornerRecessRadius: 5,
4900
+ bodyCornerFilletRadius: 2.5,
4901
+ chamferedBodyCornerChamfer: 15
4855
4902
  },
4856
4903
  34: {
4857
4904
  // Illustrative rear details; independent of the mounting flange.
@@ -4869,7 +4916,8 @@ var nemaMotorDimensions = {
4869
4916
  pilotLength: 1.6,
4870
4917
  shaftDiameter: 14,
4871
4918
  shaftLength: 32,
4872
- shaftShape: "round",
4919
+ roundShaft: true,
4920
+ dShaft: false,
4873
4921
  shaftFlatDepth: 0.5,
4874
4922
  shaftFlatLength: 15,
4875
4923
  shaftFlatAngle: 0,
@@ -4882,13 +4930,21 @@ var nemaMotorDimensions = {
4882
4930
  var shape3 = {
4883
4931
  nemaSize: nemaSizeSchema,
4884
4932
  /** Rear cap at Z=-bodyLength: bare, open bores or installed cap screws. */
4885
- backFace: external_exports.enum(["plain", "holes", "screws"]).optional(),
4933
+ backFacePlain: external_exports.boolean().optional(),
4934
+ backFaceHoles: external_exports.boolean().optional(),
4935
+ backFaceScrews: external_exports.boolean().optional(),
4886
4936
  backFaceHoleSpacing: positiveModelLengthSchema.optional(),
4937
+ /** Axis overrides; the legacy scalar still sets a square pattern when supplied. */
4938
+ backFaceHoleSpacingX: positiveModelLengthSchema.optional(),
4939
+ backFaceHoleSpacingY: positiveModelLengthSchema.optional(),
4887
4940
  backFaceHoleDiameter: positiveModelLengthSchema.optional(),
4888
4941
  backFaceHoleDepth: positiveModelLengthSchema.optional(),
4889
4942
  backFaceScrewSize: metricBoltSizeSchema.optional(),
4890
4943
  /** Visual termination; the wireside reference exists even when hidden. */
4891
- wireConnection: external_exports.union([external_exports.enum(["none", "stubs"]), jstMotorWireConnectionSchema]).optional(),
4944
+ noWires: external_exports.boolean().optional(),
4945
+ wireStubs: external_exports.boolean().optional(),
4946
+ /** JST catalog designation, mutually exclusive with the visible-wire flags. */
4947
+ wireConnection: jstMotorWireConnectionSchema.optional(),
4892
4948
  /** Counterclockwise angle about local +Z; 0 is the +X side. */
4893
4949
  wireSideAngle: external_exports.number().finite().optional(),
4894
4950
  wireLength: positiveModelLengthSchema.optional(),
@@ -4905,7 +4961,8 @@ var shape3 = {
4905
4961
  /** Distance from mounting face Z=0 to shaft tip, including pilot height. */
4906
4962
  shaftLength: positiveModelLengthSchema.optional(),
4907
4963
  shaftDiameter: positiveModelLengthSchema.optional(),
4908
- shaftShape: external_exports.enum(["round", "d"]).optional(),
4964
+ roundShaft: external_exports.boolean().optional(),
4965
+ dShaft: external_exports.boolean().optional(),
4909
4966
  /** Radial material removed from the +X side before rotation. */
4910
4967
  shaftFlatDepth: nonnegativeModelLengthSchema.optional(),
4911
4968
  /** Flat extends back from the tip; leaves a round shoulder above the pilot. */
@@ -4916,23 +4973,74 @@ var shape3 = {
4916
4973
  rearCapLength: positiveModelLengthSchema.optional(),
4917
4974
  faceCornerChamfer: nonnegativeModelLengthSchema.optional(),
4918
4975
  bodyCornerChamfer: nonnegativeModelLengthSchema.optional(),
4976
+ bodyCornerRecessRadius: nonnegativeModelLengthSchema.optional(),
4977
+ bodyCornerFilletRadius: nonnegativeModelLengthSchema.optional(),
4919
4978
  /** Rear outline is independent of the front mounting flange. */
4920
4979
  rearCornerChamfer: nonnegativeModelLengthSchema.optional(),
4921
- cornerProfile: external_exports.enum(["chamfered", "stepped"]).optional()
4922
- };
4923
- var resolve = (p) => {
4980
+ rearCornerRecessRadius: nonnegativeModelLengthSchema.optional(),
4981
+ rearCornerFilletRadius: nonnegativeModelLengthSchema.optional(),
4982
+ rearChamfered: external_exports.boolean().optional(),
4983
+ rearStepped: external_exports.boolean().optional(),
4984
+ chamfered: external_exports.boolean().optional(),
4985
+ stepped: external_exports.boolean().optional()
4986
+ };
4987
+ var resolve = (p, ctx) => {
4988
+ const select = (flags11, fallback) => {
4989
+ const selected = flags11.filter((flag) => p[flag] === true);
4990
+ const specified = flags11.some((flag) => p[flag] !== void 0);
4991
+ if (selected.length > 1 || specified && selected.length === 0)
4992
+ ctx.addIssue({
4993
+ code: "custom",
4994
+ message: `Select exactly one of ${flags11.join(", ")}`
4995
+ });
4996
+ return selected[0] ?? fallback;
4997
+ };
4998
+ const back = select(
4999
+ ["backFacePlain", "backFaceHoles", "backFaceScrews"],
5000
+ p.nemaSize === 34 ? "backFacePlain" : "backFaceScrews"
5001
+ );
5002
+ const shaft = select(
5003
+ ["roundShaft", "dShaft"],
5004
+ nemaMotorDimensions[p.nemaSize].roundShaft ? "roundShaft" : "dShaft"
5005
+ );
5006
+ const corner = select(
5007
+ ["chamfered", "stepped"],
5008
+ p.nemaSize === 23 || p.nemaSize === 34 ? "stepped" : "chamfered"
5009
+ );
5010
+ const rearCorner = select(
5011
+ ["rearChamfered", "rearStepped"],
5012
+ p.chamfered === true ? "rearChamfered" : p.nemaSize === 23 || corner === "stepped" ? "rearStepped" : "rearChamfered"
5013
+ );
5014
+ const wireSpecified = p.noWires !== void 0 || p.wireStubs !== void 0 || p.wireConnection !== void 0;
5015
+ const wireSelections = Number(p.noWires === true) + Number(p.wireStubs === true) + Number(p.wireConnection !== void 0);
5016
+ if (wireSelections > 1 || wireSpecified && wireSelections === 0)
5017
+ ctx.addIssue({
5018
+ code: "custom",
5019
+ message: "Select exactly one of noWires, wireStubs or a JST wireConnection"
5020
+ });
4924
5021
  const d = nemaMotorDimensions[p.nemaSize];
5022
+ const bodyCornerChamfer = p.bodyCornerChamfer ?? (corner === "chamfered" && "chamferedBodyCornerChamfer" in d ? d.chamferedBodyCornerChamfer : d.bodyCornerChamfer);
5023
+ const defaultBodyRadius = Math.min(4, bodyCornerChamfer / Math.SQRT2 / 4);
4925
5024
  const backFaceScrewSize = p.backFaceScrewSize ?? d.backFaceScrewSize;
4926
5025
  const screw = hexSocketBoltDimensions[backFaceScrewSize];
5026
+ const backFaceHoleSpacing = p.backFaceHoleSpacing ?? ("backFaceHoleSpacing" in d ? d.backFaceHoleSpacing : p.mountingHoleSpacing ?? d.mountingHoleSpacing);
5027
+ const rearCornerChamfer = p.rearCornerChamfer ?? (p.nemaSize === 23 && rearCorner === "rearChamfered" ? p.faceCornerChamfer ?? d.faceCornerChamfer : "rearCornerChamfer" in d ? d.rearCornerChamfer : back === "backFacePlain" && (p.mountingHoleThrough ?? d.mountingHoleThrough) ? p.bodyCornerChamfer ?? d.bodyCornerChamfer : p.faceCornerChamfer ?? d.faceCornerChamfer);
5028
+ const defaultRearRadius = Math.min(4, rearCornerChamfer / Math.SQRT2 / 4);
4927
5029
  return {
4928
5030
  nemaSize: p.nemaSize,
4929
- wireConnection: p.wireConnection ?? "stubs",
5031
+ noWires: p.noWires === true,
5032
+ wireStubs: p.wireStubs === true || !wireSpecified,
5033
+ ...p.wireConnection !== void 0 ? { wireConnection: p.wireConnection } : {},
4930
5034
  wireSideAngle: ((p.wireSideAngle ?? (p.nemaSize === 34 ? 90 : 0)) % 360 + 360) % 360,
4931
5035
  wireLength: p.wireLength ?? (p.nemaSize === 34 ? 25 : 6),
4932
5036
  wireDiameter: p.wireDiameter ?? (p.nemaSize === 34 ? 2 : 1.2),
4933
5037
  wireCount: p.wireCount ?? 4,
4934
- backFace: p.backFace ?? (p.nemaSize === 34 ? "plain" : "screws"),
4935
- backFaceHoleSpacing: p.backFaceHoleSpacing ?? ("backFaceHoleSpacing" in d ? d.backFaceHoleSpacing : p.mountingHoleSpacing ?? d.mountingHoleSpacing),
5038
+ backFacePlain: back === "backFacePlain",
5039
+ backFaceHoles: back === "backFaceHoles",
5040
+ backFaceScrews: back === "backFaceScrews",
5041
+ backFaceHoleSpacing,
5042
+ backFaceHoleSpacingX: p.backFaceHoleSpacingX ?? backFaceHoleSpacing,
5043
+ backFaceHoleSpacingY: p.backFaceHoleSpacingY ?? p.backFaceHoleSpacing ?? ("backFaceHoleSpacingY" in d ? d.backFaceHoleSpacingY : backFaceHoleSpacing),
4936
5044
  backFaceHoleDiameter: p.backFaceHoleDiameter ?? screw.diameter,
4937
5045
  backFaceHoleDepth: p.backFaceHoleDepth ?? d.backFaceHoleDepth,
4938
5046
  backFaceScrewSize,
@@ -4946,43 +5054,64 @@ var resolve = (p) => {
4946
5054
  pilotLength: p.pilotLength ?? d.pilotLength,
4947
5055
  shaftDiameter: p.shaftDiameter ?? d.shaftDiameter,
4948
5056
  shaftLength: p.shaftLength ?? d.shaftLength,
4949
- shaftShape: p.shaftShape ?? d.shaftShape,
5057
+ roundShaft: shaft === "roundShaft",
5058
+ dShaft: shaft === "dShaft",
4950
5059
  shaftFlatDepth: p.shaftFlatDepth ?? d.shaftFlatDepth,
4951
5060
  shaftFlatLength: p.shaftFlatLength ?? d.shaftFlatLength,
4952
5061
  shaftFlatAngle: p.shaftFlatAngle ?? d.shaftFlatAngle,
4953
5062
  frontCapLength: p.frontCapLength ?? d.frontCapLength,
4954
5063
  rearCapLength: p.rearCapLength ?? d.rearCapLength,
4955
5064
  faceCornerChamfer: p.faceCornerChamfer ?? d.faceCornerChamfer,
4956
- bodyCornerChamfer: p.bodyCornerChamfer ?? d.bodyCornerChamfer,
4957
- ...p.nemaSize === 34 || p.cornerProfile !== void 0 ? { cornerProfile: p.cornerProfile ?? "stepped" } : {},
4958
- ..."rearCornerChamfer" in d || p.rearCornerChamfer !== void 0 ? {
4959
- rearCornerChamfer: p.rearCornerChamfer ?? ("rearCornerChamfer" in d ? d.rearCornerChamfer : p.faceCornerChamfer ?? d.faceCornerChamfer)
4960
- } : {}
5065
+ bodyCornerChamfer,
5066
+ bodyCornerRecessRadius: p.bodyCornerRecessRadius ?? ("bodyCornerRecessRadius" in d ? d.bodyCornerRecessRadius : defaultBodyRadius),
5067
+ bodyCornerFilletRadius: p.bodyCornerFilletRadius ?? ("bodyCornerFilletRadius" in d ? d.bodyCornerFilletRadius : defaultBodyRadius),
5068
+ chamfered: corner === "chamfered",
5069
+ stepped: corner === "stepped",
5070
+ rearCornerChamfer,
5071
+ rearStepped: rearCorner === "rearStepped",
5072
+ rearChamfered: rearCorner === "rearChamfered",
5073
+ rearCornerRecessRadius: p.rearCornerRecessRadius ?? ("rearCornerRecessRadius" in d ? d.rearCornerRecessRadius : defaultRearRadius),
5074
+ rearCornerFilletRadius: p.rearCornerFilletRadius ?? ("rearCornerFilletRadius" in d ? d.rearCornerFilletRadius : defaultRearRadius)
4961
5075
  };
4962
5076
  };
4963
5077
  var validate3 = (p, ctx) => {
4964
5078
  const issue = (message) => ctx.addIssue({ code: "custom", message });
4965
- const rearChamfer = p.rearCornerChamfer ?? (p.backFace === "plain" && p.mountingHoleThrough ? p.bodyCornerChamfer : p.faceCornerChamfer);
4966
- const rearEdge = p.cornerProfile === "stepped" ? rearChamfer / Math.SQRT2 : rearChamfer;
4967
- if (p.backFace !== "plain") {
5079
+ if (p.stepped && p.bodyCornerRecessRadius + p.bodyCornerFilletRadius > p.bodyCornerChamfer / Math.SQRT2)
5080
+ issue("Body recess and fillet radii must fit within the corner shoulder");
5081
+ const rearChamfer = p.rearCornerChamfer ?? (p.backFacePlain && p.mountingHoleThrough ? p.bodyCornerChamfer : p.faceCornerChamfer);
5082
+ const rearEdge = p.rearStepped ? rearChamfer / Math.SQRT2 : rearChamfer;
5083
+ if (p.rearStepped && p.rearCornerRecessRadius + p.rearCornerFilletRadius > rearEdge)
5084
+ issue("Rear recess and fillet radii must fit within the corner shoulder");
5085
+ if (!p.backFacePlain) {
4968
5086
  const screw = hexSocketBoltDimensions[p.backFaceScrewSize];
4969
- const radius = (p.backFace === "screws" ? Math.max(p.backFaceHoleDiameter, screw.headDiameter) : p.backFaceHoleDiameter) / 2;
4970
- if (p.backFaceHoleSpacing <= 2 * radius)
5087
+ const radius = (p.backFaceScrews ? Math.max(p.backFaceHoleDiameter, screw.headDiameter) : p.backFaceHoleDiameter) / 2;
5088
+ if (p.backFaceHoleSpacingX <= 2 * radius || p.backFaceHoleSpacingY <= 2 * radius)
4971
5089
  issue("Rear holes and screw heads must not overlap each other");
4972
- if (p.backFaceHoleSpacing + 2 * radius >= p.bodyWidth || (p.cornerProfile === "stepped" ? p.backFaceHoleSpacing + 2 * radius >= p.bodyWidth - 2 * rearEdge : p.bodyWidth - rearChamfer - p.backFaceHoleSpacing <= radius * Math.SQRT2))
5090
+ if (!fitsNemaCircle(
5091
+ p.backFaceHoleSpacingX / 2,
5092
+ p.backFaceHoleSpacingY / 2,
5093
+ radius,
5094
+ {
5095
+ width: p.bodyWidth,
5096
+ chamfer: p.rearCornerChamfer,
5097
+ stepped: p.rearStepped,
5098
+ recessRadius: p.rearCornerRecessRadius,
5099
+ filletRadius: p.rearCornerFilletRadius
5100
+ }
5101
+ ))
4973
5102
  issue("Rear holes and screw heads must fit entirely inside the rear face");
4974
5103
  if (p.backFaceHoleDepth >= p.rearCapLength)
4975
5104
  issue("Rear hole depth must be less than the rear cap length");
4976
- if (p.backFace === "screws" && p.backFaceHoleDiameter < screw.diameter)
5105
+ if (p.backFaceScrews && p.backFaceHoleDiameter < screw.diameter)
4977
5106
  issue("Rear holes must accommodate the screw diameter");
4978
5107
  }
4979
- if (p.wireConnection === "stubs") {
5108
+ if (p.wireStubs) {
4980
5109
  if (p.wireDiameter * p.wireCount * 1.5 > p.bodyWidth - 2 * rearEdge)
4981
5110
  issue("Wire bundle must fit on the cap side");
4982
5111
  if (p.wireDiameter >= p.rearCapLength)
4983
5112
  issue("Wire diameter must fit within the rear cap");
4984
5113
  }
4985
- const connector = getJstMotorConnector(p.wireConnection);
5114
+ const connector = p.wireConnection === void 0 ? void 0 : getJstMotorConnector(p.wireConnection);
4986
5115
  if (connector && p.bodyWidth - 2 * rearEdge < connector.bodyWidth)
4987
5116
  issue("JST header must fit on the motor side");
4988
5117
  const r = p.mountingHoleDiameter / 2;
@@ -5000,17 +5129,23 @@ var validate3 = (p, ctx) => {
5000
5129
  issue("Mounting holes must clear the pilot");
5001
5130
  if (!p.mountingHoleThrough && p.mountingHoleDepth >= p.frontCapLength)
5002
5131
  issue("Blind mounting hole depth must be less than the front cap length");
5003
- if (p.mountingHoleThrough && (p.cornerProfile === "stepped" ? p.bodyWidth / 2 - p.bodyCornerChamfer / Math.SQRT2 + Math.min(4, p.bodyCornerChamfer / Math.SQRT2 / 4) >= p.mountingHoleSpacing / 2 - r : p.bodyWidth - p.bodyCornerChamfer - p.mountingHoleSpacing >= -r * Math.SQRT2))
5132
+ if (p.mountingHoleThrough && !clearsNemaCircle(p.mountingHoleSpacing / 2, p.mountingHoleSpacing / 2, r, {
5133
+ width: p.bodyWidth,
5134
+ chamfer: p.bodyCornerChamfer,
5135
+ stepped: p.stepped,
5136
+ recessRadius: p.bodyCornerRecessRadius,
5137
+ filletRadius: p.bodyCornerFilletRadius
5138
+ }))
5004
5139
  issue("Body must clear through front mounting holes");
5005
5140
  if (p.shaftLength <= p.pilotLength)
5006
5141
  issue("Shaft tip must extend beyond the pilot");
5007
- if (p.shaftShape === "d" && (p.shaftFlatDepth <= 0 || p.shaftFlatDepth >= p.shaftDiameter / 2 || p.shaftFlatLength <= 0 || p.shaftFlatLength > p.shaftLength - p.pilotLength))
5142
+ if (p.dShaft && (p.shaftFlatDepth <= 0 || p.shaftFlatDepth >= p.shaftDiameter / 2 || p.shaftFlatLength <= 0 || p.shaftFlatLength > p.shaftLength - p.pilotLength))
5008
5143
  issue(
5009
5144
  "D flat must have positive depth less than the shaft radius and fit above the pilot"
5010
5145
  );
5011
5146
  };
5012
5147
  var nemaMotorModelPropsSchema = external_exports.object(shape3).strict().transform(resolve).superRefine(validate3);
5013
- var nemaMotorModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("nema"), ...shape3 }).strict().transform(({ fn, ...props }) => ({ fn, ...resolve(props) })).superRefine(validate3);
5148
+ var nemaMotorModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("nema"), ...shape3 }).strict().transform(({ fn, ...props }, ctx) => ({ fn, ...resolve(props, ctx) })).superRefine(validate3);
5014
5149
  var lengths2 = {
5015
5150
  wirelength: "wireLength",
5016
5151
  wirediameter: "wireDiameter",
@@ -5019,6 +5154,8 @@ var lengths2 = {
5019
5154
  bodylength: "bodyLength",
5020
5155
  bodywidth: "bodyWidth",
5021
5156
  backholespacing: "backFaceHoleSpacing",
5157
+ backholespacingx: "backFaceHoleSpacingX",
5158
+ backholespacingy: "backFaceHoleSpacingY",
5022
5159
  backholediameter: "backFaceHoleDiameter",
5023
5160
  backholedepth: "backFaceHoleDepth",
5024
5161
  shaftlength: "shaftLength",
@@ -5034,7 +5171,26 @@ var lengths2 = {
5034
5171
  rearcap: "rearCapLength",
5035
5172
  facechamfer: "faceCornerChamfer",
5036
5173
  bodychamfer: "bodyCornerChamfer",
5037
- rearchamfer: "rearCornerChamfer"
5174
+ bodyrecess: "bodyCornerRecessRadius",
5175
+ bodyfillet: "bodyCornerFilletRadius",
5176
+ rearchamfer: "rearCornerChamfer",
5177
+ rearrecess: "rearCornerRecessRadius",
5178
+ rearfillet: "rearCornerFilletRadius"
5179
+ };
5180
+ var flags = {
5181
+ backfaceplain: "backFacePlain",
5182
+ plainbackface: "backFacePlain",
5183
+ backfaceholes: "backFaceHoles",
5184
+ backfacescrews: "backFaceScrews",
5185
+ round: "roundShaft",
5186
+ roundshaft: "roundShaft",
5187
+ dshaft: "dShaft",
5188
+ chamfered: "chamfered",
5189
+ stepped: "stepped",
5190
+ rearstepped: "rearStepped",
5191
+ rearchamfered: "rearChamfered",
5192
+ nowires: "noWires",
5193
+ wirestubs: "wireStubs"
5038
5194
  };
5039
5195
  function parseNemaMotorModelParams(raw) {
5040
5196
  const tokens = normalizeNemaMotorModelString(raw.string).split("_");
@@ -5052,20 +5208,15 @@ function parseNemaMotorModelParams(raw) {
5052
5208
  if (name in lengths2) {
5053
5209
  property = lengths2[name];
5054
5210
  parsed = value;
5055
- } else if (["backfaceholes", "backfacescrews", "plainbackface"].includes(name)) {
5211
+ } else if (name in flags) {
5056
5212
  if (value) throw new Error(`NEMA flag "${name}" does not accept a value`);
5057
- property = "backFace";
5058
- parsed = name === "backfaceholes" ? "holes" : name === "backfacescrews" ? "screws" : "plain";
5059
- } else if (["wirestubs", "nowires", "jstph", "jstsh"].includes(name)) {
5060
- const isJst = name === "jstph" || name === "jstsh";
5061
- if (isJst && !/^[1-9][0-9]*$/.test(value) || !isJst && value)
5213
+ property = flags[name];
5214
+ parsed = true;
5215
+ } else if (name === "jstph" || name === "jstsh") {
5216
+ if (!/^[1-9][0-9]*$/.test(value))
5062
5217
  throw new Error(`Invalid wire connection token "${token}"`);
5063
5218
  property = "wireConnection";
5064
- parsed = isJst ? `jst-${name.slice(3)}-${value}` : name === "nowires" ? "none" : "stubs";
5065
- } else if (name === "stepped" || name === "chamfered") {
5066
- if (value) throw new Error(`NEMA flag "${name}" does not accept a value`);
5067
- property = "cornerProfile";
5068
- parsed = name;
5219
+ parsed = `jst-${name.slice(3)}-${value}`;
5069
5220
  } else if (name === "wirecount") {
5070
5221
  property = "wireCount";
5071
5222
  if (!/^\d+$/.test(value)) throw new Error("wirecount requires an integer");
@@ -5078,10 +5229,10 @@ function parseNemaMotorModelParams(raw) {
5078
5229
  if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:deg)?$/i.test(value))
5079
5230
  throw new Error(`${name} requires a numeric angle in degrees`);
5080
5231
  parsed = Number(value.replace(/deg$/i, ""));
5081
- } else if (["round", "dshaft", "throughholes", "blindholes"].includes(name)) {
5232
+ } else if (name === "throughholes" || name === "blindholes") {
5082
5233
  if (value) throw new Error(`NEMA flag "${name}" does not accept a value`);
5083
- property = name === "round" || name === "dshaft" ? "shaftShape" : "mountingHoleThrough";
5084
- parsed = name === "round" ? "round" : name === "dshaft" ? "d" : name === "throughholes";
5234
+ property = "mountingHoleThrough";
5235
+ parsed = name === "throughholes";
5085
5236
  } else throw new Error(`Unknown NEMA motor token "${token}"`);
5086
5237
  if (property in props)
5087
5238
  throw new Error(`NEMA property "${property}" is set more than once`);
@@ -5104,22 +5255,26 @@ var holePosition = {
5104
5255
  u: modelLengthSchema,
5105
5256
  v: modelLengthSchema
5106
5257
  };
5107
- var sheetMetalHoleSchema = external_exports.discriminatedUnion("shape", [
5258
+ var sheetMetalHoleSchema = external_exports.union([
5108
5259
  external_exports.object({
5109
5260
  ...holePosition,
5110
- shape: external_exports.literal("round"),
5261
+ round: external_exports.literal(true),
5262
+ slot: external_exports.literal(false).default(false),
5111
5263
  diameter: positiveModelLengthSchema
5112
5264
  }).strict(),
5113
5265
  external_exports.object({
5114
5266
  ...holePosition,
5115
- shape: external_exports.literal("slot"),
5267
+ round: external_exports.literal(false).default(false),
5268
+ slot: external_exports.literal(true),
5116
5269
  length: positiveModelLengthSchema,
5117
5270
  width: positiveModelLengthSchema,
5118
5271
  axis: external_exports.enum(["u", "v"]).default("v")
5119
5272
  }).strict()
5120
5273
  ]);
5121
5274
  var sheetMetalShape = {
5122
- profile: external_exports.enum(["plate", "angle", "channel"]).default("plate"),
5275
+ plate: external_exports.boolean().default(false),
5276
+ angle: external_exports.boolean().default(false),
5277
+ channel: external_exports.boolean().default(false),
5123
5278
  width: positiveModelLengthSchema,
5124
5279
  /** Straight base length between bend tangencies, not overall width. */
5125
5280
  baseLength: positiveModelLengthSchema,
@@ -5129,9 +5284,19 @@ var sheetMetalShape = {
5129
5284
  insideBendRadius: positiveModelLengthSchema.default(1),
5130
5285
  holes: external_exports.array(sheetMetalHoleSchema).max(100).default([])
5131
5286
  };
5132
- var sheetMetalModelPropsSchema = external_exports.object(sheetMetalShape).strict();
5133
- var sheetMetalModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("sheetmetal"), ...sheetMetalShape }).strict();
5134
- function parseSheetMetalFeature(feature, profile6) {
5287
+ function validateFlags(props, context) {
5288
+ if ([props.plate, props.angle, props.channel].filter(Boolean).length > 1)
5289
+ context.addIssue({
5290
+ code: "custom",
5291
+ message: "Specify at most one of plate, angle, or channel"
5292
+ });
5293
+ }
5294
+ function resolveFlags(props) {
5295
+ return { ...props, plate: props.plate || !(props.angle || props.channel) };
5296
+ }
5297
+ var sheetMetalModelPropsSchema = external_exports.object(sheetMetalShape).strict().superRefine(validateFlags).transform(resolveFlags);
5298
+ var sheetMetalModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("sheetmetal"), ...sheetMetalShape }).strict().superRefine(validateFlags).transform((props) => ({ fn: props.fn, ...resolveFlags(props) }));
5299
+ function parseSheetMetalFeature(feature, flags11) {
5135
5300
  const match = feature.match(/^(hole|slot)[1-9]\d*\(([^()]+)\)$/);
5136
5301
  if (!match) throw new Error(`Invalid sheet metal feature "${feature}"`);
5137
5302
  const kind = match[1];
@@ -5164,8 +5329,8 @@ function parseSheetMetalFeature(feature, profile6) {
5164
5329
  if (faces.length !== 1)
5165
5330
  throw new Error(`Specify exactly one face in ${feature}`);
5166
5331
  const face2 = faces[0];
5167
- if (face2 === "angledface" && profile6 !== "angle" || ["leftface", "rightface"].includes(face2) && profile6 !== "channel")
5168
- throw new Error(`No ${face2} on ${profile6}`);
5332
+ if (face2 === "angledface" && !flags11.angle || ["leftface", "rightface"].includes(face2) && !flags11.channel)
5333
+ throw new Error(`No ${face2} on the selected sheet metal form`);
5169
5334
  for (const [a, b] of [
5170
5335
  ["leftofcenter", "rightofcenter"],
5171
5336
  ["abovecenter", "belowcenter"],
@@ -5189,7 +5354,7 @@ function parseSheetMetalFeature(feature, profile6) {
5189
5354
  throw new Error(`Hole requires r or d in ${feature}`);
5190
5355
  return sheetMetalHoleSchema.parse({
5191
5356
  ...position,
5192
- shape: "round",
5357
+ round: true,
5193
5358
  diameter: "r" in values ? 2 * distance2("r") : distance2("d")
5194
5359
  });
5195
5360
  }
@@ -5199,7 +5364,7 @@ function parseSheetMetalFeature(feature, profile6) {
5199
5364
  if (length32 < width10) throw new Error("Slot length must be at least its width");
5200
5365
  return sheetMetalHoleSchema.parse({
5201
5366
  ...position,
5202
- shape: "slot",
5367
+ slot: true,
5203
5368
  length: length32,
5204
5369
  width: width10,
5205
5370
  axis: panel === "base" === !("vertical" in values) ? "u" : "v"
@@ -5227,8 +5392,9 @@ function parseSheetMetalModelParams(raw) {
5227
5392
  continue;
5228
5393
  }
5229
5394
  if (["plate", "angle", "channel"].includes(token)) {
5230
- if (props.profile) throw new Error("Duplicate sheet metal profile");
5231
- props.profile = token;
5395
+ if (["plate", "angle", "channel"].some((flag) => flag in props))
5396
+ throw new Error("Duplicate sheet metal form flag");
5397
+ props[token] = true;
5232
5398
  continue;
5233
5399
  }
5234
5400
  const key = lengths3[token[0]];
@@ -5242,7 +5408,7 @@ function parseSheetMetalModelParams(raw) {
5242
5408
  ...props
5243
5409
  });
5244
5410
  definition.holes = features.map(
5245
- (feature) => parseSheetMetalFeature(feature, definition.profile)
5411
+ (feature) => parseSheetMetalFeature(feature, definition)
5246
5412
  );
5247
5413
  return definition;
5248
5414
  }
@@ -5254,14 +5420,6 @@ var model4 = defineModel({
5254
5420
  function register4(registry2) {
5255
5421
  registry2.register(model4);
5256
5422
  }
5257
- var flexScreenOrientationSchema = external_exports.enum([
5258
- "sitsFlat",
5259
- "sitsFlatBelowBoard",
5260
- "foldedToFaceAboveBoard",
5261
- "foldedToFaceBelowBoard",
5262
- "foldedToRightAngleAboveBoard",
5263
- "foldedToRightAngleBelowBoard"
5264
- ]);
5265
5423
  var positiveFiniteNumberSchema = external_exports.number().finite().positive();
5266
5424
  var aspectRatioStringSchema = external_exports.string().refine((value) => {
5267
5425
  const parts = value.split(":");
@@ -5303,7 +5461,6 @@ var flexScreenModelPropsShape = {
5303
5461
  aspectRatio: flexScreenAspectRatioSchema.optional(),
5304
5462
  ratio: flexScreenAspectRatioSchema.optional(),
5305
5463
  defaultDiagonal: positiveModelLengthSchema.optional(),
5306
- orientation: flexScreenOrientationSchema.optional(),
5307
5464
  sitsFlat: external_exports.boolean().optional(),
5308
5465
  sitsFlatBelowBoard: external_exports.boolean().optional(),
5309
5466
  foldedToFaceAboveBoard: external_exports.boolean().optional(),
@@ -5371,7 +5528,42 @@ var addOrientationShortcutIssue = (props, addIssue) => {
5371
5528
  const selectedShortcuts = orientationShortcutKeys.filter(
5372
5529
  (key) => props[key] === true
5373
5530
  );
5374
- if (selectedShortcuts.length > 1) addIssue(selectedShortcuts[1]);
5531
+ if (selectedShortcuts.length > 1) {
5532
+ addIssue(
5533
+ selectedShortcuts[1],
5534
+ "Only one FlexScreen orientation shortcut can be true"
5535
+ );
5536
+ } else if (selectedShortcuts.length === 0 && orientationShortcutKeys.some((key) => props[key] !== void 0)) {
5537
+ addIssue(
5538
+ "sitsFlat",
5539
+ "A FlexScreen orientation flag must be true when placement flags are supplied"
5540
+ );
5541
+ }
5542
+ };
5543
+ var normalizeOrientationFlags = (props) => {
5544
+ const {
5545
+ sitsFlat,
5546
+ sitsFlatBelowBoard,
5547
+ foldedToFaceAboveBoard,
5548
+ foldedToFaceBelowBoard,
5549
+ foldedToRightAngleAboveBoard,
5550
+ foldedToRightAngleBelowBoard,
5551
+ foldsAboveBoard,
5552
+ foldsBelowBoard,
5553
+ ...rest
5554
+ } = props;
5555
+ const above = foldedToFaceAboveBoard === true || foldsAboveBoard === true;
5556
+ const below = foldedToFaceBelowBoard === true || foldsBelowBoard === true;
5557
+ const selected = sitsFlatBelowBoard === true || above || below || foldedToRightAngleAboveBoard === true || foldedToRightAngleBelowBoard === true;
5558
+ return {
5559
+ ...rest,
5560
+ sitsFlat: sitsFlat === true || !selected,
5561
+ sitsFlatBelowBoard: sitsFlatBelowBoard === true,
5562
+ foldedToFaceAboveBoard: above,
5563
+ foldedToFaceBelowBoard: below,
5564
+ foldedToRightAngleAboveBoard: foldedToRightAngleAboveBoard === true,
5565
+ foldedToRightAngleBelowBoard: foldedToRightAngleBelowBoard === true
5566
+ };
5375
5567
  };
5376
5568
  var validateTailLengths = (props, context) => {
5377
5569
  if (props.flexCableLength === void 0) return;
@@ -5392,27 +5584,27 @@ var validateTailLengths = (props, context) => {
5392
5584
  };
5393
5585
  var flexScreenModelPropsSchema = external_exports.object(flexScreenModelPropsShape).strict().superRefine((props, context) => {
5394
5586
  validateTailLengths(props, context);
5395
- addOrientationShortcutIssue(props, (path) => {
5587
+ addOrientationShortcutIssue(props, (path, message) => {
5396
5588
  context.addIssue({
5397
5589
  code: "custom",
5398
- message: "Only one FlexScreen orientation shortcut can be true",
5590
+ message,
5399
5591
  path: [path]
5400
5592
  });
5401
5593
  });
5402
- });
5594
+ }).transform(normalizeOrientationFlags);
5403
5595
  var flexScreenModelDefinitionSchema = external_exports.object({
5404
5596
  fn: external_exports.literal("flexscreen"),
5405
5597
  ...flexScreenModelPropsShape
5406
5598
  }).strict().superRefine((model552, context) => {
5407
5599
  validateTailLengths(model552, context);
5408
- addOrientationShortcutIssue(model552, (path) => {
5600
+ addOrientationShortcutIssue(model552, (path, message) => {
5409
5601
  context.addIssue({
5410
5602
  code: "custom",
5411
- message: "Only one FlexScreen orientation shortcut can be true",
5603
+ message,
5412
5604
  path: [path]
5413
5605
  });
5414
5606
  });
5415
- });
5607
+ }).transform(normalizeOrientationFlags);
5416
5608
  var orientationTokens = {
5417
5609
  sitsflat: "sitsFlat",
5418
5610
  sitsflatbelow: "sitsFlatBelowBoard",
@@ -5538,6 +5730,23 @@ var parseFlexScreenModelParams = (rawParams) => {
5538
5730
  }
5539
5731
  const props = {};
5540
5732
  let orientation;
5733
+ for (const part of splitModelStringTokens(rawParams.string).slice(1)) {
5734
+ const match = part.match(/^([a-zA-Z]+)(.*)$/);
5735
+ if (!match) continue;
5736
+ const token = match[1].toLowerCase();
5737
+ const selected = orientationTokens[token];
5738
+ if (!selected) continue;
5739
+ if (match[2] !== "") {
5740
+ throw new Error(`FlexScreen token "${token}" does not accept a value`);
5741
+ }
5742
+ if (orientation !== void 0) {
5743
+ throw new Error(
5744
+ orientation === selected ? `Duplicate FlexScreen orientation flag "${token}"` : "A FlexScreen model string can only set one orientation"
5745
+ );
5746
+ }
5747
+ orientation = selected;
5748
+ props[selected] = true;
5749
+ }
5541
5750
  let relativeDistance;
5542
5751
  for (const [token, value] of Object.entries(rawParams)) {
5543
5752
  if (token === "fn" || token === "string" || token === "flexscreen") {
@@ -5546,13 +5755,6 @@ var parseFlexScreenModelParams = (rawParams) => {
5546
5755
  const tokenOrientation = orientationTokens[token];
5547
5756
  if (tokenOrientation) {
5548
5757
  assertBareToken(value, token);
5549
- if (orientation && orientation !== tokenOrientation) {
5550
- throw new Error(
5551
- "A FlexScreen model string can only set one orientation"
5552
- );
5553
- }
5554
- orientation = tokenOrientation;
5555
- props.orientation = tokenOrientation;
5556
5758
  continue;
5557
5759
  }
5558
5760
  if (token in booleanTokens) {
@@ -5728,7 +5930,7 @@ var shape4 = {
5728
5930
  /** Additional radial dedendum beyond one module, in millimeters. */
5729
5931
  clearance: nonnegativeGearLengthSchema.default(0.25),
5730
5932
  boreDiameter: nonnegativeGearLengthSchema.default(0),
5731
- handedness: external_exports.enum(["right", "left"]).default("right"),
5933
+ leftHanded: external_exports.boolean().default(false),
5732
5934
  /** Counterclockwise degrees about +Z, beginning at the Z=0 end. */
5733
5935
  phase: external_exports.number().finite().default(0),
5734
5936
  radialSegments: external_exports.number().int().min(24).max(256).refine(
@@ -5798,7 +6000,7 @@ function parseWormGearModelParams(raw) {
5798
6000
  const [name, value] = parseGearToken(token);
5799
6001
  let property;
5800
6002
  let parsed;
5801
- if (name in lengths5) {
6003
+ if (Object.hasOwn(lengths5, name)) {
5802
6004
  property = lengths5[name];
5803
6005
  parsed = value;
5804
6006
  } else if (["starts", "segments", "turnsegments"].includes(name)) {
@@ -5809,8 +6011,8 @@ function parseWormGearModelParams(raw) {
5809
6011
  parsed = parseGearAngle(value, property);
5810
6012
  } else if (name === "right" || name === "left") {
5811
6013
  if (value) throw new Error(`Worm flag "${name}" does not accept a value`);
5812
- property = "handedness";
5813
- parsed = name;
6014
+ property = "leftHanded";
6015
+ parsed = name === "left";
5814
6016
  } else throw new Error(`Unknown worm gear token "${token}"`);
5815
6017
  if (property in props)
5816
6018
  throw new Error(`Worm gear property "${property}" is set more than once`);
@@ -6129,7 +6331,7 @@ var lengths7 = {
6129
6331
  w: "width",
6130
6332
  width: "width"
6131
6333
  };
6132
- var flags = {
6334
+ var flags2 = {
6133
6335
  open: "bothSidesOpen",
6134
6336
  bothsidesopen: "bothSidesOpen",
6135
6337
  shielded: "bothSidesShielded",
@@ -6163,8 +6365,8 @@ function parseBallBearingModelParams(raw) {
6163
6365
  const scopes = /* @__PURE__ */ new Set();
6164
6366
  for (const token of tokens.slice(1)) {
6165
6367
  const lower = token.toLowerCase();
6166
- if (Object.hasOwn(flags, lower)) {
6167
- const property2 = flags[lower];
6368
+ if (Object.hasOwn(flags2, lower)) {
6369
+ const property2 = flags2[lower];
6168
6370
  const scope = property2.startsWith("both") ? "both" : property2.startsWith("top") ? "top" : "bottom";
6169
6371
  if (scopes.has(scope))
6170
6372
  throw new Error(
@@ -6358,7 +6560,7 @@ function parseBallTransferUnitModelParams(raw) {
6358
6560
  const lower = token.toLowerCase();
6359
6561
  let property;
6360
6562
  let value;
6361
- if (lower === "face3hole" || lower === "mount(face3hole)") {
6563
+ if (lower === "face3hole") {
6362
6564
  property = "faceThreeHole";
6363
6565
  value = true;
6364
6566
  } else {
@@ -6384,12 +6586,6 @@ var model12 = defineModel({
6384
6586
  function register12(registry2) {
6385
6587
  registry2.register(model12);
6386
6588
  }
6387
- var stringFlags2 = {
6388
- hexsocket: "drive(hexsocket)",
6389
- fullthread: "thread(full)",
6390
- righthanded: "threadhand(right)"
6391
- };
6392
- var omittedStringFlags2 = ["righthanded"];
6393
6589
  var buttonScrewDimensions = {
6394
6590
  M3: {
6395
6591
  diameter: 3,
@@ -6450,10 +6646,10 @@ var shape9 = {
6450
6646
  metricSize: buttonScrewMetricSizeSchema,
6451
6647
  /** Under-head length; this family supports short fully threaded screws only. */
6452
6648
  length: length4,
6453
- drive: external_exports.literal("hexsocket").default("hexsocket"),
6454
- thread: external_exports.literal("full").default("full"),
6649
+ hexSocket: external_exports.literal(true).default(true),
6650
+ fullThread: external_exports.literal(true).default(true),
6455
6651
  threadPitch: length4.optional(),
6456
- threadHand: external_exports.literal("right").default("right"),
6652
+ leftHanded: external_exports.literal(false).default(false),
6457
6653
  threadClass: external_exports.literal("6g").default("6g"),
6458
6654
  showThreads: external_exports.boolean().default(true)
6459
6655
  };
@@ -6509,9 +6705,7 @@ function getButtonScrewDimensions(input) {
6509
6705
  };
6510
6706
  }
6511
6707
  function parseButtonScrewModelParams(raw) {
6512
- const tokens = splitModelStringTokens(
6513
- expandModelStringFlags(raw.string, stringFlags2)
6514
- );
6708
+ const tokens = splitModelStringTokens(raw.string);
6515
6709
  if (raw.fn !== "buttonscrew" || tokens[0]?.toLowerCase() !== "buttonscrew")
6516
6710
  throw new Error("Expected buttonscrew without an inline argument");
6517
6711
  const props = { fn: "buttonscrew" };
@@ -6520,12 +6714,15 @@ function parseButtonScrewModelParams(raw) {
6520
6714
  length: "length",
6521
6715
  threadpitch: "threadPitch"
6522
6716
  };
6523
- const selectors7 = {
6524
- drive: "drive",
6525
- thread: "thread",
6526
- threadhand: "threadHand",
6717
+ const selectors5 = {
6527
6718
  threadclass: "threadClass"
6528
6719
  };
6720
+ const booleanFlags4 = {
6721
+ hexsocket: { property: "hexSocket", value: true },
6722
+ fullthread: { property: "fullThread", value: true },
6723
+ righthanded: { property: "leftHanded", value: false },
6724
+ lefthanded: { property: "leftHanded", value: true }
6725
+ };
6529
6726
  for (const token of tokens.slice(1)) {
6530
6727
  if (token.toLowerCase() === "iso7380-1") {
6531
6728
  if ("iso73801" in props)
@@ -6539,12 +6736,17 @@ function parseButtonScrewModelParams(raw) {
6539
6736
  const value = match[2];
6540
6737
  let property;
6541
6738
  let parsed = value;
6542
- if (name === "m") {
6739
+ if (Object.hasOwn(booleanFlags4, name)) {
6740
+ if (value) throw new Error(`Flag "${name}" does not accept a value`);
6741
+ property = booleanFlags4[name].property;
6742
+ parsed = booleanFlags4[name].value;
6743
+ } else if (name === "m") {
6543
6744
  property = "metricSize";
6544
6745
  parsed = `M${value}`;
6545
- } else if (name in lengths25) property = lengths25[name];
6546
- else if (name in selectors7) {
6547
- property = selectors7[name];
6746
+ } else if (Object.hasOwn(lengths25, name))
6747
+ property = lengths25[name];
6748
+ else if (Object.hasOwn(selectors5, name)) {
6749
+ property = selectors5[name];
6548
6750
  if (!/^\([^()]+\)$/.test(value))
6549
6751
  throw new Error(`Expected a parenthesized ${name}`);
6550
6752
  parsed = value.slice(1, -1).toLowerCase();
@@ -6562,21 +6764,11 @@ function parseButtonScrewModelParams(raw) {
6562
6764
  var model13 = defineModel({
6563
6765
  name: "buttonscrew",
6564
6766
  schema: buttonScrewModelDefinitionSchema,
6565
- parse: parseButtonScrewModelParams,
6566
- normalizeString: (value) => normalizeModelStringFlags(
6567
- value,
6568
- stringFlags2,
6569
- parseButtonScrewModelParams,
6570
- omittedStringFlags2
6571
- )
6767
+ parse: parseButtonScrewModelParams
6572
6768
  });
6573
6769
  function register13(registry2) {
6574
6770
  registry2.register(model13);
6575
6771
  }
6576
- var stringFlags3 = {
6577
- symmetricring: "shape(symmetricring)"
6578
- };
6579
- var omittedStringFlags3 = [];
6580
6772
  var length5 = external_exports.union([
6581
6773
  external_exports.number(),
6582
6774
  external_exports.string().regex(
@@ -6592,7 +6784,7 @@ var shape10 = {
6592
6784
  grooveWidth: length5,
6593
6785
  /** Radial recess from the flange outside diameter. */
6594
6786
  grooveDepth: length5,
6595
- shape: external_exports.literal("symmetricring").default("symmetricring")
6787
+ symmetricRing: external_exports.literal(true).default(true)
6596
6788
  };
6597
6789
  function validate9(props, context) {
6598
6790
  if (props.innerDiameter >= props.panelHoleDiameter || props.panelHoleDiameter >= props.outerDiameter)
@@ -6649,9 +6841,7 @@ var lengths9 = {
6649
6841
  groovedepth: "grooveDepth"
6650
6842
  };
6651
6843
  function parseCableGrommetModelParams(raw) {
6652
- const tokens = splitModelStringTokens(
6653
- expandModelStringFlags(raw.string, stringFlags3)
6654
- );
6844
+ const tokens = splitModelStringTokens(raw.string);
6655
6845
  if (raw.fn !== "cablegrommet" || !/^cablegrommet$/i.test(tokens[0]))
6656
6846
  throw new Error("Expected cablegrommet without an inline value");
6657
6847
  const props = { fn: "cablegrommet" };
@@ -6659,15 +6849,14 @@ function parseCableGrommetModelParams(raw) {
6659
6849
  const match = token.match(/^([a-z]+)(.*)$/i);
6660
6850
  const name = match?.[1]?.toLowerCase();
6661
6851
  const value = match?.[2];
6662
- if (!name || !value) throw new Error(`Invalid grommet token "${token}"`);
6852
+ if (!name) throw new Error(`Invalid grommet token "${token}"`);
6663
6853
  let property;
6664
6854
  let parsed = value;
6665
6855
  if (name in lengths9) property = lengths9[name];
6666
- else if (name === "shape") {
6667
- property = "shape";
6668
- const selector = value.match(/^\(([a-z]+)\)$/i);
6669
- if (!selector) throw new Error("Grommet shape requires parentheses");
6670
- parsed = selector[1].toLowerCase();
6856
+ else if (name === "symmetricring") {
6857
+ property = "symmetricRing";
6858
+ if (value !== "") throw new Error("symmetricring is a value-free flag");
6859
+ parsed = true;
6671
6860
  } else throw new Error(`Unknown grommet token "${token}"`);
6672
6861
  if (property in props)
6673
6862
  throw new Error(`Grommet property "${property}" is set more than once`);
@@ -6678,13 +6867,7 @@ function parseCableGrommetModelParams(raw) {
6678
6867
  var model14 = defineModel({
6679
6868
  name: "cablegrommet",
6680
6869
  schema: cableGrommetModelDefinitionSchema,
6681
- parse: parseCableGrommetModelParams,
6682
- normalizeString: (value) => normalizeModelStringFlags(
6683
- value,
6684
- stringFlags3,
6685
- parseCableGrommetModelParams,
6686
- omittedStringFlags3
6687
- )
6870
+ parse: parseCableGrommetModelParams
6688
6871
  });
6689
6872
  function register14(registry2) {
6690
6873
  registry2.register(model14);
@@ -6781,12 +6964,6 @@ var model15 = defineModel({
6781
6964
  function register15(registry2) {
6782
6965
  registry2.register(model15);
6783
6966
  }
6784
- var stringFlags4 = {
6785
- singleclamp: "mount(singleclamp)",
6786
- lefthanded: "threadhand(left)",
6787
- righthanded: "threadhand(right)"
6788
- };
6789
- var omittedStringFlags4 = ["righthanded"];
6790
6967
  var length7 = external_exports.union([
6791
6968
  external_exports.number().finite(),
6792
6969
  external_exports.string().regex(
@@ -6807,10 +6984,10 @@ var shape12 = {
6807
6984
  splitWidth: positive3,
6808
6985
  clampX: positive3.optional(),
6809
6986
  clearanceHoleDiameter: positive3.optional(),
6810
- mount: external_exports.literal("singleclamp").default("singleclamp"),
6987
+ singleClamp: external_exports.literal(true).default(true),
6811
6988
  metricSize: metricBoltSizeSchema,
6812
6989
  threadPitch: positive3.optional(),
6813
- threadHand: external_exports.enum(["right", "left"]).default("right"),
6990
+ leftHanded: external_exports.boolean().default(false),
6814
6991
  threadClass: external_exports.literal("6H").default("6H"),
6815
6992
  chamfer: nonnegative2.default(0)
6816
6993
  };
@@ -6886,7 +7063,7 @@ function getClampingShaftCollarDimensions(input) {
6886
7063
  diameter: threadDiameter,
6887
7064
  depth: threadedExtent - p.splitWidth / 2,
6888
7065
  threadPitch: p.threadPitch,
6889
- threadHand: p.threadHand,
7066
+ leftHanded: p.leftHanded,
6890
7067
  threadGender: "female",
6891
7068
  threadClass: p.threadClass
6892
7069
  }
@@ -6895,10 +7072,8 @@ function getClampingShaftCollarDimensions(input) {
6895
7072
  var tokenProperties = {
6896
7073
  bore: "boreDiameter",
6897
7074
  od: "outerDiameter",
6898
- mount: "mount",
6899
7075
  m: "metricSize",
6900
7076
  threadpitch: "threadPitch",
6901
- threadhand: "threadHand",
6902
7077
  threadclass: "threadClass",
6903
7078
  chamfer: "chamfer",
6904
7079
  w: "width",
@@ -6909,27 +7084,35 @@ var tokenProperties = {
6909
7084
  clearance: "clearanceHoleDiameter"
6910
7085
  };
6911
7086
  function parseClampingShaftCollarModelParams(raw) {
6912
- const tokens = splitModelStringTokens(
6913
- expandModelStringFlags(raw.string, stringFlags4)
6914
- );
7087
+ const tokens = splitModelStringTokens(raw.string);
6915
7088
  if (raw.fn !== "clampingshaftcollar" || tokens[0]?.toLowerCase() !== "clampingshaftcollar")
6916
7089
  throw new Error("Expected an unadorned clampingshaftcollar function");
6917
7090
  const props = {};
6918
7091
  for (const token of tokens.slice(1)) {
6919
7092
  const match = token.match(/^([a-z]+)(.*)$/i);
6920
7093
  const name = match?.[1]?.toLowerCase() ?? "";
7094
+ const value = match?.[2] ?? "";
7095
+ if (["singleclamp", "lefthanded", "righthanded"].includes(name)) {
7096
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
7097
+ const property2 = name === "singleclamp" ? "singleClamp" : "leftHanded";
7098
+ if (property2 in props)
7099
+ throw new Error(
7100
+ `Duplicate or conflicting clampingshaftcollar flag "${property2}"`
7101
+ );
7102
+ props[property2] = name !== "righthanded";
7103
+ continue;
7104
+ }
6921
7105
  const property = tokenProperties[name];
6922
7106
  if (!property)
6923
7107
  throw new Error(`Unknown clampingshaftcollar token "${token}"`);
6924
7108
  if (property in props)
6925
7109
  throw new Error(`Duplicate clampingshaftcollar property "${property}"`);
6926
- const value = match[2];
6927
7110
  if (!value) throw new Error(`Token "${name}" requires a value`);
6928
- if (["mount", "threadHand", "threadClass"].includes(property)) {
7111
+ if (property === "threadClass") {
6929
7112
  const argument = value.match(/^\(([^()]+)\)$/);
6930
7113
  if (!argument)
6931
7114
  throw new Error(`Token "${name}" requires one parenthesized argument`);
6932
- props[property] = property === "threadClass" ? argument[1].toUpperCase() : argument[1].toLowerCase();
7115
+ props[property] = argument[1].toUpperCase();
6933
7116
  } else if (property === "metricSize") {
6934
7117
  if (!/^\d+(?:\.\d+)?$/.test(value))
6935
7118
  throw new Error("m requires a unitless metric designation");
@@ -6946,41 +7129,27 @@ function parseClampingShaftCollarModelParams(raw) {
6946
7129
  var model16 = defineModel({
6947
7130
  name: "clampingshaftcollar",
6948
7131
  schema: clampingShaftCollarModelDefinitionSchema,
6949
- parse: parseClampingShaftCollarModelParams,
6950
- normalizeString: (value) => normalizeModelStringFlags(
6951
- value,
6952
- stringFlags4,
6953
- parseClampingShaftCollarModelParams,
6954
- omittedStringFlags4
6955
- )
7132
+ parse: parseClampingShaftCollarModelParams
6956
7133
  });
6957
7134
  function register16(registry2) {
6958
7135
  registry2.register(model16);
6959
7136
  }
6960
- var stringFlags5 = {
6961
- closedground: "ends(closedground)",
6962
- lefthanded: "hand(left)",
6963
- righthanded: "hand(right)",
6964
- custom: "spec(custom)",
6965
- free: "state(free)"
6966
- };
6967
- var omittedStringFlags5 = ["righthanded", "custom", "free"];
6968
7137
  var length8 = external_exports.union([
6969
7138
  external_exports.number(),
6970
7139
  external_exports.string().regex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:mm|cm|m|in|inch|mil|ft|feet)?$/i).transform((value) => value.toLowerCase())
6971
7140
  ]).pipe(modelLengthSchema).refine((value) => value > 0, "Length must be positive");
6972
7141
  var turnCount = external_exports.number().int().min(1).max(Number.MAX_SAFE_INTEGER);
6973
7142
  var shape13 = {
6974
- spec: external_exports.literal("custom").default("custom"),
7143
+ custom: external_exports.literal(true).default(true),
6975
7144
  outerDiameter: length8,
6976
7145
  wireDiameter: length8,
6977
7146
  freeLength: length8,
6978
7147
  totalTurns: turnCount.min(3),
6979
7148
  /** Closed-ground ends have exactly one inactive turn at each end. */
6980
7149
  activeTurns: turnCount.optional(),
6981
- ends: external_exports.literal("closedground").default("closedground"),
6982
- hand: external_exports.enum(["right", "left"]).default("right"),
6983
- state: external_exports.literal("free").default("free")
7150
+ closedGround: external_exports.literal(true).default(true),
7151
+ leftHanded: external_exports.boolean().default(false),
7152
+ free: external_exports.literal(true).default(true)
6984
7153
  };
6985
7154
  function validate12(props, context) {
6986
7155
  if (props.outerDiameter <= 2 * props.wireDiameter)
@@ -7033,14 +7202,12 @@ function getCompressionSpringCenterlinePoint(input, turn) {
7033
7202
  throw new Error("Turn coordinate must be finite and within [0,totalTurns]");
7034
7203
  const radius = (props.outerDiameter - props.wireDiameter) / 2;
7035
7204
  const pitch = (props.freeLength - 2 * props.wireDiameter) / props.activeTurns;
7036
- const phase = (props.hand === "right" ? 1 : -1) * 2 * Math.PI * (turn % 1);
7205
+ const phase = (props.leftHanded ? -1 : 1) * 2 * Math.PI * (turn % 1);
7037
7206
  const z59 = turn <= 1 ? props.wireDiameter * turn : turn >= props.totalTurns - 1 ? props.freeLength - props.wireDiameter * (props.totalTurns - turn) : props.wireDiameter + pitch * (turn - 1);
7038
7207
  return { x: radius * Math.cos(phase), y: radius * Math.sin(phase), z: z59 };
7039
7208
  }
7040
7209
  function parseCompressionSpringModelParams(raw) {
7041
- const tokens = splitModelStringTokens(
7042
- expandModelStringFlags(raw.string, stringFlags5)
7043
- );
7210
+ const tokens = splitModelStringTokens(raw.string);
7044
7211
  if (raw.fn !== "compressionspring" || tokens[0]?.toLowerCase() !== "compressionspring")
7045
7212
  throw new Error("Expected compressionspring without an inline argument");
7046
7213
  const props = { fn: "compressionspring" };
@@ -7051,11 +7218,12 @@ function parseCompressionSpringModelParams(raw) {
7051
7218
  length: "freeLength",
7052
7219
  freelength: "freeLength"
7053
7220
  };
7054
- const selectors7 = {
7055
- spec: "spec",
7056
- ends: "ends",
7057
- hand: "hand",
7058
- state: "state"
7221
+ const flags11 = {
7222
+ custom: "custom",
7223
+ closedground: "closedGround",
7224
+ free: "free",
7225
+ lefthanded: "leftHanded",
7226
+ righthanded: "leftHanded"
7059
7227
  };
7060
7228
  for (const token of tokens.slice(1)) {
7061
7229
  const match = token.match(/^([a-z]+)(.*)$/i);
@@ -7070,11 +7238,10 @@ function parseCompressionSpringModelParams(raw) {
7070
7238
  if (!/^\d+$/.test(value))
7071
7239
  throw new Error(`${name} requires a unitless integer`);
7072
7240
  parsed = Number(value);
7073
- } else if (name in selectors7) {
7074
- property = selectors7[name];
7075
- if (!/^\([^()]+\)$/.test(value))
7076
- throw new Error(`Expected a parenthesized ${name}`);
7077
- parsed = value.slice(1, -1).toLowerCase();
7241
+ } else if (Object.hasOwn(flags11, name)) {
7242
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
7243
+ property = flags11[name];
7244
+ parsed = name !== "righthanded";
7078
7245
  } else throw new Error(`Unknown compression spring token "${token}"`);
7079
7246
  if (property in props)
7080
7247
  throw new Error(`Duplicate compression spring property "${property}"`);
@@ -7085,13 +7252,7 @@ function parseCompressionSpringModelParams(raw) {
7085
7252
  var model17 = defineModel({
7086
7253
  name: "compressionspring",
7087
7254
  schema: compressionSpringModelDefinitionSchema,
7088
- parse: parseCompressionSpringModelParams,
7089
- normalizeString: (value) => normalizeModelStringFlags(
7090
- value,
7091
- stringFlags5,
7092
- parseCompressionSpringModelParams,
7093
- omittedStringFlags5
7094
- )
7255
+ parse: parseCompressionSpringModelParams
7095
7256
  });
7096
7257
  function register17(registry2) {
7097
7258
  registry2.register(model17);
@@ -7544,7 +7705,7 @@ var aliases4 = {
7544
7705
  threadrootdiameter: "threadRootDiameter",
7545
7706
  threadpitchdiameter: "threadPitchDiameter"
7546
7707
  };
7547
- var flags2 = {
7708
+ var flags3 = {
7548
7709
  fullthread: "fullThread",
7549
7710
  plainface: "plainFace",
7550
7711
  lefthanded: "leftHand",
@@ -7570,9 +7731,9 @@ function parseFlangeBoltModelParams(raw) {
7570
7731
  const value = match?.[2] ?? "";
7571
7732
  let property;
7572
7733
  let parsed;
7573
- if (flags2[key]) {
7734
+ if (flags3[key]) {
7574
7735
  if (value) throw new Error(`Flag ${key} does not accept a value`);
7575
- property = flags2[key];
7736
+ property = flags3[key];
7576
7737
  parsed = !["righthanded", "nothreads"].includes(key);
7577
7738
  } else {
7578
7739
  if (key === "standard" || key === "headstandard")
@@ -7610,10 +7771,6 @@ var model20 = defineModel({
7610
7771
  function register20(registry2) {
7611
7772
  registry2.register(model20);
7612
7773
  }
7613
- var stringFlags6 = {
7614
- plainclosed: "style(plainclosed)"
7615
- };
7616
- var omittedStringFlags6 = ["plainclosed"];
7617
7774
  var positiveLength6 = external_exports.union([
7618
7775
  external_exports.number(),
7619
7776
  external_exports.string().regex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:mm|cm|m|in|inch|mil|ft|feet)?$/i).transform((value) => value.toLowerCase())
@@ -7625,7 +7782,7 @@ var shape17 = {
7625
7782
  /** Overall end-plane distance including the integral flange. */
7626
7783
  length: positiveLength6,
7627
7784
  flangeThickness: positiveLength6,
7628
- style: external_exports.literal("plainclosed").default("plainclosed")
7785
+ plainClosed: external_exports.literal(true).default(true)
7629
7786
  };
7630
7787
  function validate16(props, context) {
7631
7788
  if (props.outerDiameter <= props.innerDiameter)
@@ -7675,9 +7832,7 @@ var lengths12 = {
7675
7832
  flangethickness: "flangeThickness"
7676
7833
  };
7677
7834
  function parseFlangedBushingModelParams(raw) {
7678
- const tokens = splitModelStringTokens(
7679
- expandModelStringFlags(raw.string, stringFlags6)
7680
- );
7835
+ const tokens = splitModelStringTokens(raw.string);
7681
7836
  if (raw.fn !== "flangedbushing" || tokens[0]?.toLowerCase() !== "flangedbushing")
7682
7837
  throw new Error("Expected flangedbushing without an inline value");
7683
7838
  const props = { fn: "flangedbushing" };
@@ -7691,11 +7846,10 @@ function parseFlangedBushingModelParams(raw) {
7691
7846
  if (Object.hasOwn(lengths12, name)) {
7692
7847
  property = lengths12[name];
7693
7848
  parsed = value;
7694
- } else if (name === "style") {
7695
- property = "style";
7696
- const style = value.match(/^\(([a-z]+)\)$/i);
7697
- if (!style) throw new Error("Style requires a parenthesized value");
7698
- parsed = style[1].toLowerCase();
7849
+ } else if (name === "plainclosed") {
7850
+ property = "plainClosed";
7851
+ if (value !== "") throw new Error("plainclosed is a value-free flag");
7852
+ parsed = true;
7699
7853
  } else throw new Error(`Unknown flanged bushing token "${token}"`);
7700
7854
  if (Object.hasOwn(props, property))
7701
7855
  throw new Error(
@@ -7708,13 +7862,7 @@ function parseFlangedBushingModelParams(raw) {
7708
7862
  var model21 = defineModel({
7709
7863
  name: "flangedbushing",
7710
7864
  schema: flangedBushingModelDefinitionSchema,
7711
- parse: parseFlangedBushingModelParams,
7712
- normalizeString: (value) => normalizeModelStringFlags(
7713
- value,
7714
- stringFlags6,
7715
- parseFlangedBushingModelParams,
7716
- omittedStringFlags6
7717
- )
7865
+ parse: parseFlangedBushingModelParams
7718
7866
  });
7719
7867
  function register21(registry2) {
7720
7868
  registry2.register(model21);
@@ -8072,10 +8220,7 @@ var lengths13 = {
8072
8220
  outerdiameter: "outerDiameter",
8073
8221
  thickness: "thickness"
8074
8222
  };
8075
- var flags3 = { flatannulus: "flatAnnulus" };
8076
- var selectors2 = {
8077
- profile: { value: "flatannulus", property: "flatAnnulus" }
8078
- };
8223
+ var flags4 = { flatannulus: "flatAnnulus" };
8079
8224
  var integers = {};
8080
8225
  var tuples = {};
8081
8226
  function parseFlatGasketModelParams(raw) {
@@ -8093,9 +8238,7 @@ function parseFlatGasketModelParams(raw) {
8093
8238
  if (!match) throw new Error(`Malformed flatgasket token "${token}"`);
8094
8239
  const key = match[1].toLowerCase(), value = match[2];
8095
8240
  if (Object.hasOwn(lengths13, key) && value) assign(lengths13[key], value);
8096
- else if (Object.hasOwn(flags3, key) && !value) assign(flags3[key], true);
8097
- else if (Object.hasOwn(selectors2, key) && value.toLowerCase() === `(${selectors2[key].value})`)
8098
- assign(selectors2[key].property, true);
8241
+ else if (Object.hasOwn(flags4, key) && !value) assign(flags4[key], true);
8099
8242
  else if (Object.hasOwn(integers, key) && /^\d+$/.test(value))
8100
8243
  assign(integers[key], Number(value));
8101
8244
  else if (Object.hasOwn(tuples, key)) {
@@ -8116,14 +8259,6 @@ var model24 = defineModel({
8116
8259
  function register24(registry2) {
8117
8260
  registry2.register(model24);
8118
8261
  }
8119
- var stringFlags7 = {
8120
- hexsocket: "drive(hexsocket)",
8121
- fullthread: "thread(full)",
8122
- lefthanded: "threadhand(left)",
8123
- righthanded: "threadhand(right)",
8124
- male: "threadgender(male)"
8125
- };
8126
- var omittedStringFlags7 = ["righthanded", "male"];
8127
8262
  var positiveModelLengthSchema3 = external_exports.union([
8128
8263
  external_exports.number(),
8129
8264
  external_exports.string().regex(/^(?:\d+(?:\.\d+)?|\.\d+)(?:mm|cm|m|in|inch)?$/i).transform((value) => value.toLowerCase())
@@ -8199,11 +8334,11 @@ var flatHeadScrewModelPropsShape = {
8199
8334
  iso10642: external_exports.literal(true).default(true),
8200
8335
  metricSize: flatHeadScrewMetricSizeSchema,
8201
8336
  length: positiveModelLengthSchema3,
8202
- thread: external_exports.literal("full").default("full"),
8203
- drive: external_exports.literal("hexsocket").default("hexsocket"),
8204
- threadHand: external_exports.enum(["right", "left"]).default("right"),
8337
+ fullThread: external_exports.literal(true).default(true),
8338
+ hexSocket: external_exports.literal(true).default(true),
8339
+ leftHanded: external_exports.boolean().default(false),
8205
8340
  threadClass: external_exports.literal("6g").default("6g"),
8206
- threadGender: external_exports.literal("male").default("male"),
8341
+ male: external_exports.literal(true).default(true),
8207
8342
  showThreads: external_exports.boolean().default(true),
8208
8343
  diameter: positiveModelLengthSchema3.optional(),
8209
8344
  threadPitch: positiveModelLengthSchema3.optional(),
@@ -8269,11 +8404,7 @@ var aliases5 = {
8269
8404
  metricsize: "metricSize",
8270
8405
  l: "length",
8271
8406
  length: "length",
8272
- thread: "thread",
8273
- drive: "drive",
8274
- threadhand: "threadHand",
8275
8407
  threadclass: "threadClass",
8276
- threadgender: "threadGender",
8277
8408
  diameter: "diameter",
8278
8409
  threadpitch: "threadPitch",
8279
8410
  headdiameter: "headDiameter",
@@ -8294,19 +8425,18 @@ var aliases5 = {
8294
8425
  socketaf: "socketAcrossFlats",
8295
8426
  socketh: "socketDepth"
8296
8427
  };
8297
- var selectors3 = /* @__PURE__ */ new Set([
8298
- "thread",
8299
- "drive",
8300
- "threadHand",
8301
- "threadClass",
8302
- "threadGender"
8303
- ]);
8428
+ var selectors2 = /* @__PURE__ */ new Set(["threadClass"]);
8429
+ var booleanFlags2 = {
8430
+ hexsocket: { property: "hexSocket", value: true },
8431
+ fullthread: { property: "fullThread", value: true },
8432
+ male: { property: "male", value: true },
8433
+ lefthanded: { property: "leftHanded", value: true },
8434
+ righthanded: { property: "leftHanded", value: false }
8435
+ };
8304
8436
  var parseFlatHeadScrewModelParams = (raw) => {
8305
8437
  if (raw.fn !== "flatheadscrew")
8306
8438
  throw new Error(`Expected flatheadscrew params, got "${raw.fn}"`);
8307
- const [root, ...tokens] = splitModelStringTokens(
8308
- expandModelStringFlags(raw.string, stringFlags7)
8309
- );
8439
+ const [root, ...tokens] = splitModelStringTokens(raw.string);
8310
8440
  if (root?.toLowerCase() !== "flatheadscrew")
8311
8441
  throw new Error(
8312
8442
  "The flatheadscrew function does not accept an inline value"
@@ -8324,16 +8454,20 @@ var parseFlatHeadScrewModelParams = (raw) => {
8324
8454
  const value = match?.[2];
8325
8455
  let property;
8326
8456
  let parsed;
8327
- if (key === "threads" || key === "nothreads") {
8457
+ if (key && Object.hasOwn(booleanFlags2, key)) {
8458
+ if (value !== "") throw new Error(`Flag "${key}" does not accept a value`);
8459
+ property = booleanFlags2[key].property;
8460
+ parsed = booleanFlags2[key].value;
8461
+ } else if (key === "threads" || key === "nothreads") {
8328
8462
  if (value !== "")
8329
8463
  throw new Error(`Token "${key}" does not accept a value`);
8330
8464
  property = "showThreads";
8331
8465
  parsed = key === "threads";
8332
8466
  } else {
8333
- property = key ? aliases5[key] ?? "" : "";
8467
+ property = key && Object.hasOwn(aliases5, key) ? aliases5[key] : "";
8334
8468
  if (!property || !value)
8335
8469
  throw new Error(`Unknown or missing flatheadscrew token "${token}"`);
8336
- if (selectors3.has(property)) {
8470
+ if (selectors2.has(property)) {
8337
8471
  const selector = value.match(/^\(([^()]+)\)$/)?.[1];
8338
8472
  if (!selector)
8339
8473
  throw new Error(`Token "${key}" requires a parenthesized selector`);
@@ -8363,13 +8497,7 @@ var parseFlatHeadScrewModelParams = (raw) => {
8363
8497
  var model25 = defineModel({
8364
8498
  name: "flatheadscrew",
8365
8499
  schema: flatHeadScrewModelDefinitionSchema,
8366
- parse: parseFlatHeadScrewModelParams,
8367
- normalizeString: (value) => normalizeModelStringFlags(
8368
- value,
8369
- stringFlags7,
8370
- parseFlatHeadScrewModelParams,
8371
- omittedStringFlags7
8372
- )
8500
+ parse: parseFlatHeadScrewModelParams
8373
8501
  });
8374
8502
  function register25(registry2) {
8375
8503
  registry2.register(model25);
@@ -8530,7 +8658,7 @@ var aliases7 = {
8530
8658
  threadpitchdiameter: "threadPitchDiameter",
8531
8659
  rootouterdiameter: "rootOuterDiameter"
8532
8660
  };
8533
- var flags4 = {
8661
+ var flags5 = {
8534
8662
  diamondknurl: "diamondKnurl",
8535
8663
  lefthanded: "leftHand",
8536
8664
  righthanded: "leftHand",
@@ -8547,9 +8675,9 @@ function parseHeatSetInsertModelParams(raw) {
8547
8675
  for (const token of tokens) {
8548
8676
  const match = token.match(/^([a-z]+)(.*)$/i), key = match?.[1]?.toLowerCase() ?? "", value = match?.[2] ?? "";
8549
8677
  let property, parsed;
8550
- if (flags4[key]) {
8678
+ if (flags5[key]) {
8551
8679
  if (value) throw new Error("Flags do not accept values");
8552
- property = flags4[key];
8680
+ property = flags5[key];
8553
8681
  parsed = !["righthanded", "nothreads"].includes(key);
8554
8682
  } else {
8555
8683
  property = aliases7[key] ?? "";
@@ -8586,10 +8714,6 @@ var model27 = defineModel({
8586
8714
  function register27(registry2) {
8587
8715
  registry2.register(model27);
8588
8716
  }
8589
- var stringFlags8 = {
8590
- righthanded: "threadhand(right)"
8591
- };
8592
- var omittedStringFlags8 = ["righthanded"];
8593
8717
  var hexNutDimensions = {
8594
8718
  M5: {
8595
8719
  diameter: 5,
@@ -8830,7 +8954,7 @@ var shape23 = {
8830
8954
  metricSize: hexNutMetricSizeSchema.optional(),
8831
8955
  imperialSize: hexNutImperialSizeSchema.optional(),
8832
8956
  threadPitch: length13.optional(),
8833
- threadHand: external_exports.literal("right").default("right"),
8957
+ rightHanded: external_exports.literal(true).default(true),
8834
8958
  threadClass: external_exports.enum(["6H", "2B"]).optional(),
8835
8959
  showThreads: external_exports.boolean().default(true)
8836
8960
  };
@@ -8856,11 +8980,11 @@ function validate22(props, context) {
8856
8980
  const issue = (path, message) => context.addIssue({ code: "custom", path: [path], message });
8857
8981
  if (!!props.metricSize === !!props.imperialSize)
8858
8982
  issue("metricSize", "Select exactly one metric or imperial size");
8859
- const flags10 = ["iso4032", "din934", "asmeb1822"];
8983
+ const flags11 = ["iso4032", "din934", "asmeb1822"];
8860
8984
  const family = selectedFamily(props);
8861
- if (flags10.filter((flag) => props[flag] === true).length > 1)
8985
+ if (flags11.filter((flag) => props[flag] === true).length > 1)
8862
8986
  issue("iso4032", "Select only one ISO, DIN or ASME family");
8863
- for (const flag of flags10)
8987
+ for (const flag of flags11)
8864
8988
  if (family[flag] && props[flag] === false)
8865
8989
  issue(
8866
8990
  flag,
@@ -8908,14 +9032,11 @@ function getHexNutDimensions(input) {
8908
9032
  };
8909
9033
  }
8910
9034
  function parseHexNutModelParams(raw) {
8911
- const tokens = splitModelStringTokens(
8912
- expandModelStringFlags(raw.string, stringFlags8)
8913
- );
9035
+ const tokens = splitModelStringTokens(raw.string);
8914
9036
  if (raw.fn !== "hexnut" || tokens[0]?.toLowerCase() !== "hexnut")
8915
9037
  throw new Error("Expected hexnut without an inline argument");
8916
9038
  const props = { fn: "hexnut" };
8917
- const selectors7 = {
8918
- threadhand: "threadHand",
9039
+ const selectors5 = {
8919
9040
  threadclass: "threadClass"
8920
9041
  };
8921
9042
  let explicitFamily = false;
@@ -8952,9 +9073,13 @@ function parseHexNutModelParams(raw) {
8952
9073
  throw new Error("Duplicate hex nut property threadPitch");
8953
9074
  props.threadPitch = 25.4 / Number(parts[1]);
8954
9075
  }
9076
+ } else if (name === "righthanded") {
9077
+ if (value) throw new Error("Handedness flags cannot have a value");
9078
+ property = "rightHanded";
9079
+ parsed = true;
8955
9080
  } else if (name === "threadpitch") property = "threadPitch";
8956
- else if (name in selectors7) {
8957
- property = selectors7[name];
9081
+ else if (name in selectors5) {
9082
+ property = selectors5[name];
8958
9083
  if (!/^\([^()]+\)$/.test(value))
8959
9084
  throw new Error(`Expected a parenthesized ${name}`);
8960
9085
  parsed = value.slice(1, -1).toLowerCase();
@@ -8973,13 +9098,7 @@ function parseHexNutModelParams(raw) {
8973
9098
  var model28 = defineModel({
8974
9099
  name: "hexnut",
8975
9100
  schema: hexNutModelDefinitionSchema,
8976
- parse: parseHexNutModelParams,
8977
- normalizeString: (value) => normalizeModelStringFlags(
8978
- value,
8979
- stringFlags8,
8980
- parseHexNutModelParams,
8981
- omittedStringFlags8
8982
- )
9101
+ parse: parseHexNutModelParams
8983
9102
  });
8984
9103
  function register28(registry2) {
8985
9104
  registry2.register(model28);
@@ -9241,7 +9360,7 @@ var lengths15 = {
9241
9360
  studchamfer: "studChamfer",
9242
9361
  mouthchamfer: "mouthChamfer"
9243
9362
  };
9244
- var flags5 = {
9363
+ var flags6 = {
9245
9364
  hex: ["hex", true],
9246
9365
  lefthanded: ["leftHand", true],
9247
9366
  righthanded: ["leftHand", false],
@@ -9257,8 +9376,8 @@ function parseMaleFemaleStandoffModelParams(raw) {
9257
9376
  const lower = token.toLowerCase();
9258
9377
  let property;
9259
9378
  let value;
9260
- if (Object.hasOwn(flags5, lower)) {
9261
- const entry = flags5[lower];
9379
+ if (Object.hasOwn(flags6, lower)) {
9380
+ const entry = flags6[lower];
9262
9381
  property = entry[0];
9263
9382
  value = entry[1];
9264
9383
  } else {
@@ -9443,14 +9562,6 @@ var model31 = defineModel({
9443
9562
  function register31(registry2) {
9444
9563
  registry2.register(model31);
9445
9564
  }
9446
- var stringFlags9 = {
9447
- phillips: "drive(phillips)",
9448
- fullthread: "thread(full)",
9449
- lefthanded: "threadhand(left)",
9450
- righthanded: "threadhand(right)",
9451
- male: "threadgender(male)"
9452
- };
9453
- var omittedStringFlags9 = ["righthanded", "male"];
9454
9565
  var positiveModelLengthSchema4 = external_exports.union([
9455
9566
  external_exports.number(),
9456
9567
  external_exports.string().regex(/^(?:\d+(?:\.\d+)?|\.\d+)(?:mm|cm|m|in|inch)?$/i).transform((value) => value.toLowerCase())
@@ -9574,11 +9685,11 @@ var panScrewModelPropsShape = {
9574
9685
  iso7045: external_exports.literal(true).default(true),
9575
9686
  metricSize: panScrewMetricSizeSchema,
9576
9687
  length: positiveModelLengthSchema4,
9577
- thread: external_exports.literal("full").default("full"),
9578
- drive: external_exports.literal("phillips").default("phillips"),
9579
- threadHand: external_exports.enum(["right", "left"]).default("right"),
9688
+ fullThread: external_exports.literal(true).default(true),
9689
+ phillips: external_exports.literal(true).default(true),
9690
+ leftHanded: external_exports.boolean().default(false),
9580
9691
  threadClass: external_exports.literal("6g").default("6g"),
9581
- threadGender: external_exports.literal("male").default("male"),
9692
+ male: external_exports.literal(true).default(true),
9582
9693
  showThreads: external_exports.boolean().default(true),
9583
9694
  iso4757: external_exports.literal(true).default(true),
9584
9695
  recessType: external_exports.literal("H").default("H"),
@@ -9645,11 +9756,7 @@ var aliases8 = {
9645
9756
  metricsize: "metricSize",
9646
9757
  l: "length",
9647
9758
  length: "length",
9648
- thread: "thread",
9649
- drive: "drive",
9650
- threadhand: "threadHand",
9651
9759
  threadclass: "threadClass",
9652
- threadgender: "threadGender",
9653
9760
  diameter: "diameter",
9654
9761
  threadpitch: "threadPitch",
9655
9762
  headdiameter: "headDiameter",
@@ -9680,19 +9787,18 @@ var aliases8 = {
9680
9787
  headh: "headHeight",
9681
9788
  headd: "headDiameter"
9682
9789
  };
9683
- var selectors4 = /* @__PURE__ */ new Set([
9684
- "thread",
9685
- "drive",
9686
- "threadHand",
9687
- "threadClass",
9688
- "threadGender"
9689
- ]);
9790
+ var selectors3 = /* @__PURE__ */ new Set(["threadClass"]);
9791
+ var booleanFlags3 = {
9792
+ phillips: { property: "phillips", value: true },
9793
+ fullthread: { property: "fullThread", value: true },
9794
+ male: { property: "male", value: true },
9795
+ lefthanded: { property: "leftHanded", value: true },
9796
+ righthanded: { property: "leftHanded", value: false }
9797
+ };
9690
9798
  var parsePanScrewModelParams = (raw) => {
9691
9799
  if (raw.fn !== "panscrew")
9692
9800
  throw new Error(`Expected panscrew params, got "${raw.fn}"`);
9693
- const [root, ...tokens] = splitModelStringTokens(
9694
- expandModelStringFlags(raw.string, stringFlags9)
9695
- );
9801
+ const [root, ...tokens] = splitModelStringTokens(raw.string);
9696
9802
  if (root?.toLowerCase() !== "panscrew")
9697
9803
  throw new Error("The panscrew function does not accept an inline value");
9698
9804
  const props = {};
@@ -9709,16 +9815,20 @@ var parsePanScrewModelParams = (raw) => {
9709
9815
  const value = match?.[2];
9710
9816
  let property;
9711
9817
  let parsed;
9712
- if (key === "threads" || key === "nothreads") {
9818
+ if (key && Object.hasOwn(booleanFlags3, key)) {
9819
+ if (value !== "") throw new Error(`Flag "${key}" does not accept a value`);
9820
+ property = booleanFlags3[key].property;
9821
+ parsed = booleanFlags3[key].value;
9822
+ } else if (key === "threads" || key === "nothreads") {
9713
9823
  if (value !== "")
9714
9824
  throw new Error(`Token "${key}" does not accept a value`);
9715
9825
  property = "showThreads";
9716
9826
  parsed = key === "threads";
9717
9827
  } else {
9718
- property = key ? aliases8[key] ?? "" : "";
9828
+ property = key && Object.hasOwn(aliases8, key) ? aliases8[key] : "";
9719
9829
  if (!property || !value)
9720
9830
  throw new Error(`Unknown or missing panscrew token "${token}"`);
9721
- if (selectors4.has(property)) {
9831
+ if (selectors3.has(property)) {
9722
9832
  const selector = value.match(/^\(([^()]+)\)$/)?.[1];
9723
9833
  if (!selector)
9724
9834
  throw new Error(`Token "${key}" requires a parenthesized selector`);
@@ -9751,13 +9861,7 @@ var parsePanScrewModelParams = (raw) => {
9751
9861
  var model32 = defineModel({
9752
9862
  name: "panscrew",
9753
9863
  schema: panScrewModelDefinitionSchema,
9754
- parse: parsePanScrewModelParams,
9755
- normalizeString: (value) => normalizeModelStringFlags(
9756
- value,
9757
- stringFlags9,
9758
- parsePanScrewModelParams,
9759
- omittedStringFlags9
9760
- )
9864
+ parse: parsePanScrewModelParams
9761
9865
  });
9762
9866
  function register32(registry2) {
9763
9867
  registry2.register(model32);
@@ -9943,10 +10047,6 @@ var model34 = defineModel({
9943
10047
  function register34(registry2) {
9944
10048
  registry2.register(model34);
9945
10049
  }
9946
- var stringFlags10 = {
9947
- plainclosed: "style(plainclosed)"
9948
- };
9949
- var omittedStringFlags10 = ["plainclosed"];
9950
10050
  var length18 = external_exports.union([
9951
10051
  external_exports.number(),
9952
10052
  external_exports.string().regex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:mm|cm|m|in|inch|mil|ft|feet)?$/i).transform((value) => value.toLowerCase())
@@ -9957,7 +10057,7 @@ var shape29 = {
9957
10057
  outerDiameter: positiveLength12,
9958
10058
  /** Overall end-plane distance, including the chamfers. */
9959
10059
  length: positiveLength12,
9960
- style: external_exports.literal("plainclosed").default("plainclosed"),
10060
+ plainClosed: external_exports.literal(true).default(true),
9961
10061
  /** Equal axial/radial setbacks of all four 45-degree bore/outside rim chamfers. */
9962
10062
  edgeChamfer: length18.refine((value) => value >= 0).default(0)
9963
10063
  };
@@ -10005,9 +10105,7 @@ var lengths16 = {
10005
10105
  edgechamfer: "edgeChamfer"
10006
10106
  };
10007
10107
  function parsePlainBushingModelParams(raw) {
10008
- const tokens = splitModelStringTokens(
10009
- expandModelStringFlags(raw.string, stringFlags10)
10010
- );
10108
+ const tokens = splitModelStringTokens(raw.string);
10011
10109
  if (raw.fn !== "plainbushing" || tokens[0]?.toLowerCase() !== "plainbushing")
10012
10110
  throw new Error("Expected plainbushing without an inline value");
10013
10111
  const props = { fn: "plainbushing" };
@@ -10021,11 +10119,10 @@ function parsePlainBushingModelParams(raw) {
10021
10119
  if (Object.hasOwn(lengths16, name)) {
10022
10120
  property = lengths16[name];
10023
10121
  parsed = value;
10024
- } else if (name === "style") {
10025
- property = "style";
10026
- const style = value.match(/^\(([a-z]+)\)$/i);
10027
- if (!style) throw new Error("Style requires a parenthesized value");
10028
- parsed = style[1].toLowerCase();
10122
+ } else if (name === "plainclosed") {
10123
+ property = "plainClosed";
10124
+ if (value !== "") throw new Error("plainclosed is a value-free flag");
10125
+ parsed = true;
10029
10126
  } else throw new Error(`Unknown plain bushing token "${token}"`);
10030
10127
  if (Object.hasOwn(props, property))
10031
10128
  throw new Error(
@@ -10038,13 +10135,7 @@ function parsePlainBushingModelParams(raw) {
10038
10135
  var model35 = defineModel({
10039
10136
  name: "plainbushing",
10040
10137
  schema: plainBushingModelDefinitionSchema,
10041
- parse: parsePlainBushingModelParams,
10042
- normalizeString: (value) => normalizeModelStringFlags(
10043
- value,
10044
- stringFlags10,
10045
- parsePlainBushingModelParams,
10046
- omittedStringFlags10
10047
- )
10138
+ parse: parsePlainBushingModelParams
10048
10139
  });
10049
10140
  function register35(registry2) {
10050
10141
  registry2.register(model35);
@@ -10242,10 +10333,7 @@ var lengths17 = {
10242
10333
  thickness: "thickness",
10243
10334
  cornerradius: "cornerRadius"
10244
10335
  };
10245
- var flags6 = { flatframe: "flatFrame" };
10246
- var selectors5 = {
10247
- profile: { value: "flatframe", property: "flatFrame" }
10248
- };
10336
+ var flags7 = { flatframe: "flatFrame" };
10249
10337
  var integers2 = {};
10250
10338
  var tuples2 = {};
10251
10339
  function parseRectangularGasketModelParams(raw) {
@@ -10263,9 +10351,7 @@ function parseRectangularGasketModelParams(raw) {
10263
10351
  if (!match) throw new Error(`Malformed rectangulargasket token "${token}"`);
10264
10352
  const key = match[1].toLowerCase(), value = match[2];
10265
10353
  if (Object.hasOwn(lengths17, key) && value) assign(lengths17[key], value);
10266
- else if (Object.hasOwn(flags6, key) && !value) assign(flags6[key], true);
10267
- else if (Object.hasOwn(selectors5, key) && value.toLowerCase() === `(${selectors5[key].value})`)
10268
- assign(selectors5[key].property, true);
10354
+ else if (Object.hasOwn(flags7, key) && !value) assign(flags7[key], true);
10269
10355
  else if (Object.hasOwn(integers2, key) && /^\d+$/.test(value))
10270
10356
  assign(integers2[key], Number(value));
10271
10357
  else if (Object.hasOwn(tuples2, key)) {
@@ -10371,12 +10457,6 @@ var model39 = defineModel({
10371
10457
  function register39(registry2) {
10372
10458
  registry2.register(model39);
10373
10459
  }
10374
- var stringFlags11 = {
10375
- setscrew: "mount(setscrew)",
10376
- lefthanded: "threadhand(left)",
10377
- righthanded: "threadhand(right)"
10378
- };
10379
- var omittedStringFlags11 = ["righthanded"];
10380
10460
  var length23 = external_exports.union([
10381
10461
  external_exports.number().finite(),
10382
10462
  external_exports.string().regex(
@@ -10397,10 +10477,10 @@ var shape34 = {
10397
10477
  screwCount: external_exports.literal(4).default(4),
10398
10478
  screwEndOffset: positive6.optional(),
10399
10479
  screwAngle: external_exports.number().finite().default(0),
10400
- mount: external_exports.literal("setscrew").default("setscrew"),
10480
+ setScrew: external_exports.literal(true).default(true),
10401
10481
  metricSize: metricBoltSizeSchema,
10402
10482
  threadPitch: positive6.optional(),
10403
- threadHand: external_exports.enum(["right", "left"]).default("right"),
10483
+ leftHanded: external_exports.boolean().default(false),
10404
10484
  threadClass: external_exports.literal("6H").default("6H"),
10405
10485
  chamfer: nonnegative5.default(0)
10406
10486
  };
@@ -10469,7 +10549,7 @@ function getRigidCouplerDimensions(input) {
10469
10549
  diameter: threadDiameter,
10470
10550
  depth: radius - Math.sqrt((hole.bore / 2) ** 2 - (threadDiameter / 2) ** 2),
10471
10551
  threadPitch: p.threadPitch,
10472
- threadHand: p.threadHand,
10552
+ leftHanded: p.leftHanded,
10473
10553
  threadGender: "female",
10474
10554
  threadClass: p.threadClass
10475
10555
  };
@@ -10484,10 +10564,8 @@ function getRigidCouplerDimensions(input) {
10484
10564
  var tokenProperties2 = {
10485
10565
  bore: "boreDiameter",
10486
10566
  od: "outerDiameter",
10487
- mount: "mount",
10488
10567
  m: "metricSize",
10489
10568
  threadpitch: "threadPitch",
10490
- threadhand: "threadHand",
10491
10569
  threadclass: "threadClass",
10492
10570
  chamfer: "chamfer",
10493
10571
  boreb: "boreBDiameter",
@@ -10498,26 +10576,34 @@ var tokenProperties2 = {
10498
10576
  screwangle: "screwAngle"
10499
10577
  };
10500
10578
  function parseRigidCouplerModelParams(raw) {
10501
- const tokens = splitModelStringTokens(
10502
- expandModelStringFlags(raw.string, stringFlags11)
10503
- );
10579
+ const tokens = splitModelStringTokens(raw.string);
10504
10580
  if (raw.fn !== "rigidcoupler" || tokens[0]?.toLowerCase() !== "rigidcoupler")
10505
10581
  throw new Error("Expected an unadorned rigidcoupler function");
10506
10582
  const props = {};
10507
10583
  for (const token of tokens.slice(1)) {
10508
10584
  const match = token.match(/^([a-z]+)(.*)$/i);
10509
10585
  const name = match?.[1]?.toLowerCase() ?? "";
10586
+ const value = match?.[2] ?? "";
10587
+ if (["setscrew", "lefthanded", "righthanded"].includes(name)) {
10588
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
10589
+ const property2 = name === "setscrew" ? "setScrew" : "leftHanded";
10590
+ if (property2 in props)
10591
+ throw new Error(
10592
+ `Duplicate or conflicting rigidcoupler flag "${property2}"`
10593
+ );
10594
+ props[property2] = name !== "righthanded";
10595
+ continue;
10596
+ }
10510
10597
  const property = tokenProperties2[name];
10511
10598
  if (!property) throw new Error(`Unknown rigidcoupler token "${token}"`);
10512
10599
  if (property in props)
10513
10600
  throw new Error(`Duplicate rigidcoupler property "${property}"`);
10514
- const value = match[2];
10515
10601
  if (!value) throw new Error(`Token "${name}" requires a value`);
10516
- if (["mount", "threadHand", "threadClass"].includes(property)) {
10602
+ if (property === "threadClass") {
10517
10603
  const argument = value.match(/^\(([^()]+)\)$/);
10518
10604
  if (!argument)
10519
10605
  throw new Error(`Token "${name}" requires one parenthesized argument`);
10520
- props[property] = property === "threadClass" ? argument[1].toUpperCase() : argument[1].toLowerCase();
10606
+ props[property] = argument[1].toUpperCase();
10521
10607
  } else if (property === "metricSize") {
10522
10608
  if (!/^\d+(?:\.\d+)?$/.test(value))
10523
10609
  throw new Error("m requires a unitless metric designation");
@@ -10542,13 +10628,7 @@ function parseRigidCouplerModelParams(raw) {
10542
10628
  var model40 = defineModel({
10543
10629
  name: "rigidcoupler",
10544
10630
  schema: rigidCouplerModelDefinitionSchema,
10545
- parse: parseRigidCouplerModelParams,
10546
- normalizeString: (value) => normalizeModelStringFlags(
10547
- value,
10548
- stringFlags11,
10549
- parseRigidCouplerModelParams,
10550
- omittedStringFlags11
10551
- )
10631
+ parse: parseRigidCouplerModelParams
10552
10632
  });
10553
10633
  function register40(registry2) {
10554
10634
  registry2.register(model40);
@@ -10664,7 +10744,7 @@ var aliases14 = {
10664
10744
  threadrootdiameter: "threadRootDiameter",
10665
10745
  threadpitchdiameter: "threadPitchDiameter"
10666
10746
  };
10667
- var flags7 = {
10747
+ var flags8 = {
10668
10748
  hexsocket: "hexSocket",
10669
10749
  cuppoint: "cupPoint",
10670
10750
  lefthanded: "leftHand",
@@ -10690,9 +10770,9 @@ function parseSetScrewModelParams(raw) {
10690
10770
  const value = match?.[2] ?? "";
10691
10771
  let property;
10692
10772
  let parsed;
10693
- if (flags7[key]) {
10773
+ if (flags8[key]) {
10694
10774
  if (value) throw new Error(`Flag ${key} does not accept a value`);
10695
- property = flags7[key];
10775
+ property = flags8[key];
10696
10776
  parsed = !["righthanded", "nothreads"].includes(key);
10697
10777
  } else {
10698
10778
  property = aliases14[key] ?? "";
@@ -10726,12 +10806,6 @@ var model41 = defineModel({
10726
10806
  function register41(registry2) {
10727
10807
  registry2.register(model41);
10728
10808
  }
10729
- var stringFlags12 = {
10730
- setscrew: "mount(setscrew)",
10731
- lefthanded: "threadhand(left)",
10732
- righthanded: "threadhand(right)"
10733
- };
10734
- var omittedStringFlags12 = ["righthanded"];
10735
10809
  var length24 = external_exports.union([
10736
10810
  external_exports.number().finite(),
10737
10811
  external_exports.string().regex(
@@ -10750,10 +10824,10 @@ var shape36 = {
10750
10824
  width: positive7,
10751
10825
  screwZ: positive7.optional(),
10752
10826
  screwAngle: external_exports.number().finite().default(0),
10753
- mount: external_exports.literal("setscrew").default("setscrew"),
10827
+ setScrew: external_exports.literal(true).default(true),
10754
10828
  metricSize: metricBoltSizeSchema,
10755
10829
  threadPitch: positive7.optional(),
10756
- threadHand: external_exports.enum(["right", "left"]).default("right"),
10830
+ leftHanded: external_exports.boolean().default(false),
10757
10831
  threadClass: external_exports.literal("6H").default("6H"),
10758
10832
  chamfer: nonnegative6.default(0)
10759
10833
  };
@@ -10803,7 +10877,7 @@ function getShaftCollarDimensions(input) {
10803
10877
  diameter: threadDiameter,
10804
10878
  depth: radius - Math.sqrt((p.boreDiameter / 2) ** 2 - (threadDiameter / 2) ** 2),
10805
10879
  threadPitch: p.threadPitch,
10806
- threadHand: p.threadHand,
10880
+ leftHanded: p.leftHanded,
10807
10881
  threadGender: "female",
10808
10882
  threadClass: p.threadClass
10809
10883
  }
@@ -10812,10 +10886,8 @@ function getShaftCollarDimensions(input) {
10812
10886
  var tokenProperties3 = {
10813
10887
  bore: "boreDiameter",
10814
10888
  od: "outerDiameter",
10815
- mount: "mount",
10816
10889
  m: "metricSize",
10817
10890
  threadpitch: "threadPitch",
10818
- threadhand: "threadHand",
10819
10891
  threadclass: "threadClass",
10820
10892
  chamfer: "chamfer",
10821
10893
  w: "width",
@@ -10824,26 +10896,34 @@ var tokenProperties3 = {
10824
10896
  screwangle: "screwAngle"
10825
10897
  };
10826
10898
  function parseShaftCollarModelParams(raw) {
10827
- const tokens = splitModelStringTokens(
10828
- expandModelStringFlags(raw.string, stringFlags12)
10829
- );
10899
+ const tokens = splitModelStringTokens(raw.string);
10830
10900
  if (raw.fn !== "shaftcollar" || tokens[0]?.toLowerCase() !== "shaftcollar")
10831
10901
  throw new Error("Expected an unadorned shaftcollar function");
10832
10902
  const props = {};
10833
10903
  for (const token of tokens.slice(1)) {
10834
10904
  const match = token.match(/^([a-z]+)(.*)$/i);
10835
10905
  const name = match?.[1]?.toLowerCase() ?? "";
10906
+ const value = match?.[2] ?? "";
10907
+ if (["setscrew", "lefthanded", "righthanded"].includes(name)) {
10908
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
10909
+ const property2 = name === "setscrew" ? "setScrew" : "leftHanded";
10910
+ if (property2 in props)
10911
+ throw new Error(
10912
+ `Duplicate or conflicting shaftcollar flag "${property2}"`
10913
+ );
10914
+ props[property2] = name !== "righthanded";
10915
+ continue;
10916
+ }
10836
10917
  const property = tokenProperties3[name];
10837
10918
  if (!property) throw new Error(`Unknown shaftcollar token "${token}"`);
10838
10919
  if (property in props)
10839
10920
  throw new Error(`Duplicate shaftcollar property "${property}"`);
10840
- const value = match[2];
10841
10921
  if (!value) throw new Error(`Token "${name}" requires a value`);
10842
- if (["mount", "threadHand", "threadClass"].includes(property)) {
10922
+ if (property === "threadClass") {
10843
10923
  const argument = value.match(/^\(([^()]+)\)$/);
10844
10924
  if (!argument)
10845
10925
  throw new Error(`Token "${name}" requires one parenthesized argument`);
10846
- props[property] = property === "threadClass" ? argument[1].toUpperCase() : argument[1].toLowerCase();
10926
+ props[property] = argument[1].toUpperCase();
10847
10927
  } else if (property === "metricSize") {
10848
10928
  if (!/^\d+(?:\.\d+)?$/.test(value))
10849
10929
  throw new Error("m requires a unitless metric designation");
@@ -10861,13 +10941,7 @@ function parseShaftCollarModelParams(raw) {
10861
10941
  var model42 = defineModel({
10862
10942
  name: "shaftcollar",
10863
10943
  schema: shaftCollarModelDefinitionSchema,
10864
- parse: parseShaftCollarModelParams,
10865
- normalizeString: (value) => normalizeModelStringFlags(
10866
- value,
10867
- stringFlags12,
10868
- parseShaftCollarModelParams,
10869
- omittedStringFlags12
10870
- )
10944
+ parse: parseShaftCollarModelParams
10871
10945
  });
10872
10946
  function register42(registry2) {
10873
10947
  registry2.register(model42);
@@ -11137,8 +11211,8 @@ var lengths18 = {
11137
11211
  groovedepth: "grooveDepth",
11138
11212
  splitwidth: "splitWidth"
11139
11213
  };
11140
- var flags8 = {};
11141
- var selectors6 = {};
11214
+ var flags9 = {};
11215
+ var selectors4 = {};
11142
11216
  var integers3 = {};
11143
11217
  var tuples3 = {};
11144
11218
  function parseSplitGrommetModelParams(raw) {
@@ -11156,9 +11230,9 @@ function parseSplitGrommetModelParams(raw) {
11156
11230
  if (!match) throw new Error(`Malformed splitgrommet token "${token}"`);
11157
11231
  const key = match[1].toLowerCase(), value = match[2];
11158
11232
  if (Object.hasOwn(lengths18, key) && value) assign(lengths18[key], value);
11159
- else if (Object.hasOwn(flags8, key) && !value) assign(flags8[key], true);
11160
- else if (Object.hasOwn(selectors6, key) && value.toLowerCase() === `(${selectors6[key].value})`)
11161
- assign(selectors6[key].property, true);
11233
+ else if (Object.hasOwn(flags9, key) && !value) assign(flags9[key], true);
11234
+ else if (Object.hasOwn(selectors4, key) && value.toLowerCase() === `(${selectors4[key].value})`)
11235
+ assign(selectors4[key].property, true);
11162
11236
  else if (Object.hasOwn(integers3, key) && /^\d+$/.test(value))
11163
11237
  assign(integers3[key], Number(value));
11164
11238
  else if (Object.hasOwn(tuples3, key)) {
@@ -11607,12 +11681,9 @@ var aliases17 = {
11607
11681
  barbh: "barbHeight",
11608
11682
  barbheight: "barbHeight"
11609
11683
  };
11610
- var flags9 = {
11684
+ var flags10 = {
11611
11685
  tee: "tee"
11612
11686
  };
11613
- var literals = {
11614
- profile: ["tee", "tee"]
11615
- };
11616
11687
  function parseTSlotCoverStripModelParams(raw) {
11617
11688
  const [name, ...tokens] = splitModelStringTokens(raw.string);
11618
11689
  if (raw.fn !== "tslotcoverstrip" || name?.toLowerCase() !== "tslotcoverstrip")
@@ -11629,15 +11700,10 @@ function parseTSlotCoverStripModelParams(raw) {
11629
11700
  const value = match?.[2];
11630
11701
  if (!key || value === void 0)
11631
11702
  throw new Error(`Malformed tslotcoverstrip token "${token}"`);
11632
- if (Object.hasOwn(flags9, key)) {
11703
+ if (Object.hasOwn(flags10, key)) {
11633
11704
  if (value !== "")
11634
11705
  throw new Error(`tslotcoverstrip flag "${key}" takes no value`);
11635
- assign(flags9[key], true);
11636
- } else if (Object.hasOwn(literals, key)) {
11637
- const [literal, property] = literals[key];
11638
- if (value.toLowerCase() !== `(${literal})`)
11639
- throw new Error(`Expected tslotcoverstrip ${key}(${literal})`);
11640
- assign(property, true);
11706
+ assign(flags10[key], true);
11641
11707
  } else if (Object.hasOwn(aliases17, key) && value) {
11642
11708
  const property = aliases17[key];
11643
11709
  assign(property, property === "pinCount" ? Number(value) : value);
@@ -11654,15 +11720,11 @@ var model49 = defineModel({
11654
11720
  function register49(registry2) {
11655
11721
  registry2.register(model49);
11656
11722
  }
11657
- var stringFlags13 = {
11658
- fourtsolid: "profile(fourtsolid)"
11659
- };
11660
- var omittedStringFlags13 = [];
11661
11723
  var shape44 = {
11662
11724
  width: positiveGearLengthSchema.default(20),
11663
11725
  height: positiveGearLengthSchema.default(20),
11664
11726
  length: positiveGearLengthSchema.default(100),
11665
- profile: external_exports.literal("fourtsolid").default("fourtsolid"),
11727
+ fourTSolid: external_exports.literal(true).default(true),
11666
11728
  slotWidth: positiveGearLengthSchema.default(6),
11667
11729
  pocketWidth: positiveGearLengthSchema.default(10),
11668
11730
  pocketDepth: positiveGearLengthSchema.default(2),
@@ -11722,9 +11784,7 @@ var lengths22 = {
11722
11784
  cornerradius: "cornerRadius"
11723
11785
  };
11724
11786
  function parseTSlotExtrusionModelParams(raw) {
11725
- const tokens = splitModelStringTokens(
11726
- expandModelStringFlags(raw.string, stringFlags13)
11727
- );
11787
+ const tokens = splitModelStringTokens(raw.string);
11728
11788
  if (raw.fn !== "tslotextrusion" || tokens[0]?.toLowerCase() !== raw.fn)
11729
11789
  throw new Error("Expected tslotextrusion without an inline value");
11730
11790
  const props = { fn: "tslotextrusion" };
@@ -11733,11 +11793,10 @@ function parseTSlotExtrusionModelParams(raw) {
11733
11793
  let property;
11734
11794
  let parsed = value;
11735
11795
  if (name in lengths22) property = lengths22[name];
11736
- else if (name === "profile") {
11737
- property = "profile";
11738
- const match = value.match(/^\(([a-z]+)\)$/i);
11739
- if (!match) throw new Error("Profile requires one parenthesized identity");
11740
- parsed = match[1].toLowerCase();
11796
+ else if (name === "fourtsolid") {
11797
+ property = "fourTSolid";
11798
+ if (value !== "") throw new Error("fourtsolid is a value-free flag");
11799
+ parsed = true;
11741
11800
  } else throw new Error(`Unknown T-slot extrusion token "${token}"`);
11742
11801
  if (property in props)
11743
11802
  throw new Error(`T-slot extrusion property "${property}" is repeated`);
@@ -11748,26 +11807,16 @@ function parseTSlotExtrusionModelParams(raw) {
11748
11807
  var model50 = defineModel({
11749
11808
  name: "tslotextrusion",
11750
11809
  schema: tSlotExtrusionModelDefinitionSchema,
11751
- parse: parseTSlotExtrusionModelParams,
11752
- normalizeString: (value) => normalizeModelStringFlags(
11753
- value,
11754
- stringFlags13,
11755
- parseTSlotExtrusionModelParams,
11756
- omittedStringFlags13
11757
- )
11810
+ parse: parseTSlotExtrusionModelParams
11758
11811
  });
11759
11812
  function register50(registry2) {
11760
11813
  registry2.register(model50);
11761
11814
  }
11762
- var stringFlags14 = {
11763
- righttriangle: "shape(righttriangle)"
11764
- };
11765
- var omittedStringFlags14 = [];
11766
11815
  var shape45 = {
11767
11816
  width: positiveGearLengthSchema.default(40),
11768
11817
  height: positiveGearLengthSchema.default(40),
11769
11818
  thickness: positiveGearLengthSchema.default(4),
11770
- shape: external_exports.literal("righttriangle").default("righttriangle"),
11819
+ rightTriangle: external_exports.literal(true).default(true),
11771
11820
  slotCount: external_exports.literal(2).default(2),
11772
11821
  /** Capsule width and overall length, including both semicircular ends. */
11773
11822
  slot: external_exports.tuple([positiveGearLengthSchema, positiveGearLengthSchema]).default([5, 12]),
@@ -11842,9 +11891,7 @@ var lengths23 = {
11842
11891
  edgemargin: "edgeMargin"
11843
11892
  };
11844
11893
  function parseTSlotGussetModelParams(raw) {
11845
- const tokens = splitModelStringTokens(
11846
- expandModelStringFlags(raw.string, stringFlags14)
11847
- );
11894
+ const tokens = splitModelStringTokens(raw.string);
11848
11895
  if (raw.fn !== "tslotgusset" || tokens[0]?.toLowerCase() !== raw.fn)
11849
11896
  throw new Error("Expected tslotgusset without an inline value");
11850
11897
  const props = { fn: "tslotgusset" };
@@ -11856,11 +11903,10 @@ function parseTSlotGussetModelParams(raw) {
11856
11903
  else if (name === "slots" || name === "slotcount") {
11857
11904
  property = "slotCount";
11858
11905
  parsed = parseGearInteger(value, "slotCount");
11859
- } else if (name === "shape") {
11860
- property = "shape";
11861
- const match = value.match(/^\(([a-z]+)\)$/i);
11862
- if (!match) throw new Error("Shape requires one parenthesized identity");
11863
- parsed = match[1].toLowerCase();
11906
+ } else if (name === "righttriangle") {
11907
+ property = "rightTriangle";
11908
+ if (value !== "") throw new Error("righttriangle is a value-free flag");
11909
+ parsed = true;
11864
11910
  } else if (name === "slot" || name === "centers") {
11865
11911
  property = name;
11866
11912
  const match = value.match(/^\(([^(),]+),([^(),]+)\)$/);
@@ -11876,13 +11922,7 @@ function parseTSlotGussetModelParams(raw) {
11876
11922
  var model51 = defineModel({
11877
11923
  name: "tslotgusset",
11878
11924
  schema: tSlotGussetModelDefinitionSchema,
11879
- parse: parseTSlotGussetModelParams,
11880
- normalizeString: (value) => normalizeModelStringFlags(
11881
- value,
11882
- stringFlags14,
11883
- parseTSlotGussetModelParams,
11884
- omittedStringFlags14
11885
- )
11925
+ parse: parseTSlotGussetModelParams
11886
11926
  });
11887
11927
  function register51(registry2) {
11888
11928
  registry2.register(model51);
@@ -12138,24 +12178,24 @@ function getNemaMotorReferencePoints(input) {
12138
12178
  p.bodyWidth / 2 / Math.max(Math.abs(x), Math.abs(y)),
12139
12179
  (p.bodyWidth - p.faceCornerChamfer) / (Math.abs(x) + Math.abs(y))
12140
12180
  );
12141
- if (p.nemaSize === 34 && p.cornerProfile === "stepped") {
12142
- const h2 = p.bodyWidth / 2, c = p.bodyCornerChamfer / Math.SQRT2, r = Math.min(4, c / 4);
12143
- const contains = (distance2) => {
12144
- const a = Math.abs(distance2 * x), b = Math.abs(distance2 * y);
12145
- if (a > h2 || b > h2) return false;
12146
- if (c === 0) return true;
12147
- if (a > h2 - c && b > h2 - c)
12148
- return a <= h2 - c + r && b <= h2 - c + r && Math.hypot(a - (h2 - c + r), b - (h2 - c + r)) >= r;
12149
- if (b > h2 - r && a > h2 - c - r)
12150
- return Math.hypot(a - (h2 - c - r), b - (h2 - r)) <= r;
12151
- if (a > h2 - r && b > h2 - c - r)
12152
- return Math.hypot(a - (h2 - r), b - (h2 - c - r)) <= r;
12153
- return true;
12181
+ if (p.nemaSize !== 34 || p.stepped) {
12182
+ const profile6 = p.nemaSize === 34 ? {
12183
+ width: p.bodyWidth,
12184
+ chamfer: p.bodyCornerChamfer,
12185
+ stepped: p.stepped,
12186
+ recessRadius: p.bodyCornerRecessRadius,
12187
+ filletRadius: p.bodyCornerFilletRadius
12188
+ } : {
12189
+ width: p.bodyWidth,
12190
+ chamfer: p.rearCornerChamfer,
12191
+ stepped: p.rearStepped,
12192
+ recessRadius: p.rearCornerRecessRadius,
12193
+ filletRadius: p.rearCornerFilletRadius
12154
12194
  };
12155
12195
  let low = 0, high = p.bodyWidth;
12156
12196
  for (let i = 0; i < 50; i++) {
12157
12197
  const mid = (low + high) / 2;
12158
- if (contains(mid)) low = mid;
12198
+ if (isInsideNemaFace(mid * x, mid * y, profile6)) low = mid;
12159
12199
  else high = mid;
12160
12200
  }
12161
12201
  radius = low;
@@ -12182,7 +12222,7 @@ function getNemaMotorReferencePoints(input) {
12182
12222
  },
12183
12223
  direction: { x, y, z: 0 }
12184
12224
  },
12185
- shaftflat: p.shaftShape === "d" ? {
12225
+ shaftflat: p.dShaft ? {
12186
12226
  position: {
12187
12227
  x: (p.shaftDiameter / 2 - p.shaftFlatDepth) * Math.cos(flatRadians),
12188
12228
  y: (p.shaftDiameter / 2 - p.shaftFlatDepth) * Math.sin(flatRadians),
@@ -13744,7 +13784,11 @@ function finishShaftMountMesh(geometry) {
13744
13784
  function createClampingShaftCollarMesh(input) {
13745
13785
  const props = clampingShaftCollarModelPropsSchema.parse(input);
13746
13786
  const dimensions5 = getClampingShaftCollarDimensions(props);
13747
- assertShaftMountResolution([dimensions5.threadedHole]);
13787
+ const threadedHole = {
13788
+ ...dimensions5.threadedHole,
13789
+ threadHand: dimensions5.threadedHole.leftHanded ? "left" : "right"
13790
+ };
13791
+ assertShaftMountResolution([threadedHole]);
13748
13792
  const body = createAnnularSleeve({
13749
13793
  outerRadius: props.outerDiameter / 2,
13750
13794
  boreARadius: props.boreDiameter / 2,
@@ -13770,7 +13814,7 @@ function createClampingShaftCollarMesh(input) {
13770
13814
  body,
13771
13815
  slit,
13772
13816
  createMountingHoleCutter(dimensions5.clearanceHole),
13773
- createMountingHoleCutter(dimensions5.threadedHole)
13817
+ createMountingHoleCutter(threadedHole)
13774
13818
  )
13775
13819
  );
13776
13820
  }
@@ -13955,7 +13999,7 @@ function createCompressionSpringMesh(input, options = {}) {
13955
13999
  }
13956
14000
  }
13957
14001
  const triangle = (a, b, c) => {
13958
- if (props.hand === "left") indices.push(a, c, b);
14002
+ if (props.leftHanded) indices.push(a, c, b);
13959
14003
  else indices.push(a, b, c);
13960
14004
  };
13961
14005
  for (let step = 0; step < steps; step++)
@@ -14690,7 +14734,7 @@ function createFlatHeadScrewMesh(input, options = {}) {
14690
14734
  levels.add(-p.length + (shaftEnd + p.length) * i / steps);
14691
14735
  for (const z of [...levels].sort((a, b) => a - b)) {
14692
14736
  const r = add((theta) => {
14693
- const phase = (((z + p.length) / pitch - (p.threadHand === "right" ? 1 : -1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
14737
+ const phase = (((z + p.length) / pitch - (p.leftHanded ? -1 : 1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
14694
14738
  const d = Math.min(phase, 1 - phase);
14695
14739
  const groove = Math.min(
14696
14740
  major - minor,
@@ -14890,6 +14934,11 @@ var resolveFlexScreenSize = ({
14890
14934
  };
14891
14935
  };
14892
14936
  var resolveOrientation = (props) => {
14937
+ if ("orientation" in props) {
14938
+ throw new Error(
14939
+ "FlexScreen orientation is no longer supported; use a boolean placement flag"
14940
+ );
14941
+ }
14893
14942
  const shortcuts = [
14894
14943
  ["sitsFlat", props.sitsFlat],
14895
14944
  ["sitsFlatBelowBoard", props.sitsFlatBelowBoard],
@@ -14899,13 +14948,24 @@ var resolveOrientation = (props) => {
14899
14948
  ["foldedToFaceBelowBoard", props.foldsBelowBoard],
14900
14949
  ["foldedToRightAngleAboveBoard", props.foldedToRightAngleAboveBoard],
14901
14950
  ["foldedToRightAngleBelowBoard", props.foldedToRightAngleBelowBoard]
14902
- ].filter((entry) => entry[1]);
14903
- if (shortcuts.length > 1) {
14951
+ ];
14952
+ for (const [name, value] of shortcuts) {
14953
+ if (value !== void 0 && typeof value !== "boolean") {
14954
+ throw new Error(`FlexScreen placement flag "${name}" must be a boolean`);
14955
+ }
14956
+ }
14957
+ const selected = shortcuts.filter((entry) => entry[1] === true);
14958
+ if (selected.length > 1) {
14904
14959
  throw new Error(
14905
14960
  "Only one FlexScreen boolean orientation shortcut can be true"
14906
14961
  );
14907
14962
  }
14908
- return shortcuts[0]?.[0] ?? props.orientation ?? "sitsFlat";
14963
+ if (selected.length === 0 && shortcuts.some((entry) => entry[1] !== void 0)) {
14964
+ throw new Error(
14965
+ "A FlexScreen orientation flag must be true when placement flags are supplied"
14966
+ );
14967
+ }
14968
+ return selected[0]?.[0] ?? "sitsFlat";
14909
14969
  };
14910
14970
  var distance = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
14911
14971
  var interpolate = (a, b, progress) => [
@@ -15777,7 +15837,7 @@ function createTwistedSpurGearMesh(input, twist = 0, segmentsPerTurn = 32) {
15777
15837
  function createHelicalGearMesh(input) {
15778
15838
  const props = helicalGearModelPropsSchema.parse(input);
15779
15839
  const { twistAngle } = getHelicalGearDimensions(props);
15780
- const { helixAngle, handedness, segmentsPerTurn, ...spur } = props;
15840
+ const { helixAngle, leftHanded, segmentsPerTurn, ...spur } = props;
15781
15841
  return createTwistedSpurGearMesh(
15782
15842
  spur,
15783
15843
  twistAngle * Math.PI / 180,
@@ -15880,7 +15940,7 @@ function createHexBoltMesh(input, options = {}) {
15880
15940
  levels.add(-p.length + (shaftEnd + p.length) * i / steps);
15881
15941
  for (const z of [...levels].sort((a, b) => a - b)) {
15882
15942
  const r = add((theta) => {
15883
- const phase = (((z + p.length) / pitch - (p.threadHand === "right" ? 1 : -1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
15943
+ const phase = (((z + p.length) / pitch - (p.leftHanded ? -1 : 1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
15884
15944
  const d = Math.min(phase, 1 - phase);
15885
15945
  const groove = Math.min(
15886
15946
  major - minor,
@@ -16510,14 +16570,14 @@ var resolveNemaMotorProps = nemaMotorModelPropsSchema.parse.bind(nemaMotorModelP
16510
16570
  // lib/utils/nemaMotorWireGeometry.ts
16511
16571
  function createNemaMotorWireGeometry(input) {
16512
16572
  const p = resolveNemaMotorProps(input);
16513
- if (p.wireConnection === "none") return [];
16573
+ if (p.noWires) return [];
16514
16574
  const { position } = getNemaMotorReferencePoints(p).wireside;
16515
16575
  const angle = p.wireSideAngle * Math.PI / 180;
16516
16576
  const place = (geometry) => jscad8.transforms.translate(
16517
16577
  [position.x, position.y, position.z],
16518
16578
  jscad8.transforms.rotateZ(angle, geometry)
16519
16579
  );
16520
- if (p.wireConnection === "stubs") {
16580
+ if (p.wireStubs) {
16521
16581
  const colors = [
16522
16582
  "#d53f3f",
16523
16583
  "#2463af",
@@ -16630,19 +16690,25 @@ var roundedFace = (width10, radius) => {
16630
16690
  })
16631
16691
  );
16632
16692
  };
16633
- var steppedFace = (width10, chamfer) => {
16693
+ var steppedFace = (width10, chamfer, recessRadius, filletRadius) => {
16634
16694
  if (chamfer === 0) return face(width10, 0);
16635
16695
  const h2 = width10 / 2, c = chamfer / Math.SQRT2, r = Math.min(4, c / 4);
16636
16696
  const corner = [];
16637
- const arc = (x, y, from, to) => {
16697
+ const arc = (x, y, radius, from, to) => {
16698
+ if (radius === 0) {
16699
+ corner.push([x, y]);
16700
+ return;
16701
+ }
16638
16702
  for (let i = 0; i <= 12; i++) {
16639
16703
  const a = (from + (to - from) * i / 12) * Math.PI / 180;
16640
- corner.push([x + r * Math.cos(a), y + r * Math.sin(a)]);
16704
+ corner.push([x + radius * Math.cos(a), y + radius * Math.sin(a)]);
16641
16705
  }
16642
16706
  };
16643
- arc(h2 - c - r, h2 - r, 90, 0);
16644
- arc(h2 - c + r, h2 - c + r, 180, 270);
16645
- arc(h2 - r, h2 - c - r, 90, 0);
16707
+ const convex = filletRadius ?? r;
16708
+ const concave = recessRadius ?? r;
16709
+ arc(h2 - c - convex, h2 - convex, convex, 90, 0);
16710
+ arc(h2 - c + concave, h2 - c + concave, concave, 180, 270);
16711
+ arc(h2 - convex, h2 - c - convex, convex, 90, 0);
16646
16712
  return [0, 1, 2, 3].flatMap(
16647
16713
  (i) => corner.map(([x, y]) => {
16648
16714
  const a = -i * Math.PI / 2;
@@ -16663,12 +16729,18 @@ function createNemaMotorSections(input) {
16663
16729
  p.bodyWidth,
16664
16730
  p.faceCornerChamfer
16665
16731
  );
16666
- const profile6 = p.cornerProfile === "stepped" ? steppedFace : face;
16667
- const core = profile6(p.bodyWidth, p.bodyCornerChamfer);
16668
- const rear = profile6(
16732
+ const core = p.stepped ? steppedFace(
16669
16733
  p.bodyWidth,
16670
- p.rearCornerChamfer ?? (p.backFace === "plain" && p.mountingHoleThrough ? p.bodyCornerChamfer : p.faceCornerChamfer)
16671
- );
16734
+ p.bodyCornerChamfer,
16735
+ p.bodyCornerRecessRadius,
16736
+ p.bodyCornerFilletRadius
16737
+ ) : face(p.bodyWidth, p.bodyCornerChamfer);
16738
+ const rear = p.rearStepped ? steppedFace(
16739
+ p.bodyWidth,
16740
+ p.rearCornerChamfer,
16741
+ p.rearCornerRecessRadius,
16742
+ p.rearCornerFilletRadius
16743
+ ) : face(p.bodyWidth, p.rearCornerChamfer);
16672
16744
  const h2 = p.mountingHoleSpacing / 2;
16673
16745
  const centers = [
16674
16746
  [-h2, -h2],
@@ -16683,12 +16755,13 @@ function createNemaMotorSections(input) {
16683
16755
  add("frontCap", front, -holeDepth, 0, holes);
16684
16756
  add("frontCap", front, -p.frontCapLength, -holeDepth);
16685
16757
  add("body", core, -p.bodyLength + p.rearCapLength, -p.frontCapLength);
16686
- if (p.backFace === "plain") {
16758
+ if (p.backFacePlain) {
16687
16759
  add("rearCap", rear, -p.bodyLength, -p.bodyLength + p.rearCapLength);
16688
16760
  } else {
16689
- const halfPitch = p.backFaceHoleSpacing / 2;
16690
- const rearHoles = [-halfPitch, halfPitch].flatMap(
16691
- (x) => [-halfPitch, halfPitch].map(
16761
+ const halfPitchX = p.backFaceHoleSpacingX / 2;
16762
+ const halfPitchY = p.backFaceHoleSpacingY / 2;
16763
+ const rearHoles = [-halfPitchX, halfPitchX].flatMap(
16764
+ (x) => [-halfPitchY, halfPitchY].map(
16692
16765
  (y) => circle(p.backFaceHoleDiameter / 2, x, y)
16693
16766
  )
16694
16767
  );
@@ -16709,17 +16782,16 @@ function createNemaMotorSections(input) {
16709
16782
  add("pilot", circle(p.pilotDiameter / 2), 0, p.pilotLength);
16710
16783
  const r = p.shaftDiameter / 2;
16711
16784
  const round = circle(r);
16712
- if (p.shaftShape === "round")
16713
- add("shaft", round, p.pilotLength, p.shaftLength);
16785
+ if (p.roundShaft) add("shaft", round, p.pilotLength, p.shaftLength);
16714
16786
  else {
16715
16787
  const cut = r - p.shaftFlatDepth;
16716
16788
  const angle = Math.acos(cut / r);
16717
- const profile7 = Array.from({ length: 97 }, (_, i) => {
16789
+ const profile6 = Array.from({ length: 97 }, (_, i) => {
16718
16790
  const a = angle + (2 * Math.PI - 2 * angle) * i / 96;
16719
16791
  return [r * Math.cos(a), r * Math.sin(a)];
16720
16792
  });
16721
16793
  const rotation = p.shaftFlatAngle * Math.PI / 180;
16722
- const d = profile7.map(([x, y]) => [
16794
+ const d = profile6.map(([x, y]) => [
16723
16795
  x * Math.cos(rotation) - y * Math.sin(rotation),
16724
16796
  x * Math.sin(rotation) + y * Math.cos(rotation)
16725
16797
  ]);
@@ -16739,7 +16811,7 @@ function NemaMotor({
16739
16811
  ...props
16740
16812
  }) {
16741
16813
  const p = resolveNemaMotorProps(props);
16742
- const screw = p.backFace === "screws" ? jscad9.transforms.rotateX(
16814
+ const screw = p.backFaceScrews ? jscad9.transforms.rotateX(
16743
16815
  Math.PI,
16744
16816
  createHexSocketBoltGeom({
16745
16817
  metricSize: p.backFaceScrewSize,
@@ -16747,7 +16819,8 @@ function NemaMotor({
16747
16819
  showThreads: false
16748
16820
  })
16749
16821
  ) : void 0;
16750
- const halfPitch = p.backFaceHoleSpacing / 2;
16822
+ const halfPitchX = p.backFaceHoleSpacingX / 2;
16823
+ const halfPitchY = p.backFaceHoleSpacingY / 2;
16751
16824
  return /* @__PURE__ */ jsxs(Fragment2, { children: [
16752
16825
  createNemaMotorSections(p).map((section, index) => {
16753
16826
  const outline = jscad9.geometries.geom2.fromPoints(section.outline);
@@ -16792,8 +16865,8 @@ function NemaMotor({
16792
16865
  },
16793
16866
  `front-screw-${index}`
16794
16867
  )),
16795
- screw && [-halfPitch, halfPitch].flatMap(
16796
- (x) => [-halfPitch, halfPitch].map((y) => /* @__PURE__ */ jsx(
16868
+ screw && [-halfPitchX, halfPitchX].flatMap(
16869
+ (x) => [-halfPitchY, halfPitchY].map((y) => /* @__PURE__ */ jsx(
16797
16870
  Colorize,
16798
16871
  {
16799
16872
  material: componentMaterials.steel,
@@ -17048,7 +17121,7 @@ function createPanScrewMesh(input, options = {}) {
17048
17121
  levels.add(-p.length + (shaftEnd + p.length) * i / steps);
17049
17122
  for (const z of [...levels].sort((a, b) => a - b)) {
17050
17123
  const r = add((theta) => {
17051
- const phase = (((z + p.length) / pitch - (p.threadHand === "right" ? 1 : -1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
17124
+ const phase = (((z + p.length) / pitch - (p.leftHanded ? -1 : 1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
17052
17125
  const d = Math.min(phase, 1 - phase);
17053
17126
  const groove = Math.min(
17054
17127
  major - minor,
@@ -17788,7 +17861,11 @@ import "@jscad/modeling";
17788
17861
  function createRigidCouplerMesh(input) {
17789
17862
  const props = rigidCouplerModelPropsSchema.parse(input);
17790
17863
  const dimensions5 = getRigidCouplerDimensions(props);
17791
- assertShaftMountResolution(dimensions5.screwHoles);
17864
+ const screwHoles = dimensions5.screwHoles.map((hole) => ({
17865
+ ...hole,
17866
+ threadHand: hole.leftHanded ? "left" : "right"
17867
+ }));
17868
+ assertShaftMountResolution(screwHoles);
17792
17869
  const body = createAnnularSleeve({
17793
17870
  outerRadius: props.outerDiameter / 2,
17794
17871
  boreARadius: props.boreDiameter / 2,
@@ -17798,10 +17875,7 @@ function createRigidCouplerMesh(input) {
17798
17875
  stepZ: dimensions5.boreADepth
17799
17876
  });
17800
17877
  return finishShaftMountMesh(
17801
- subtractShaftMountParts(
17802
- body,
17803
- ...dimensions5.screwHoles.map(createMountingHoleCutter)
17804
- )
17878
+ subtractShaftMountParts(body, ...screwHoles.map(createMountingHoleCutter))
17805
17879
  );
17806
17880
  }
17807
17881
 
@@ -17954,7 +18028,11 @@ import "@jscad/modeling";
17954
18028
  function createShaftCollarMesh(input) {
17955
18029
  const props = shaftCollarModelPropsSchema.parse(input);
17956
18030
  const dimensions5 = getShaftCollarDimensions(props);
17957
- assertShaftMountResolution([dimensions5.screwHole]);
18031
+ const screwHole = {
18032
+ ...dimensions5.screwHole,
18033
+ threadHand: dimensions5.screwHole.leftHanded ? "left" : "right"
18034
+ };
18035
+ assertShaftMountResolution([screwHole]);
17958
18036
  const body = createAnnularSleeve({
17959
18037
  outerRadius: props.outerDiameter / 2,
17960
18038
  boreARadius: props.boreDiameter / 2,
@@ -17962,10 +18040,7 @@ function createShaftCollarMesh(input) {
17962
18040
  chamfer: props.chamfer
17963
18041
  });
17964
18042
  return finishShaftMountMesh(
17965
- subtractShaftMountParts(
17966
- body,
17967
- createMountingHoleCutter(dimensions5.screwHole)
17968
- )
18043
+ subtractShaftMountParts(body, createMountingHoleCutter(screwHole))
17969
18044
  );
17970
18045
  }
17971
18046
 
@@ -17991,11 +18066,11 @@ function register92(registry2) {
17991
18066
  // lib/mechanical/sheet-metal-mesh.ts
17992
18067
  import earcut5 from "earcut";
17993
18068
  function holeOutline(hole) {
17994
- const radius = (hole.shape === "round" ? hole.diameter : hole.width) / 2;
17995
- const straight = hole.shape === "slot" ? (hole.length - hole.width) / 2 : 0;
18069
+ const radius = (hole.round ? hole.diameter : hole.width) / 2;
18070
+ const straight = hole.slot ? (hole.length - hole.width) / 2 : 0;
17996
18071
  if (straight < 0) throw new Error("Slot length must be at least its width");
17997
18072
  return Array.from({ length: 64 }, (_, i) => {
17998
- if (hole.shape === "round") {
18073
+ if (hole.round) {
17999
18074
  const angle2 = i * Math.PI / 32;
18000
18075
  return {
18001
18076
  u: hole.u + radius * Math.cos(angle2),
@@ -18006,12 +18081,13 @@ function holeOutline(hole) {
18006
18081
  const angle = -Math.PI / 2 + (i < 32 ? i : i - 32) * Math.PI / 31 + (i < 32 ? 0 : Math.PI);
18007
18082
  const along = half * straight + radius * Math.cos(angle);
18008
18083
  const across = radius * Math.sin(angle);
18009
- return hole.shape === "slot" && hole.axis === "v" ? { u: hole.u - across, v: hole.v + along } : { u: hole.u + along, v: hole.v + across };
18084
+ return hole.slot && hole.axis === "v" ? { u: hole.u - across, v: hole.v + along } : { u: hole.u + along, v: hole.v + across };
18010
18085
  });
18011
18086
  }
18012
18087
  function createSheetMetalMesh(input) {
18013
18088
  const props = sheetMetalModelPropsSchema.parse(input);
18014
- const { width: width10, baseLength, flangeHeight, thickness, profile: profile6 } = props;
18089
+ const { width: width10, baseLength, flangeHeight, thickness } = props;
18090
+ const profile6 = props.channel ? "channel" : props.angle ? "angle" : "plate";
18015
18091
  const radius = props.insideBendRadius + thickness / 2;
18016
18092
  const positions = [];
18017
18093
  const indices = [];
@@ -19154,7 +19230,7 @@ var createWormGearMesh = (input) => {
19154
19230
  const positions = [];
19155
19231
  const indices = [];
19156
19232
  const phase = props.phase % 360 * Math.PI / 180;
19157
- const direction = props.handedness === "right" ? 1 : -1;
19233
+ const direction = props.leftHanded ? -1 : 1;
19158
19234
  const ring = (z) => {
19159
19235
  const first = positions.length / 3;
19160
19236
  const rotation = phase + direction * (z / lead) * Math.PI * 2;