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/index.js CHANGED
@@ -748,7 +748,7 @@ var Crystal = ({
748
748
  ] });
749
749
  };
750
750
 
751
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/external.js
751
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/external.js
752
752
  var external_exports = {};
753
753
  __export(external_exports, {
754
754
  BRAND: () => BRAND,
@@ -860,7 +860,7 @@ __export(external_exports, {
860
860
  void: () => voidType
861
861
  });
862
862
 
863
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/helpers/util.js
863
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/helpers/util.js
864
864
  var util;
865
865
  (function(util2) {
866
866
  util2.assertEqual = (_) => {
@@ -994,7 +994,7 @@ var getParsedType = (data) => {
994
994
  }
995
995
  };
996
996
 
997
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/ZodError.js
997
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/ZodError.js
998
998
  var ZodIssueCode = util.arrayToEnum([
999
999
  "invalid_type",
1000
1000
  "invalid_literal",
@@ -1112,7 +1112,7 @@ ZodError.create = (issues) => {
1112
1112
  return error;
1113
1113
  };
1114
1114
 
1115
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/locales/en.js
1115
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/locales/en.js
1116
1116
  var errorMap = (issue, _ctx) => {
1117
1117
  let message;
1118
1118
  switch (issue.code) {
@@ -1215,7 +1215,7 @@ var errorMap = (issue, _ctx) => {
1215
1215
  };
1216
1216
  var en_default = errorMap;
1217
1217
 
1218
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/errors.js
1218
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/errors.js
1219
1219
  var overrideErrorMap = en_default;
1220
1220
  function setErrorMap(map) {
1221
1221
  overrideErrorMap = map;
@@ -1224,7 +1224,7 @@ function getErrorMap() {
1224
1224
  return overrideErrorMap;
1225
1225
  }
1226
1226
 
1227
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/helpers/parseUtil.js
1227
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/helpers/parseUtil.js
1228
1228
  var makeIssue = (params) => {
1229
1229
  const { data, path, errorMaps, issueData } = params;
1230
1230
  const fullPath = [...path, ...issueData.path || []];
@@ -1334,14 +1334,14 @@ var isDirty = (x) => x.status === "dirty";
1334
1334
  var isValid = (x) => x.status === "valid";
1335
1335
  var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
1336
1336
 
1337
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/helpers/errorUtil.js
1337
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/helpers/errorUtil.js
1338
1338
  var errorUtil;
1339
1339
  (function(errorUtil2) {
1340
1340
  errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
1341
1341
  errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
1342
1342
  })(errorUtil || (errorUtil = {}));
1343
1343
 
1344
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/zod/v3/types.js
1344
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/zod/v3/types.js
1345
1345
  var ParseInputLazyPath = class {
1346
1346
  constructor(parent, value, path, key) {
1347
1347
  this._cachedPath = [];
@@ -4789,7 +4789,7 @@ var coerce = {
4789
4789
  };
4790
4790
  var NEVER = INVALID;
4791
4791
 
4792
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/node_modules/@tscircuit/mm/dist/index.js
4792
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/node_modules/@tscircuit/mm/dist/index.js
4793
4793
  var unitToMm = {
4794
4794
  in: 25.4,
4795
4795
  inch: 25.4,
@@ -4816,7 +4816,7 @@ var mm = (n) => {
4816
4816
  throw new Error(`Unsupported unit: ${unit}`);
4817
4817
  };
4818
4818
 
4819
- // .model-contracts/generations/fa75ee0265d6d666dfb181dbec270fd94d15a3b5-v2/index.js
4819
+ // .model-contracts/generations/ad649bc7a403a3e6e9b12ce32ac0b3d0fa61c6bf-v2/index.js
4820
4820
  function normalizeNemaMotorModelString(value) {
4821
4821
  if (!/^nema(?:8|17|23|34)(?:_|$)/i.test(value)) return value;
4822
4822
  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");
@@ -4872,36 +4872,6 @@ var parseModelStringParams = (definition) => {
4872
4872
  params.string = normalizedDefinition;
4873
4873
  return params;
4874
4874
  };
4875
- function expandModelStringFlags(value, flags10) {
4876
- return splitModelStringTokens(value).map(
4877
- (token, index) => index > 0 && Object.hasOwn(flags10, token.toLowerCase()) ? flags10[token.toLowerCase()] : token
4878
- ).join("_");
4879
- }
4880
- function normalizeModelStringFlags(value, flags10, parse, omittedFlags) {
4881
- const tokens = splitModelStringTokens(value);
4882
- if (!tokens.slice(1).some((token) => Object.hasOwn(flags10, token.toLowerCase())))
4883
- return value;
4884
- const expanded = expandModelStringFlags(value, flags10);
4885
- parse(parseModelStringParams(expanded));
4886
- const preferred = new Map(
4887
- Object.entries(flags10).map(([flag, selector]) => [selector, flag])
4888
- );
4889
- const omitted = new Set(omittedFlags.map((flag) => flags10[flag]));
4890
- return splitModelStringTokens(expanded).flatMap((token, index) => {
4891
- if (index === 0) return [token.toLowerCase()];
4892
- const lower = token.toLowerCase();
4893
- if (omitted.has(lower)) return [];
4894
- return [preferred.get(lower) ?? token];
4895
- }).join("_");
4896
- }
4897
- var stringFlags = {
4898
- hex: "drive(hex)",
4899
- fullthread: "thread(full)",
4900
- lefthanded: "threadhand(left)",
4901
- righthanded: "threadhand(right)",
4902
- male: "threadgender(male)"
4903
- };
4904
- var omittedStringFlags = ["righthanded", "male"];
4905
4875
  function defineModel(model552) {
4906
4876
  return model552;
4907
4877
  }
@@ -5048,11 +5018,11 @@ var hexBoltModelPropsShape = {
5048
5018
  iso4017: external_exports.literal(true).default(true),
5049
5019
  metricSize: hexBoltMetricSizeSchema,
5050
5020
  length: positiveModelLengthSchema2,
5051
- thread: external_exports.literal("full").default("full"),
5052
- drive: external_exports.literal("hex").default("hex"),
5053
- threadHand: external_exports.enum(["right", "left"]).default("right"),
5021
+ fullThread: external_exports.literal(true).default(true),
5022
+ hex: external_exports.literal(true).default(true),
5023
+ leftHanded: external_exports.boolean().default(false),
5054
5024
  threadClass: external_exports.literal("6g").default("6g"),
5055
- threadGender: external_exports.literal("male").default("male"),
5025
+ male: external_exports.literal(true).default(true),
5056
5026
  showThreads: external_exports.boolean().default(true),
5057
5027
  diameter: positiveModelLengthSchema2.optional(),
5058
5028
  threadPitch: positiveModelLengthSchema2.optional(),
@@ -5097,11 +5067,7 @@ var aliases = {
5097
5067
  metricsize: "metricSize",
5098
5068
  l: "length",
5099
5069
  length: "length",
5100
- thread: "thread",
5101
- drive: "drive",
5102
- threadhand: "threadHand",
5103
5070
  threadclass: "threadClass",
5104
- threadgender: "threadGender",
5105
5071
  diameter: "diameter",
5106
5072
  threadpitch: "threadPitch",
5107
5073
  headacrossflats: "headAcrossFlats",
@@ -5119,19 +5085,18 @@ var aliases = {
5119
5085
  headh: "headHeight",
5120
5086
  af: "headAcrossFlats"
5121
5087
  };
5122
- var selectors = /* @__PURE__ */ new Set([
5123
- "thread",
5124
- "drive",
5125
- "threadHand",
5126
- "threadClass",
5127
- "threadGender"
5128
- ]);
5088
+ var selectors = /* @__PURE__ */ new Set(["threadClass"]);
5089
+ var booleanFlags = {
5090
+ hex: { property: "hex", value: true },
5091
+ fullthread: { property: "fullThread", value: true },
5092
+ male: { property: "male", value: true },
5093
+ lefthanded: { property: "leftHanded", value: true },
5094
+ righthanded: { property: "leftHanded", value: false }
5095
+ };
5129
5096
  var parseHexBoltModelParams = (raw) => {
5130
5097
  if (raw.fn !== "hexbolt")
5131
5098
  throw new Error(`Expected hexbolt params, got "${raw.fn}"`);
5132
- const [root, ...tokens] = splitModelStringTokens(
5133
- expandModelStringFlags(raw.string, stringFlags)
5134
- );
5099
+ const [root, ...tokens] = splitModelStringTokens(raw.string);
5135
5100
  if (root?.toLowerCase() !== "hexbolt")
5136
5101
  throw new Error("The hexbolt function does not accept an inline value");
5137
5102
  const props = {};
@@ -5147,13 +5112,17 @@ var parseHexBoltModelParams = (raw) => {
5147
5112
  const value = match?.[2];
5148
5113
  let property;
5149
5114
  let parsed;
5150
- if (key === "threads" || key === "nothreads") {
5115
+ if (key && Object.hasOwn(booleanFlags, key)) {
5116
+ if (value !== "") throw new Error(`Flag "${key}" does not accept a value`);
5117
+ property = booleanFlags[key].property;
5118
+ parsed = booleanFlags[key].value;
5119
+ } else if (key === "threads" || key === "nothreads") {
5151
5120
  if (value !== "")
5152
5121
  throw new Error(`Token "${key}" does not accept a value`);
5153
5122
  property = "showThreads";
5154
5123
  parsed = key === "threads";
5155
5124
  } else {
5156
- property = key ? aliases[key] ?? "" : "";
5125
+ property = key && Object.hasOwn(aliases, key) ? aliases[key] : "";
5157
5126
  if (!property || !value)
5158
5127
  throw new Error(`Unknown or missing hexbolt token "${token}"`);
5159
5128
  if (selectors.has(property)) {
@@ -5185,13 +5154,7 @@ var parseHexBoltModelParams = (raw) => {
5185
5154
  var model = defineModel({
5186
5155
  name: "hexbolt",
5187
5156
  schema: hexBoltModelDefinitionSchema,
5188
- parse: parseHexBoltModelParams,
5189
- normalizeString: (value) => normalizeModelStringFlags(
5190
- value,
5191
- stringFlags,
5192
- parseHexBoltModelParams,
5193
- omittedStringFlags
5194
- )
5157
+ parse: parseHexBoltModelParams
5195
5158
  });
5196
5159
  function register(registry2) {
5197
5160
  registry2.register(model);
@@ -5298,11 +5261,11 @@ var shape2 = {
5298
5261
  /** Unsigned pitch-cylinder helix angle to +Z, in degrees. */
5299
5262
  helixAngle: external_exports.number().finite().min(0).lt(90).default(20),
5300
5263
  /** Right-hand teeth advance counterclockwise as Z increases. */
5301
- handedness: external_exports.enum(["right", "left"]).default("right"),
5264
+ leftHanded: external_exports.boolean().default(false),
5302
5265
  segmentsPerTurn: external_exports.number().int().min(12).max(128).default(32)
5303
5266
  };
5304
5267
  function dimensions2(props) {
5305
- const { helixAngle, handedness, segmentsPerTurn, ...spur } = props;
5268
+ const { helixAngle, leftHanded, segmentsPerTurn, ...spur } = props;
5306
5269
  const d = getSpurGearDimensions(spur);
5307
5270
  const beta = helixAngle * Math.PI / 180;
5308
5271
  return {
@@ -5313,18 +5276,18 @@ function dimensions2(props) {
5313
5276
  ) * 180 / Math.PI,
5314
5277
  normalPitch: d.circularPitch * Math.cos(beta),
5315
5278
  /** Signed angular advance between the lower and upper faces, in degrees. */
5316
- twistAngle: helixAngle === 0 ? 0 : (handedness === "right" ? 1 : -1) * (2 * props.faceWidth / d.pitchDiameter * Math.tan(beta) * 180) / Math.PI
5279
+ twistAngle: helixAngle === 0 ? 0 : (leftHanded ? -1 : 1) * (2 * props.faceWidth / d.pitchDiameter * Math.tan(beta) * 180) / Math.PI
5317
5280
  };
5318
5281
  }
5319
5282
  function validate2(props, context) {
5320
- const { fn, helixAngle, handedness, segmentsPerTurn, ...spur } = props;
5283
+ const { fn, helixAngle, leftHanded, segmentsPerTurn, ...spur } = props;
5321
5284
  const result = spurGearModelPropsSchema.safeParse(spur);
5322
5285
  if (!result.success) {
5323
5286
  for (const issue of result.error.issues) context.addIssue(issue);
5324
5287
  return;
5325
5288
  }
5326
5289
  if (Object.values(
5327
- dimensions2({ ...result.data, helixAngle, handedness, segmentsPerTurn })
5290
+ dimensions2({ ...result.data, helixAngle, leftHanded, segmentsPerTurn })
5328
5291
  ).some((value) => !Number.isFinite(value)))
5329
5292
  context.addIssue({
5330
5293
  code: "custom",
@@ -5358,7 +5321,7 @@ function parseHelicalGearModelParams(raw) {
5358
5321
  const [name, value] = parseGearToken(token);
5359
5322
  let property;
5360
5323
  let parsed;
5361
- if (name in lengths) {
5324
+ if (Object.hasOwn(lengths, name)) {
5362
5325
  property = lengths[name];
5363
5326
  parsed = value;
5364
5327
  } else if (name === "teeth" || name === "segments" || name === "turnsegments") {
@@ -5370,8 +5333,8 @@ function parseHelicalGearModelParams(raw) {
5370
5333
  } else if (name === "right" || name === "left") {
5371
5334
  if (value)
5372
5335
  throw new Error(`Helical flag "${name}" does not accept a value`);
5373
- property = "handedness";
5374
- parsed = name;
5336
+ property = "leftHanded";
5337
+ parsed = name === "left";
5375
5338
  } else throw new Error(`Unknown helical gear token "${token}"`);
5376
5339
  if (property in props)
5377
5340
  throw new Error(
@@ -5408,6 +5371,76 @@ var jstMotorWireConnectionSchema = external_exports.string().refine((value) => {
5408
5371
  const connector = getJstMotorConnector(value);
5409
5372
  return connector !== void 0 && connector.pinCount >= 2 && connector.pinCount <= connector.maxPinCount;
5410
5373
  }, "Expected jst-ph-N (2\u201316 pins) or jst-sh-N (2\u201315 pins)");
5374
+ function isInsideNemaFace(x, y, p) {
5375
+ const h = p.width / 2;
5376
+ x = Math.abs(x);
5377
+ y = Math.abs(y);
5378
+ if (x > h || y > h) return false;
5379
+ const chamfer = p.chamfer;
5380
+ if (!p.stepped) return x + y <= 2 * h - chamfer;
5381
+ const c = chamfer / Math.SQRT2;
5382
+ if (c === 0) return true;
5383
+ const recess = p.recessRadius;
5384
+ const fillet = p.filletRadius;
5385
+ const k = h - c + recess;
5386
+ if (x > h - c && y > h - c)
5387
+ return x <= k && y <= k && Math.hypot(x - k, y - k) >= recess;
5388
+ if (y > h - fillet && x > h - c - fillet)
5389
+ return Math.hypot(x - (h - c - fillet), y - (h - fillet)) <= fillet;
5390
+ if (x > h - fillet && y > h - c - fillet)
5391
+ return Math.hypot(x - (h - fillet), y - (h - c - fillet)) <= fillet;
5392
+ return true;
5393
+ }
5394
+ var segmentDistance = (x, y, ax, ay, bx, by) => {
5395
+ const dx = bx - ax;
5396
+ const dy = by - ay;
5397
+ const t = Math.max(
5398
+ 0,
5399
+ Math.min(1, ((x - ax) * dx + (y - ay) * dy) / (dx * dx + dy * dy || 1))
5400
+ );
5401
+ return Math.hypot(x - ax - t * dx, y - ay - t * dy);
5402
+ };
5403
+ var arcDistance = (x, y, cx, cy, r, start, end) => {
5404
+ const angle = (Math.atan2(y - cy, x - cx) + 2 * Math.PI) % (2 * Math.PI);
5405
+ if (angle >= start && angle <= end)
5406
+ return Math.abs(Math.hypot(x - cx, y - cy) - r);
5407
+ return Math.min(
5408
+ Math.hypot(x - cx - r * Math.cos(start), y - cy - r * Math.sin(start)),
5409
+ Math.hypot(x - cx - r * Math.cos(end), y - cy - r * Math.sin(end))
5410
+ );
5411
+ };
5412
+ var boundaryDistance = (x, y, p) => {
5413
+ const h = p.width / 2;
5414
+ x = Math.abs(x);
5415
+ y = Math.abs(y);
5416
+ const chamfer = p.chamfer;
5417
+ if (!p.stepped || chamfer === 0)
5418
+ return Math.min(
5419
+ Math.abs(h - x),
5420
+ Math.abs(h - y),
5421
+ Math.abs(2 * h - chamfer - x - y) / Math.SQRT2
5422
+ );
5423
+ const c = chamfer / Math.SQRT2;
5424
+ const recess = p.recessRadius;
5425
+ const fillet = p.filletRadius;
5426
+ const a = h - c - fillet;
5427
+ const k = h - c + recess;
5428
+ return Math.min(
5429
+ segmentDistance(x, y, -a, h, a, h),
5430
+ segmentDistance(x, y, h, -a, h, a),
5431
+ segmentDistance(x, y, h - c, h - fillet, h - c, k),
5432
+ segmentDistance(x, y, k, h - c, h - fillet, h - c),
5433
+ arcDistance(x, y, a, h - fillet, fillet, 0, Math.PI / 2),
5434
+ arcDistance(x, y, k, k, recess, Math.PI, 3 * Math.PI / 2),
5435
+ arcDistance(x, y, h - fillet, a, fillet, 0, Math.PI / 2)
5436
+ );
5437
+ };
5438
+ function fitsNemaCircle(x, y, radius, p) {
5439
+ return isInsideNemaFace(x, y, p) && boundaryDistance(x, y, p) > radius;
5440
+ }
5441
+ function clearsNemaCircle(x, y, radius, p) {
5442
+ return !isInsideNemaFace(x, y, p) && boundaryDistance(x, y, p) > radius;
5443
+ }
5411
5444
  var hexSocketBoltDimensions = {
5412
5445
  M2: {
5413
5446
  diameter: 2,
@@ -5524,7 +5557,8 @@ var nemaMotorDimensions = {
5524
5557
  pilotLength: 1.5,
5525
5558
  shaftDiameter: 4,
5526
5559
  shaftLength: 15,
5527
- shaftShape: "round",
5560
+ roundShaft: true,
5561
+ dShaft: false,
5528
5562
  shaftFlatDepth: 0.5,
5529
5563
  shaftFlatLength: 10,
5530
5564
  shaftFlatAngle: 0,
@@ -5546,7 +5580,8 @@ var nemaMotorDimensions = {
5546
5580
  pilotLength: 2,
5547
5581
  shaftDiameter: 5,
5548
5582
  shaftLength: 24,
5549
- shaftShape: "d",
5583
+ roundShaft: false,
5584
+ dShaft: true,
5550
5585
  shaftFlatDepth: 0.5,
5551
5586
  shaftFlatLength: 15,
5552
5587
  shaftFlatAngle: 0,
@@ -5556,7 +5591,15 @@ var nemaMotorDimensions = {
5556
5591
  bodyCornerChamfer: 6
5557
5592
  },
5558
5593
  23: {
5559
- backFaceScrewSize: "M4",
5594
+ // Representative side-tab rear pattern from STEPPERONLINE's ST-M10 adapter.
5595
+ // The adapter's R5/R2.5 profile is a visual reference, not a frame guarantee:
5596
+ // https://www.omc-stepperonline.com/image/catalog/dimensions/ST-M10_Dimension.svg
5597
+ backFaceHoleSpacing: 50,
5598
+ backFaceHoleSpacingY: 32,
5599
+ rearCornerChamfer: 9 * Math.SQRT2,
5600
+ rearCornerRecessRadius: 5,
5601
+ rearCornerFilletRadius: 2.5,
5602
+ backFaceScrewSize: "M3",
5560
5603
  backFaceHoleDepth: 4.5,
5561
5604
  bodyWidth: 56.4,
5562
5605
  bodyLength: 51,
@@ -5568,14 +5611,18 @@ var nemaMotorDimensions = {
5568
5611
  pilotLength: 1.6,
5569
5612
  shaftDiameter: 6.35,
5570
5613
  shaftLength: 20.6,
5571
- shaftShape: "d",
5614
+ roundShaft: false,
5615
+ dShaft: true,
5572
5616
  shaftFlatDepth: 0.5,
5573
5617
  shaftFlatLength: 15,
5574
5618
  shaftFlatAngle: 0,
5575
5619
  frontCapLength: 5,
5576
5620
  rearCapLength: 5,
5577
5621
  faceCornerChamfer: 2,
5578
- bodyCornerChamfer: 15
5622
+ bodyCornerChamfer: 9 * Math.SQRT2,
5623
+ bodyCornerRecessRadius: 5,
5624
+ bodyCornerFilletRadius: 2.5,
5625
+ chamferedBodyCornerChamfer: 15
5579
5626
  },
5580
5627
  34: {
5581
5628
  // Illustrative rear details; independent of the mounting flange.
@@ -5593,7 +5640,8 @@ var nemaMotorDimensions = {
5593
5640
  pilotLength: 1.6,
5594
5641
  shaftDiameter: 14,
5595
5642
  shaftLength: 32,
5596
- shaftShape: "round",
5643
+ roundShaft: true,
5644
+ dShaft: false,
5597
5645
  shaftFlatDepth: 0.5,
5598
5646
  shaftFlatLength: 15,
5599
5647
  shaftFlatAngle: 0,
@@ -5606,13 +5654,21 @@ var nemaMotorDimensions = {
5606
5654
  var shape3 = {
5607
5655
  nemaSize: nemaSizeSchema,
5608
5656
  /** Rear cap at Z=-bodyLength: bare, open bores or installed cap screws. */
5609
- backFace: external_exports.enum(["plain", "holes", "screws"]).optional(),
5657
+ backFacePlain: external_exports.boolean().optional(),
5658
+ backFaceHoles: external_exports.boolean().optional(),
5659
+ backFaceScrews: external_exports.boolean().optional(),
5610
5660
  backFaceHoleSpacing: positiveModelLengthSchema.optional(),
5661
+ /** Axis overrides; the legacy scalar still sets a square pattern when supplied. */
5662
+ backFaceHoleSpacingX: positiveModelLengthSchema.optional(),
5663
+ backFaceHoleSpacingY: positiveModelLengthSchema.optional(),
5611
5664
  backFaceHoleDiameter: positiveModelLengthSchema.optional(),
5612
5665
  backFaceHoleDepth: positiveModelLengthSchema.optional(),
5613
5666
  backFaceScrewSize: metricBoltSizeSchema.optional(),
5614
5667
  /** Visual termination; the wireside reference exists even when hidden. */
5615
- wireConnection: external_exports.union([external_exports.enum(["none", "stubs"]), jstMotorWireConnectionSchema]).optional(),
5668
+ noWires: external_exports.boolean().optional(),
5669
+ wireStubs: external_exports.boolean().optional(),
5670
+ /** JST catalog designation, mutually exclusive with the visible-wire flags. */
5671
+ wireConnection: jstMotorWireConnectionSchema.optional(),
5616
5672
  /** Counterclockwise angle about local +Z; 0 is the +X side. */
5617
5673
  wireSideAngle: external_exports.number().finite().optional(),
5618
5674
  wireLength: positiveModelLengthSchema.optional(),
@@ -5629,7 +5685,8 @@ var shape3 = {
5629
5685
  /** Distance from mounting face Z=0 to shaft tip, including pilot height. */
5630
5686
  shaftLength: positiveModelLengthSchema.optional(),
5631
5687
  shaftDiameter: positiveModelLengthSchema.optional(),
5632
- shaftShape: external_exports.enum(["round", "d"]).optional(),
5688
+ roundShaft: external_exports.boolean().optional(),
5689
+ dShaft: external_exports.boolean().optional(),
5633
5690
  /** Radial material removed from the +X side before rotation. */
5634
5691
  shaftFlatDepth: nonnegativeModelLengthSchema.optional(),
5635
5692
  /** Flat extends back from the tip; leaves a round shoulder above the pilot. */
@@ -5640,23 +5697,74 @@ var shape3 = {
5640
5697
  rearCapLength: positiveModelLengthSchema.optional(),
5641
5698
  faceCornerChamfer: nonnegativeModelLengthSchema.optional(),
5642
5699
  bodyCornerChamfer: nonnegativeModelLengthSchema.optional(),
5700
+ bodyCornerRecessRadius: nonnegativeModelLengthSchema.optional(),
5701
+ bodyCornerFilletRadius: nonnegativeModelLengthSchema.optional(),
5643
5702
  /** Rear outline is independent of the front mounting flange. */
5644
5703
  rearCornerChamfer: nonnegativeModelLengthSchema.optional(),
5645
- cornerProfile: external_exports.enum(["chamfered", "stepped"]).optional()
5646
- };
5647
- var resolve = (p) => {
5704
+ rearCornerRecessRadius: nonnegativeModelLengthSchema.optional(),
5705
+ rearCornerFilletRadius: nonnegativeModelLengthSchema.optional(),
5706
+ rearChamfered: external_exports.boolean().optional(),
5707
+ rearStepped: external_exports.boolean().optional(),
5708
+ chamfered: external_exports.boolean().optional(),
5709
+ stepped: external_exports.boolean().optional()
5710
+ };
5711
+ var resolve = (p, ctx) => {
5712
+ const select = (flags11, fallback) => {
5713
+ const selected = flags11.filter((flag) => p[flag] === true);
5714
+ const specified = flags11.some((flag) => p[flag] !== void 0);
5715
+ if (selected.length > 1 || specified && selected.length === 0)
5716
+ ctx.addIssue({
5717
+ code: "custom",
5718
+ message: `Select exactly one of ${flags11.join(", ")}`
5719
+ });
5720
+ return selected[0] ?? fallback;
5721
+ };
5722
+ const back = select(
5723
+ ["backFacePlain", "backFaceHoles", "backFaceScrews"],
5724
+ p.nemaSize === 34 ? "backFacePlain" : "backFaceScrews"
5725
+ );
5726
+ const shaft = select(
5727
+ ["roundShaft", "dShaft"],
5728
+ nemaMotorDimensions[p.nemaSize].roundShaft ? "roundShaft" : "dShaft"
5729
+ );
5730
+ const corner = select(
5731
+ ["chamfered", "stepped"],
5732
+ p.nemaSize === 23 || p.nemaSize === 34 ? "stepped" : "chamfered"
5733
+ );
5734
+ const rearCorner = select(
5735
+ ["rearChamfered", "rearStepped"],
5736
+ p.chamfered === true ? "rearChamfered" : p.nemaSize === 23 || corner === "stepped" ? "rearStepped" : "rearChamfered"
5737
+ );
5738
+ const wireSpecified = p.noWires !== void 0 || p.wireStubs !== void 0 || p.wireConnection !== void 0;
5739
+ const wireSelections = Number(p.noWires === true) + Number(p.wireStubs === true) + Number(p.wireConnection !== void 0);
5740
+ if (wireSelections > 1 || wireSpecified && wireSelections === 0)
5741
+ ctx.addIssue({
5742
+ code: "custom",
5743
+ message: "Select exactly one of noWires, wireStubs or a JST wireConnection"
5744
+ });
5648
5745
  const d = nemaMotorDimensions[p.nemaSize];
5746
+ const bodyCornerChamfer = p.bodyCornerChamfer ?? (corner === "chamfered" && "chamferedBodyCornerChamfer" in d ? d.chamferedBodyCornerChamfer : d.bodyCornerChamfer);
5747
+ const defaultBodyRadius = Math.min(4, bodyCornerChamfer / Math.SQRT2 / 4);
5649
5748
  const backFaceScrewSize = p.backFaceScrewSize ?? d.backFaceScrewSize;
5650
5749
  const screw = hexSocketBoltDimensions[backFaceScrewSize];
5750
+ const backFaceHoleSpacing = p.backFaceHoleSpacing ?? ("backFaceHoleSpacing" in d ? d.backFaceHoleSpacing : p.mountingHoleSpacing ?? d.mountingHoleSpacing);
5751
+ 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);
5752
+ const defaultRearRadius = Math.min(4, rearCornerChamfer / Math.SQRT2 / 4);
5651
5753
  return {
5652
5754
  nemaSize: p.nemaSize,
5653
- wireConnection: p.wireConnection ?? "stubs",
5755
+ noWires: p.noWires === true,
5756
+ wireStubs: p.wireStubs === true || !wireSpecified,
5757
+ ...p.wireConnection !== void 0 ? { wireConnection: p.wireConnection } : {},
5654
5758
  wireSideAngle: ((p.wireSideAngle ?? (p.nemaSize === 34 ? 90 : 0)) % 360 + 360) % 360,
5655
5759
  wireLength: p.wireLength ?? (p.nemaSize === 34 ? 25 : 6),
5656
5760
  wireDiameter: p.wireDiameter ?? (p.nemaSize === 34 ? 2 : 1.2),
5657
5761
  wireCount: p.wireCount ?? 4,
5658
- backFace: p.backFace ?? (p.nemaSize === 34 ? "plain" : "screws"),
5659
- backFaceHoleSpacing: p.backFaceHoleSpacing ?? ("backFaceHoleSpacing" in d ? d.backFaceHoleSpacing : p.mountingHoleSpacing ?? d.mountingHoleSpacing),
5762
+ backFacePlain: back === "backFacePlain",
5763
+ backFaceHoles: back === "backFaceHoles",
5764
+ backFaceScrews: back === "backFaceScrews",
5765
+ backFaceHoleSpacing,
5766
+ backFaceHoleSpacingX: p.backFaceHoleSpacingX ?? backFaceHoleSpacing,
5767
+ backFaceHoleSpacingY: p.backFaceHoleSpacingY ?? p.backFaceHoleSpacing ?? ("backFaceHoleSpacingY" in d ? d.backFaceHoleSpacingY : backFaceHoleSpacing),
5660
5768
  backFaceHoleDiameter: p.backFaceHoleDiameter ?? screw.diameter,
5661
5769
  backFaceHoleDepth: p.backFaceHoleDepth ?? d.backFaceHoleDepth,
5662
5770
  backFaceScrewSize,
@@ -5670,43 +5778,64 @@ var resolve = (p) => {
5670
5778
  pilotLength: p.pilotLength ?? d.pilotLength,
5671
5779
  shaftDiameter: p.shaftDiameter ?? d.shaftDiameter,
5672
5780
  shaftLength: p.shaftLength ?? d.shaftLength,
5673
- shaftShape: p.shaftShape ?? d.shaftShape,
5781
+ roundShaft: shaft === "roundShaft",
5782
+ dShaft: shaft === "dShaft",
5674
5783
  shaftFlatDepth: p.shaftFlatDepth ?? d.shaftFlatDepth,
5675
5784
  shaftFlatLength: p.shaftFlatLength ?? d.shaftFlatLength,
5676
5785
  shaftFlatAngle: p.shaftFlatAngle ?? d.shaftFlatAngle,
5677
5786
  frontCapLength: p.frontCapLength ?? d.frontCapLength,
5678
5787
  rearCapLength: p.rearCapLength ?? d.rearCapLength,
5679
5788
  faceCornerChamfer: p.faceCornerChamfer ?? d.faceCornerChamfer,
5680
- bodyCornerChamfer: p.bodyCornerChamfer ?? d.bodyCornerChamfer,
5681
- ...p.nemaSize === 34 || p.cornerProfile !== void 0 ? { cornerProfile: p.cornerProfile ?? "stepped" } : {},
5682
- ..."rearCornerChamfer" in d || p.rearCornerChamfer !== void 0 ? {
5683
- rearCornerChamfer: p.rearCornerChamfer ?? ("rearCornerChamfer" in d ? d.rearCornerChamfer : p.faceCornerChamfer ?? d.faceCornerChamfer)
5684
- } : {}
5789
+ bodyCornerChamfer,
5790
+ bodyCornerRecessRadius: p.bodyCornerRecessRadius ?? ("bodyCornerRecessRadius" in d ? d.bodyCornerRecessRadius : defaultBodyRadius),
5791
+ bodyCornerFilletRadius: p.bodyCornerFilletRadius ?? ("bodyCornerFilletRadius" in d ? d.bodyCornerFilletRadius : defaultBodyRadius),
5792
+ chamfered: corner === "chamfered",
5793
+ stepped: corner === "stepped",
5794
+ rearCornerChamfer,
5795
+ rearStepped: rearCorner === "rearStepped",
5796
+ rearChamfered: rearCorner === "rearChamfered",
5797
+ rearCornerRecessRadius: p.rearCornerRecessRadius ?? ("rearCornerRecessRadius" in d ? d.rearCornerRecessRadius : defaultRearRadius),
5798
+ rearCornerFilletRadius: p.rearCornerFilletRadius ?? ("rearCornerFilletRadius" in d ? d.rearCornerFilletRadius : defaultRearRadius)
5685
5799
  };
5686
5800
  };
5687
5801
  var validate3 = (p, ctx) => {
5688
5802
  const issue = (message) => ctx.addIssue({ code: "custom", message });
5689
- const rearChamfer = p.rearCornerChamfer ?? (p.backFace === "plain" && p.mountingHoleThrough ? p.bodyCornerChamfer : p.faceCornerChamfer);
5690
- const rearEdge = p.cornerProfile === "stepped" ? rearChamfer / Math.SQRT2 : rearChamfer;
5691
- if (p.backFace !== "plain") {
5803
+ if (p.stepped && p.bodyCornerRecessRadius + p.bodyCornerFilletRadius > p.bodyCornerChamfer / Math.SQRT2)
5804
+ issue("Body recess and fillet radii must fit within the corner shoulder");
5805
+ const rearChamfer = p.rearCornerChamfer ?? (p.backFacePlain && p.mountingHoleThrough ? p.bodyCornerChamfer : p.faceCornerChamfer);
5806
+ const rearEdge = p.rearStepped ? rearChamfer / Math.SQRT2 : rearChamfer;
5807
+ if (p.rearStepped && p.rearCornerRecessRadius + p.rearCornerFilletRadius > rearEdge)
5808
+ issue("Rear recess and fillet radii must fit within the corner shoulder");
5809
+ if (!p.backFacePlain) {
5692
5810
  const screw = hexSocketBoltDimensions[p.backFaceScrewSize];
5693
- const radius = (p.backFace === "screws" ? Math.max(p.backFaceHoleDiameter, screw.headDiameter) : p.backFaceHoleDiameter) / 2;
5694
- if (p.backFaceHoleSpacing <= 2 * radius)
5811
+ const radius = (p.backFaceScrews ? Math.max(p.backFaceHoleDiameter, screw.headDiameter) : p.backFaceHoleDiameter) / 2;
5812
+ if (p.backFaceHoleSpacingX <= 2 * radius || p.backFaceHoleSpacingY <= 2 * radius)
5695
5813
  issue("Rear holes and screw heads must not overlap each other");
5696
- 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))
5814
+ if (!fitsNemaCircle(
5815
+ p.backFaceHoleSpacingX / 2,
5816
+ p.backFaceHoleSpacingY / 2,
5817
+ radius,
5818
+ {
5819
+ width: p.bodyWidth,
5820
+ chamfer: p.rearCornerChamfer,
5821
+ stepped: p.rearStepped,
5822
+ recessRadius: p.rearCornerRecessRadius,
5823
+ filletRadius: p.rearCornerFilletRadius
5824
+ }
5825
+ ))
5697
5826
  issue("Rear holes and screw heads must fit entirely inside the rear face");
5698
5827
  if (p.backFaceHoleDepth >= p.rearCapLength)
5699
5828
  issue("Rear hole depth must be less than the rear cap length");
5700
- if (p.backFace === "screws" && p.backFaceHoleDiameter < screw.diameter)
5829
+ if (p.backFaceScrews && p.backFaceHoleDiameter < screw.diameter)
5701
5830
  issue("Rear holes must accommodate the screw diameter");
5702
5831
  }
5703
- if (p.wireConnection === "stubs") {
5832
+ if (p.wireStubs) {
5704
5833
  if (p.wireDiameter * p.wireCount * 1.5 > p.bodyWidth - 2 * rearEdge)
5705
5834
  issue("Wire bundle must fit on the cap side");
5706
5835
  if (p.wireDiameter >= p.rearCapLength)
5707
5836
  issue("Wire diameter must fit within the rear cap");
5708
5837
  }
5709
- const connector = getJstMotorConnector(p.wireConnection);
5838
+ const connector = p.wireConnection === void 0 ? void 0 : getJstMotorConnector(p.wireConnection);
5710
5839
  if (connector && p.bodyWidth - 2 * rearEdge < connector.bodyWidth)
5711
5840
  issue("JST header must fit on the motor side");
5712
5841
  const r = p.mountingHoleDiameter / 2;
@@ -5724,17 +5853,23 @@ var validate3 = (p, ctx) => {
5724
5853
  issue("Mounting holes must clear the pilot");
5725
5854
  if (!p.mountingHoleThrough && p.mountingHoleDepth >= p.frontCapLength)
5726
5855
  issue("Blind mounting hole depth must be less than the front cap length");
5727
- 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))
5856
+ if (p.mountingHoleThrough && !clearsNemaCircle(p.mountingHoleSpacing / 2, p.mountingHoleSpacing / 2, r, {
5857
+ width: p.bodyWidth,
5858
+ chamfer: p.bodyCornerChamfer,
5859
+ stepped: p.stepped,
5860
+ recessRadius: p.bodyCornerRecessRadius,
5861
+ filletRadius: p.bodyCornerFilletRadius
5862
+ }))
5728
5863
  issue("Body must clear through front mounting holes");
5729
5864
  if (p.shaftLength <= p.pilotLength)
5730
5865
  issue("Shaft tip must extend beyond the pilot");
5731
- if (p.shaftShape === "d" && (p.shaftFlatDepth <= 0 || p.shaftFlatDepth >= p.shaftDiameter / 2 || p.shaftFlatLength <= 0 || p.shaftFlatLength > p.shaftLength - p.pilotLength))
5866
+ if (p.dShaft && (p.shaftFlatDepth <= 0 || p.shaftFlatDepth >= p.shaftDiameter / 2 || p.shaftFlatLength <= 0 || p.shaftFlatLength > p.shaftLength - p.pilotLength))
5732
5867
  issue(
5733
5868
  "D flat must have positive depth less than the shaft radius and fit above the pilot"
5734
5869
  );
5735
5870
  };
5736
5871
  var nemaMotorModelPropsSchema = external_exports.object(shape3).strict().transform(resolve).superRefine(validate3);
5737
- var nemaMotorModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("nema"), ...shape3 }).strict().transform(({ fn, ...props }) => ({ fn, ...resolve(props) })).superRefine(validate3);
5872
+ var nemaMotorModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("nema"), ...shape3 }).strict().transform(({ fn, ...props }, ctx) => ({ fn, ...resolve(props, ctx) })).superRefine(validate3);
5738
5873
  var lengths2 = {
5739
5874
  wirelength: "wireLength",
5740
5875
  wirediameter: "wireDiameter",
@@ -5743,6 +5878,8 @@ var lengths2 = {
5743
5878
  bodylength: "bodyLength",
5744
5879
  bodywidth: "bodyWidth",
5745
5880
  backholespacing: "backFaceHoleSpacing",
5881
+ backholespacingx: "backFaceHoleSpacingX",
5882
+ backholespacingy: "backFaceHoleSpacingY",
5746
5883
  backholediameter: "backFaceHoleDiameter",
5747
5884
  backholedepth: "backFaceHoleDepth",
5748
5885
  shaftlength: "shaftLength",
@@ -5758,7 +5895,26 @@ var lengths2 = {
5758
5895
  rearcap: "rearCapLength",
5759
5896
  facechamfer: "faceCornerChamfer",
5760
5897
  bodychamfer: "bodyCornerChamfer",
5761
- rearchamfer: "rearCornerChamfer"
5898
+ bodyrecess: "bodyCornerRecessRadius",
5899
+ bodyfillet: "bodyCornerFilletRadius",
5900
+ rearchamfer: "rearCornerChamfer",
5901
+ rearrecess: "rearCornerRecessRadius",
5902
+ rearfillet: "rearCornerFilletRadius"
5903
+ };
5904
+ var flags = {
5905
+ backfaceplain: "backFacePlain",
5906
+ plainbackface: "backFacePlain",
5907
+ backfaceholes: "backFaceHoles",
5908
+ backfacescrews: "backFaceScrews",
5909
+ round: "roundShaft",
5910
+ roundshaft: "roundShaft",
5911
+ dshaft: "dShaft",
5912
+ chamfered: "chamfered",
5913
+ stepped: "stepped",
5914
+ rearstepped: "rearStepped",
5915
+ rearchamfered: "rearChamfered",
5916
+ nowires: "noWires",
5917
+ wirestubs: "wireStubs"
5762
5918
  };
5763
5919
  function parseNemaMotorModelParams(raw) {
5764
5920
  const tokens = normalizeNemaMotorModelString(raw.string).split("_");
@@ -5776,20 +5932,15 @@ function parseNemaMotorModelParams(raw) {
5776
5932
  if (name in lengths2) {
5777
5933
  property = lengths2[name];
5778
5934
  parsed = value;
5779
- } else if (["backfaceholes", "backfacescrews", "plainbackface"].includes(name)) {
5935
+ } else if (name in flags) {
5780
5936
  if (value) throw new Error(`NEMA flag "${name}" does not accept a value`);
5781
- property = "backFace";
5782
- parsed = name === "backfaceholes" ? "holes" : name === "backfacescrews" ? "screws" : "plain";
5783
- } else if (["wirestubs", "nowires", "jstph", "jstsh"].includes(name)) {
5784
- const isJst = name === "jstph" || name === "jstsh";
5785
- if (isJst && !/^[1-9][0-9]*$/.test(value) || !isJst && value)
5937
+ property = flags[name];
5938
+ parsed = true;
5939
+ } else if (name === "jstph" || name === "jstsh") {
5940
+ if (!/^[1-9][0-9]*$/.test(value))
5786
5941
  throw new Error(`Invalid wire connection token "${token}"`);
5787
5942
  property = "wireConnection";
5788
- parsed = isJst ? `jst-${name.slice(3)}-${value}` : name === "nowires" ? "none" : "stubs";
5789
- } else if (name === "stepped" || name === "chamfered") {
5790
- if (value) throw new Error(`NEMA flag "${name}" does not accept a value`);
5791
- property = "cornerProfile";
5792
- parsed = name;
5943
+ parsed = `jst-${name.slice(3)}-${value}`;
5793
5944
  } else if (name === "wirecount") {
5794
5945
  property = "wireCount";
5795
5946
  if (!/^\d+$/.test(value)) throw new Error("wirecount requires an integer");
@@ -5802,10 +5953,10 @@ function parseNemaMotorModelParams(raw) {
5802
5953
  if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:deg)?$/i.test(value))
5803
5954
  throw new Error(`${name} requires a numeric angle in degrees`);
5804
5955
  parsed = Number(value.replace(/deg$/i, ""));
5805
- } else if (["round", "dshaft", "throughholes", "blindholes"].includes(name)) {
5956
+ } else if (name === "throughholes" || name === "blindholes") {
5806
5957
  if (value) throw new Error(`NEMA flag "${name}" does not accept a value`);
5807
- property = name === "round" || name === "dshaft" ? "shaftShape" : "mountingHoleThrough";
5808
- parsed = name === "round" ? "round" : name === "dshaft" ? "d" : name === "throughholes";
5958
+ property = "mountingHoleThrough";
5959
+ parsed = name === "throughholes";
5809
5960
  } else throw new Error(`Unknown NEMA motor token "${token}"`);
5810
5961
  if (property in props)
5811
5962
  throw new Error(`NEMA property "${property}" is set more than once`);
@@ -5828,22 +5979,26 @@ var holePosition = {
5828
5979
  u: modelLengthSchema,
5829
5980
  v: modelLengthSchema
5830
5981
  };
5831
- var sheetMetalHoleSchema = external_exports.discriminatedUnion("shape", [
5982
+ var sheetMetalHoleSchema = external_exports.union([
5832
5983
  external_exports.object({
5833
5984
  ...holePosition,
5834
- shape: external_exports.literal("round"),
5985
+ round: external_exports.literal(true),
5986
+ slot: external_exports.literal(false).default(false),
5835
5987
  diameter: positiveModelLengthSchema
5836
5988
  }).strict(),
5837
5989
  external_exports.object({
5838
5990
  ...holePosition,
5839
- shape: external_exports.literal("slot"),
5991
+ round: external_exports.literal(false).default(false),
5992
+ slot: external_exports.literal(true),
5840
5993
  length: positiveModelLengthSchema,
5841
5994
  width: positiveModelLengthSchema,
5842
5995
  axis: external_exports.enum(["u", "v"]).default("v")
5843
5996
  }).strict()
5844
5997
  ]);
5845
5998
  var sheetMetalShape = {
5846
- profile: external_exports.enum(["plate", "angle", "channel"]).default("plate"),
5999
+ plate: external_exports.boolean().default(false),
6000
+ angle: external_exports.boolean().default(false),
6001
+ channel: external_exports.boolean().default(false),
5847
6002
  width: positiveModelLengthSchema,
5848
6003
  /** Straight base length between bend tangencies, not overall width. */
5849
6004
  baseLength: positiveModelLengthSchema,
@@ -5853,9 +6008,19 @@ var sheetMetalShape = {
5853
6008
  insideBendRadius: positiveModelLengthSchema.default(1),
5854
6009
  holes: external_exports.array(sheetMetalHoleSchema).max(100).default([])
5855
6010
  };
5856
- var sheetMetalModelPropsSchema = external_exports.object(sheetMetalShape).strict();
5857
- var sheetMetalModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("sheetmetal"), ...sheetMetalShape }).strict();
5858
- function parseSheetMetalFeature(feature, profile6) {
6011
+ function validateFlags(props, context) {
6012
+ if ([props.plate, props.angle, props.channel].filter(Boolean).length > 1)
6013
+ context.addIssue({
6014
+ code: "custom",
6015
+ message: "Specify at most one of plate, angle, or channel"
6016
+ });
6017
+ }
6018
+ function resolveFlags(props) {
6019
+ return { ...props, plate: props.plate || !(props.angle || props.channel) };
6020
+ }
6021
+ var sheetMetalModelPropsSchema = external_exports.object(sheetMetalShape).strict().superRefine(validateFlags).transform(resolveFlags);
6022
+ var sheetMetalModelDefinitionSchema = external_exports.object({ fn: external_exports.literal("sheetmetal"), ...sheetMetalShape }).strict().superRefine(validateFlags).transform((props) => ({ fn: props.fn, ...resolveFlags(props) }));
6023
+ function parseSheetMetalFeature(feature, flags11) {
5859
6024
  const match = feature.match(/^(hole|slot)[1-9]\d*\(([^()]+)\)$/);
5860
6025
  if (!match) throw new Error(`Invalid sheet metal feature "${feature}"`);
5861
6026
  const kind = match[1];
@@ -5888,8 +6053,8 @@ function parseSheetMetalFeature(feature, profile6) {
5888
6053
  if (faces.length !== 1)
5889
6054
  throw new Error(`Specify exactly one face in ${feature}`);
5890
6055
  const face2 = faces[0];
5891
- if (face2 === "angledface" && profile6 !== "angle" || ["leftface", "rightface"].includes(face2) && profile6 !== "channel")
5892
- throw new Error(`No ${face2} on ${profile6}`);
6056
+ if (face2 === "angledface" && !flags11.angle || ["leftface", "rightface"].includes(face2) && !flags11.channel)
6057
+ throw new Error(`No ${face2} on the selected sheet metal form`);
5893
6058
  for (const [a, b] of [
5894
6059
  ["leftofcenter", "rightofcenter"],
5895
6060
  ["abovecenter", "belowcenter"],
@@ -5913,7 +6078,7 @@ function parseSheetMetalFeature(feature, profile6) {
5913
6078
  throw new Error(`Hole requires r or d in ${feature}`);
5914
6079
  return sheetMetalHoleSchema.parse({
5915
6080
  ...position,
5916
- shape: "round",
6081
+ round: true,
5917
6082
  diameter: "r" in values ? 2 * distance2("r") : distance2("d")
5918
6083
  });
5919
6084
  }
@@ -5923,7 +6088,7 @@ function parseSheetMetalFeature(feature, profile6) {
5923
6088
  if (length32 < width10) throw new Error("Slot length must be at least its width");
5924
6089
  return sheetMetalHoleSchema.parse({
5925
6090
  ...position,
5926
- shape: "slot",
6091
+ slot: true,
5927
6092
  length: length32,
5928
6093
  width: width10,
5929
6094
  axis: panel === "base" === !("vertical" in values) ? "u" : "v"
@@ -5951,8 +6116,9 @@ function parseSheetMetalModelParams(raw) {
5951
6116
  continue;
5952
6117
  }
5953
6118
  if (["plate", "angle", "channel"].includes(token)) {
5954
- if (props.profile) throw new Error("Duplicate sheet metal profile");
5955
- props.profile = token;
6119
+ if (["plate", "angle", "channel"].some((flag) => flag in props))
6120
+ throw new Error("Duplicate sheet metal form flag");
6121
+ props[token] = true;
5956
6122
  continue;
5957
6123
  }
5958
6124
  const key = lengths3[token[0]];
@@ -5966,7 +6132,7 @@ function parseSheetMetalModelParams(raw) {
5966
6132
  ...props
5967
6133
  });
5968
6134
  definition.holes = features.map(
5969
- (feature) => parseSheetMetalFeature(feature, definition.profile)
6135
+ (feature) => parseSheetMetalFeature(feature, definition)
5970
6136
  );
5971
6137
  return definition;
5972
6138
  }
@@ -5978,14 +6144,6 @@ var model4 = defineModel({
5978
6144
  function register4(registry2) {
5979
6145
  registry2.register(model4);
5980
6146
  }
5981
- var flexScreenOrientationSchema = external_exports.enum([
5982
- "sitsFlat",
5983
- "sitsFlatBelowBoard",
5984
- "foldedToFaceAboveBoard",
5985
- "foldedToFaceBelowBoard",
5986
- "foldedToRightAngleAboveBoard",
5987
- "foldedToRightAngleBelowBoard"
5988
- ]);
5989
6147
  var positiveFiniteNumberSchema = external_exports.number().finite().positive();
5990
6148
  var aspectRatioStringSchema = external_exports.string().refine((value) => {
5991
6149
  const parts = value.split(":");
@@ -6027,7 +6185,6 @@ var flexScreenModelPropsShape = {
6027
6185
  aspectRatio: flexScreenAspectRatioSchema.optional(),
6028
6186
  ratio: flexScreenAspectRatioSchema.optional(),
6029
6187
  defaultDiagonal: positiveModelLengthSchema.optional(),
6030
- orientation: flexScreenOrientationSchema.optional(),
6031
6188
  sitsFlat: external_exports.boolean().optional(),
6032
6189
  sitsFlatBelowBoard: external_exports.boolean().optional(),
6033
6190
  foldedToFaceAboveBoard: external_exports.boolean().optional(),
@@ -6095,7 +6252,42 @@ var addOrientationShortcutIssue = (props, addIssue) => {
6095
6252
  const selectedShortcuts = orientationShortcutKeys.filter(
6096
6253
  (key) => props[key] === true
6097
6254
  );
6098
- if (selectedShortcuts.length > 1) addIssue(selectedShortcuts[1]);
6255
+ if (selectedShortcuts.length > 1) {
6256
+ addIssue(
6257
+ selectedShortcuts[1],
6258
+ "Only one FlexScreen orientation shortcut can be true"
6259
+ );
6260
+ } else if (selectedShortcuts.length === 0 && orientationShortcutKeys.some((key) => props[key] !== void 0)) {
6261
+ addIssue(
6262
+ "sitsFlat",
6263
+ "A FlexScreen orientation flag must be true when placement flags are supplied"
6264
+ );
6265
+ }
6266
+ };
6267
+ var normalizeOrientationFlags = (props) => {
6268
+ const {
6269
+ sitsFlat,
6270
+ sitsFlatBelowBoard,
6271
+ foldedToFaceAboveBoard,
6272
+ foldedToFaceBelowBoard,
6273
+ foldedToRightAngleAboveBoard,
6274
+ foldedToRightAngleBelowBoard,
6275
+ foldsAboveBoard,
6276
+ foldsBelowBoard,
6277
+ ...rest
6278
+ } = props;
6279
+ const above = foldedToFaceAboveBoard === true || foldsAboveBoard === true;
6280
+ const below = foldedToFaceBelowBoard === true || foldsBelowBoard === true;
6281
+ const selected = sitsFlatBelowBoard === true || above || below || foldedToRightAngleAboveBoard === true || foldedToRightAngleBelowBoard === true;
6282
+ return {
6283
+ ...rest,
6284
+ sitsFlat: sitsFlat === true || !selected,
6285
+ sitsFlatBelowBoard: sitsFlatBelowBoard === true,
6286
+ foldedToFaceAboveBoard: above,
6287
+ foldedToFaceBelowBoard: below,
6288
+ foldedToRightAngleAboveBoard: foldedToRightAngleAboveBoard === true,
6289
+ foldedToRightAngleBelowBoard: foldedToRightAngleBelowBoard === true
6290
+ };
6099
6291
  };
6100
6292
  var validateTailLengths = (props, context) => {
6101
6293
  if (props.flexCableLength === void 0) return;
@@ -6116,27 +6308,27 @@ var validateTailLengths = (props, context) => {
6116
6308
  };
6117
6309
  var flexScreenModelPropsSchema = external_exports.object(flexScreenModelPropsShape).strict().superRefine((props, context) => {
6118
6310
  validateTailLengths(props, context);
6119
- addOrientationShortcutIssue(props, (path) => {
6311
+ addOrientationShortcutIssue(props, (path, message) => {
6120
6312
  context.addIssue({
6121
6313
  code: "custom",
6122
- message: "Only one FlexScreen orientation shortcut can be true",
6314
+ message,
6123
6315
  path: [path]
6124
6316
  });
6125
6317
  });
6126
- });
6318
+ }).transform(normalizeOrientationFlags);
6127
6319
  var flexScreenModelDefinitionSchema = external_exports.object({
6128
6320
  fn: external_exports.literal("flexscreen"),
6129
6321
  ...flexScreenModelPropsShape
6130
6322
  }).strict().superRefine((model552, context) => {
6131
6323
  validateTailLengths(model552, context);
6132
- addOrientationShortcutIssue(model552, (path) => {
6324
+ addOrientationShortcutIssue(model552, (path, message) => {
6133
6325
  context.addIssue({
6134
6326
  code: "custom",
6135
- message: "Only one FlexScreen orientation shortcut can be true",
6327
+ message,
6136
6328
  path: [path]
6137
6329
  });
6138
6330
  });
6139
- });
6331
+ }).transform(normalizeOrientationFlags);
6140
6332
  var orientationTokens = {
6141
6333
  sitsflat: "sitsFlat",
6142
6334
  sitsflatbelow: "sitsFlatBelowBoard",
@@ -6262,6 +6454,23 @@ var parseFlexScreenModelParams = (rawParams) => {
6262
6454
  }
6263
6455
  const props = {};
6264
6456
  let orientation;
6457
+ for (const part of splitModelStringTokens(rawParams.string).slice(1)) {
6458
+ const match = part.match(/^([a-zA-Z]+)(.*)$/);
6459
+ if (!match) continue;
6460
+ const token = match[1].toLowerCase();
6461
+ const selected = orientationTokens[token];
6462
+ if (!selected) continue;
6463
+ if (match[2] !== "") {
6464
+ throw new Error(`FlexScreen token "${token}" does not accept a value`);
6465
+ }
6466
+ if (orientation !== void 0) {
6467
+ throw new Error(
6468
+ orientation === selected ? `Duplicate FlexScreen orientation flag "${token}"` : "A FlexScreen model string can only set one orientation"
6469
+ );
6470
+ }
6471
+ orientation = selected;
6472
+ props[selected] = true;
6473
+ }
6265
6474
  let relativeDistance;
6266
6475
  for (const [token, value] of Object.entries(rawParams)) {
6267
6476
  if (token === "fn" || token === "string" || token === "flexscreen") {
@@ -6270,13 +6479,6 @@ var parseFlexScreenModelParams = (rawParams) => {
6270
6479
  const tokenOrientation = orientationTokens[token];
6271
6480
  if (tokenOrientation) {
6272
6481
  assertBareToken(value, token);
6273
- if (orientation && orientation !== tokenOrientation) {
6274
- throw new Error(
6275
- "A FlexScreen model string can only set one orientation"
6276
- );
6277
- }
6278
- orientation = tokenOrientation;
6279
- props.orientation = tokenOrientation;
6280
6482
  continue;
6281
6483
  }
6282
6484
  if (token in booleanTokens) {
@@ -6452,7 +6654,7 @@ var shape4 = {
6452
6654
  /** Additional radial dedendum beyond one module, in millimeters. */
6453
6655
  clearance: nonnegativeGearLengthSchema.default(0.25),
6454
6656
  boreDiameter: nonnegativeGearLengthSchema.default(0),
6455
- handedness: external_exports.enum(["right", "left"]).default("right"),
6657
+ leftHanded: external_exports.boolean().default(false),
6456
6658
  /** Counterclockwise degrees about +Z, beginning at the Z=0 end. */
6457
6659
  phase: external_exports.number().finite().default(0),
6458
6660
  radialSegments: external_exports.number().int().min(24).max(256).refine(
@@ -6522,7 +6724,7 @@ function parseWormGearModelParams(raw) {
6522
6724
  const [name, value] = parseGearToken(token);
6523
6725
  let property;
6524
6726
  let parsed;
6525
- if (name in lengths5) {
6727
+ if (Object.hasOwn(lengths5, name)) {
6526
6728
  property = lengths5[name];
6527
6729
  parsed = value;
6528
6730
  } else if (["starts", "segments", "turnsegments"].includes(name)) {
@@ -6533,8 +6735,8 @@ function parseWormGearModelParams(raw) {
6533
6735
  parsed = parseGearAngle(value, property);
6534
6736
  } else if (name === "right" || name === "left") {
6535
6737
  if (value) throw new Error(`Worm flag "${name}" does not accept a value`);
6536
- property = "handedness";
6537
- parsed = name;
6738
+ property = "leftHanded";
6739
+ parsed = name === "left";
6538
6740
  } else throw new Error(`Unknown worm gear token "${token}"`);
6539
6741
  if (property in props)
6540
6742
  throw new Error(`Worm gear property "${property}" is set more than once`);
@@ -6853,7 +7055,7 @@ var lengths7 = {
6853
7055
  w: "width",
6854
7056
  width: "width"
6855
7057
  };
6856
- var flags = {
7058
+ var flags2 = {
6857
7059
  open: "bothSidesOpen",
6858
7060
  bothsidesopen: "bothSidesOpen",
6859
7061
  shielded: "bothSidesShielded",
@@ -6887,8 +7089,8 @@ function parseBallBearingModelParams(raw) {
6887
7089
  const scopes = /* @__PURE__ */ new Set();
6888
7090
  for (const token of tokens.slice(1)) {
6889
7091
  const lower = token.toLowerCase();
6890
- if (Object.hasOwn(flags, lower)) {
6891
- const property2 = flags[lower];
7092
+ if (Object.hasOwn(flags2, lower)) {
7093
+ const property2 = flags2[lower];
6892
7094
  const scope = property2.startsWith("both") ? "both" : property2.startsWith("top") ? "top" : "bottom";
6893
7095
  if (scopes.has(scope))
6894
7096
  throw new Error(
@@ -7082,7 +7284,7 @@ function parseBallTransferUnitModelParams(raw) {
7082
7284
  const lower = token.toLowerCase();
7083
7285
  let property;
7084
7286
  let value;
7085
- if (lower === "face3hole" || lower === "mount(face3hole)") {
7287
+ if (lower === "face3hole") {
7086
7288
  property = "faceThreeHole";
7087
7289
  value = true;
7088
7290
  } else {
@@ -7108,12 +7310,6 @@ var model12 = defineModel({
7108
7310
  function register12(registry2) {
7109
7311
  registry2.register(model12);
7110
7312
  }
7111
- var stringFlags2 = {
7112
- hexsocket: "drive(hexsocket)",
7113
- fullthread: "thread(full)",
7114
- righthanded: "threadhand(right)"
7115
- };
7116
- var omittedStringFlags2 = ["righthanded"];
7117
7313
  var buttonScrewDimensions = {
7118
7314
  M3: {
7119
7315
  diameter: 3,
@@ -7174,10 +7370,10 @@ var shape9 = {
7174
7370
  metricSize: buttonScrewMetricSizeSchema,
7175
7371
  /** Under-head length; this family supports short fully threaded screws only. */
7176
7372
  length: length4,
7177
- drive: external_exports.literal("hexsocket").default("hexsocket"),
7178
- thread: external_exports.literal("full").default("full"),
7373
+ hexSocket: external_exports.literal(true).default(true),
7374
+ fullThread: external_exports.literal(true).default(true),
7179
7375
  threadPitch: length4.optional(),
7180
- threadHand: external_exports.literal("right").default("right"),
7376
+ leftHanded: external_exports.literal(false).default(false),
7181
7377
  threadClass: external_exports.literal("6g").default("6g"),
7182
7378
  showThreads: external_exports.boolean().default(true)
7183
7379
  };
@@ -7233,9 +7429,7 @@ function getButtonScrewDimensions(input) {
7233
7429
  };
7234
7430
  }
7235
7431
  function parseButtonScrewModelParams(raw) {
7236
- const tokens = splitModelStringTokens(
7237
- expandModelStringFlags(raw.string, stringFlags2)
7238
- );
7432
+ const tokens = splitModelStringTokens(raw.string);
7239
7433
  if (raw.fn !== "buttonscrew" || tokens[0]?.toLowerCase() !== "buttonscrew")
7240
7434
  throw new Error("Expected buttonscrew without an inline argument");
7241
7435
  const props = { fn: "buttonscrew" };
@@ -7244,12 +7438,15 @@ function parseButtonScrewModelParams(raw) {
7244
7438
  length: "length",
7245
7439
  threadpitch: "threadPitch"
7246
7440
  };
7247
- const selectors7 = {
7248
- drive: "drive",
7249
- thread: "thread",
7250
- threadhand: "threadHand",
7441
+ const selectors5 = {
7251
7442
  threadclass: "threadClass"
7252
7443
  };
7444
+ const booleanFlags4 = {
7445
+ hexsocket: { property: "hexSocket", value: true },
7446
+ fullthread: { property: "fullThread", value: true },
7447
+ righthanded: { property: "leftHanded", value: false },
7448
+ lefthanded: { property: "leftHanded", value: true }
7449
+ };
7253
7450
  for (const token of tokens.slice(1)) {
7254
7451
  if (token.toLowerCase() === "iso7380-1") {
7255
7452
  if ("iso73801" in props)
@@ -7263,12 +7460,17 @@ function parseButtonScrewModelParams(raw) {
7263
7460
  const value = match[2];
7264
7461
  let property;
7265
7462
  let parsed = value;
7266
- if (name === "m") {
7463
+ if (Object.hasOwn(booleanFlags4, name)) {
7464
+ if (value) throw new Error(`Flag "${name}" does not accept a value`);
7465
+ property = booleanFlags4[name].property;
7466
+ parsed = booleanFlags4[name].value;
7467
+ } else if (name === "m") {
7267
7468
  property = "metricSize";
7268
7469
  parsed = `M${value}`;
7269
- } else if (name in lengths25) property = lengths25[name];
7270
- else if (name in selectors7) {
7271
- property = selectors7[name];
7470
+ } else if (Object.hasOwn(lengths25, name))
7471
+ property = lengths25[name];
7472
+ else if (Object.hasOwn(selectors5, name)) {
7473
+ property = selectors5[name];
7272
7474
  if (!/^\([^()]+\)$/.test(value))
7273
7475
  throw new Error(`Expected a parenthesized ${name}`);
7274
7476
  parsed = value.slice(1, -1).toLowerCase();
@@ -7286,21 +7488,11 @@ function parseButtonScrewModelParams(raw) {
7286
7488
  var model13 = defineModel({
7287
7489
  name: "buttonscrew",
7288
7490
  schema: buttonScrewModelDefinitionSchema,
7289
- parse: parseButtonScrewModelParams,
7290
- normalizeString: (value) => normalizeModelStringFlags(
7291
- value,
7292
- stringFlags2,
7293
- parseButtonScrewModelParams,
7294
- omittedStringFlags2
7295
- )
7491
+ parse: parseButtonScrewModelParams
7296
7492
  });
7297
7493
  function register13(registry2) {
7298
7494
  registry2.register(model13);
7299
7495
  }
7300
- var stringFlags3 = {
7301
- symmetricring: "shape(symmetricring)"
7302
- };
7303
- var omittedStringFlags3 = [];
7304
7496
  var length5 = external_exports.union([
7305
7497
  external_exports.number(),
7306
7498
  external_exports.string().regex(
@@ -7316,7 +7508,7 @@ var shape10 = {
7316
7508
  grooveWidth: length5,
7317
7509
  /** Radial recess from the flange outside diameter. */
7318
7510
  grooveDepth: length5,
7319
- shape: external_exports.literal("symmetricring").default("symmetricring")
7511
+ symmetricRing: external_exports.literal(true).default(true)
7320
7512
  };
7321
7513
  function validate9(props, context) {
7322
7514
  if (props.innerDiameter >= props.panelHoleDiameter || props.panelHoleDiameter >= props.outerDiameter)
@@ -7373,9 +7565,7 @@ var lengths9 = {
7373
7565
  groovedepth: "grooveDepth"
7374
7566
  };
7375
7567
  function parseCableGrommetModelParams(raw) {
7376
- const tokens = splitModelStringTokens(
7377
- expandModelStringFlags(raw.string, stringFlags3)
7378
- );
7568
+ const tokens = splitModelStringTokens(raw.string);
7379
7569
  if (raw.fn !== "cablegrommet" || !/^cablegrommet$/i.test(tokens[0]))
7380
7570
  throw new Error("Expected cablegrommet without an inline value");
7381
7571
  const props = { fn: "cablegrommet" };
@@ -7383,15 +7573,14 @@ function parseCableGrommetModelParams(raw) {
7383
7573
  const match = token.match(/^([a-z]+)(.*)$/i);
7384
7574
  const name = match?.[1]?.toLowerCase();
7385
7575
  const value = match?.[2];
7386
- if (!name || !value) throw new Error(`Invalid grommet token "${token}"`);
7576
+ if (!name) throw new Error(`Invalid grommet token "${token}"`);
7387
7577
  let property;
7388
7578
  let parsed = value;
7389
7579
  if (name in lengths9) property = lengths9[name];
7390
- else if (name === "shape") {
7391
- property = "shape";
7392
- const selector = value.match(/^\(([a-z]+)\)$/i);
7393
- if (!selector) throw new Error("Grommet shape requires parentheses");
7394
- parsed = selector[1].toLowerCase();
7580
+ else if (name === "symmetricring") {
7581
+ property = "symmetricRing";
7582
+ if (value !== "") throw new Error("symmetricring is a value-free flag");
7583
+ parsed = true;
7395
7584
  } else throw new Error(`Unknown grommet token "${token}"`);
7396
7585
  if (property in props)
7397
7586
  throw new Error(`Grommet property "${property}" is set more than once`);
@@ -7402,13 +7591,7 @@ function parseCableGrommetModelParams(raw) {
7402
7591
  var model14 = defineModel({
7403
7592
  name: "cablegrommet",
7404
7593
  schema: cableGrommetModelDefinitionSchema,
7405
- parse: parseCableGrommetModelParams,
7406
- normalizeString: (value) => normalizeModelStringFlags(
7407
- value,
7408
- stringFlags3,
7409
- parseCableGrommetModelParams,
7410
- omittedStringFlags3
7411
- )
7594
+ parse: parseCableGrommetModelParams
7412
7595
  });
7413
7596
  function register14(registry2) {
7414
7597
  registry2.register(model14);
@@ -7505,12 +7688,6 @@ var model15 = defineModel({
7505
7688
  function register15(registry2) {
7506
7689
  registry2.register(model15);
7507
7690
  }
7508
- var stringFlags4 = {
7509
- singleclamp: "mount(singleclamp)",
7510
- lefthanded: "threadhand(left)",
7511
- righthanded: "threadhand(right)"
7512
- };
7513
- var omittedStringFlags4 = ["righthanded"];
7514
7691
  var length7 = external_exports.union([
7515
7692
  external_exports.number().finite(),
7516
7693
  external_exports.string().regex(
@@ -7531,10 +7708,10 @@ var shape12 = {
7531
7708
  splitWidth: positive3,
7532
7709
  clampX: positive3.optional(),
7533
7710
  clearanceHoleDiameter: positive3.optional(),
7534
- mount: external_exports.literal("singleclamp").default("singleclamp"),
7711
+ singleClamp: external_exports.literal(true).default(true),
7535
7712
  metricSize: metricBoltSizeSchema,
7536
7713
  threadPitch: positive3.optional(),
7537
- threadHand: external_exports.enum(["right", "left"]).default("right"),
7714
+ leftHanded: external_exports.boolean().default(false),
7538
7715
  threadClass: external_exports.literal("6H").default("6H"),
7539
7716
  chamfer: nonnegative2.default(0)
7540
7717
  };
@@ -7610,7 +7787,7 @@ function getClampingShaftCollarDimensions(input) {
7610
7787
  diameter: threadDiameter,
7611
7788
  depth: threadedExtent - p.splitWidth / 2,
7612
7789
  threadPitch: p.threadPitch,
7613
- threadHand: p.threadHand,
7790
+ leftHanded: p.leftHanded,
7614
7791
  threadGender: "female",
7615
7792
  threadClass: p.threadClass
7616
7793
  }
@@ -7619,10 +7796,8 @@ function getClampingShaftCollarDimensions(input) {
7619
7796
  var tokenProperties = {
7620
7797
  bore: "boreDiameter",
7621
7798
  od: "outerDiameter",
7622
- mount: "mount",
7623
7799
  m: "metricSize",
7624
7800
  threadpitch: "threadPitch",
7625
- threadhand: "threadHand",
7626
7801
  threadclass: "threadClass",
7627
7802
  chamfer: "chamfer",
7628
7803
  w: "width",
@@ -7633,27 +7808,35 @@ var tokenProperties = {
7633
7808
  clearance: "clearanceHoleDiameter"
7634
7809
  };
7635
7810
  function parseClampingShaftCollarModelParams(raw) {
7636
- const tokens = splitModelStringTokens(
7637
- expandModelStringFlags(raw.string, stringFlags4)
7638
- );
7811
+ const tokens = splitModelStringTokens(raw.string);
7639
7812
  if (raw.fn !== "clampingshaftcollar" || tokens[0]?.toLowerCase() !== "clampingshaftcollar")
7640
7813
  throw new Error("Expected an unadorned clampingshaftcollar function");
7641
7814
  const props = {};
7642
7815
  for (const token of tokens.slice(1)) {
7643
7816
  const match = token.match(/^([a-z]+)(.*)$/i);
7644
7817
  const name = match?.[1]?.toLowerCase() ?? "";
7818
+ const value = match?.[2] ?? "";
7819
+ if (["singleclamp", "lefthanded", "righthanded"].includes(name)) {
7820
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
7821
+ const property2 = name === "singleclamp" ? "singleClamp" : "leftHanded";
7822
+ if (property2 in props)
7823
+ throw new Error(
7824
+ `Duplicate or conflicting clampingshaftcollar flag "${property2}"`
7825
+ );
7826
+ props[property2] = name !== "righthanded";
7827
+ continue;
7828
+ }
7645
7829
  const property = tokenProperties[name];
7646
7830
  if (!property)
7647
7831
  throw new Error(`Unknown clampingshaftcollar token "${token}"`);
7648
7832
  if (property in props)
7649
7833
  throw new Error(`Duplicate clampingshaftcollar property "${property}"`);
7650
- const value = match[2];
7651
7834
  if (!value) throw new Error(`Token "${name}" requires a value`);
7652
- if (["mount", "threadHand", "threadClass"].includes(property)) {
7835
+ if (property === "threadClass") {
7653
7836
  const argument = value.match(/^\(([^()]+)\)$/);
7654
7837
  if (!argument)
7655
7838
  throw new Error(`Token "${name}" requires one parenthesized argument`);
7656
- props[property] = property === "threadClass" ? argument[1].toUpperCase() : argument[1].toLowerCase();
7839
+ props[property] = argument[1].toUpperCase();
7657
7840
  } else if (property === "metricSize") {
7658
7841
  if (!/^\d+(?:\.\d+)?$/.test(value))
7659
7842
  throw new Error("m requires a unitless metric designation");
@@ -7670,41 +7853,27 @@ function parseClampingShaftCollarModelParams(raw) {
7670
7853
  var model16 = defineModel({
7671
7854
  name: "clampingshaftcollar",
7672
7855
  schema: clampingShaftCollarModelDefinitionSchema,
7673
- parse: parseClampingShaftCollarModelParams,
7674
- normalizeString: (value) => normalizeModelStringFlags(
7675
- value,
7676
- stringFlags4,
7677
- parseClampingShaftCollarModelParams,
7678
- omittedStringFlags4
7679
- )
7856
+ parse: parseClampingShaftCollarModelParams
7680
7857
  });
7681
7858
  function register16(registry2) {
7682
7859
  registry2.register(model16);
7683
7860
  }
7684
- var stringFlags5 = {
7685
- closedground: "ends(closedground)",
7686
- lefthanded: "hand(left)",
7687
- righthanded: "hand(right)",
7688
- custom: "spec(custom)",
7689
- free: "state(free)"
7690
- };
7691
- var omittedStringFlags5 = ["righthanded", "custom", "free"];
7692
7861
  var length8 = external_exports.union([
7693
7862
  external_exports.number(),
7694
7863
  external_exports.string().regex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:mm|cm|m|in|inch|mil|ft|feet)?$/i).transform((value) => value.toLowerCase())
7695
7864
  ]).pipe(modelLengthSchema).refine((value) => value > 0, "Length must be positive");
7696
7865
  var turnCount = external_exports.number().int().min(1).max(Number.MAX_SAFE_INTEGER);
7697
7866
  var shape13 = {
7698
- spec: external_exports.literal("custom").default("custom"),
7867
+ custom: external_exports.literal(true).default(true),
7699
7868
  outerDiameter: length8,
7700
7869
  wireDiameter: length8,
7701
7870
  freeLength: length8,
7702
7871
  totalTurns: turnCount.min(3),
7703
7872
  /** Closed-ground ends have exactly one inactive turn at each end. */
7704
7873
  activeTurns: turnCount.optional(),
7705
- ends: external_exports.literal("closedground").default("closedground"),
7706
- hand: external_exports.enum(["right", "left"]).default("right"),
7707
- state: external_exports.literal("free").default("free")
7874
+ closedGround: external_exports.literal(true).default(true),
7875
+ leftHanded: external_exports.boolean().default(false),
7876
+ free: external_exports.literal(true).default(true)
7708
7877
  };
7709
7878
  function validate12(props, context) {
7710
7879
  if (props.outerDiameter <= 2 * props.wireDiameter)
@@ -7757,14 +7926,12 @@ function getCompressionSpringCenterlinePoint(input, turn) {
7757
7926
  throw new Error("Turn coordinate must be finite and within [0,totalTurns]");
7758
7927
  const radius = (props.outerDiameter - props.wireDiameter) / 2;
7759
7928
  const pitch = (props.freeLength - 2 * props.wireDiameter) / props.activeTurns;
7760
- const phase = (props.hand === "right" ? 1 : -1) * 2 * Math.PI * (turn % 1);
7929
+ const phase = (props.leftHanded ? -1 : 1) * 2 * Math.PI * (turn % 1);
7761
7930
  const z59 = turn <= 1 ? props.wireDiameter * turn : turn >= props.totalTurns - 1 ? props.freeLength - props.wireDiameter * (props.totalTurns - turn) : props.wireDiameter + pitch * (turn - 1);
7762
7931
  return { x: radius * Math.cos(phase), y: radius * Math.sin(phase), z: z59 };
7763
7932
  }
7764
7933
  function parseCompressionSpringModelParams(raw) {
7765
- const tokens = splitModelStringTokens(
7766
- expandModelStringFlags(raw.string, stringFlags5)
7767
- );
7934
+ const tokens = splitModelStringTokens(raw.string);
7768
7935
  if (raw.fn !== "compressionspring" || tokens[0]?.toLowerCase() !== "compressionspring")
7769
7936
  throw new Error("Expected compressionspring without an inline argument");
7770
7937
  const props = { fn: "compressionspring" };
@@ -7775,11 +7942,12 @@ function parseCompressionSpringModelParams(raw) {
7775
7942
  length: "freeLength",
7776
7943
  freelength: "freeLength"
7777
7944
  };
7778
- const selectors7 = {
7779
- spec: "spec",
7780
- ends: "ends",
7781
- hand: "hand",
7782
- state: "state"
7945
+ const flags11 = {
7946
+ custom: "custom",
7947
+ closedground: "closedGround",
7948
+ free: "free",
7949
+ lefthanded: "leftHanded",
7950
+ righthanded: "leftHanded"
7783
7951
  };
7784
7952
  for (const token of tokens.slice(1)) {
7785
7953
  const match = token.match(/^([a-z]+)(.*)$/i);
@@ -7794,11 +7962,10 @@ function parseCompressionSpringModelParams(raw) {
7794
7962
  if (!/^\d+$/.test(value))
7795
7963
  throw new Error(`${name} requires a unitless integer`);
7796
7964
  parsed = Number(value);
7797
- } else if (name in selectors7) {
7798
- property = selectors7[name];
7799
- if (!/^\([^()]+\)$/.test(value))
7800
- throw new Error(`Expected a parenthesized ${name}`);
7801
- parsed = value.slice(1, -1).toLowerCase();
7965
+ } else if (Object.hasOwn(flags11, name)) {
7966
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
7967
+ property = flags11[name];
7968
+ parsed = name !== "righthanded";
7802
7969
  } else throw new Error(`Unknown compression spring token "${token}"`);
7803
7970
  if (property in props)
7804
7971
  throw new Error(`Duplicate compression spring property "${property}"`);
@@ -7809,13 +7976,7 @@ function parseCompressionSpringModelParams(raw) {
7809
7976
  var model17 = defineModel({
7810
7977
  name: "compressionspring",
7811
7978
  schema: compressionSpringModelDefinitionSchema,
7812
- parse: parseCompressionSpringModelParams,
7813
- normalizeString: (value) => normalizeModelStringFlags(
7814
- value,
7815
- stringFlags5,
7816
- parseCompressionSpringModelParams,
7817
- omittedStringFlags5
7818
- )
7979
+ parse: parseCompressionSpringModelParams
7819
7980
  });
7820
7981
  function register17(registry2) {
7821
7982
  registry2.register(model17);
@@ -8268,7 +8429,7 @@ var aliases4 = {
8268
8429
  threadrootdiameter: "threadRootDiameter",
8269
8430
  threadpitchdiameter: "threadPitchDiameter"
8270
8431
  };
8271
- var flags2 = {
8432
+ var flags3 = {
8272
8433
  fullthread: "fullThread",
8273
8434
  plainface: "plainFace",
8274
8435
  lefthanded: "leftHand",
@@ -8294,9 +8455,9 @@ function parseFlangeBoltModelParams(raw) {
8294
8455
  const value = match?.[2] ?? "";
8295
8456
  let property;
8296
8457
  let parsed;
8297
- if (flags2[key]) {
8458
+ if (flags3[key]) {
8298
8459
  if (value) throw new Error(`Flag ${key} does not accept a value`);
8299
- property = flags2[key];
8460
+ property = flags3[key];
8300
8461
  parsed = !["righthanded", "nothreads"].includes(key);
8301
8462
  } else {
8302
8463
  if (key === "standard" || key === "headstandard")
@@ -8334,10 +8495,6 @@ var model20 = defineModel({
8334
8495
  function register20(registry2) {
8335
8496
  registry2.register(model20);
8336
8497
  }
8337
- var stringFlags6 = {
8338
- plainclosed: "style(plainclosed)"
8339
- };
8340
- var omittedStringFlags6 = ["plainclosed"];
8341
8498
  var positiveLength6 = external_exports.union([
8342
8499
  external_exports.number(),
8343
8500
  external_exports.string().regex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:mm|cm|m|in|inch|mil|ft|feet)?$/i).transform((value) => value.toLowerCase())
@@ -8349,7 +8506,7 @@ var shape17 = {
8349
8506
  /** Overall end-plane distance including the integral flange. */
8350
8507
  length: positiveLength6,
8351
8508
  flangeThickness: positiveLength6,
8352
- style: external_exports.literal("plainclosed").default("plainclosed")
8509
+ plainClosed: external_exports.literal(true).default(true)
8353
8510
  };
8354
8511
  function validate16(props, context) {
8355
8512
  if (props.outerDiameter <= props.innerDiameter)
@@ -8399,9 +8556,7 @@ var lengths12 = {
8399
8556
  flangethickness: "flangeThickness"
8400
8557
  };
8401
8558
  function parseFlangedBushingModelParams(raw) {
8402
- const tokens = splitModelStringTokens(
8403
- expandModelStringFlags(raw.string, stringFlags6)
8404
- );
8559
+ const tokens = splitModelStringTokens(raw.string);
8405
8560
  if (raw.fn !== "flangedbushing" || tokens[0]?.toLowerCase() !== "flangedbushing")
8406
8561
  throw new Error("Expected flangedbushing without an inline value");
8407
8562
  const props = { fn: "flangedbushing" };
@@ -8415,11 +8570,10 @@ function parseFlangedBushingModelParams(raw) {
8415
8570
  if (Object.hasOwn(lengths12, name)) {
8416
8571
  property = lengths12[name];
8417
8572
  parsed = value;
8418
- } else if (name === "style") {
8419
- property = "style";
8420
- const style = value.match(/^\(([a-z]+)\)$/i);
8421
- if (!style) throw new Error("Style requires a parenthesized value");
8422
- parsed = style[1].toLowerCase();
8573
+ } else if (name === "plainclosed") {
8574
+ property = "plainClosed";
8575
+ if (value !== "") throw new Error("plainclosed is a value-free flag");
8576
+ parsed = true;
8423
8577
  } else throw new Error(`Unknown flanged bushing token "${token}"`);
8424
8578
  if (Object.hasOwn(props, property))
8425
8579
  throw new Error(
@@ -8432,13 +8586,7 @@ function parseFlangedBushingModelParams(raw) {
8432
8586
  var model21 = defineModel({
8433
8587
  name: "flangedbushing",
8434
8588
  schema: flangedBushingModelDefinitionSchema,
8435
- parse: parseFlangedBushingModelParams,
8436
- normalizeString: (value) => normalizeModelStringFlags(
8437
- value,
8438
- stringFlags6,
8439
- parseFlangedBushingModelParams,
8440
- omittedStringFlags6
8441
- )
8589
+ parse: parseFlangedBushingModelParams
8442
8590
  });
8443
8591
  function register21(registry2) {
8444
8592
  registry2.register(model21);
@@ -8796,10 +8944,7 @@ var lengths13 = {
8796
8944
  outerdiameter: "outerDiameter",
8797
8945
  thickness: "thickness"
8798
8946
  };
8799
- var flags3 = { flatannulus: "flatAnnulus" };
8800
- var selectors2 = {
8801
- profile: { value: "flatannulus", property: "flatAnnulus" }
8802
- };
8947
+ var flags4 = { flatannulus: "flatAnnulus" };
8803
8948
  var integers = {};
8804
8949
  var tuples = {};
8805
8950
  function parseFlatGasketModelParams(raw) {
@@ -8817,9 +8962,7 @@ function parseFlatGasketModelParams(raw) {
8817
8962
  if (!match) throw new Error(`Malformed flatgasket token "${token}"`);
8818
8963
  const key = match[1].toLowerCase(), value = match[2];
8819
8964
  if (Object.hasOwn(lengths13, key) && value) assign(lengths13[key], value);
8820
- else if (Object.hasOwn(flags3, key) && !value) assign(flags3[key], true);
8821
- else if (Object.hasOwn(selectors2, key) && value.toLowerCase() === `(${selectors2[key].value})`)
8822
- assign(selectors2[key].property, true);
8965
+ else if (Object.hasOwn(flags4, key) && !value) assign(flags4[key], true);
8823
8966
  else if (Object.hasOwn(integers, key) && /^\d+$/.test(value))
8824
8967
  assign(integers[key], Number(value));
8825
8968
  else if (Object.hasOwn(tuples, key)) {
@@ -8840,14 +8983,6 @@ var model24 = defineModel({
8840
8983
  function register24(registry2) {
8841
8984
  registry2.register(model24);
8842
8985
  }
8843
- var stringFlags7 = {
8844
- hexsocket: "drive(hexsocket)",
8845
- fullthread: "thread(full)",
8846
- lefthanded: "threadhand(left)",
8847
- righthanded: "threadhand(right)",
8848
- male: "threadgender(male)"
8849
- };
8850
- var omittedStringFlags7 = ["righthanded", "male"];
8851
8986
  var positiveModelLengthSchema3 = external_exports.union([
8852
8987
  external_exports.number(),
8853
8988
  external_exports.string().regex(/^(?:\d+(?:\.\d+)?|\.\d+)(?:mm|cm|m|in|inch)?$/i).transform((value) => value.toLowerCase())
@@ -8923,11 +9058,11 @@ var flatHeadScrewModelPropsShape = {
8923
9058
  iso10642: external_exports.literal(true).default(true),
8924
9059
  metricSize: flatHeadScrewMetricSizeSchema,
8925
9060
  length: positiveModelLengthSchema3,
8926
- thread: external_exports.literal("full").default("full"),
8927
- drive: external_exports.literal("hexsocket").default("hexsocket"),
8928
- threadHand: external_exports.enum(["right", "left"]).default("right"),
9061
+ fullThread: external_exports.literal(true).default(true),
9062
+ hexSocket: external_exports.literal(true).default(true),
9063
+ leftHanded: external_exports.boolean().default(false),
8929
9064
  threadClass: external_exports.literal("6g").default("6g"),
8930
- threadGender: external_exports.literal("male").default("male"),
9065
+ male: external_exports.literal(true).default(true),
8931
9066
  showThreads: external_exports.boolean().default(true),
8932
9067
  diameter: positiveModelLengthSchema3.optional(),
8933
9068
  threadPitch: positiveModelLengthSchema3.optional(),
@@ -8993,11 +9128,7 @@ var aliases5 = {
8993
9128
  metricsize: "metricSize",
8994
9129
  l: "length",
8995
9130
  length: "length",
8996
- thread: "thread",
8997
- drive: "drive",
8998
- threadhand: "threadHand",
8999
9131
  threadclass: "threadClass",
9000
- threadgender: "threadGender",
9001
9132
  diameter: "diameter",
9002
9133
  threadpitch: "threadPitch",
9003
9134
  headdiameter: "headDiameter",
@@ -9018,19 +9149,18 @@ var aliases5 = {
9018
9149
  socketaf: "socketAcrossFlats",
9019
9150
  socketh: "socketDepth"
9020
9151
  };
9021
- var selectors3 = /* @__PURE__ */ new Set([
9022
- "thread",
9023
- "drive",
9024
- "threadHand",
9025
- "threadClass",
9026
- "threadGender"
9027
- ]);
9152
+ var selectors2 = /* @__PURE__ */ new Set(["threadClass"]);
9153
+ var booleanFlags2 = {
9154
+ hexsocket: { property: "hexSocket", value: true },
9155
+ fullthread: { property: "fullThread", value: true },
9156
+ male: { property: "male", value: true },
9157
+ lefthanded: { property: "leftHanded", value: true },
9158
+ righthanded: { property: "leftHanded", value: false }
9159
+ };
9028
9160
  var parseFlatHeadScrewModelParams = (raw) => {
9029
9161
  if (raw.fn !== "flatheadscrew")
9030
9162
  throw new Error(`Expected flatheadscrew params, got "${raw.fn}"`);
9031
- const [root, ...tokens] = splitModelStringTokens(
9032
- expandModelStringFlags(raw.string, stringFlags7)
9033
- );
9163
+ const [root, ...tokens] = splitModelStringTokens(raw.string);
9034
9164
  if (root?.toLowerCase() !== "flatheadscrew")
9035
9165
  throw new Error(
9036
9166
  "The flatheadscrew function does not accept an inline value"
@@ -9048,16 +9178,20 @@ var parseFlatHeadScrewModelParams = (raw) => {
9048
9178
  const value = match?.[2];
9049
9179
  let property;
9050
9180
  let parsed;
9051
- if (key === "threads" || key === "nothreads") {
9181
+ if (key && Object.hasOwn(booleanFlags2, key)) {
9182
+ if (value !== "") throw new Error(`Flag "${key}" does not accept a value`);
9183
+ property = booleanFlags2[key].property;
9184
+ parsed = booleanFlags2[key].value;
9185
+ } else if (key === "threads" || key === "nothreads") {
9052
9186
  if (value !== "")
9053
9187
  throw new Error(`Token "${key}" does not accept a value`);
9054
9188
  property = "showThreads";
9055
9189
  parsed = key === "threads";
9056
9190
  } else {
9057
- property = key ? aliases5[key] ?? "" : "";
9191
+ property = key && Object.hasOwn(aliases5, key) ? aliases5[key] : "";
9058
9192
  if (!property || !value)
9059
9193
  throw new Error(`Unknown or missing flatheadscrew token "${token}"`);
9060
- if (selectors3.has(property)) {
9194
+ if (selectors2.has(property)) {
9061
9195
  const selector = value.match(/^\(([^()]+)\)$/)?.[1];
9062
9196
  if (!selector)
9063
9197
  throw new Error(`Token "${key}" requires a parenthesized selector`);
@@ -9087,13 +9221,7 @@ var parseFlatHeadScrewModelParams = (raw) => {
9087
9221
  var model25 = defineModel({
9088
9222
  name: "flatheadscrew",
9089
9223
  schema: flatHeadScrewModelDefinitionSchema,
9090
- parse: parseFlatHeadScrewModelParams,
9091
- normalizeString: (value) => normalizeModelStringFlags(
9092
- value,
9093
- stringFlags7,
9094
- parseFlatHeadScrewModelParams,
9095
- omittedStringFlags7
9096
- )
9224
+ parse: parseFlatHeadScrewModelParams
9097
9225
  });
9098
9226
  function register25(registry2) {
9099
9227
  registry2.register(model25);
@@ -9254,7 +9382,7 @@ var aliases7 = {
9254
9382
  threadpitchdiameter: "threadPitchDiameter",
9255
9383
  rootouterdiameter: "rootOuterDiameter"
9256
9384
  };
9257
- var flags4 = {
9385
+ var flags5 = {
9258
9386
  diamondknurl: "diamondKnurl",
9259
9387
  lefthanded: "leftHand",
9260
9388
  righthanded: "leftHand",
@@ -9271,9 +9399,9 @@ function parseHeatSetInsertModelParams(raw) {
9271
9399
  for (const token of tokens) {
9272
9400
  const match = token.match(/^([a-z]+)(.*)$/i), key = match?.[1]?.toLowerCase() ?? "", value = match?.[2] ?? "";
9273
9401
  let property, parsed;
9274
- if (flags4[key]) {
9402
+ if (flags5[key]) {
9275
9403
  if (value) throw new Error("Flags do not accept values");
9276
- property = flags4[key];
9404
+ property = flags5[key];
9277
9405
  parsed = !["righthanded", "nothreads"].includes(key);
9278
9406
  } else {
9279
9407
  property = aliases7[key] ?? "";
@@ -9310,10 +9438,6 @@ var model27 = defineModel({
9310
9438
  function register27(registry2) {
9311
9439
  registry2.register(model27);
9312
9440
  }
9313
- var stringFlags8 = {
9314
- righthanded: "threadhand(right)"
9315
- };
9316
- var omittedStringFlags8 = ["righthanded"];
9317
9441
  var hexNutDimensions = {
9318
9442
  M5: {
9319
9443
  diameter: 5,
@@ -9554,7 +9678,7 @@ var shape23 = {
9554
9678
  metricSize: hexNutMetricSizeSchema.optional(),
9555
9679
  imperialSize: hexNutImperialSizeSchema.optional(),
9556
9680
  threadPitch: length13.optional(),
9557
- threadHand: external_exports.literal("right").default("right"),
9681
+ rightHanded: external_exports.literal(true).default(true),
9558
9682
  threadClass: external_exports.enum(["6H", "2B"]).optional(),
9559
9683
  showThreads: external_exports.boolean().default(true)
9560
9684
  };
@@ -9580,11 +9704,11 @@ function validate22(props, context) {
9580
9704
  const issue = (path, message) => context.addIssue({ code: "custom", path: [path], message });
9581
9705
  if (!!props.metricSize === !!props.imperialSize)
9582
9706
  issue("metricSize", "Select exactly one metric or imperial size");
9583
- const flags10 = ["iso4032", "din934", "asmeb1822"];
9707
+ const flags11 = ["iso4032", "din934", "asmeb1822"];
9584
9708
  const family = selectedFamily(props);
9585
- if (flags10.filter((flag) => props[flag] === true).length > 1)
9709
+ if (flags11.filter((flag) => props[flag] === true).length > 1)
9586
9710
  issue("iso4032", "Select only one ISO, DIN or ASME family");
9587
- for (const flag of flags10)
9711
+ for (const flag of flags11)
9588
9712
  if (family[flag] && props[flag] === false)
9589
9713
  issue(
9590
9714
  flag,
@@ -9632,14 +9756,11 @@ function getHexNutDimensions(input) {
9632
9756
  };
9633
9757
  }
9634
9758
  function parseHexNutModelParams(raw) {
9635
- const tokens = splitModelStringTokens(
9636
- expandModelStringFlags(raw.string, stringFlags8)
9637
- );
9759
+ const tokens = splitModelStringTokens(raw.string);
9638
9760
  if (raw.fn !== "hexnut" || tokens[0]?.toLowerCase() !== "hexnut")
9639
9761
  throw new Error("Expected hexnut without an inline argument");
9640
9762
  const props = { fn: "hexnut" };
9641
- const selectors7 = {
9642
- threadhand: "threadHand",
9763
+ const selectors5 = {
9643
9764
  threadclass: "threadClass"
9644
9765
  };
9645
9766
  let explicitFamily = false;
@@ -9676,9 +9797,13 @@ function parseHexNutModelParams(raw) {
9676
9797
  throw new Error("Duplicate hex nut property threadPitch");
9677
9798
  props.threadPitch = 25.4 / Number(parts[1]);
9678
9799
  }
9800
+ } else if (name === "righthanded") {
9801
+ if (value) throw new Error("Handedness flags cannot have a value");
9802
+ property = "rightHanded";
9803
+ parsed = true;
9679
9804
  } else if (name === "threadpitch") property = "threadPitch";
9680
- else if (name in selectors7) {
9681
- property = selectors7[name];
9805
+ else if (name in selectors5) {
9806
+ property = selectors5[name];
9682
9807
  if (!/^\([^()]+\)$/.test(value))
9683
9808
  throw new Error(`Expected a parenthesized ${name}`);
9684
9809
  parsed = value.slice(1, -1).toLowerCase();
@@ -9697,13 +9822,7 @@ function parseHexNutModelParams(raw) {
9697
9822
  var model28 = defineModel({
9698
9823
  name: "hexnut",
9699
9824
  schema: hexNutModelDefinitionSchema,
9700
- parse: parseHexNutModelParams,
9701
- normalizeString: (value) => normalizeModelStringFlags(
9702
- value,
9703
- stringFlags8,
9704
- parseHexNutModelParams,
9705
- omittedStringFlags8
9706
- )
9825
+ parse: parseHexNutModelParams
9707
9826
  });
9708
9827
  function register28(registry2) {
9709
9828
  registry2.register(model28);
@@ -9965,7 +10084,7 @@ var lengths15 = {
9965
10084
  studchamfer: "studChamfer",
9966
10085
  mouthchamfer: "mouthChamfer"
9967
10086
  };
9968
- var flags5 = {
10087
+ var flags6 = {
9969
10088
  hex: ["hex", true],
9970
10089
  lefthanded: ["leftHand", true],
9971
10090
  righthanded: ["leftHand", false],
@@ -9981,8 +10100,8 @@ function parseMaleFemaleStandoffModelParams(raw) {
9981
10100
  const lower = token.toLowerCase();
9982
10101
  let property;
9983
10102
  let value;
9984
- if (Object.hasOwn(flags5, lower)) {
9985
- const entry = flags5[lower];
10103
+ if (Object.hasOwn(flags6, lower)) {
10104
+ const entry = flags6[lower];
9986
10105
  property = entry[0];
9987
10106
  value = entry[1];
9988
10107
  } else {
@@ -10167,14 +10286,6 @@ var model31 = defineModel({
10167
10286
  function register31(registry2) {
10168
10287
  registry2.register(model31);
10169
10288
  }
10170
- var stringFlags9 = {
10171
- phillips: "drive(phillips)",
10172
- fullthread: "thread(full)",
10173
- lefthanded: "threadhand(left)",
10174
- righthanded: "threadhand(right)",
10175
- male: "threadgender(male)"
10176
- };
10177
- var omittedStringFlags9 = ["righthanded", "male"];
10178
10289
  var positiveModelLengthSchema4 = external_exports.union([
10179
10290
  external_exports.number(),
10180
10291
  external_exports.string().regex(/^(?:\d+(?:\.\d+)?|\.\d+)(?:mm|cm|m|in|inch)?$/i).transform((value) => value.toLowerCase())
@@ -10298,11 +10409,11 @@ var panScrewModelPropsShape = {
10298
10409
  iso7045: external_exports.literal(true).default(true),
10299
10410
  metricSize: panScrewMetricSizeSchema,
10300
10411
  length: positiveModelLengthSchema4,
10301
- thread: external_exports.literal("full").default("full"),
10302
- drive: external_exports.literal("phillips").default("phillips"),
10303
- threadHand: external_exports.enum(["right", "left"]).default("right"),
10412
+ fullThread: external_exports.literal(true).default(true),
10413
+ phillips: external_exports.literal(true).default(true),
10414
+ leftHanded: external_exports.boolean().default(false),
10304
10415
  threadClass: external_exports.literal("6g").default("6g"),
10305
- threadGender: external_exports.literal("male").default("male"),
10416
+ male: external_exports.literal(true).default(true),
10306
10417
  showThreads: external_exports.boolean().default(true),
10307
10418
  iso4757: external_exports.literal(true).default(true),
10308
10419
  recessType: external_exports.literal("H").default("H"),
@@ -10369,11 +10480,7 @@ var aliases8 = {
10369
10480
  metricsize: "metricSize",
10370
10481
  l: "length",
10371
10482
  length: "length",
10372
- thread: "thread",
10373
- drive: "drive",
10374
- threadhand: "threadHand",
10375
10483
  threadclass: "threadClass",
10376
- threadgender: "threadGender",
10377
10484
  diameter: "diameter",
10378
10485
  threadpitch: "threadPitch",
10379
10486
  headdiameter: "headDiameter",
@@ -10404,19 +10511,18 @@ var aliases8 = {
10404
10511
  headh: "headHeight",
10405
10512
  headd: "headDiameter"
10406
10513
  };
10407
- var selectors4 = /* @__PURE__ */ new Set([
10408
- "thread",
10409
- "drive",
10410
- "threadHand",
10411
- "threadClass",
10412
- "threadGender"
10413
- ]);
10514
+ var selectors3 = /* @__PURE__ */ new Set(["threadClass"]);
10515
+ var booleanFlags3 = {
10516
+ phillips: { property: "phillips", value: true },
10517
+ fullthread: { property: "fullThread", value: true },
10518
+ male: { property: "male", value: true },
10519
+ lefthanded: { property: "leftHanded", value: true },
10520
+ righthanded: { property: "leftHanded", value: false }
10521
+ };
10414
10522
  var parsePanScrewModelParams = (raw) => {
10415
10523
  if (raw.fn !== "panscrew")
10416
10524
  throw new Error(`Expected panscrew params, got "${raw.fn}"`);
10417
- const [root, ...tokens] = splitModelStringTokens(
10418
- expandModelStringFlags(raw.string, stringFlags9)
10419
- );
10525
+ const [root, ...tokens] = splitModelStringTokens(raw.string);
10420
10526
  if (root?.toLowerCase() !== "panscrew")
10421
10527
  throw new Error("The panscrew function does not accept an inline value");
10422
10528
  const props = {};
@@ -10433,16 +10539,20 @@ var parsePanScrewModelParams = (raw) => {
10433
10539
  const value = match?.[2];
10434
10540
  let property;
10435
10541
  let parsed;
10436
- if (key === "threads" || key === "nothreads") {
10542
+ if (key && Object.hasOwn(booleanFlags3, key)) {
10543
+ if (value !== "") throw new Error(`Flag "${key}" does not accept a value`);
10544
+ property = booleanFlags3[key].property;
10545
+ parsed = booleanFlags3[key].value;
10546
+ } else if (key === "threads" || key === "nothreads") {
10437
10547
  if (value !== "")
10438
10548
  throw new Error(`Token "${key}" does not accept a value`);
10439
10549
  property = "showThreads";
10440
10550
  parsed = key === "threads";
10441
10551
  } else {
10442
- property = key ? aliases8[key] ?? "" : "";
10552
+ property = key && Object.hasOwn(aliases8, key) ? aliases8[key] : "";
10443
10553
  if (!property || !value)
10444
10554
  throw new Error(`Unknown or missing panscrew token "${token}"`);
10445
- if (selectors4.has(property)) {
10555
+ if (selectors3.has(property)) {
10446
10556
  const selector = value.match(/^\(([^()]+)\)$/)?.[1];
10447
10557
  if (!selector)
10448
10558
  throw new Error(`Token "${key}" requires a parenthesized selector`);
@@ -10475,13 +10585,7 @@ var parsePanScrewModelParams = (raw) => {
10475
10585
  var model32 = defineModel({
10476
10586
  name: "panscrew",
10477
10587
  schema: panScrewModelDefinitionSchema,
10478
- parse: parsePanScrewModelParams,
10479
- normalizeString: (value) => normalizeModelStringFlags(
10480
- value,
10481
- stringFlags9,
10482
- parsePanScrewModelParams,
10483
- omittedStringFlags9
10484
- )
10588
+ parse: parsePanScrewModelParams
10485
10589
  });
10486
10590
  function register32(registry2) {
10487
10591
  registry2.register(model32);
@@ -10667,10 +10771,6 @@ var model34 = defineModel({
10667
10771
  function register34(registry2) {
10668
10772
  registry2.register(model34);
10669
10773
  }
10670
- var stringFlags10 = {
10671
- plainclosed: "style(plainclosed)"
10672
- };
10673
- var omittedStringFlags10 = ["plainclosed"];
10674
10774
  var length18 = external_exports.union([
10675
10775
  external_exports.number(),
10676
10776
  external_exports.string().regex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:mm|cm|m|in|inch|mil|ft|feet)?$/i).transform((value) => value.toLowerCase())
@@ -10681,7 +10781,7 @@ var shape29 = {
10681
10781
  outerDiameter: positiveLength12,
10682
10782
  /** Overall end-plane distance, including the chamfers. */
10683
10783
  length: positiveLength12,
10684
- style: external_exports.literal("plainclosed").default("plainclosed"),
10784
+ plainClosed: external_exports.literal(true).default(true),
10685
10785
  /** Equal axial/radial setbacks of all four 45-degree bore/outside rim chamfers. */
10686
10786
  edgeChamfer: length18.refine((value) => value >= 0).default(0)
10687
10787
  };
@@ -10729,9 +10829,7 @@ var lengths16 = {
10729
10829
  edgechamfer: "edgeChamfer"
10730
10830
  };
10731
10831
  function parsePlainBushingModelParams(raw) {
10732
- const tokens = splitModelStringTokens(
10733
- expandModelStringFlags(raw.string, stringFlags10)
10734
- );
10832
+ const tokens = splitModelStringTokens(raw.string);
10735
10833
  if (raw.fn !== "plainbushing" || tokens[0]?.toLowerCase() !== "plainbushing")
10736
10834
  throw new Error("Expected plainbushing without an inline value");
10737
10835
  const props = { fn: "plainbushing" };
@@ -10745,11 +10843,10 @@ function parsePlainBushingModelParams(raw) {
10745
10843
  if (Object.hasOwn(lengths16, name)) {
10746
10844
  property = lengths16[name];
10747
10845
  parsed = value;
10748
- } else if (name === "style") {
10749
- property = "style";
10750
- const style = value.match(/^\(([a-z]+)\)$/i);
10751
- if (!style) throw new Error("Style requires a parenthesized value");
10752
- parsed = style[1].toLowerCase();
10846
+ } else if (name === "plainclosed") {
10847
+ property = "plainClosed";
10848
+ if (value !== "") throw new Error("plainclosed is a value-free flag");
10849
+ parsed = true;
10753
10850
  } else throw new Error(`Unknown plain bushing token "${token}"`);
10754
10851
  if (Object.hasOwn(props, property))
10755
10852
  throw new Error(
@@ -10762,13 +10859,7 @@ function parsePlainBushingModelParams(raw) {
10762
10859
  var model35 = defineModel({
10763
10860
  name: "plainbushing",
10764
10861
  schema: plainBushingModelDefinitionSchema,
10765
- parse: parsePlainBushingModelParams,
10766
- normalizeString: (value) => normalizeModelStringFlags(
10767
- value,
10768
- stringFlags10,
10769
- parsePlainBushingModelParams,
10770
- omittedStringFlags10
10771
- )
10862
+ parse: parsePlainBushingModelParams
10772
10863
  });
10773
10864
  function register35(registry2) {
10774
10865
  registry2.register(model35);
@@ -10966,10 +11057,7 @@ var lengths17 = {
10966
11057
  thickness: "thickness",
10967
11058
  cornerradius: "cornerRadius"
10968
11059
  };
10969
- var flags6 = { flatframe: "flatFrame" };
10970
- var selectors5 = {
10971
- profile: { value: "flatframe", property: "flatFrame" }
10972
- };
11060
+ var flags7 = { flatframe: "flatFrame" };
10973
11061
  var integers2 = {};
10974
11062
  var tuples2 = {};
10975
11063
  function parseRectangularGasketModelParams(raw) {
@@ -10987,9 +11075,7 @@ function parseRectangularGasketModelParams(raw) {
10987
11075
  if (!match) throw new Error(`Malformed rectangulargasket token "${token}"`);
10988
11076
  const key = match[1].toLowerCase(), value = match[2];
10989
11077
  if (Object.hasOwn(lengths17, key) && value) assign(lengths17[key], value);
10990
- else if (Object.hasOwn(flags6, key) && !value) assign(flags6[key], true);
10991
- else if (Object.hasOwn(selectors5, key) && value.toLowerCase() === `(${selectors5[key].value})`)
10992
- assign(selectors5[key].property, true);
11078
+ else if (Object.hasOwn(flags7, key) && !value) assign(flags7[key], true);
10993
11079
  else if (Object.hasOwn(integers2, key) && /^\d+$/.test(value))
10994
11080
  assign(integers2[key], Number(value));
10995
11081
  else if (Object.hasOwn(tuples2, key)) {
@@ -11095,12 +11181,6 @@ var model39 = defineModel({
11095
11181
  function register39(registry2) {
11096
11182
  registry2.register(model39);
11097
11183
  }
11098
- var stringFlags11 = {
11099
- setscrew: "mount(setscrew)",
11100
- lefthanded: "threadhand(left)",
11101
- righthanded: "threadhand(right)"
11102
- };
11103
- var omittedStringFlags11 = ["righthanded"];
11104
11184
  var length23 = external_exports.union([
11105
11185
  external_exports.number().finite(),
11106
11186
  external_exports.string().regex(
@@ -11121,10 +11201,10 @@ var shape34 = {
11121
11201
  screwCount: external_exports.literal(4).default(4),
11122
11202
  screwEndOffset: positive6.optional(),
11123
11203
  screwAngle: external_exports.number().finite().default(0),
11124
- mount: external_exports.literal("setscrew").default("setscrew"),
11204
+ setScrew: external_exports.literal(true).default(true),
11125
11205
  metricSize: metricBoltSizeSchema,
11126
11206
  threadPitch: positive6.optional(),
11127
- threadHand: external_exports.enum(["right", "left"]).default("right"),
11207
+ leftHanded: external_exports.boolean().default(false),
11128
11208
  threadClass: external_exports.literal("6H").default("6H"),
11129
11209
  chamfer: nonnegative5.default(0)
11130
11210
  };
@@ -11193,7 +11273,7 @@ function getRigidCouplerDimensions(input) {
11193
11273
  diameter: threadDiameter,
11194
11274
  depth: radius - Math.sqrt((hole.bore / 2) ** 2 - (threadDiameter / 2) ** 2),
11195
11275
  threadPitch: p.threadPitch,
11196
- threadHand: p.threadHand,
11276
+ leftHanded: p.leftHanded,
11197
11277
  threadGender: "female",
11198
11278
  threadClass: p.threadClass
11199
11279
  };
@@ -11208,10 +11288,8 @@ function getRigidCouplerDimensions(input) {
11208
11288
  var tokenProperties2 = {
11209
11289
  bore: "boreDiameter",
11210
11290
  od: "outerDiameter",
11211
- mount: "mount",
11212
11291
  m: "metricSize",
11213
11292
  threadpitch: "threadPitch",
11214
- threadhand: "threadHand",
11215
11293
  threadclass: "threadClass",
11216
11294
  chamfer: "chamfer",
11217
11295
  boreb: "boreBDiameter",
@@ -11222,26 +11300,34 @@ var tokenProperties2 = {
11222
11300
  screwangle: "screwAngle"
11223
11301
  };
11224
11302
  function parseRigidCouplerModelParams(raw) {
11225
- const tokens = splitModelStringTokens(
11226
- expandModelStringFlags(raw.string, stringFlags11)
11227
- );
11303
+ const tokens = splitModelStringTokens(raw.string);
11228
11304
  if (raw.fn !== "rigidcoupler" || tokens[0]?.toLowerCase() !== "rigidcoupler")
11229
11305
  throw new Error("Expected an unadorned rigidcoupler function");
11230
11306
  const props = {};
11231
11307
  for (const token of tokens.slice(1)) {
11232
11308
  const match = token.match(/^([a-z]+)(.*)$/i);
11233
11309
  const name = match?.[1]?.toLowerCase() ?? "";
11310
+ const value = match?.[2] ?? "";
11311
+ if (["setscrew", "lefthanded", "righthanded"].includes(name)) {
11312
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
11313
+ const property2 = name === "setscrew" ? "setScrew" : "leftHanded";
11314
+ if (property2 in props)
11315
+ throw new Error(
11316
+ `Duplicate or conflicting rigidcoupler flag "${property2}"`
11317
+ );
11318
+ props[property2] = name !== "righthanded";
11319
+ continue;
11320
+ }
11234
11321
  const property = tokenProperties2[name];
11235
11322
  if (!property) throw new Error(`Unknown rigidcoupler token "${token}"`);
11236
11323
  if (property in props)
11237
11324
  throw new Error(`Duplicate rigidcoupler property "${property}"`);
11238
- const value = match[2];
11239
11325
  if (!value) throw new Error(`Token "${name}" requires a value`);
11240
- if (["mount", "threadHand", "threadClass"].includes(property)) {
11326
+ if (property === "threadClass") {
11241
11327
  const argument = value.match(/^\(([^()]+)\)$/);
11242
11328
  if (!argument)
11243
11329
  throw new Error(`Token "${name}" requires one parenthesized argument`);
11244
- props[property] = property === "threadClass" ? argument[1].toUpperCase() : argument[1].toLowerCase();
11330
+ props[property] = argument[1].toUpperCase();
11245
11331
  } else if (property === "metricSize") {
11246
11332
  if (!/^\d+(?:\.\d+)?$/.test(value))
11247
11333
  throw new Error("m requires a unitless metric designation");
@@ -11266,13 +11352,7 @@ function parseRigidCouplerModelParams(raw) {
11266
11352
  var model40 = defineModel({
11267
11353
  name: "rigidcoupler",
11268
11354
  schema: rigidCouplerModelDefinitionSchema,
11269
- parse: parseRigidCouplerModelParams,
11270
- normalizeString: (value) => normalizeModelStringFlags(
11271
- value,
11272
- stringFlags11,
11273
- parseRigidCouplerModelParams,
11274
- omittedStringFlags11
11275
- )
11355
+ parse: parseRigidCouplerModelParams
11276
11356
  });
11277
11357
  function register40(registry2) {
11278
11358
  registry2.register(model40);
@@ -11388,7 +11468,7 @@ var aliases14 = {
11388
11468
  threadrootdiameter: "threadRootDiameter",
11389
11469
  threadpitchdiameter: "threadPitchDiameter"
11390
11470
  };
11391
- var flags7 = {
11471
+ var flags8 = {
11392
11472
  hexsocket: "hexSocket",
11393
11473
  cuppoint: "cupPoint",
11394
11474
  lefthanded: "leftHand",
@@ -11414,9 +11494,9 @@ function parseSetScrewModelParams(raw) {
11414
11494
  const value = match?.[2] ?? "";
11415
11495
  let property;
11416
11496
  let parsed;
11417
- if (flags7[key]) {
11497
+ if (flags8[key]) {
11418
11498
  if (value) throw new Error(`Flag ${key} does not accept a value`);
11419
- property = flags7[key];
11499
+ property = flags8[key];
11420
11500
  parsed = !["righthanded", "nothreads"].includes(key);
11421
11501
  } else {
11422
11502
  property = aliases14[key] ?? "";
@@ -11450,12 +11530,6 @@ var model41 = defineModel({
11450
11530
  function register41(registry2) {
11451
11531
  registry2.register(model41);
11452
11532
  }
11453
- var stringFlags12 = {
11454
- setscrew: "mount(setscrew)",
11455
- lefthanded: "threadhand(left)",
11456
- righthanded: "threadhand(right)"
11457
- };
11458
- var omittedStringFlags12 = ["righthanded"];
11459
11533
  var length24 = external_exports.union([
11460
11534
  external_exports.number().finite(),
11461
11535
  external_exports.string().regex(
@@ -11474,10 +11548,10 @@ var shape36 = {
11474
11548
  width: positive7,
11475
11549
  screwZ: positive7.optional(),
11476
11550
  screwAngle: external_exports.number().finite().default(0),
11477
- mount: external_exports.literal("setscrew").default("setscrew"),
11551
+ setScrew: external_exports.literal(true).default(true),
11478
11552
  metricSize: metricBoltSizeSchema,
11479
11553
  threadPitch: positive7.optional(),
11480
- threadHand: external_exports.enum(["right", "left"]).default("right"),
11554
+ leftHanded: external_exports.boolean().default(false),
11481
11555
  threadClass: external_exports.literal("6H").default("6H"),
11482
11556
  chamfer: nonnegative6.default(0)
11483
11557
  };
@@ -11527,7 +11601,7 @@ function getShaftCollarDimensions(input) {
11527
11601
  diameter: threadDiameter,
11528
11602
  depth: radius - Math.sqrt((p.boreDiameter / 2) ** 2 - (threadDiameter / 2) ** 2),
11529
11603
  threadPitch: p.threadPitch,
11530
- threadHand: p.threadHand,
11604
+ leftHanded: p.leftHanded,
11531
11605
  threadGender: "female",
11532
11606
  threadClass: p.threadClass
11533
11607
  }
@@ -11536,10 +11610,8 @@ function getShaftCollarDimensions(input) {
11536
11610
  var tokenProperties3 = {
11537
11611
  bore: "boreDiameter",
11538
11612
  od: "outerDiameter",
11539
- mount: "mount",
11540
11613
  m: "metricSize",
11541
11614
  threadpitch: "threadPitch",
11542
- threadhand: "threadHand",
11543
11615
  threadclass: "threadClass",
11544
11616
  chamfer: "chamfer",
11545
11617
  w: "width",
@@ -11548,26 +11620,34 @@ var tokenProperties3 = {
11548
11620
  screwangle: "screwAngle"
11549
11621
  };
11550
11622
  function parseShaftCollarModelParams(raw) {
11551
- const tokens = splitModelStringTokens(
11552
- expandModelStringFlags(raw.string, stringFlags12)
11553
- );
11623
+ const tokens = splitModelStringTokens(raw.string);
11554
11624
  if (raw.fn !== "shaftcollar" || tokens[0]?.toLowerCase() !== "shaftcollar")
11555
11625
  throw new Error("Expected an unadorned shaftcollar function");
11556
11626
  const props = {};
11557
11627
  for (const token of tokens.slice(1)) {
11558
11628
  const match = token.match(/^([a-z]+)(.*)$/i);
11559
11629
  const name = match?.[1]?.toLowerCase() ?? "";
11630
+ const value = match?.[2] ?? "";
11631
+ if (["setscrew", "lefthanded", "righthanded"].includes(name)) {
11632
+ if (value) throw new Error(`Flag "${name}" does not take a value`);
11633
+ const property2 = name === "setscrew" ? "setScrew" : "leftHanded";
11634
+ if (property2 in props)
11635
+ throw new Error(
11636
+ `Duplicate or conflicting shaftcollar flag "${property2}"`
11637
+ );
11638
+ props[property2] = name !== "righthanded";
11639
+ continue;
11640
+ }
11560
11641
  const property = tokenProperties3[name];
11561
11642
  if (!property) throw new Error(`Unknown shaftcollar token "${token}"`);
11562
11643
  if (property in props)
11563
11644
  throw new Error(`Duplicate shaftcollar property "${property}"`);
11564
- const value = match[2];
11565
11645
  if (!value) throw new Error(`Token "${name}" requires a value`);
11566
- if (["mount", "threadHand", "threadClass"].includes(property)) {
11646
+ if (property === "threadClass") {
11567
11647
  const argument = value.match(/^\(([^()]+)\)$/);
11568
11648
  if (!argument)
11569
11649
  throw new Error(`Token "${name}" requires one parenthesized argument`);
11570
- props[property] = property === "threadClass" ? argument[1].toUpperCase() : argument[1].toLowerCase();
11650
+ props[property] = argument[1].toUpperCase();
11571
11651
  } else if (property === "metricSize") {
11572
11652
  if (!/^\d+(?:\.\d+)?$/.test(value))
11573
11653
  throw new Error("m requires a unitless metric designation");
@@ -11585,13 +11665,7 @@ function parseShaftCollarModelParams(raw) {
11585
11665
  var model42 = defineModel({
11586
11666
  name: "shaftcollar",
11587
11667
  schema: shaftCollarModelDefinitionSchema,
11588
- parse: parseShaftCollarModelParams,
11589
- normalizeString: (value) => normalizeModelStringFlags(
11590
- value,
11591
- stringFlags12,
11592
- parseShaftCollarModelParams,
11593
- omittedStringFlags12
11594
- )
11668
+ parse: parseShaftCollarModelParams
11595
11669
  });
11596
11670
  function register42(registry2) {
11597
11671
  registry2.register(model42);
@@ -11861,8 +11935,8 @@ var lengths18 = {
11861
11935
  groovedepth: "grooveDepth",
11862
11936
  splitwidth: "splitWidth"
11863
11937
  };
11864
- var flags8 = {};
11865
- var selectors6 = {};
11938
+ var flags9 = {};
11939
+ var selectors4 = {};
11866
11940
  var integers3 = {};
11867
11941
  var tuples3 = {};
11868
11942
  function parseSplitGrommetModelParams(raw) {
@@ -11880,9 +11954,9 @@ function parseSplitGrommetModelParams(raw) {
11880
11954
  if (!match) throw new Error(`Malformed splitgrommet token "${token}"`);
11881
11955
  const key = match[1].toLowerCase(), value = match[2];
11882
11956
  if (Object.hasOwn(lengths18, key) && value) assign(lengths18[key], value);
11883
- else if (Object.hasOwn(flags8, key) && !value) assign(flags8[key], true);
11884
- else if (Object.hasOwn(selectors6, key) && value.toLowerCase() === `(${selectors6[key].value})`)
11885
- assign(selectors6[key].property, true);
11957
+ else if (Object.hasOwn(flags9, key) && !value) assign(flags9[key], true);
11958
+ else if (Object.hasOwn(selectors4, key) && value.toLowerCase() === `(${selectors4[key].value})`)
11959
+ assign(selectors4[key].property, true);
11886
11960
  else if (Object.hasOwn(integers3, key) && /^\d+$/.test(value))
11887
11961
  assign(integers3[key], Number(value));
11888
11962
  else if (Object.hasOwn(tuples3, key)) {
@@ -12331,12 +12405,9 @@ var aliases17 = {
12331
12405
  barbh: "barbHeight",
12332
12406
  barbheight: "barbHeight"
12333
12407
  };
12334
- var flags9 = {
12408
+ var flags10 = {
12335
12409
  tee: "tee"
12336
12410
  };
12337
- var literals = {
12338
- profile: ["tee", "tee"]
12339
- };
12340
12411
  function parseTSlotCoverStripModelParams(raw) {
12341
12412
  const [name, ...tokens] = splitModelStringTokens(raw.string);
12342
12413
  if (raw.fn !== "tslotcoverstrip" || name?.toLowerCase() !== "tslotcoverstrip")
@@ -12353,15 +12424,10 @@ function parseTSlotCoverStripModelParams(raw) {
12353
12424
  const value = match?.[2];
12354
12425
  if (!key || value === void 0)
12355
12426
  throw new Error(`Malformed tslotcoverstrip token "${token}"`);
12356
- if (Object.hasOwn(flags9, key)) {
12427
+ if (Object.hasOwn(flags10, key)) {
12357
12428
  if (value !== "")
12358
12429
  throw new Error(`tslotcoverstrip flag "${key}" takes no value`);
12359
- assign(flags9[key], true);
12360
- } else if (Object.hasOwn(literals, key)) {
12361
- const [literal, property] = literals[key];
12362
- if (value.toLowerCase() !== `(${literal})`)
12363
- throw new Error(`Expected tslotcoverstrip ${key}(${literal})`);
12364
- assign(property, true);
12430
+ assign(flags10[key], true);
12365
12431
  } else if (Object.hasOwn(aliases17, key) && value) {
12366
12432
  const property = aliases17[key];
12367
12433
  assign(property, property === "pinCount" ? Number(value) : value);
@@ -12378,15 +12444,11 @@ var model49 = defineModel({
12378
12444
  function register49(registry2) {
12379
12445
  registry2.register(model49);
12380
12446
  }
12381
- var stringFlags13 = {
12382
- fourtsolid: "profile(fourtsolid)"
12383
- };
12384
- var omittedStringFlags13 = [];
12385
12447
  var shape44 = {
12386
12448
  width: positiveGearLengthSchema.default(20),
12387
12449
  height: positiveGearLengthSchema.default(20),
12388
12450
  length: positiveGearLengthSchema.default(100),
12389
- profile: external_exports.literal("fourtsolid").default("fourtsolid"),
12451
+ fourTSolid: external_exports.literal(true).default(true),
12390
12452
  slotWidth: positiveGearLengthSchema.default(6),
12391
12453
  pocketWidth: positiveGearLengthSchema.default(10),
12392
12454
  pocketDepth: positiveGearLengthSchema.default(2),
@@ -12446,9 +12508,7 @@ var lengths22 = {
12446
12508
  cornerradius: "cornerRadius"
12447
12509
  };
12448
12510
  function parseTSlotExtrusionModelParams(raw) {
12449
- const tokens = splitModelStringTokens(
12450
- expandModelStringFlags(raw.string, stringFlags13)
12451
- );
12511
+ const tokens = splitModelStringTokens(raw.string);
12452
12512
  if (raw.fn !== "tslotextrusion" || tokens[0]?.toLowerCase() !== raw.fn)
12453
12513
  throw new Error("Expected tslotextrusion without an inline value");
12454
12514
  const props = { fn: "tslotextrusion" };
@@ -12457,11 +12517,10 @@ function parseTSlotExtrusionModelParams(raw) {
12457
12517
  let property;
12458
12518
  let parsed = value;
12459
12519
  if (name in lengths22) property = lengths22[name];
12460
- else if (name === "profile") {
12461
- property = "profile";
12462
- const match = value.match(/^\(([a-z]+)\)$/i);
12463
- if (!match) throw new Error("Profile requires one parenthesized identity");
12464
- parsed = match[1].toLowerCase();
12520
+ else if (name === "fourtsolid") {
12521
+ property = "fourTSolid";
12522
+ if (value !== "") throw new Error("fourtsolid is a value-free flag");
12523
+ parsed = true;
12465
12524
  } else throw new Error(`Unknown T-slot extrusion token "${token}"`);
12466
12525
  if (property in props)
12467
12526
  throw new Error(`T-slot extrusion property "${property}" is repeated`);
@@ -12472,26 +12531,16 @@ function parseTSlotExtrusionModelParams(raw) {
12472
12531
  var model50 = defineModel({
12473
12532
  name: "tslotextrusion",
12474
12533
  schema: tSlotExtrusionModelDefinitionSchema,
12475
- parse: parseTSlotExtrusionModelParams,
12476
- normalizeString: (value) => normalizeModelStringFlags(
12477
- value,
12478
- stringFlags13,
12479
- parseTSlotExtrusionModelParams,
12480
- omittedStringFlags13
12481
- )
12534
+ parse: parseTSlotExtrusionModelParams
12482
12535
  });
12483
12536
  function register50(registry2) {
12484
12537
  registry2.register(model50);
12485
12538
  }
12486
- var stringFlags14 = {
12487
- righttriangle: "shape(righttriangle)"
12488
- };
12489
- var omittedStringFlags14 = [];
12490
12539
  var shape45 = {
12491
12540
  width: positiveGearLengthSchema.default(40),
12492
12541
  height: positiveGearLengthSchema.default(40),
12493
12542
  thickness: positiveGearLengthSchema.default(4),
12494
- shape: external_exports.literal("righttriangle").default("righttriangle"),
12543
+ rightTriangle: external_exports.literal(true).default(true),
12495
12544
  slotCount: external_exports.literal(2).default(2),
12496
12545
  /** Capsule width and overall length, including both semicircular ends. */
12497
12546
  slot: external_exports.tuple([positiveGearLengthSchema, positiveGearLengthSchema]).default([5, 12]),
@@ -12566,9 +12615,7 @@ var lengths23 = {
12566
12615
  edgemargin: "edgeMargin"
12567
12616
  };
12568
12617
  function parseTSlotGussetModelParams(raw) {
12569
- const tokens = splitModelStringTokens(
12570
- expandModelStringFlags(raw.string, stringFlags14)
12571
- );
12618
+ const tokens = splitModelStringTokens(raw.string);
12572
12619
  if (raw.fn !== "tslotgusset" || tokens[0]?.toLowerCase() !== raw.fn)
12573
12620
  throw new Error("Expected tslotgusset without an inline value");
12574
12621
  const props = { fn: "tslotgusset" };
@@ -12580,11 +12627,10 @@ function parseTSlotGussetModelParams(raw) {
12580
12627
  else if (name === "slots" || name === "slotcount") {
12581
12628
  property = "slotCount";
12582
12629
  parsed = parseGearInteger(value, "slotCount");
12583
- } else if (name === "shape") {
12584
- property = "shape";
12585
- const match = value.match(/^\(([a-z]+)\)$/i);
12586
- if (!match) throw new Error("Shape requires one parenthesized identity");
12587
- parsed = match[1].toLowerCase();
12630
+ } else if (name === "righttriangle") {
12631
+ property = "rightTriangle";
12632
+ if (value !== "") throw new Error("righttriangle is a value-free flag");
12633
+ parsed = true;
12588
12634
  } else if (name === "slot" || name === "centers") {
12589
12635
  property = name;
12590
12636
  const match = value.match(/^\(([^(),]+),([^(),]+)\)$/);
@@ -12600,13 +12646,7 @@ function parseTSlotGussetModelParams(raw) {
12600
12646
  var model51 = defineModel({
12601
12647
  name: "tslotgusset",
12602
12648
  schema: tSlotGussetModelDefinitionSchema,
12603
- parse: parseTSlotGussetModelParams,
12604
- normalizeString: (value) => normalizeModelStringFlags(
12605
- value,
12606
- stringFlags14,
12607
- parseTSlotGussetModelParams,
12608
- omittedStringFlags14
12609
- )
12649
+ parse: parseTSlotGussetModelParams
12610
12650
  });
12611
12651
  function register51(registry2) {
12612
12652
  registry2.register(model51);
@@ -12862,24 +12902,24 @@ function getNemaMotorReferencePoints(input) {
12862
12902
  p.bodyWidth / 2 / Math.max(Math.abs(x), Math.abs(y)),
12863
12903
  (p.bodyWidth - p.faceCornerChamfer) / (Math.abs(x) + Math.abs(y))
12864
12904
  );
12865
- if (p.nemaSize === 34 && p.cornerProfile === "stepped") {
12866
- const h = p.bodyWidth / 2, c = p.bodyCornerChamfer / Math.SQRT2, r = Math.min(4, c / 4);
12867
- const contains = (distance2) => {
12868
- const a = Math.abs(distance2 * x), b = Math.abs(distance2 * y);
12869
- if (a > h || b > h) return false;
12870
- if (c === 0) return true;
12871
- if (a > h - c && b > h - c)
12872
- return a <= h - c + r && b <= h - c + r && Math.hypot(a - (h - c + r), b - (h - c + r)) >= r;
12873
- if (b > h - r && a > h - c - r)
12874
- return Math.hypot(a - (h - c - r), b - (h - r)) <= r;
12875
- if (a > h - r && b > h - c - r)
12876
- return Math.hypot(a - (h - r), b - (h - c - r)) <= r;
12877
- return true;
12905
+ if (p.nemaSize !== 34 || p.stepped) {
12906
+ const profile6 = p.nemaSize === 34 ? {
12907
+ width: p.bodyWidth,
12908
+ chamfer: p.bodyCornerChamfer,
12909
+ stepped: p.stepped,
12910
+ recessRadius: p.bodyCornerRecessRadius,
12911
+ filletRadius: p.bodyCornerFilletRadius
12912
+ } : {
12913
+ width: p.bodyWidth,
12914
+ chamfer: p.rearCornerChamfer,
12915
+ stepped: p.rearStepped,
12916
+ recessRadius: p.rearCornerRecessRadius,
12917
+ filletRadius: p.rearCornerFilletRadius
12878
12918
  };
12879
12919
  let low = 0, high = p.bodyWidth;
12880
12920
  for (let i = 0; i < 50; i++) {
12881
12921
  const mid = (low + high) / 2;
12882
- if (contains(mid)) low = mid;
12922
+ if (isInsideNemaFace(mid * x, mid * y, profile6)) low = mid;
12883
12923
  else high = mid;
12884
12924
  }
12885
12925
  radius = low;
@@ -12906,7 +12946,7 @@ function getNemaMotorReferencePoints(input) {
12906
12946
  },
12907
12947
  direction: { x, y, z: 0 }
12908
12948
  },
12909
- shaftflat: p.shaftShape === "d" ? {
12949
+ shaftflat: p.dShaft ? {
12910
12950
  position: {
12911
12951
  x: (p.shaftDiameter / 2 - p.shaftFlatDepth) * Math.cos(flatRadians),
12912
12952
  y: (p.shaftDiameter / 2 - p.shaftFlatDepth) * Math.sin(flatRadians),
@@ -14486,7 +14526,11 @@ function finishShaftMountMesh(geometry) {
14486
14526
  function createClampingShaftCollarMesh(input) {
14487
14527
  const props = clampingShaftCollarModelPropsSchema.parse(input);
14488
14528
  const dimensions5 = getClampingShaftCollarDimensions(props);
14489
- assertShaftMountResolution([dimensions5.threadedHole]);
14529
+ const threadedHole = {
14530
+ ...dimensions5.threadedHole,
14531
+ threadHand: dimensions5.threadedHole.leftHanded ? "left" : "right"
14532
+ };
14533
+ assertShaftMountResolution([threadedHole]);
14490
14534
  const body = createAnnularSleeve({
14491
14535
  outerRadius: props.outerDiameter / 2,
14492
14536
  boreARadius: props.boreDiameter / 2,
@@ -14512,7 +14556,7 @@ function createClampingShaftCollarMesh(input) {
14512
14556
  body,
14513
14557
  slit,
14514
14558
  createMountingHoleCutter(dimensions5.clearanceHole),
14515
- createMountingHoleCutter(dimensions5.threadedHole)
14559
+ createMountingHoleCutter(threadedHole)
14516
14560
  )
14517
14561
  );
14518
14562
  }
@@ -14702,7 +14746,7 @@ function createCompressionSpringMesh(input, options = {}) {
14702
14746
  }
14703
14747
  }
14704
14748
  const triangle = (a, b, c) => {
14705
- if (props.hand === "left") indices.push(a, c, b);
14749
+ if (props.leftHanded) indices.push(a, c, b);
14706
14750
  else indices.push(a, b, c);
14707
14751
  };
14708
14752
  for (let step = 0; step < steps; step++)
@@ -15477,7 +15521,7 @@ function createFlatHeadScrewMesh(input, options = {}) {
15477
15521
  levels.add(-p.length + (shaftEnd + p.length) * i / steps);
15478
15522
  for (const z of [...levels].sort((a, b) => a - b)) {
15479
15523
  const r = add((theta) => {
15480
- const phase = (((z + p.length) / pitch - (p.threadHand === "right" ? 1 : -1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
15524
+ const phase = (((z + p.length) / pitch - (p.leftHanded ? -1 : 1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
15481
15525
  const d = Math.min(phase, 1 - phase);
15482
15526
  const groove = Math.min(
15483
15527
  major - minor,
@@ -15694,6 +15738,11 @@ var resolveFlexScreenSize = ({
15694
15738
  };
15695
15739
  };
15696
15740
  var resolveOrientation = (props) => {
15741
+ if ("orientation" in props) {
15742
+ throw new Error(
15743
+ "FlexScreen orientation is no longer supported; use a boolean placement flag"
15744
+ );
15745
+ }
15697
15746
  const shortcuts = [
15698
15747
  ["sitsFlat", props.sitsFlat],
15699
15748
  ["sitsFlatBelowBoard", props.sitsFlatBelowBoard],
@@ -15703,13 +15752,24 @@ var resolveOrientation = (props) => {
15703
15752
  ["foldedToFaceBelowBoard", props.foldsBelowBoard],
15704
15753
  ["foldedToRightAngleAboveBoard", props.foldedToRightAngleAboveBoard],
15705
15754
  ["foldedToRightAngleBelowBoard", props.foldedToRightAngleBelowBoard]
15706
- ].filter((entry) => entry[1]);
15707
- if (shortcuts.length > 1) {
15755
+ ];
15756
+ for (const [name, value] of shortcuts) {
15757
+ if (value !== void 0 && typeof value !== "boolean") {
15758
+ throw new Error(`FlexScreen placement flag "${name}" must be a boolean`);
15759
+ }
15760
+ }
15761
+ const selected = shortcuts.filter((entry) => entry[1] === true);
15762
+ if (selected.length > 1) {
15708
15763
  throw new Error(
15709
15764
  "Only one FlexScreen boolean orientation shortcut can be true"
15710
15765
  );
15711
15766
  }
15712
- return shortcuts[0]?.[0] ?? props.orientation ?? "sitsFlat";
15767
+ if (selected.length === 0 && shortcuts.some((entry) => entry[1] !== void 0)) {
15768
+ throw new Error(
15769
+ "A FlexScreen orientation flag must be true when placement flags are supplied"
15770
+ );
15771
+ }
15772
+ return selected[0]?.[0] ?? "sitsFlat";
15713
15773
  };
15714
15774
  var distance = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
15715
15775
  var interpolate = (a, b, progress) => [
@@ -16595,7 +16655,7 @@ function createTwistedSpurGearMesh(input, twist = 0, segmentsPerTurn = 32) {
16595
16655
  function createHelicalGearMesh(input) {
16596
16656
  const props = helicalGearModelPropsSchema.parse(input);
16597
16657
  const { twistAngle } = getHelicalGearDimensions(props);
16598
- const { helixAngle, handedness, segmentsPerTurn, ...spur } = props;
16658
+ const { helixAngle, leftHanded, segmentsPerTurn, ...spur } = props;
16599
16659
  return createTwistedSpurGearMesh(
16600
16660
  spur,
16601
16661
  twistAngle * Math.PI / 180,
@@ -16703,7 +16763,7 @@ function createHexBoltMesh(input, options = {}) {
16703
16763
  levels.add(-p.length + (shaftEnd + p.length) * i / steps);
16704
16764
  for (const z of [...levels].sort((a, b) => a - b)) {
16705
16765
  const r = add((theta) => {
16706
- const phase = (((z + p.length) / pitch - (p.threadHand === "right" ? 1 : -1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
16766
+ const phase = (((z + p.length) / pitch - (p.leftHanded ? -1 : 1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
16707
16767
  const d = Math.min(phase, 1 - phase);
16708
16768
  const groove = Math.min(
16709
16769
  major - minor,
@@ -17355,14 +17415,14 @@ var resolveNemaMotorProps = nemaMotorModelPropsSchema.parse.bind(nemaMotorModelP
17355
17415
  // lib/utils/nemaMotorWireGeometry.ts
17356
17416
  function createNemaMotorWireGeometry(input) {
17357
17417
  const p = resolveNemaMotorProps(input);
17358
- if (p.wireConnection === "none") return [];
17418
+ if (p.noWires) return [];
17359
17419
  const { position } = getNemaMotorReferencePoints(p).wireside;
17360
17420
  const angle = p.wireSideAngle * Math.PI / 180;
17361
17421
  const place = (geometry) => jscad8.transforms.translate(
17362
17422
  [position.x, position.y, position.z],
17363
17423
  jscad8.transforms.rotateZ(angle, geometry)
17364
17424
  );
17365
- if (p.wireConnection === "stubs") {
17425
+ if (p.wireStubs) {
17366
17426
  const colors = [
17367
17427
  "#d53f3f",
17368
17428
  "#2463af",
@@ -17475,19 +17535,25 @@ var roundedFace = (width10, radius) => {
17475
17535
  })
17476
17536
  );
17477
17537
  };
17478
- var steppedFace = (width10, chamfer) => {
17538
+ var steppedFace = (width10, chamfer, recessRadius, filletRadius) => {
17479
17539
  if (chamfer === 0) return face(width10, 0);
17480
17540
  const h = width10 / 2, c = chamfer / Math.SQRT2, r = Math.min(4, c / 4);
17481
17541
  const corner = [];
17482
- const arc = (x, y, from, to) => {
17542
+ const arc = (x, y, radius, from, to) => {
17543
+ if (radius === 0) {
17544
+ corner.push([x, y]);
17545
+ return;
17546
+ }
17483
17547
  for (let i = 0; i <= 12; i++) {
17484
17548
  const a = (from + (to - from) * i / 12) * Math.PI / 180;
17485
- corner.push([x + r * Math.cos(a), y + r * Math.sin(a)]);
17549
+ corner.push([x + radius * Math.cos(a), y + radius * Math.sin(a)]);
17486
17550
  }
17487
17551
  };
17488
- arc(h - c - r, h - r, 90, 0);
17489
- arc(h - c + r, h - c + r, 180, 270);
17490
- arc(h - r, h - c - r, 90, 0);
17552
+ const convex = filletRadius ?? r;
17553
+ const concave = recessRadius ?? r;
17554
+ arc(h - c - convex, h - convex, convex, 90, 0);
17555
+ arc(h - c + concave, h - c + concave, concave, 180, 270);
17556
+ arc(h - convex, h - c - convex, convex, 90, 0);
17491
17557
  return [0, 1, 2, 3].flatMap(
17492
17558
  (i) => corner.map(([x, y]) => {
17493
17559
  const a = -i * Math.PI / 2;
@@ -17508,12 +17574,18 @@ function createNemaMotorSections(input) {
17508
17574
  p.bodyWidth,
17509
17575
  p.faceCornerChamfer
17510
17576
  );
17511
- const profile6 = p.cornerProfile === "stepped" ? steppedFace : face;
17512
- const core = profile6(p.bodyWidth, p.bodyCornerChamfer);
17513
- const rear = profile6(
17577
+ const core = p.stepped ? steppedFace(
17514
17578
  p.bodyWidth,
17515
- p.rearCornerChamfer ?? (p.backFace === "plain" && p.mountingHoleThrough ? p.bodyCornerChamfer : p.faceCornerChamfer)
17516
- );
17579
+ p.bodyCornerChamfer,
17580
+ p.bodyCornerRecessRadius,
17581
+ p.bodyCornerFilletRadius
17582
+ ) : face(p.bodyWidth, p.bodyCornerChamfer);
17583
+ const rear = p.rearStepped ? steppedFace(
17584
+ p.bodyWidth,
17585
+ p.rearCornerChamfer,
17586
+ p.rearCornerRecessRadius,
17587
+ p.rearCornerFilletRadius
17588
+ ) : face(p.bodyWidth, p.rearCornerChamfer);
17517
17589
  const h = p.mountingHoleSpacing / 2;
17518
17590
  const centers = [
17519
17591
  [-h, -h],
@@ -17528,12 +17600,13 @@ function createNemaMotorSections(input) {
17528
17600
  add("frontCap", front, -holeDepth, 0, holes);
17529
17601
  add("frontCap", front, -p.frontCapLength, -holeDepth);
17530
17602
  add("body", core, -p.bodyLength + p.rearCapLength, -p.frontCapLength);
17531
- if (p.backFace === "plain") {
17603
+ if (p.backFacePlain) {
17532
17604
  add("rearCap", rear, -p.bodyLength, -p.bodyLength + p.rearCapLength);
17533
17605
  } else {
17534
- const halfPitch = p.backFaceHoleSpacing / 2;
17535
- const rearHoles = [-halfPitch, halfPitch].flatMap(
17536
- (x) => [-halfPitch, halfPitch].map(
17606
+ const halfPitchX = p.backFaceHoleSpacingX / 2;
17607
+ const halfPitchY = p.backFaceHoleSpacingY / 2;
17608
+ const rearHoles = [-halfPitchX, halfPitchX].flatMap(
17609
+ (x) => [-halfPitchY, halfPitchY].map(
17537
17610
  (y) => circle(p.backFaceHoleDiameter / 2, x, y)
17538
17611
  )
17539
17612
  );
@@ -17554,17 +17627,16 @@ function createNemaMotorSections(input) {
17554
17627
  add("pilot", circle(p.pilotDiameter / 2), 0, p.pilotLength);
17555
17628
  const r = p.shaftDiameter / 2;
17556
17629
  const round = circle(r);
17557
- if (p.shaftShape === "round")
17558
- add("shaft", round, p.pilotLength, p.shaftLength);
17630
+ if (p.roundShaft) add("shaft", round, p.pilotLength, p.shaftLength);
17559
17631
  else {
17560
17632
  const cut = r - p.shaftFlatDepth;
17561
17633
  const angle = Math.acos(cut / r);
17562
- const profile7 = Array.from({ length: 97 }, (_, i) => {
17634
+ const profile6 = Array.from({ length: 97 }, (_, i) => {
17563
17635
  const a = angle + (2 * Math.PI - 2 * angle) * i / 96;
17564
17636
  return [r * Math.cos(a), r * Math.sin(a)];
17565
17637
  });
17566
17638
  const rotation = p.shaftFlatAngle * Math.PI / 180;
17567
- const d = profile7.map(([x, y]) => [
17639
+ const d = profile6.map(([x, y]) => [
17568
17640
  x * Math.cos(rotation) - y * Math.sin(rotation),
17569
17641
  x * Math.sin(rotation) + y * Math.cos(rotation)
17570
17642
  ]);
@@ -17586,7 +17658,7 @@ function NemaMotor({
17586
17658
  ...props
17587
17659
  }) {
17588
17660
  const p = resolveNemaMotorProps(props);
17589
- const screw = p.backFace === "screws" ? jscad9.transforms.rotateX(
17661
+ const screw = p.backFaceScrews ? jscad9.transforms.rotateX(
17590
17662
  Math.PI,
17591
17663
  createHexSocketBoltGeom({
17592
17664
  metricSize: p.backFaceScrewSize,
@@ -17594,7 +17666,8 @@ function NemaMotor({
17594
17666
  showThreads: false
17595
17667
  })
17596
17668
  ) : void 0;
17597
- const halfPitch = p.backFaceHoleSpacing / 2;
17669
+ const halfPitchX = p.backFaceHoleSpacingX / 2;
17670
+ const halfPitchY = p.backFaceHoleSpacingY / 2;
17598
17671
  return /* @__PURE__ */ jsxs17(Fragment17, { children: [
17599
17672
  createNemaMotorSections(p).map((section, index) => {
17600
17673
  const outline = jscad9.geometries.geom2.fromPoints(section.outline);
@@ -17639,8 +17712,8 @@ function NemaMotor({
17639
17712
  },
17640
17713
  `front-screw-${index}`
17641
17714
  )),
17642
- screw && [-halfPitch, halfPitch].flatMap(
17643
- (x) => [-halfPitch, halfPitch].map((y) => /* @__PURE__ */ jsx67(
17715
+ screw && [-halfPitchX, halfPitchX].flatMap(
17716
+ (x) => [-halfPitchY, halfPitchY].map((y) => /* @__PURE__ */ jsx67(
17644
17717
  Colorize33,
17645
17718
  {
17646
17719
  material: componentMaterials.steel,
@@ -17904,7 +17977,7 @@ function createPanScrewMesh(input, options = {}) {
17904
17977
  levels.add(-p.length + (shaftEnd + p.length) * i / steps);
17905
17978
  for (const z of [...levels].sort((a, b) => a - b)) {
17906
17979
  const r = add((theta) => {
17907
- const phase = (((z + p.length) / pitch - (p.threadHand === "right" ? 1 : -1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
17980
+ const phase = (((z + p.length) / pitch - (p.leftHanded ? -1 : 1) * theta / (2 * Math.PI)) % 1 + 1) % 1;
17908
17981
  const d = Math.min(phase, 1 - phase);
17909
17982
  const groove = Math.min(
17910
17983
  major - minor,
@@ -18684,7 +18757,11 @@ import "@jscad/modeling";
18684
18757
  function createRigidCouplerMesh(input) {
18685
18758
  const props = rigidCouplerModelPropsSchema.parse(input);
18686
18759
  const dimensions5 = getRigidCouplerDimensions(props);
18687
- assertShaftMountResolution(dimensions5.screwHoles);
18760
+ const screwHoles = dimensions5.screwHoles.map((hole) => ({
18761
+ ...hole,
18762
+ threadHand: hole.leftHanded ? "left" : "right"
18763
+ }));
18764
+ assertShaftMountResolution(screwHoles);
18688
18765
  const body = createAnnularSleeve({
18689
18766
  outerRadius: props.outerDiameter / 2,
18690
18767
  boreARadius: props.boreDiameter / 2,
@@ -18694,10 +18771,7 @@ function createRigidCouplerMesh(input) {
18694
18771
  stepZ: dimensions5.boreADepth
18695
18772
  });
18696
18773
  return finishShaftMountMesh(
18697
- subtractShaftMountParts(
18698
- body,
18699
- ...dimensions5.screwHoles.map(createMountingHoleCutter)
18700
- )
18774
+ subtractShaftMountParts(body, ...screwHoles.map(createMountingHoleCutter))
18701
18775
  );
18702
18776
  }
18703
18777
 
@@ -18860,7 +18934,11 @@ import "@jscad/modeling";
18860
18934
  function createShaftCollarMesh(input) {
18861
18935
  const props = shaftCollarModelPropsSchema.parse(input);
18862
18936
  const dimensions5 = getShaftCollarDimensions(props);
18863
- assertShaftMountResolution([dimensions5.screwHole]);
18937
+ const screwHole = {
18938
+ ...dimensions5.screwHole,
18939
+ threadHand: dimensions5.screwHole.leftHanded ? "left" : "right"
18940
+ };
18941
+ assertShaftMountResolution([screwHole]);
18864
18942
  const body = createAnnularSleeve({
18865
18943
  outerRadius: props.outerDiameter / 2,
18866
18944
  boreARadius: props.boreDiameter / 2,
@@ -18868,10 +18946,7 @@ function createShaftCollarMesh(input) {
18868
18946
  chamfer: props.chamfer
18869
18947
  });
18870
18948
  return finishShaftMountMesh(
18871
- subtractShaftMountParts(
18872
- body,
18873
- createMountingHoleCutter(dimensions5.screwHole)
18874
- )
18949
+ subtractShaftMountParts(body, createMountingHoleCutter(screwHole))
18875
18950
  );
18876
18951
  }
18877
18952
 
@@ -18902,11 +18977,11 @@ import { Colorize as Colorize46, Custom as Custom38 } from "jscad-fiber";
18902
18977
  // lib/mechanical/sheet-metal-mesh.ts
18903
18978
  import earcut5 from "earcut";
18904
18979
  function holeOutline(hole) {
18905
- const radius = (hole.shape === "round" ? hole.diameter : hole.width) / 2;
18906
- const straight = hole.shape === "slot" ? (hole.length - hole.width) / 2 : 0;
18980
+ const radius = (hole.round ? hole.diameter : hole.width) / 2;
18981
+ const straight = hole.slot ? (hole.length - hole.width) / 2 : 0;
18907
18982
  if (straight < 0) throw new Error("Slot length must be at least its width");
18908
18983
  return Array.from({ length: 64 }, (_, i) => {
18909
- if (hole.shape === "round") {
18984
+ if (hole.round) {
18910
18985
  const angle2 = i * Math.PI / 32;
18911
18986
  return {
18912
18987
  u: hole.u + radius * Math.cos(angle2),
@@ -18917,12 +18992,13 @@ function holeOutline(hole) {
18917
18992
  const angle = -Math.PI / 2 + (i < 32 ? i : i - 32) * Math.PI / 31 + (i < 32 ? 0 : Math.PI);
18918
18993
  const along = half * straight + radius * Math.cos(angle);
18919
18994
  const across = radius * Math.sin(angle);
18920
- return hole.shape === "slot" && hole.axis === "v" ? { u: hole.u - across, v: hole.v + along } : { u: hole.u + along, v: hole.v + across };
18995
+ return hole.slot && hole.axis === "v" ? { u: hole.u - across, v: hole.v + along } : { u: hole.u + along, v: hole.v + across };
18921
18996
  });
18922
18997
  }
18923
18998
  function createSheetMetalMesh(input) {
18924
18999
  const props = sheetMetalModelPropsSchema.parse(input);
18925
- const { width: width10, baseLength, flangeHeight, thickness, profile: profile6 } = props;
19000
+ const { width: width10, baseLength, flangeHeight, thickness } = props;
19001
+ const profile6 = props.channel ? "channel" : props.angle ? "angle" : "plate";
18926
19002
  const radius = props.insideBendRadius + thickness / 2;
18927
19003
  const positions = [];
18928
19004
  const indices = [];
@@ -20128,7 +20204,7 @@ var createWormGearMesh = (input) => {
20128
20204
  const positions = [];
20129
20205
  const indices = [];
20130
20206
  const phase = props.phase % 360 * Math.PI / 180;
20131
- const direction = props.handedness === "right" ? 1 : -1;
20207
+ const direction = props.leftHanded ? -1 : 1;
20132
20208
  const ring = (z) => {
20133
20209
  const first = positions.length / 3;
20134
20210
  const rotation = phase + direction * (z / lead) * Math.PI * 2;