brep-io-kernel 1.0.165 → 1.0.166
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/apiExamples/BREP_Booleans.html +2 -2
- package/dist/apiExamples/BREP_Export.html +2 -2
- package/dist/apiExamples/BREP_Primitives.html +2 -2
- package/dist/apiExamples/BREP_Transforms.html +2 -2
- package/dist/apiExamples/Embeded_2D_Sketcher.html +2 -2
- package/dist/apiExamples/Embeded_CAD.html +2 -2
- package/dist/apiExamples/Embeded_CAD_Integration_Test.html +2 -2
- package/dist/assembly-constraint-capture.html +5 -5
- package/dist/assets/{AnnotationRegistry-CXzWv0O4.js → AnnotationRegistry-0p-JvbWc.js} +1 -1
- package/dist/assets/{AssemblyConstraintRegistry-B9zYtUPa.js → AssemblyConstraintRegistry-D_je-ie-.js} +1 -1
- package/dist/assets/{FeatureRegistry-C8QUptOR.js → FeatureRegistry-CbzxapvH.js} +3 -3
- package/dist/assets/{PartHistory-BT0SV2I5.js → PartHistory-CpTryl-X.js} +1 -1
- package/dist/assets/{Tube-OTYPNFqf.js → Tube-BXbVhwOj.js} +1 -1
- package/dist/assets/{annUtils-DooqNh7u.js → annUtils-DcxkV20w.js} +1 -1
- package/dist/assets/{apiExample_BREP_Booleans-DX0mDm5o.js → apiExample_BREP_Booleans-Bzu4TbBN.js} +1 -1
- package/dist/assets/{apiExample_BREP_Export-DbPRP7aB.js → apiExample_BREP_Export-CqSlAGR2.js} +1 -1
- package/dist/assets/{apiExample_BREP_Primitives-Ck5Bs5_N.js → apiExample_BREP_Primitives-BTFxPM-7.js} +1 -1
- package/dist/assets/{apiExample_BREP_Transforms-dRWUyTjs.js → apiExample_BREP_Transforms-OLb25wNp.js} +1 -1
- package/dist/assets/{apiExample_Embeded_2D_Sketcher-mq3UXNnH.js → apiExample_Embeded_2D_Sketcher-ioZryvFJ.js} +1 -1
- package/dist/assets/{apiExample_Embeded_CAD-DmG27xiK.js → apiExample_Embeded_CAD-enaf0P7w.js} +1 -1
- package/dist/assets/{apiExample_Embeded_CAD_Integration_Test-BkLtONsP.js → apiExample_Embeded_CAD_Integration_Test-qsIfYhe5.js} +1 -1
- package/dist/assets/{assemblyConstraintDialogs-Bp6JwQGM.js → assemblyConstraintDialogs-DOrF7vUw.js} +1 -1
- package/dist/assets/{brep-kernel-Cy1Y5Ou0.js → brep-kernel-CwgaO_st.js} +12 -12
- package/dist/assets/{browserTests-DyOFL-SK.js → browserTests-BUklfMO3.js} +12 -12
- package/dist/assets/{chamfer-CQEYwOuD.js → chamfer-DRqXmQEN.js} +1 -1
- package/dist/assets/{dialogCapturePageFactory-C8ZgGbz3.js → dialogCapturePageFactory-DHAJd9no.js} +1 -1
- package/dist/assets/{featureDialogs-CTxnmH67.js → featureDialogs-CDtV5J5B.js} +1 -1
- package/dist/assets/{featureDialogs-Dw07GP5f.js → featureDialogs-DEpvCr2m.js} +24 -24
- package/dist/assets/{fillet-C6-sxYCV.js → fillet-D-ZkRPFl.js} +1 -1
- package/dist/assets/{index.es-DgjLCvnw.js → index.es-Bj8hUP_M.js} +1 -1
- package/dist/assets/{javascript-CoXQsEOO.js → javascript-ClNCyVlO.js} +1 -1
- package/dist/assets/{main-cad-BxSaytVM.js → main-cad-IYyl4fag.js} +6 -6
- package/dist/assets/{pmiDialogs-BzGjz4Z4.js → pmiDialogs-CtAI9GYO.js} +1 -1
- package/dist/assets/{test-BubuSRs4.js → test-DDWa8W9C.js} +3 -3
- package/dist/cad.html +10 -10
- package/dist/feature-dialog-capture.html +7 -7
- package/dist/pmi-dialog-capture.html +5 -5
- package/dist/test.html +1 -1
- package/dist/viewer.html +10 -10
- package/dist-kernel/brep-kernel.js +4098 -4095
- package/package.json +1 -1
- package/src/BREP/SolidMethods/fillet.js +1 -1
- package/src/BREP/SolidMethods/transforms.js +8 -3
- package/src/BREP/fillets/fillet.js +4 -4
- package/src/tests/test_fillet_corner_bridge.js +16 -12
- package/src/tests/test_fillet_nonClosed.js +15 -14
- package/src/tests/test_sheetMetal_corner_fillet_compound_reference.js +11 -8
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var K0=Object.defineProperty;var i=(e,t)=>K0(e,"name",{value:t,configurable:!0});import{g as st,D as Lt,a as q0,r as at,b as it,c as Bt,d as zt,e as Z0,f as H0,h as Q0,i as ct,j as jt,k as er,l as Be,m as Ut,n as tr,o as rr,p as nr,q as or,s as sr,t as ar,u as Wt,v as ir,w as cr,x as lr,y as dr,z as ur,A as fr,B as Yt,V as Vt,F as Gt}from"./main-cad-
|
|
1
|
+
var K0=Object.defineProperty;var i=(e,t)=>K0(e,"name",{value:t,configurable:!0});import{g as st,D as Lt,a as q0,r as at,b as it,c as Bt,d as zt,e as Z0,f as H0,h as Q0,i as ct,j as jt,k as er,l as Be,m as Ut,n as tr,o as rr,p as nr,q as or,s as sr,t as ar,u as Wt,v as ir,w as cr,x as lr,y as dr,z as ur,A as fr,B as Yt,V as Vt,F as Gt}from"./main-cad-IYyl4fag.js";import{q as S}from"./browserStorage-CvgF8ovw.js";import{S as Nt,q as pr,P as Ke}from"./PartHistory-CpTryl-X.js";import{C as b0,B as Ge,l as Xt,m as Dt,o as Jt,p as mr,E as P0,q as hr,O as xr,_ as F0,t as Se,u as Kt,v as gr,S as Fe,w as yr,a as A0}from"./FeatureRegistry-CbzxapvH.js";import{V,B as wr,ap as qt,a$ as vr,a3 as ke,a4 as Re,aY as _r,aJ as I0,aQ as Er,Q as Sr}from"./featureDialogs-DEpvCr2m.js";import{computeFilletCenterline as T0}from"./fillet-D-ZkRPFl.js";import"./Tube-BXbVhwOj.js";import"./droppedWorkspaceFiles-8o-ZYOrf.js";import"./annUtils-DcxkV20w.js";import"./AnnotationRegistry-0p-JvbWc.js";import"./deepClone-Dj59xCHB.js";import"./preload-helper-ZNr0Qq7Q.js";import"./AssemblyConstraintRegistry-D_je-ie-.js";import"./chamfer-DRqXmQEN.js";const kt=class kt{constructor(t={}){this.options={captureStack:!!t.captureStack,withTimestamp:t.withTimestamp!==!1,levels:t.levels||null,maxEntries:Number.isFinite(t.maxEntries)?t.maxEntries:1/0},this._orig=(typeof window<"u"?window.console:console)||{},this._installed=!1,this._capturing=!0,this._logs=[],this._nextId=1,this._proxy=this._buildProxy(),this.install()}install(){if(this._installed)return;const t=typeof window<"u"?window:globalThis;this._prevConsole=t.console,t.console=this._proxy,this._installed=!0}restore(){if(!this._installed)return;const t=typeof window<"u"?window:globalThis;t.console=this._prevConsole||this._orig,this._installed=!1}pause(){this._capturing=!1}resume(){this._capturing=!0}getLogs(t={}){const{level:r,since:n,until:o}=t;return this._logs.filter(s=>!(r&&s.level!==r||n&&s.time&&s.time<n||o&&s.time&&s.time>o)).slice()}clearLogs(){this._logs.length=0}get logs(){return this.getLogs()}getLogsAll(){return this.getLogs()}_buildProxy(){const t=this._orig,r={},n=new Set([...Object.getOwnPropertyNames(t),...Object.keys(t)]);["log","info","warn","error","debug","trace","group","groupCollapsed","groupEnd","table","time","timeEnd","timeLog","count","countReset","assert","clear","dir","dirxml"].forEach(s=>n.add(s));for(const s of n){const a=this._safeGetDescriptor(t,s);if(a)if(typeof a.value=="function")r[s]=this._wrapMethod(s,t[s]);else if(a.get||a.set)Object.defineProperty(r,s,{configurable:!0,enumerable:a.enumerable,get:a.get?a.get.bind(t):void 0,set:a.set?a.set.bind(t):void 0});else try{r[s]=t[s]}catch{}}return Object.defineProperty(r,"__capturing__",{value:!0,enumerable:!1}),r}_safeGetDescriptor(t,r){try{return Object.getOwnPropertyDescriptor(t,r)}catch{return null}}_wrapMethod(t,r){const n=this,o=!n.options.levels||n.options.levels.includes(t);function s(...a){const l=t==="assert"?!a[0]:!0;if(n._capturing&&o&&l){const d={id:n._nextId++,level:t,args:a};if(n.options.withTimestamp&&(d.time=new Date),n.options.captureStack){const f=new Error().stack||"";d.stack=f.split(`
|
|
2
2
|
`).slice(2).join(`
|
|
3
|
-
`)}n.options.maxEntries!==1/0&&n._logs.length>=n.options.maxEntries&&n._logs.shift(),n._logs.push(d)}try{if(typeof r=="function")return r.apply(n._orig,a)}catch(d){try{return n._orig&&typeof n._orig.log=="function"?n._orig.log("ConsoleCapture forwarding error:",d):void 0}catch{}}}i(s,"wrapped");try{Object.defineProperty(s,"name",{value:`capture_${t}`})}catch{}return s}};i(Rt,"ConsoleCapture");let yt=Rt;const C=!!globalThis?.process?.versions?.node&&typeof window>"u";function $(){if(!C)return A;if(typeof require=="function"){const t=require("node:path");return(t.default??t).posix}const e=globalThis?.module;if(e&&typeof e.createRequire=="function"){const r=e.createRequire(globalThis.__filename||globalThis.process.cwd())("node:path");return(r.default??r).posix}return A}i($,"getNodePosixSync");const N0="/",br=":";function oe(e){if(typeof e!="string")throw new TypeError("Path must be a string")}i(oe,"_assertString");function rt(e){return e.startsWith(N0)}i(rt,"_isAbsolute");function D0(e,t){const r=e.split("/"),n=[];for(let o=0;o<r.length;o++){const s=r[o];!s||s==="."||(s===".."?n.length&&n[n.length-1]!==".."?n.pop():t&&n.push(".."):n.push(s))}return n.join("/")}i(D0,"_normalizeString");function M0(e){if(oe(e),e==="")return".";const t=rt(e),r=e.endsWith("/");let n=D0(e,!t);return n===""&&!t&&(n="."),n!==""&&r&&(n+="/"),(t?"/":"")+n}i(M0,"_normalize");function Pr(...e){let t="";for(let r=0;r<e.length;r++){const n=e[r];oe(n),n!==""&&(t===""?t=n:t+="/"+n)}return M0(t)}i(Pr,"_join");function vt(...e){let t="",r=!1;for(let o=e.length-1;o>=0;o--){const s=e[o];if(s!==void 0&&(oe(s),s!==""&&(t=s+"/"+t,s.startsWith("/")))){r=!0;break}}r||(t="/"+t,r=!0);const n=D0(t,!1);return(r?"/":"")+n||(r?"/":".")}i(vt,"_resolve");function Fr(e,t){if(oe(e),oe(t),e===t||(e=vt(e),t=vt(t),e===t))return"";const r=e.slice(1).split("/").filter(Boolean),n=t.slice(1).split("/").filter(Boolean);let o=0;const s=Math.min(r.length,n.length);for(;o<s&&r[o]===n[o];o++);const a=r.slice(o).map(()=>".."),c=n.slice(o);return a.concat(c).join("/")||""}i(Fr,"_relative");function C0(e){if(oe(e),e.length===0)return".";const t=rt(e),r=e.endsWith("/")?e.length-1:e.length;let n=-1;for(let o=r-1;o>=0;--o)if(e.charCodeAt(o)===47){n=o;break}return n===-1?t?"/":".":t&&n===0?"/":e.slice(0,n)}i(C0,"_dirname");function k0(e,t=""){if(oe(e),t!==void 0&&typeof t!="string")throw new TypeError("ext must be a string");let r=e.length;if(r===0)return"";for(;r>0&&e.charCodeAt(r-1)===47;)r--;if(r===0)return"/";let n=0;for(let s=r-1;s>=0;--s)if(e.charCodeAt(s)===47){n=s+1;break}let o=e.slice(n,r);return t&&o.endsWith(t)&&t!==""&&t!==o&&(o=o.slice(0,o.length-t.length)),o}i(k0,"_basename");function R0(e){oe(e);let t=-1,r=0,n=-1,o=0;for(let s=e.length-1;s>=0;--s){const a=e.charCodeAt(s);if(a===47){if(n!==-1){r=s+1;break}continue}n===-1&&(n=s+1),a===46?t===-1?t=s:o!==1&&(o=1):t!==-1&&(o=-1)}return t===-1||n===-1||o===0||t===r?"":e.slice(t,n)}i(R0,"_extname");function Ar(e){oe(e);const t=rt(e)?"/":"",r=C0(e),n=k0(e),o=R0(n),s=o?n.slice(0,n.length-o.length):n;return{root:t,dir:r,base:n,ext:o,name:s}}i(Ar,"_parse");function Ir(e){const t=e.dir||e.root||"",r=e.base||(e.name||"")+(e.ext||"");return t?t.endsWith("/")?t+r:t+"/"+r:r||"."}i(Ir,"_format");function Tr(e){return e}i(Tr,"_toNamespacedPath");const A={sep:N0,delimiter:br,normalize:M0,join:Pr,resolve:vt,isAbsolute:rt,relative:Fr,dirname:C0,basename:k0,extname:R0,parse:Ar,format:Ir,toNamespacedPath:Tr,posix:null,win32:null};A.posix=A;const qe={get sep(){return C?$().sep:A.sep},get delimiter(){return C?$().delimiter:A.delimiter},normalize:i((...e)=>C?$().normalize(...e):A.normalize(...e),"normalize"),join:i((...e)=>C?$().join(...e):A.join(...e),"join"),resolve:i((...e)=>C?$().resolve(...e):A.resolve(...e),"resolve"),isAbsolute:i((...e)=>C?$().isAbsolute(...e):A.isAbsolute(...e),"isAbsolute"),relative:i((...e)=>C?$().relative(...e):A.relative(...e),"relative"),dirname:i((...e)=>C?$().dirname(...e):A.dirname(...e),"dirname"),basename:i((...e)=>C?$().basename(...e):A.basename(...e),"basename"),extname:i((...e)=>C?$().extname(...e):A.extname(...e),"extname"),parse:i((...e)=>C?$().parse(...e):A.parse(...e),"parse"),format:i((...e)=>C?$().format(...e):A.format(...e),"format"),toNamespacedPath:i((...e)=>C?$().toNamespacedPath(...e):A.toNamespacedPath(...e),"toNamespacedPath"),posix:null,win32:null};qe.posix=qe;const Nr=new Proxy({},{get(){throw new Error("POSIX-only path module: use the named export `posix` with your VFS.")}});qe.win32=Nr;const G=qe,lt=.01,Zt="src/tests/fixtures/sketchSolverTopology";function Y(e,t){if(!e)throw new Error(t)}i(Y,"assert$h");function Ze(e,t){const r=e.points.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Point ${t} not found`);return r}i(Ze,"getPoint$1");function Dr(e,t){const r=e.constraints.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Constraint ${t} not found`);return r}i(Dr,"getConstraint$1");function Ht(e,t,r){const n=Ze(e,t),o=Ze(e,r);return Math.hypot(o.x-n.x,o.y-n.y)}i(Ht,"dist$1");function Qt(e,t){const r=t.map(o=>Ze(e,o));let n=0;for(let o=0;o<r.length;o++){const s=r[o],a=r[(o+1)%r.length];n+=s.x*a.y-a.x*s.y}return n*.5}i(Qt,"signedArea$1");function e0(e){const t=(e.points||[]).map(o=>Number(o.id)).sort((o,s)=>o-s),r=(e.geometries||[]).map(o=>({id:Number(o.id),type:String(o.type),construction:!!o.construction,points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id),n=(e.constraints||[]).map(o=>({id:Number(o.id),type:String(o.type),points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id);return{pointIds:t,geometries:r,constraints:n}}i(e0,"topologySnapshot$1");function Mr(e,t){const r=new Set((e.points||[]).map(n=>Number(n.id)));for(const n of e.geometries||[])for(const o of n.points||[])Y(r.has(Number(o)),`${t}: geometry ${n.id} references missing point ${o}`);for(const n of e.constraints||[])for(const o of n.points||[])Y(r.has(Number(o)),`${t}: constraint ${n.id} references missing point ${o}`)}i(Mr,"assertTopologyIntegrity$1");function Cr(e,t="fixture"){const r=String(e||"").trim();return r&&r.toLowerCase().replace(/[^a-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(0,120)||t}i(Cr,"sanitizeName");function dt(e,t,r,n){if(!Number.isFinite(e)||!Number.isFinite(t)||Math.abs(e-t)>r)throw new Error(`${n}. Expected ${t}, got ${e}`)}i(dt,"assertNear$1");function kr(e,t={}){if(typeof e!="string"||!e.trim())return null;const r=Object.keys(t||{}),n=r.map(o=>Number(t[o]));if(!n.every(o=>Number.isFinite(o)))return null;try{const s=Function(...r,`"use strict"; return (${e});`)(...n),a=Number(s);return Number.isFinite(a)?a:null}catch{return null}}i(kr,"evaluateExpressionWithVars");function $0(e,{maxPasses:t=8,stopWhenConstraintsClear:r=!0}={}){const n=Math.max(1,Number(t)||1);let o=null;for(let s=0;s<n;s++){e.solveSketch("full");const a=e.sketchObject,c=JSON.stringify(a?.points||[]),l=Array.isArray(a?.constraints)?a.constraints.some(d=>typeof d?.error=="string"&&d.error.length>0):!1;if(r&&!l||c===o)break;o=c}}i($0,"solveWithSettle");function Rr(e,t,r){const n=t&&typeof t=="object"?t:null;if(!n)throw new Error(`${r}: expressionValues edit requires an object`);for(const o of e.sketchObject?.constraints||[]){if(typeof o?.valueExpr!="string"||!o.valueExpr.length)continue;const s=kr(o.valueExpr,n);if(!Number.isFinite(s))continue;const a=o?.type==="⟺"&&o?.displayStyle==="diameter"&&o?.valueExprMode==="diameter";o.value=a?Number(s)*.5:Number(s)}}i(Rr,"applyExpressionValuesToConstraints");function $r(e,t,r){for(const n of t||[])if(!(!n||typeof n!="object")){if(Object.prototype.hasOwnProperty.call(n,"expressionValues"))Rr(e,n.expressionValues,r);else{const o=Number(n.constraintId),s=Number(n.value);if(!Number.isFinite(o))throw new Error(`${r}: edit has invalid constraintId`);if(!Number.isFinite(s))throw new Error(`${r}: edit for constraint ${o} has invalid value`);const a=Dr(e.sketchObject,o);a.value=s}$0(e,{maxPasses:Number.isFinite(Number(n.maxPasses))?Number(n.maxPasses):8,stopWhenConstraintsClear:n.stopWhenConstraintsClear!==!1})}}i($r,"applyEdits");function Or({before:e,after:t,expect:r,contextLabel:n}){if(Mr(t,n),r?.topologyUnchanged!==!1){const s=JSON.stringify(e0(e)),a=JSON.stringify(e0(t));Y(s===a,`${n}: topology changed`)}for(const s of r?.distances||[]){const a=Number(s.a),c=Number(s.b),l=Number(s.value),d=Number.isFinite(Number(s.tol))?Number(s.tol):lt;Y(Number.isFinite(a)&&Number.isFinite(c),`${n}: invalid distance pair`),dt(Ht(t,a,c),l,d,`${n}: distance [${a},${c}] mismatch`)}for(const s of r?.anchors||[]){const a=Number(s.pointId),c=Number.isFinite(Number(s.tol))?Number(s.tol):lt,l=Ze(t,a);Number.isFinite(Number(s.x))&&dt(l.x,Number(s.x),c,`${n}: anchor ${a} x drift`),Number.isFinite(Number(s.y))&&dt(l.y,Number(s.y),c,`${n}: anchor ${a} y drift`)}for(const s of r?.coincidentPairs||[]){const a=Number(s.a),c=Number(s.b),l=Number.isFinite(Number(s.tol))?Number(s.tol):lt;Y(Number.isFinite(a)&&Number.isFinite(c),`${n}: invalid coincident pair`),Y(Ht(t,a,c)<=l,`${n}: coincident pair [${a},${c}] broke`)}for(const s of r?.orientationLoops||[]){const a=Array.isArray(s?.pointIds)?s.pointIds.map(p=>Number(p)):[];Y(a.length>=3,`${n}: orientation loop needs at least 3 points`);const c=Number.isFinite(Number(s.minAbsArea))?Number(s.minAbsArea):1,l=Qt(e,a),d=Qt(t,a);Y(Math.abs(d)>c,`${n}: loop collapsed`),s.preserveSign!==!1&&Y(Math.sign(d)===Math.sign(l),`${n}: loop orientation flipped`)}}i(Or,"runExpectations");async function Lr(e,t){const n=`sketch fixture ${e?.name||G.basename(t)}`;Y(e&&typeof e=="object",`${n}: fixture is not an object`),Y(Array.isArray(e.edits),`${n}: missing edits array`);let o=null;if(e.sketch&&typeof e.sketch=="object")o=e.sketch;else if(typeof e.sourcePartFile=="string"&&e.sourcePartFile.trim().length){const l=e.sourcePartFile.trim(),d=await S.promises.readFile(l,"utf8"),p=JSON.parse(d),u=Array.isArray(p?.features)?p.features:[];let f=null;if(e.sourceFeatureId!=null){const m=String(e.sourceFeatureId);f=u.find(h=>h?.type==="S"&&(String(h?.inputParams?.id??"")===m||String(h?.inputParams?.featureID??"")===m))||null}f||(f=u.find(m=>m?.type==="S"&&m?.persistentData?.sketch)||null),o=f?.persistentData?.sketch||null}Y(o&&typeof o=="object",`${n}: missing sketch or sourcePartFile sketch`);const s=new b0({sketch:JSON.parse(JSON.stringify(o))});$0(s,{maxPasses:Number.isFinite(Number(e.initialSolvePasses))?Number(e.initialSolvePasses):4,stopWhenConstraintsClear:!0});const a=JSON.parse(JSON.stringify(s.sketchObject));$r(s,e.edits,n);const c=s.sketchObject;Or({before:a,after:c,expect:e.expect||{},contextLabel:n})}i(Lr,"runFixture");async function Br(e){if(!(typeof process<"u"&&process.versions&&process.versions.node))return 0;const t=new Set((e||[]).map(s=>s?._sourceFile).filter(s=>typeof s=="string"&&s.length>0));let r=[];try{r=await S.promises.readdir(Zt)}catch{return 0}const n=r.filter(s=>typeof s=="string"&&s.toLowerCase().endsWith(".json")).sort((s,a)=>s.localeCompare(a));let o=0;for(const s of n){const a=G.join(Zt,s);if(t.has(a))continue;let c=null;try{const f=await S.promises.readFile(a,"utf8");c=JSON.parse(f)}catch(f){const m=f?.message||String(f);throw new Error(`Failed to read sketch fixture ${a}: ${m}`)}const l=String(s).replace(/\.[^.]+$/,""),p=`test_sketch_solver_fixture_${Cr(c?.name||l,`fixture_${o}`)}`,u=i(async function(){await Lr(c,a)},"sketchSolverFixtureTest");try{Object.defineProperty(u,"name",{value:p,configurable:!0})}catch{}e.push({test:u,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0,_sourceFile:a}),t.add(a),o+=1}return o}i(Br,"registerSketchSolverTopologyFixtureTests");async function zr(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20;const r=await e.newFeature("P.CU");r.inputParams.sizeX=2,r.inputParams.sizeY=2,r.inputParams.sizeZ=20;const n=await e.newFeature("B");return n.inputParams.targetSolid=r.inputParams.featureID,n.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(zr,"test_boolean_subtract");async function jr(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=10,t.inputParams.sizeY=10,t.inputParams.sizeZ=10;const r=await e.newFeature("P.CU");return r.inputParams.sizeX=10,r.inputParams.sizeY=10,r.inputParams.sizeZ=10,r.inputParams.transform={position:[5,0,0],rotationEuler:[0,0,0],scale:[1,1,1]},r.inputParams.boolean={operation:"UNION",targets:[t.inputParams.featureID]},e}i(jr,"test_boolean_operation_target_name_preserved");async function Ur(e){const t=(e?.features||[]).filter(c=>c?.type==="P.CU");if(t.length<2)throw new Error("[boolean target name] Expected at least two primitive cube features.");const r=t[0]?.inputParams?.featureID,n=t[1]?.inputParams?.featureID;if(!r||!n)throw new Error("[boolean target name] Missing feature IDs for cube setup.");const o=(e?.scene?.children||[]).filter(c=>c?.type==="SOLID");if(o.length!==1)throw new Error(`[boolean target name] Expected exactly one solid after union, got ${o.length}.`);const s=o[0];if(String(s?.name||"")!==String(r))throw new Error(`[boolean target name] Expected result name "${r}", got "${String(s?.name||"")}".`);if(e?.scene?.getObjectByName?.(n))throw new Error(`[boolean target name] Tool-named solid "${n}" should have been replaced.`)}i(Ur,"afterRun_boolean_operation_target_name_preserved");function ut(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ut,"assert$g");async function Wr(){const e=new Ge.Cube({x:10,y:10,z:10,name:"BASE"}),t=new Ge.Cube({x:6,y:6,z:6,name:"TOOL"});e.setFaceMetadata("BASE_NX",{sourceFeatureId:"BASE_FEATURE",marker:"base-nx"}),t.setFaceMetadata("TOOL_PX",{sourceFeatureId:"TOOL_FEATURE",marker:"tool-px"}),t.bakeTRS({position:[7,2,2],rotationEuler:[0,0,0],scale:[1,1,1]}),e.union=()=>{throw new Error("forced union failure for metadata fallback test")};const r={scene:{getObjectByName(){return null}}},n=await Ge.applyBooleanOperation(r,e,{operation:"UNION",targets:[t]},"BOOL_META"),o=Array.isArray(n?.added)?n.added[0]:null;ut(o,"Expected boolean to return a result solid.");const s=o.getFaceMetadata("BASE_NX");ut(s&&s.sourceFeatureId==="BASE_FEATURE","Base face provenance should survive union fallback."),ut(s&&s.marker==="base-nx","Base face custom metadata should survive union fallback.")}i(Wr,"test_boolean_face_metadata_preserved");async function Yr(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=20,t.inputParams.sizeY=20,t.inputParams.sizeZ=20;const r=await e.newFeature("CHAMFER");return r.inputParams.edges=[`${t.inputParams.featureID}_PZ`],r.inputParams.distance=3,r.inputParams.inflate=5e-4,r.inputParams.direction="INSET",e}i(Yr,"test_Chamfer");function nt(e,t){const r=e[0]-t[0],n=e[1]-t[1],o=e[2]-t[2];return r*r+n*n+o*o}i(nt,"pointDistanceSq");function le(e,t,r=1e-12){if(nt(e,t)>r*r)throw new Error(`Expected point ${JSON.stringify(e)} near ${JSON.stringify(t)} (eps=${r})`)}i(le,"assertPointNear");async function Vr(){const e=[[0,0,0],[1,.35,0],[2,-.5,0],[3,.45,0],[4,0,0]],t=Xt(e,{fitStrength:1});if(!Array.isArray(t)||t.length!==e.length)throw new Error("Curve fit should return one output point per input point.");if(le(t[0],e[0],1e-12),le(t[t.length-1],e[e.length-1],1e-12),Dt(e,t))throw new Error("Fitted polyline should not locally reverse direction against the source edge.");let r=0;for(let o=1;o<e.length-1;o++)nt(e[o],t[o])>1e-12&&r++;if(r<=0)throw new Error("At least one interior point should be snapped to the fitted curve.");const n=Xt(e,{fitStrength:0});for(let o=0;o<e.length;o++)le(n[o],e[o],1e-12)}i(Vr,"test_edge_smooth_curve_fit");function Gr(e,t){const r=Array.isArray(e)?e:[],n=Array.isArray(t)?t:[],o=r.length;if(o!==n.length||o<3)return!0;for(let s=0;s<o;s++){const a=(s+1)%o,c=r[s],l=r[a],d=n[s],p=n[a],u=[l[0]-c[0],l[1]-c[1],l[2]-c[2]],f=[p[0]-d[0],p[1]-d[1],p[2]-d[2]],m=Math.hypot(u[0],u[1],u[2]),h=Math.hypot(f[0],f[1],f[2]);if(!(m>1e-12)||!(h>1e-12))continue;if((u[0]*f[0]+u[1]*f[1]+u[2]*f[2])/(m*h)<-1e-6)return!0}return!1}i(Gr,"hasLocalBacktrackingAgainstSourceClosed");async function Xr(){const e=[[1,0,0],[.45,.95,.02],[-.55,.8,-.03],[-1,0,0],[-.45,-.85,.03],[.55,-.75,-.02]],t=Jt(e,{fitStrength:1});if(!Array.isArray(t)||t.length!==e.length)throw new Error("Closed-loop curve fit should return one output point per input point.");if(Gr(e,t))throw new Error("Closed-loop fitted polyline should not locally reverse direction.");let r=0;for(let o=0;o<e.length;o++)nt(e[o],t[o])>1e-12&&r++;if(r<=0)throw new Error("Closed-loop fit should move at least one loop point.");const n=Jt(e,{fitStrength:0});for(let o=0;o<e.length;o++)le(n[o],e[o],1e-12)}i(Xr,"test_edge_smooth_curve_fit_closed_loop");async function Jr(){const e=[0,0,0,1,0,0,1,1,0,0,1,0],t=[0,1,2,0,2,3],r=new Map;r.set(1,{x:.2,y:.8,z:0,count:1});const n=mr(e,t,r,{minArea2Ratio:.04,minNormalDot:.1,minArea2Abs:1e-24});if((Number(n?.movedVertices)||0)!==1)throw new Error("Constrained edge smoothing should move the target vertex.");if((Number(n?.constrainedVertices)||0)<=0)throw new Error("Constrained edge smoothing should scale back fold-causing targets.");const o=e[3],s=e[4];if(!Number.isFinite(o)||!Number.isFinite(s))throw new Error("Constrained edge smoothing produced invalid coordinates.");if(Math.abs(o-.2)<1e-9&&Math.abs(s-.8)<1e-9)throw new Error("Constrained smoothing should not apply a fold-causing target at full displacement.");const a=i((c,l,d)=>{const p=c*3,u=l*3,f=d*3,m=e[u+0]-e[p+0],h=e[u+1]-e[p+1],x=e[f+0]-e[p+0],w=e[f+1]-e[p+1];return m*w-h*x},"triNormalZ");if(!(a(0,1,2)>0))throw new Error("Primary triangle normal flipped after constrained smoothing.");if(!(a(0,2,3)>0))throw new Error("Adjacent triangle normal flipped after constrained smoothing.")}i(Jr,"test_edge_smooth_constraints_prevent_triangle_foldback");function O0(e){const t=Array.isArray(e)?e.map(o=>[o[0],o[1],o[2]]):[],r=[];for(const o of t)r.push(o[0],o[1],o[2]);const n=i((o,s)=>{const a=o.slice(),c=s.map(u=>[u[0],u[1],u[2]]),l={type:"EDGE",name:"MOCK_EDGE_0",userData:{faceA:"FACE_A",faceB:"FACE_B",polylineLocal:c.map(u=>[u[0],u[1],u[2]])},parent:null,parentSolid:null},d={type:"SOLID",name:"MOCK_SOLID",_vertProperties:a,_triVerts:[0,1,2,0,2,3,0,3,4],_triIDs:[0,0,0],_vertKeyToIndex:new Map,_dirty:!1,_faceIndex:null,_manifold:null,traverse(u){typeof u=="function"&&u(l)},visualize(){},_manifoldize(){},getBoundaryEdgePolylines(){return[{name:l.name,faceA:l.userData.faceA,faceB:l.userData.faceB,indices:[0,1,2,3,4],positions:c.map(u=>[u[0],u[1],u[2]]),closedLoop:!1}]},clone(){return n(a,c)}},p={type:"FACE",name:"MOCK_FACE_0",edges:[l],parent:d,parentSolid:d,userData:{faceName:"MOCK_FACE_0"}};return l.parent=d,l.parentSolid=d,d.__testFace=p,d},"createSolid");return n(r,t)}i(O0,"makeMockSolidFromPolyline");async function Kr(){const e=[[0,0,0],[1,.4,0],[2,-.45,0],[3,.35,0],[4,0,0]],t=O0(e),r=new P0;r.inputParams={edges:[t],fitStrength:1,id:"EDGE_SMOOTH_SOLID_TEST"};const n=await r.run();if(!n||!Array.isArray(n.added)||n.added.length!==1)throw new Error("EdgeSmoothFeature should add one smoothed solid when selecting a whole solid.");if(!Array.isArray(n.removed)||n.removed.length!==1||n.removed[0]!==t)throw new Error("EdgeSmoothFeature should remove exactly the selected source solid.");const o=n.added[0],s=Array.isArray(o?._vertProperties)?o._vertProperties:[];if(s.length<15)throw new Error("Smoothed solid is missing expected vertex properties.");const a=[];for(let l=0;l<5;l++){const d=l*3;a.push([s[d+0],s[d+1],s[d+2]])}if(le(a[0],e[0],1e-12),le(a[a.length-1],e[e.length-1],1e-12),Dt(e,a))throw new Error("Whole-solid smoothing should not create local edge backtracking.");let c=0;for(let l=1;l<e.length-1;l++)nt(e[l],a[l])>1e-12&&c++;if(c<=0)throw new Error("Whole-solid smoothing should move at least one interior edge point.");if(!r.persistentData||r.persistentData.selectedSolidCount<1)throw new Error("Feature metadata should record at least one selected solid.")}i(Kr,"test_edge_smooth_whole_solid_selection");async function qr(){const e=[[0,0,0],[1,.3,0],[2,-.35,0],[3,.25,0],[4,0,0]],t=O0(e),r=t.__testFace;if(!r||r.type!=="FACE")throw new Error("Mock setup should provide a face selection target.");const n=new P0;n.inputParams={edges:[r],fitStrength:1,id:"EDGE_SMOOTH_FACE_TEST"};const o=await n.run();if(!o||!Array.isArray(o.added)||o.added.length!==1)throw new Error("EdgeSmoothFeature should add one smoothed solid when selecting a face.");if(!Array.isArray(o.removed)||o.removed.length!==1||o.removed[0]!==t)throw new Error("EdgeSmoothFeature should remove exactly the source solid for face selection.");const s=o.added[0],a=Array.isArray(s?._vertProperties)?s._vertProperties:[];if(a.length<15)throw new Error("Face-selected smoothing output is missing expected vertex properties.");const c=[];for(let l=0;l<5;l++){const d=l*3;c.push([a[d+0],a[d+1],a[d+2]])}if(le(c[0],e[0],1e-12),le(c[c.length-1],e[e.length-1],1e-12),Dt(e,c))throw new Error("Face-selected smoothing should not create local edge backtracking.");if(!n.persistentData||n.persistentData.selectedFaceCount<1)throw new Error("Feature metadata should record at least one selected face.")}i(qr,"test_edge_smooth_face_selection");const _t=-4,Et=15.7,ze=1e-4,Zr=i(()=>({points:[{id:0,x:-2,y:-2,fixed:!0},{id:1,x:2,y:-2,fixed:!1},{id:2,x:2,y:2,fixed:!1},{id:3,x:-2,y:2,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1},{id:102,type:"line",points:[2,3],construction:!1},{id:103,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}),"makeRectSketch$1");function Hr(e){const r=e?.geometry?.getAttribute?.("position");if(!r)return[];const n=[],o=new V;for(let s=0;s<r.count;s++)o.set(r.getX(s),r.getY(s),r.getZ(s)).applyMatrix4(e.matrixWorld),n.push([o.x,o.y,o.z]);return n}i(Hr,"collectWorldVerticesFromFaceObject");function t0(e){const t=[];for(const r of Array.isArray(e)?e:[])Array.isArray(r?.p1)&&t.push(r.p1),Array.isArray(r?.p2)&&t.push(r.p2),Array.isArray(r?.p3)&&t.push(r.p3);return t}i(t0,"collectTriangleVertices");function r0(e,t,r){let n=Number.POSITIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=0,a=0;for(const c of e){const d=c[0]*t.x+c[1]*t.y+c[2]*t.z-r;d<n&&(n=d),d>o&&(o=d),s+=d,a+=1}return{mean:a?s/a:0,spread:a?o-n:0,count:a}}i(r0,"analyzeProjectedOffsets");async function Qr(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch=Zr();const n=await e.newFeature("E");return n.inputParams.profile=r.inputParams.featureID,n.inputParams.consumeProfileSketch=!1,n.inputParams.distance=_t,n.inputParams.distanceBack=Et,e}i(Qr,"test_extrude_negative_distance_cap_alignment");async function en(e){const t=e.features.find(h=>h?.type==="E"),r=e.features.find(h=>h?.type==="S");if(!t?.inputParams?.featureID)throw new Error("[extrude-negative] missing extrude feature id");if(!r?.inputParams?.featureID)throw new Error("[extrude-negative] missing sketch feature id");const n=e.scene.getObjectByName(t.inputParams.featureID);if(!n||typeof n.getFaces!="function")throw new Error("[extrude-negative] extrude solid missing from scene");const o=e.scene.getObjectByName(r.inputParams.featureID),s=o?.children?.find?.(h=>h?.type==="FACE")||o?.children?.find?.(h=>h?.userData?.faceName);if(!s||typeof s.getAverageNormal!="function")throw new Error("[extrude-negative] profile face missing from sketch");const a=s.getAverageNormal().clone();if(a.lengthSq()<1e-20)throw new Error("[extrude-negative] profile normal is degenerate");a.normalize();const c=Hr(s);if(!c.length)throw new Error("[extrude-negative] profile face has no vertices");const l=c.reduce((h,x)=>h+x[0]*a.x+x[1]*a.y+x[2]*a.z,0)/c.length,d=n.getFaces(!1),p=d.find(h=>String(h?.faceName||"").endsWith("_START")),u=d.find(h=>String(h?.faceName||"").endsWith("_END"));if(!p||!u){const h=d.map(x=>String(x?.faceName||""));throw new Error(`[extrude-negative] missing start/end caps. Faces: ${h.join(", ")}`)}const f=r0(t0(p.triangles),a,l),m=r0(t0(u.triangles),a,l);if(f.count===0||m.count===0)throw new Error("[extrude-negative] cap triangles are empty");if(f.spread>ze)throw new Error(`[extrude-negative] start cap not planar on expected axis (spread=${f.spread})`);if(m.spread>ze)throw new Error(`[extrude-negative] end cap not planar on expected axis (spread=${m.spread})`);if(Math.abs(f.mean- -Et)>ze)throw new Error(`[extrude-negative] start cap offset mismatch: got ${f.mean}, expected ${-Et}`);if(Math.abs(m.mean-_t)>ze)throw new Error(`[extrude-negative] end cap offset mismatch: got ${m.mean}, expected ${_t}`)}i(en,"afterRun_extrude_negative_distance_cap_alignment");function Ae(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(Ae,"assert$f");async function tn(e){const t=await e.newFeature("P.CY"),r=String(t?.inputParams?.featureID||"");Ae(r,"Cylinder feature should have a featureID."),await e.runHistory();const n=e?.scene?.getObjectByName?.(r)||null;Ae(n,"Expected cylinder solid in the scene."),Ae(typeof n.getFaceMetadata=="function","Expected solid face metadata API.");const o=n.getFaceMetadata(`${r}_S`),s=n.getFaceMetadata(`${r}_T`);Ae(o?.sourceFeatureId===r,"Cylinder side face should be seeded with sourceFeatureId."),Ae(s?.sourceFeatureId===r,"Cylinder top face should be seeded with sourceFeatureId.")}i(tn,"test_face_source_feature_seed");async function rn(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20;const r=await e.newFeature("E");return r.inputParams.profile=`${t.inputParams.featureID}_T`,r.inputParams.distance=0,r.inputParams.distanceBack=5,r.inputParams.boolean={targets:[t.inputParams.featureID],operation:"UNION"},e}i(rn,"test_ExtrudeFace");async function nn(e){const t=await e.newFeature("P.CY");t.inputParams.radius=5,t.inputParams.height=10,t.inputParams.resolution=48;const r=await e.newFeature("F");return r.inputParams.edges=[`${t.inputParams.featureID}_T`],r.inputParams.radius=1,r.inputParams.direction="INSET",e}i(nn,"test_Fillet");const on="src/tests/partFiles/fillet_angle_test.BREP.json";async function sn(e){const t=await S.promises.readFile(on,"utf8");return await e.reset(),await e.fromJSON(t),e.expressions=`//Examples:
|
|
4
|
-
angle = 35;`,e.currentHistoryStepId="E2",e}i(sn,"test_fillet_angle");async function an(e){const t=e.features.find(l=>l?.type==="F");if(!t)throw new Error("Fillet feature missing from part file.");const n=(Array.isArray(t.inputParams?.edges)?t.inputParams.edges:[]).find(l=>typeof l=="string")||null;if(!n)throw new Error("Fillet edge name not found in test part file.");const o=e.getObjectByName(n);if(!o)throw new Error(`Edge object "${n}" not found after extrude.`);const s=Number(t.inputParams?.radius)||2,a=String(t.inputParams?.direction||"INSET").toUpperCase(),c=T0(o,s,a);if(!Array.isArray(c.points)||!Array.isArray(c.edge))throw new Error("computeFilletCenterline should return point arrays.");if(!Array.isArray(c.tangentA)||!Array.isArray(c.tangentB))throw new Error("computeFilletCenterline should return tangent arrays.");console.log(`✓ Fillet angle tangent test passed: points=${c.points.length}, tangentsA=${c.tangentA.length}, tangentsB=${c.tangentB.length}`)}i(an,"afterRun_fillet_angle");const cn="src/tests/partFiles/fillet_test.BREP.json";async function ln(e){const t=await S.promises.readFile(cn,"utf8");return await e.reset(),await e.fromJSON(t),e.currentHistoryStepId="E7",e}i(ln,"test_fillet_edge_degenerate_segment");async function dn(e){const t=e.features.find(l=>l?.type==="F");if(!t)throw new Error("Fillet feature missing from part file.");const r=Array.isArray(t.inputParams?.edges)?t.inputParams.edges[0]:null;if(!r)throw new Error("Fillet edge reference missing from part file.");const n=typeof r=="object"?r:e.getObjectByName(String(r));if(!n)throw new Error(`Fillet source edge not found: ${String(r)}`);const o=[.5,.675,1],s=o.map(l=>({radius:l,res:T0(n,l,"INSET")}));for(const{radius:l,res:d}of s){if(!Array.isArray(d.points)||!Array.isArray(d.edge))throw new Error(`computeFilletCenterline should provide point arrays (radius=${l}).`);if(d.points.length<2)throw new Error(`computeFilletCenterline should produce at least 2 points (radius=${l}).`);let p=1/0,u=0;for(let f=1;f<d.points.length;f++){const m=d.points[f-1],h=d.points[f],x=Math.hypot(h.x-m.x,h.y-m.y,h.z-m.z);x<p&&(p=x),x>u&&(u=x)}if(Number.isFinite(p)&&Number.isFinite(u)&&u>0&&p<u*1e-5)throw new Error(`Fillet centerline contains a degenerate segment at radius=${l}: minSeg=${p}, maxSeg=${u}`)}const a=s.map(({res:l})=>l.points.length);if(!a.every(l=>l===a[0]))throw new Error(`Degenerate-edge sampling changed with radius: pointCounts=${JSON.stringify(a)}`);console.log(`✓ Fillet degenerate-edge tangent test passed: radii=${o.join(", ")}, points=${a.join(",")}`)}i(dn,"afterRun_fillet_edge_degenerate_segment");const un="src/tests/partFiles/medium_fillets.BREP.json",St="E2",fn="F3";function n0(e,t){const r=(e?.scene?.children||[]).filter(n=>n?.type==="SOLID");return r.find(n=>String(n?.name||"")===String(t))||r[0]||null}i(n0,"getSolidByName$1");async function pn(e){const t=await S.promises.readFile(un,"utf8");return await e.reset(),await e.fromJSON(t),e.currentHistoryStepId=St,e}i(pn,"test_fillet_preserves_original_face_names");async function mn(e){const t=n0(e,St);if(!t||typeof t.getFaceNames!="function")throw new Error("[fillet face-name preserve] Failed to resolve pre-fillet solid.");const r=(t.getFaceNames()||[]).map(a=>String(a||"").trim()).filter(a=>a.length>0);if(r.length===0)throw new Error("[fillet face-name preserve] Pre-fillet solid has no face names.");e.currentHistoryStepId=fn,await e.runHistory();const n=n0(e,St);if(!n||typeof n.getFaceNames!="function")throw new Error("[fillet face-name preserve] Failed to resolve post-fillet solid.");const o=new Set((n.getFaceNames()||[]).map(a=>String(a||"").trim()).filter(a=>a.length>0)),s=r.filter(a=>!o.has(a));if(s.length>0)throw new Error(`[fillet face-name preserve] Lost ${s.length} original face names: ${s.join(", ")}`);console.log(`✓ Fillet preserved ${r.length} original face names on ${String(n?.name||"solid")}.`)}i(mn,"afterRun_fillet_preserves_original_face_names");async function hn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=10,t.inputParams.sizeY=10,t.inputParams.sizeZ=10;const r=await e.newFeature("F");return r.inputParams.edges=[`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_NY[0]`,`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_PZ[0]`,`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_PY[0]`],r.inputParams.radius=1.2,r.inputParams.direction="INSET",r.inputParams.resolution=32,e}i(hn,"test_fillet_corner_bridge");async function xn(e){const t=e.features.find(a=>a?.type==="F");if(!t)throw new Error("Fillet feature missing from history.");const r=t?.persistentData?.miterSummary||null;if(!r||typeof r!="object")throw new Error("Fillet corner bridge summary metadata missing.");const n=Number(r?.cornerBridgeCount||0);if(n<1)throw new Error(`Expected at least one corner bridge, received ${n}.`);const o=(e.scene?.children||[]).find(a=>a?.owningFeatureID===t.inputParams.featureID);if(!o)throw new Error("Fillet group not found in scene.");let s=null;if(o.traverse(a=>{!s&&a?.type==="SOLID"&&(s=a)}),!s||typeof s._manifoldize!="function")throw new Error("Corner-bridge fillet did not produce a manifold-capable solid.");try{s._manifoldize()}catch(a){const c=String(a?.message||a||"Unknown manifold error");throw new Error(`Corner-bridge fillet manifoldization failed: ${c}`)}console.log(`✓ Fillet corner bridge test passed: bridges=${n}`)}i(xn,"afterRun_fillet_corner_bridge");async function gn(e){const t=await e.newFeature("P.CU");t.inputParams.width=10,t.inputParams.height=10,t.inputParams.depth=10;const r=await e.newFeature("F");return r.inputParams.edges=[`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_NY[0]`],r.inputParams.radius=.5,r.inputParams.direction="INSET",e}i(gn,"test_Fillet_NonClosed");async function wn(e){const t=e.features.find(a=>a?.type==="F");if(!t)throw new Error("Fillet feature missing from history");const r=t?.persistentData?.stubbed===!0,n=String(t?.persistentData?.strategy||"");if(!r||n!=="miter_tangent_boolean")throw new Error("Fillet feature should report stub metadata.");const o=(e.scene?.children||[]).find(a=>a?.owningFeatureID===t.inputParams.featureID);if(!o)throw new Error("Fillet group not found in scene");let s=0;if(o.traverse(a=>{a?.type==="SOLID"&&s++}),s===0)throw new Error("Fillet feature should produce at least one solid");console.log(`✓ Non-closed fillet stub test passed: ${s} solid(s) created`)}i(wn,"afterRun_Fillet_NonClosed");async function yn(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution="128";const r=await e.newFeature("P.CU");r.inputParams.sizeX=5,r.inputParams.sizeY=2,r.inputParams.sizeZ=10,r.inputParams.boolean.targets=[t.inputParams.featureID],r.inputParams.boolean.operation="UNION";const n=await e.newFeature("F");n.inputParams.edges=[`${t.inputParams.featureID}_S|${r.inputParams.featureID}_PY[0]`],n.inputParams.radius=1,n.inputParams.direction="AUTO";const o=await e.newFeature("F");return o.inputParams.edges=[`${t.inputParams.featureID}_S|${t.inputParams.featureID}_T[0]`],o.inputParams.radius=1,o.inputParams.direction="AUTO",e}i(yn,"test_filletsMoreDifficult");async function vn(e){return yn(e)}i(vn,"test_fillets_more_dificult");async function _n(e){await e.newFeature("P.CU")}i(_n,"test_history_expand_does_not_dirty");async function En(e){const t=Array.isArray(e?.features)?e.features[0]:null;if(!t)throw new Error("Expand-state dirty test requires one feature in history.");const r=Number(t.timestamp);if(!Number.isFinite(r))throw new Error("Feature timestamp missing after initial run.");const n=t.lastRun;if(!n||n.ok!==!0)throw new Error("Feature lastRun state missing after initial run.");if(t.inputParams=t.inputParams||{},t.inputParams.__open=!0,await new Promise(o=>setTimeout(o,5)),await e.runHistory(),Number(t.timestamp)!==r)throw new Error("Expanding a feature should not mark it dirty or rerun.");if(t.lastRun!==n)throw new Error("Expanding a feature should not replace lastRun metadata.");if(t.inputParams.__open=!1,await new Promise(o=>setTimeout(o,5)),await e.runHistory(),Number(t.timestamp)!==r)throw new Error("Collapsing a feature should not mark it dirty or rerun.");if(t.lastRun!==n)throw new Error("Collapsing a feature should not replace lastRun metadata.")}i(En,"afterRun_history_expand_does_not_dirty");const ft=i((e,t,r,n,o=100)=>({points:[{id:0,x:e,y:t,fixed:!0},{id:1,x:r,y:t,fixed:!1},{id:2,x:r,y:n,fixed:!1},{id:3,x:e,y:n,fixed:!1}],geometries:[{id:o+0,type:"line",points:[0,1],construction:!1},{id:o+1,type:"line",points:[1,2],construction:!1},{id:o+2,type:"line",points:[2,3],construction:!1},{id:o+3,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}),"makeRectSketch"),Sn=i((e,t,r,n,o=200)=>({points:[{id:0,x:e,y:t,fixed:!0},{id:1,x:r,y:n,fixed:!1}],geometries:[{id:o,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}),"makeLineSketch");async function bn(e){const t=await e.newFeature("D");t.inputParams.transform={position:[1,2,3],rotationEuler:[0,30,0],scale:[1,1,1]};const r=await e.newFeature("SP");r.persistentData.spline={points:[{id:"p0",position:[0,0,0],forwardDistance:1,backwardDistance:1,flipDirection:!1},{id:"p1",position:[5,2,0],forwardDistance:1,backwardDistance:1,flipDirection:!1},{id:"p2",position:[10,0,0],forwardDistance:1,backwardDistance:1,flipDirection:!1}]};const n=await e.newFeature("HX");n.inputParams.radius=2,n.inputParams.endRadius=1.5,n.inputParams.height=8,n.inputParams.turns=2,n.inputParams.resolution=32;const o=await e.newFeature("P");o.inputParams.orientation="XY";const s=await e.newFeature("P");s.inputParams.orientation="XY",s.inputParams.offset_distance=5;const a=await e.newFeature("S");a.inputParams.sketchPlane=o.inputParams.featureID,a.persistentData.sketch=ft(0,0,10,10,100);const c=await e.newFeature("S");c.inputParams.sketchPlane=s.inputParams.featureID,c.persistentData.sketch=ft(2,2,8,8,200);const l=await e.newFeature("LOFT");l.inputParams.profiles=[a.inputParams.featureID,c.inputParams.featureID];const d=await e.newFeature("S");d.inputParams.sketchPlane=o.inputParams.featureID,d.persistentData.sketch=Sn(0,-5,0,5,300);const p=`${d.inputParams.featureID}:G300`,u=await e.newFeature("S");u.inputParams.sketchPlane=o.inputParams.featureID,u.persistentData.sketch=ft(4,-2,7,2,400);const f=await e.newFeature("R");f.inputParams.profile=u.inputParams.featureID,f.inputParams.axis=p,f.inputParams.angle=180,f.inputParams.resolution=32;const m=await e.newFeature("P.CU"),h=await e.newFeature("RM");h.inputParams.targetSolid=m.inputParams.featureID,h.inputParams.mode="Simplify",h.inputParams.tolerance=.05;const x=await e.newFeature("P.CU"),w=await e.newFeature("SWS");w.inputParams.targetSolid=x.inputParams.featureID,w.inputParams.subdivisionLoops=1;const g=await e.newFeature("P.CU"),y=await e.newFeature("XFORM");y.inputParams.solids=[g.inputParams.featureID],y.inputParams.translate=[2,0,0],y.inputParams.rotateEulerDeg=[0,0,45],y.inputParams.copy=!0;const v=await e.newFeature("P.CU"),E=await e.newFeature("OVL");E.inputParams.targetSolid=v.inputParams.featureID,E.inputParams.distance=5e-4;const D=await e.newFeature("P.CU"),R=await e.newFeature("PATLIN");R.inputParams.solids=[D.inputParams.featureID],R.inputParams.count=3,R.inputParams.offset={position:[3,0,0],rotationEuler:[0,0,0],scale:[1,1,1]};const q=await e.newFeature("P.CU"),T=await e.newFeature("PATRAD");T.inputParams.solids=[q.inputParams.featureID],T.inputParams.axisRef=p,T.inputParams.count=4,T.inputParams.totalAngleDeg=180;const Z=await e.newFeature("P.CU"),X=await e.newFeature("PATTERN");X.inputParams.solids=[Z.inputParams.featureID],X.inputParams.mode="LINEAR",X.inputParams.count=2;const M=await e.newFeature("ACOMP");M.inputParams.componentName="missing_component";const H=await e.newFeature("IMAGE");H.inputParams.fileToImport="";const Pe=await e.newFeature("HEIGHTMAP");return Pe.inputParams.fileToImport="",e}i(bn,"test_history_features_basic");async function Pn(e){const t=i((r,n)=>{const o=e.features.find(c=>c?.type===r);if(!o)throw new Error(`${n} feature missing from history`);const s=o?.inputParams?.featureID;if(!s)throw new Error(`${n} feature missing featureID`);if(!e.scene.getObjectByName(s))throw new Error(`${n} object not found in scene`)},"requireFeatureObject");t("D","Datium"),t("SP","Spline"),t("HX","Helix"),t("LOFT","Loft"),t("R","Revolve")}i(Pn,"afterRun_history_features_basic");const _=12,Mt=2,L0=6,B0=90,Fn=6,z0=6,bt=3,j0=7,An=1e-6;function $e(e,t){const r=[{id:0,x:0,y:0,fixed:!0},...t.map((n,o)=>({id:o+1,x:n[0],y:n[1],fixed:!1}))];e.persistentData.sketch={points:r,geometries:[],constraints:[{id:0,type:"⏚",points:[0]}]}}i($e,"buildSketchWithPoints");function Oe(e){return e.features.find(t=>t?.type==="H")||null}i(Oe,"findHoleFeature");function Ct(e){let t=0;for(const r of e){const n=r?._faceMetadata;if(n instanceof Map)for(const o of n.values())o?.hole&&t++}return t}i(Ct,"countHoleMetadata");function kt(e){return(e.scene?.children||[]).filter(r=>r?.type==="SOLID")[0]||null}i(kt,"getPrimarySolid");function Me(e,t,r){if(!Number.isFinite(e)||Math.abs(e-t)>An)throw new Error(`${r} expected ${t}, got ${e}`)}i(Me,"expectApprox$1");async function In(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.5,_*.5]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="SIMPLE",o.inputParams.diameter=Mt,o.inputParams.throughAll=!0,o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(In,"test_hole_through");async function Tn(e){const t=Oe(e);if(!t)throw new Error("[hole_through] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_through] No hole records");if(!r[0]?.throughAll)throw new Error("[hole_through] Expected through-all hole record");if(String(r[0]?.type||"").toUpperCase()!=="SIMPLE")throw new Error(`[hole_through] Expected SIMPLE hole, got ${r[0]?.type}`);const n=kt(e);if(!n)throw new Error("[hole_through] No solids created");if((n._faceNameToID?.size||0)<=6)throw new Error("[hole_through] Hole did not add faces");if(Ct([n])===0)throw new Error("[hole_through] No hole face metadata found")}i(Tn,"afterRun_hole_through");async function Nn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.35,_*.35]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="THREADED",o.inputParams.depth=6,o.inputParams.throughAll=!1,o.inputParams.threadStandard="ISO_METRIC",o.inputParams.threadDesignation="M6x1",o.inputParams.threadMode="SYMBOLIC",o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(Nn,"test_hole_thread_symbolic");async function Dn(e){const t=Oe(e);if(!t)throw new Error("[hole_thread_symbolic] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_thread_symbolic] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="THREADED")throw new Error(`[hole_thread_symbolic] Expected THREADED hole, got ${n?.type}`);if(!n?.thread||String(n.thread.mode||"").toUpperCase()!=="SYMBOLIC")throw new Error("[hole_thread_symbolic] Expected symbolic thread metadata")}i(Dn,"afterRun_hole_thread_symbolic");async function Mn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.65,_*.35]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="THREADED",o.inputParams.depth=4,o.inputParams.throughAll=!1,o.inputParams.threadStandard="ISO_METRIC",o.inputParams.threadDesignation="M6x1",o.inputParams.threadMode="MODELED",o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(Mn,"test_hole_thread_modeled");async function Cn(e){const t=Oe(e);if(!t)throw new Error("[hole_thread_modeled] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_thread_modeled] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="THREADED")throw new Error(`[hole_thread_modeled] Expected THREADED hole, got ${n?.type}`);if(!n?.thread||String(n.thread.mode||"").toUpperCase()!=="MODELED")throw new Error("[hole_thread_modeled] Expected modeled thread metadata")}i(Cn,"afterRun_hole_thread_modeled");async function kn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.25,_*.65]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="COUNTERSINK",o.inputParams.diameter=Mt,o.inputParams.depth=Fn,o.inputParams.throughAll=!1,o.inputParams.countersinkDiameter=L0,o.inputParams.countersinkAngle=B0,o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(kn,"test_hole_countersink");async function Rn(e){const t=Oe(e);if(!t)throw new Error("[hole_countersink] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_countersink] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="COUNTERSINK")throw new Error(`[hole_countersink] Expected COUNTERSINK hole, got ${n?.type}`);if(n?.throughAll)throw new Error("[hole_countersink] Expected non-through hole");if(!(n?.countersinkHeight>0))throw new Error("[hole_countersink] Expected countersink height > 0");Me(n?.countersinkDiameter,L0,"[hole_countersink] countersink diameter"),Me(n?.countersinkAngle,B0,"[hole_countersink] countersink angle");const o=kt(e);if(!o)throw new Error("[hole_countersink] No solids created");if(Ct([o])===0)throw new Error("[hole_countersink] No hole face metadata found")}i(Rn,"afterRun_hole_countersink");async function $n(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.75,_*.65]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="COUNTERBORE",o.inputParams.diameter=Mt,o.inputParams.depth=j0,o.inputParams.throughAll=!1,o.inputParams.counterboreDiameter=z0,o.inputParams.counterboreDepth=bt,o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i($n,"test_hole_counterbore");async function On(e){const t=Oe(e);if(!t)throw new Error("[hole_counterbore] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_counterbore] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="COUNTERBORE")throw new Error(`[hole_counterbore] Expected COUNTERBORE hole, got ${n?.type}`);if(n?.throughAll)throw new Error("[hole_counterbore] Expected non-through hole");Me(n?.counterboreDiameter,z0,"[hole_counterbore] counterbore diameter"),Me(n?.counterboreDepth,bt,"[hole_counterbore] counterbore depth"),Me(n?.straightDepth,j0-bt,"[hole_counterbore] straight depth");const o=kt(e);if(!o)throw new Error("[hole_counterbore] No solids created");if(Ct([o])===0)throw new Error("[hole_counterbore] No hole face metadata found")}i(On,"afterRun_hole_counterbore");const U0="IMPORT3D_DECIMATION_BASELINE",W0="IMPORT3D_DECIMATION_REDUCED",Pt="IMPORT3D_DECIMATION_STABILITY",Te="IMPORT3D_DECIMATION_RESTORE",Ne="IMPORT3D_DECIMATION_LEGACY_CACHE",Xe="IMPORT3D_DECIMATION_SNAPSHOT_CLONE_RESILIENCE";function B(e){const t=Number(e);return!Number.isFinite(t)||Math.abs(t)<1e-16?"0":t.toFixed(9)}i(B,"formatNumber$2");function je(e,t,r,n){const o=r[0]-t[0],s=r[1]-t[1],a=r[2]-t[2],c=n[0]-t[0],l=n[1]-t[1],d=n[2]-t[2];let p=s*d-a*l,u=a*c-o*d,f=o*l-s*c;const m=Math.hypot(p,u,f)||1;p/=m,u/=m,f/=m,e.push(` facet normal ${B(p)} ${B(u)} ${B(f)}`),e.push(" outer loop"),e.push(` vertex ${B(t[0])} ${B(t[1])} ${B(t[2])}`),e.push(` vertex ${B(r[0])} ${B(r[1])} ${B(r[2])}`),e.push(` vertex ${B(n[0])} ${B(n[1])} ${B(n[2])}`),e.push(" endloop"),e.push(" endfacet")}i(je,"appendFacet$2");function Le({radius:e=6,height:t=10,segments:r=96}={}){const n=Math.max(24,Number(r)|0),o=Math.max(.1,Number(e)||1),a=Math.max(.1,Number(t)||1)*.5,c=[0,a,0],l=[0,-a,0],d=["solid import3d_decimation_cylinder"];for(let p=0;p<n;p+=1){const u=p/n*Math.PI*2,f=(p+1)/n*Math.PI*2,m=Math.cos(u),h=Math.sin(u),x=Math.cos(f),w=Math.sin(f),g=[o*m,-a,o*h],y=[o*x,-a,o*w],v=[o*m,a,o*h],E=[o*x,a,o*w];je(d,g,y,E),je(d,g,E,v),je(d,c,E,v),je(d,l,g,y)}return d.push("endsolid import3d_decimation_cylinder"),d.join(`
|
|
5
|
-
`)}i(Le,"buildCylinderStl");function _e(e,t,r){e.inputParams.id=e.inputParams.featureID,e.inputParams.fileToImport=t,e.inputParams.centerMesh=!1,e.inputParams.deflectionAngle=8,e.inputParams.decimationLevel=r,e.inputParams.meshRepairLevel="NONE",e.inputParams.extractPlanarFaces=!1,e.inputParams.segmentAnalyticPrimitives=!1}i(_e,"createImportFeature");function He(e){const t=Array.isArray(e?._vertProperties)?e._vertProperties:[],r=Array.isArray(e?._triVerts)?e._triVerts:[];let n=2166136261;const o=i(a=>{n^=a>>>0,n=Math.imul(n,16777619)>>>0},"mix"),s=i(a=>{const c=String(a||"");for(let l=0;l<c.length;l+=1)o(c.charCodeAt(l)&255),o(c.charCodeAt(l)>>>8&255)},"mixString");for(let a=0;a<t.length;a+=1){const c=Number(t[a]);s(Number.isFinite(c)?c.toFixed(9):"NaN")}for(let a=0;a<r.length;a+=1)o(Number(r[a])|0);return n>>>0}i(He,"hashSolidMeshSignature");function de(e,t){return(e.scene?.children||[]).filter(n=>n?.type==="SOLID").find(n=>String(n?.name||"")===t)||null}i(de,"getSolidByName");function ue(e){return Math.floor((Array.isArray(e?._triVerts)?e._triVerts.length:0)/3)}i(ue,"getSolidTriangleCount");function ot(e,t){return(e.features||[]).find(r=>r&&String(r?.type||"").toUpperCase()==="IMPORT3D"&&(String(r?.inputParams?.featureID||"")===t||String(r?.inputParams?.id||"")===t))||null}i(ot,"getImportFeatureById$1");async function Ln(e){const t=Le({radius:5,height:10,segments:96}),r=await e.newFeature("IMPORT3D");r.inputParams.featureID=U0,_e(r,t,100);const n=await e.newFeature("IMPORT3D");return n.inputParams.featureID=W0,_e(n,t,55),e}i(Ln,"test_import3d_decimation_reduces_triangle_count");async function Bn(e){const t=(e.scene?.children||[]).filter(a=>a?.type==="SOLID");if(!t.length)throw new Error("[import3d decimation] No solids were generated");const r=t.find(a=>String(a?.name||"")===U0),n=t.find(a=>String(a?.name||"")===W0);if(!r||!n)throw new Error("[import3d decimation] Could not locate baseline and reduced solids");const o=Math.floor((Array.isArray(r._triVerts)?r._triVerts.length:0)/3),s=Math.floor((Array.isArray(n._triVerts)?n._triVerts.length:0)/3);if(!(o>0))throw new Error("[import3d decimation] Baseline triangle count is invalid");if(!(s>0))throw new Error("[import3d decimation] Reduced triangle count is invalid");if(!(s<o))throw new Error(`[import3d decimation] Expected reduced triangle count < baseline (${s} >= ${o})`)}i(Bn,"afterRun_import3d_decimation_reduces_triangle_count");async function zn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Pt,_e(t,Le({radius:5,height:10,segments:96}),80),e}i(zn,"test_import3d_decimation_reapplies_from_cached_source_mesh");async function jn(e){const t=ot(e,Pt);if(!t)throw new Error("[import3d decimation stability] Import feature not found");const r=i(()=>{const a=(e.scene?.children||[]).filter(c=>c?.type==="SOLID").find(c=>String(c?.name||"")===Pt);return a?Math.floor((Array.isArray(a._triVerts)?a._triVerts.length:0)/3):0},"getSolidTriangleCount");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=55,await e.runHistory();const n=r();if(!(n>0))throw new Error("[import3d decimation stability] First decimated rebuild produced no triangles");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=55,await e.runHistory();const o=r();if(!(o>0))throw new Error("[import3d decimation stability] Second decimated rebuild produced no triangles");if(n!==o)throw new Error(`[import3d decimation stability] Expected stable triangle count when re-running same decimation level (${n} !== ${o})`)}i(jn,"afterRun_import3d_decimation_reapplies_from_cached_source_mesh");async function Un(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Te,_e(t,Le({radius:5,height:10,segments:96}),100),e}i(Un,"test_import3d_decimation_100_restores_original_geometry");async function Wn(e){const t=ot(e,Te);if(!t)throw new Error("[import3d decimation restore] Import feature not found");const r=de(e,Te);if(!r)throw new Error("[import3d decimation restore] Baseline solid not found");const n=ue(r),o=He(r);t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory();const s=de(e,Te);if(!s)throw new Error("[import3d decimation restore] Decimated solid not found");const a=ue(s);if(!(a<n))throw new Error(`[import3d decimation restore] Expected 90% decimation to reduce triangles (${a} >= ${n})`);t.inputParams.fileToImport=null,t.inputParams.decimationLevel=100,await e.runHistory();const c=de(e,Te);if(!c)throw new Error("[import3d decimation restore] Restored solid not found");const l=ue(c),d=He(c);if(l!==n||d!==o)throw new Error(`[import3d decimation restore] 100% did not restore original mesh (baseline triangles=${n}, restored triangles=${l}, baseline hash=${o}, restored hash=${d})`)}i(Wn,"afterRun_import3d_decimation_100_restores_original_geometry");async function Yn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Ne,_e(t,Le({radius:5,height:10,segments:96}),100),e}i(Yn,"test_import3d_decimation_seeds_source_snapshot_for_legacy_cache");async function Vn(e){const t=ot(e,Ne);if(!t)throw new Error("[import3d decimation legacy cache] Import feature not found");if(!t?.persistentData?.importCache)throw new Error("[import3d decimation legacy cache] Missing import cache");delete t.persistentData.importCache.sourceMeshSnapshot,t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory();const r=ue(de(e,Ne));if(!(r>0))throw new Error("[import3d decimation legacy cache] 90% pass produced no triangles");if(!t?.persistentData?.importCache?.sourceMeshSnapshot)throw new Error("[import3d decimation legacy cache] Expected source mesh snapshot to be seeded");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=80,await e.runHistory();const n=ue(de(e,Ne));if(!(n>0&&n<r))throw new Error(`[import3d decimation legacy cache] Expected 80% to reduce triangles (${n} !< ${r})`);t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory();const o=ue(de(e,Ne));if(o!==r)throw new Error(`[import3d decimation legacy cache] 90% result changed after intermediate edit (${r} !== ${o})`)}i(Vn,"afterRun_import3d_decimation_seeds_source_snapshot_for_legacy_cache");async function Gn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Xe,_e(t,Le({radius:5,height:10,segments:96}),100),e}i(Gn,"test_import3d_decimation_preserves_source_snapshot_without_json_clone");async function Xn(e){const t=ot(e,Xe);if(!t)throw new Error("[import3d decimation snapshot resilience] Import feature not found");const r=de(e,Xe);if(!r)throw new Error("[import3d decimation snapshot resilience] Baseline solid not found");const n=ue(r),o=He(r),s=t?.persistentData?.importCache?.sourceMeshSnapshot;if(!s||!Array.isArray(s.position))throw new Error("[import3d decimation snapshot resilience] Initial source mesh snapshot missing");const a=JSON.stringify;try{JSON.stringify=i(function(
|
|
6
|
-
`)}i(Jn,"buildTwoCubeIslandsStl");function Kn(e,t){return(e.features||[]).find(r=>r&&String(r?.type||"").toUpperCase()==="IMPORT3D"&&(String(r?.inputParams?.featureID||"")===t||String(r?.inputParams?.id||"")===t))||null}i(Kn,"getImportFeatureById");function pt(e,t){const r=`${t}_SOLID_`;return(e.scene?.children||[]).filter(n=>n?.type==="SOLID"&&(String(n?.name||"")===t||String(n?.name||"").startsWith(r)))}i(pt,"listFeatureSolids");async function qn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.id=te,t.inputParams.featureID=te,t.inputParams.fileToImport=Jn(),t.inputParams.centerMesh=!1,t.inputParams.deflectionAngle=8,t.inputParams.decimationLevel=100,t.inputParams.meshRepairLevel="NONE",t.inputParams.extractMultipleSolids=!1,t.inputParams.extractPlanarFaces=!1,t.inputParams.segmentAnalyticPrimitives=!1,e}i(qn,"test_import3d_extract_multiple_solids_toggle");async function Zn(e){const t=Kn(e,te);if(!t)throw new Error("[import3d multi solids] Import feature not found");const r=pt(e,te);if(r.length!==1)throw new Error(`[import3d multi solids] Expected 1 solid with checkbox off, got ${r.length}`);t.inputParams.fileToImport=null,t.inputParams.extractMultipleSolids=!0,await e.runHistory();const n=pt(e,te);if(n.length!==2)throw new Error(`[import3d multi solids] Expected 2 solids with checkbox on, got ${n.length}`);const o=new Set(n.map(a=>String(a?.name||"")));if(!o.has(`${te}_SOLID_01`)||!o.has(`${te}_SOLID_02`))throw new Error(`[import3d multi solids] Unexpected split solid names: ${Array.from(o).join(", ")}`);t.inputParams.fileToImport=null,t.inputParams.extractMultipleSolids=!1,await e.runHistory();const s=pt(e,te);if(s.length!==1)throw new Error(`[import3d multi solids] Expected 1 solid after turning checkbox off again, got ${s.length}`);if(String(s[0]?.name||"")!==te)throw new Error(`[import3d multi solids] Expected merged solid name to reset to feature ID, got ${String(s[0]?.name||"")}`)}i(Zn,"afterRun_import3d_extract_multiple_solids_toggle");const Ft="IMPORT3D_PLANAR_SLIVER_BRIDGE";function U(e){const t=Number(e);return!Number.isFinite(t)||Math.abs(t)<1e-16?"0":t.toFixed(9)}i(U,"formatNumber");function Hn(e,t,r,n){const o=r[0]-t[0],s=r[1]-t[1],a=r[2]-t[2],c=n[0]-t[0],l=n[1]-t[1],d=n[2]-t[2];let
|
|
7
|
-
`)}i(Qn,"buildCoplanarSliverBridgeStl");async function eo(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.id=Ft,t.inputParams.featureID=Ft,t.inputParams.fileToImport=Qn(),t.inputParams.centerMesh=!1,t.inputParams.deflectionAngle=5,t.inputParams.extractPlanarFaces=!0,t.inputParams.planarFaceMinAreaPercent=20,t.inputParams.segmentAnalyticPrimitives=!1,e}i(eo,"test_import3d_planar_extraction_merges_sliver_bridge");async function to(e){const t=(e.scene?.children||[]).filter(d=>d?.type==="SOLID");if(!t.length)throw new Error("[import3d planar sliver] No solids were generated");const r=t.find(d=>String(d?.name||"")===Ft)||t[0];if(!r)throw new Error("[import3d planar sliver] Could not find imported solid");const o=(Array.isArray(r._triVerts)?r._triVerts:[]).length/3|0;if(o!==5)throw new Error(`[import3d planar sliver] Expected 5 triangles, got ${o}`);const s=Array.isArray(r._triIDs)?r._triIDs:[],a=r._idToFaceName instanceof Map?r._idToFaceName:new Map;if(s.length!==o)throw new Error("[import3d planar sliver] Triangle IDs missing or inconsistent");const c=s.map(d=>String(a.get(d)||"")),l=new Set(c);if(l.size!==1)throw new Error(`[import3d planar sliver] Expected one merged planar face, got ${l.size} (${Array.from(l).join(", ")})`)}i(to,"afterRun_import3d_planar_extraction_merges_sliver_bridge");async function ro(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=4,t.inputParams.sizeY=3,t.inputParams.sizeZ=2;const r=await e.newFeature("M");return r.inputParams.solids=[t.inputParams.featureID],r.inputParams.mirrorPlane=`${t.inputParams.featureID}_PX`,r.inputParams.offsetDistance=0,e}i(ro,"test_mirror");function O(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(O,"assert$e");function s0(e,t){return(e?.features||[]).find(r=>r?.type===t)||null}i(s0,"getFeatureEntry");async function no(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=5,t.inputParams.sizeY=5,t.inputParams.sizeZ=5;const r=String(t?.inputParams?.featureID||"");O(r,"Cube feature should have a featureID.");const n=await e.newFeature("O.F");n.inputParams.faces=[`${r}_PZ`],n.inputParams.distance=2}i(no,"test_offsetFace_preserves_individual_edges");async function oo(e){const t=s0(e,"P.CU"),r=s0(e,"O.F"),n=String(t?.inputParams?.featureID||""),o=String(r?.inputParams?.featureID||"");O(n,"Cube featureID missing after history run."),O(o,"Offset-face featureID missing after history run.");const s=`${o}:${n}_PZ`,a=e?.scene?.getObjectByName?.(s)||null;O(a&&a.type==="SKETCH",`Expected offset-face sketch group "${s}".`);const c=(a.children||[]).filter(f=>f?.type==="EDGE"),l=(a.children||[]).filter(f=>f?.type==="VERTEX"),d=(a.children||[]).find(f=>f?.type==="FACE"&&f?.name===`${s}:PROFILE`)||null;O(d,"Expected offset-face profile face."),O(c.length===4,`Expected 4 individual offset edges for square face, got ${c.length}.`),O(l.length===4,`Expected 4 boundary points for square face, got ${l.length}.`),O(Array.isArray(d.edges)&&d.edges.length===4,"Profile face should retain the 4 individual edges."),O(c.every(f=>f?.closedLoop===!1),"Square-face offset edges should remain individual open edges.");const p=Array.isArray(d?.userData?.boundaryLoopsWorld)?d.userData.boundaryLoopsWorld:[];O(p.length===1,`Expected one outer boundary loop, got ${p.length}.`);const u=Array.isArray(d?.userData?.boundaryEdgeGroups)?d.userData.boundaryEdgeGroups:[];O(u.length===4,`Expected 4 boundary edge groups, got ${u.length}.`),O(u.every(f=>Array.isArray(f?.pts)&&f.pts.length===2),"Cube edge groups should remain 2-point segments."),O(c.every(f=>f?.userData?.isHole===!1),"Outer square edges should not be marked as holes.")}i(oo,"afterRun_offsetFace_preserves_individual_edges");async function so(e){const s=new hr({radius:2,height:4,resolution:16,name:"CYL"}),c=xr.generate(s,-1,{newSolidName:"CYL_shell",featureId:"TEST"}).getFaces(!1),l="CYL_shell_CYL_T",d="CYL_shell_CYL_B",p=c.find(w=>w.faceName===l),u=c.find(w=>w.faceName===d);if(!p)throw new Error(`Offset shell test could not find expected top face "${l}".`);if(!u)throw new Error(`Offset shell test could not find expected bottom face "${d}".`);if(!Array.isArray(p.triangles)||!p.triangles.length)throw new Error(`Offset shell top face "${l}" has no triangles to validate.`);if(!Array.isArray(u.triangles)||!u.triangles.length)throw new Error(`Offset shell bottom face "${d}" has no triangles to validate.`);const f=-1,m=.35,h=i(w=>(w.p1[1]+w.p2[1]+w.p3[1])/3,"centroidY"),x=i((w,g,y,v)=>{const[E,D,R]=g,[q,T,Z]=y;let X=0;for(const M of w.triangles){const H=(M.p1[0]+M.p2[0]+M.p3[0])/3,Pe=h(M),xe=(M.p1[2]+M.p2[2]+M.p3[2])/3,J0=E*(H-q)+D*(Pe-T)+R*(xe-Z),Ot=Math.abs(J0-f);Ot>X&&(X=Ot)}if(X>m){const M=w.triangles.reduce((H,Pe)=>{const xe=h(Pe);return xe<H.min&&(H.min=xe),xe>H.max&&(H.max=xe),H},{min:1/0,max:-1/0});throw new Error([`Offset shell ${v} face deviated ${X.toFixed(3)} (tolerance ${m}).`,`Centroid Y range: ${M.min.toFixed(3)} .. ${M.max.toFixed(3)}`,`Triangles: ${w.triangles.length}`].join(" "))}},"checkFaceOffsets");return x(p,[0,1,0],[0,4,0],"top"),x(u,[0,-1,0],[0,0,0],"bottom"),e}i(so,"test_offsetShellGrouping");async function ao(e){return await e.newFeature("PLANE"),e}i(ao,"test_plane");async function io(e){const t=await e.newFeature("P.CO");return t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20,e}i(io,"test_primitiveCone");async function co(e){const t=await e.newFeature("P.CU");return t.inputParams.sizeX=5,t.inputParams.sizeY=10,t.inputParams.sizeZ=15,e}i(co,"test_primitiveCube");async function lo(e){const t=await e.newFeature("P.CY");return t.inputParams.radius=1,t.inputParams.height=5,t.inputParams.resolution=30,e}i(lo,"test_primitiveCylinder");async function uo(e){const t=await e.newFeature("P.PY");return t.inputParams.baseSideLength=5,t.inputParams.height=8,t.inputParams.sides=4,e}i(uo,"test_primitivePyramid");async function fo(e){const t=await e.newFeature("P.S");return t.inputParams.radius=5,t.inputParams.resolution=10,e}i(fo,"test_primitiveSphere");async function po(e){const t=await e.newFeature("P.T");t.inputParams.majorRadius=20,t.inputParams.tubeRadius=5,t.inputParams.resolution=10,t.inputParams.arc=300;const r=await e.newFeature("P.T");return r.inputParams.majorRadius=5,r.inputParams.tubeRadius=3,r.inputParams.resolution=30,r.inputParams.arc=360,e}i(po,"test_primitiveTorus");const mo="PUSHFACE_CUBE",Y0="PUSHFACE_CYL",ce=6,ho=1.25,a0=.5;async function xo(e){const t=await e.newFeature("P.CU");t.inputParams.id=mo,t.inputParams.sizeX=ce,t.inputParams.sizeY=ce,t.inputParams.sizeZ=ce;const r=await e.newFeature("P.CY");r.inputParams.id=Y0,r.inputParams.radius=ho,r.inputParams.height=ce,r.inputParams.transform={position:[ce/2,0,ce/2],rotationEuler:[0,0,0],scale:[1,1,1]};const n=await e.newFeature("B");return n.inputParams.targetSolid=t.inputParams.featureID,n.inputParams.boolean={operation:"SUBTRACT",targets:[r.inputParams.featureID]},e}i(xo,"test_pushFace");function Ue(e,t,r,n){if(typeof e.getFace!="function")throw new Error("Solid missing getFace()");const o=e.getFace(t);if(!Array.isArray(o)||o.length===0)throw new Error(`Face "${t}" not found on solid ${e.name||""}`);const s=new Set,a=[];for(const u of o)for(const f of[u.p1,u.p2,u.p3]){if(!Array.isArray(f)||f.length<3)continue;const m=`${f[0]},${f[1]},${f[2]}`;if(s.has(m))continue;s.add(m);const h=f[0],x=f[2];a.push(Math.hypot(h-r,x-n))}if(!a.length)throw new Error(`No vertices collected for face "${t}"`);const l=a.reduce((u,f)=>u+f,0)/a.length,d=Math.min(...a),p=Math.max(...a);return{avg:l,min:d,max:p}}i(Ue,"measureRadialExtent");function go(e,t,r,n){const o=e.getFace(t);if(!Array.isArray(o)||o.length===0)return 0;let s=0;for(const a of o){if(!Array.isArray(a.p1)||!Array.isArray(a.p2)||!Array.isArray(a.p3))continue;const c=a.p1[0],l=a.p1[1],d=a.p1[2],p=a.p2[0],u=a.p2[1],f=a.p2[2],m=a.p3[0],h=a.p3[1],x=a.p3[2],w=p-c,g=u-l,y=f-d,v=m-c,E=h-l,D=x-d,R=g*D-y*E,q=w*E-g*v,T=(c+p+m)/3,Z=(d+f+x)/3,X=T-r,M=Z-n;s+=X*R+M*q}return s===0?0:s>0?1:-1}i(go,"estimateRadialNormalSign");async function wo(e){const t=(e.scene?.children||[]).filter(f=>f?.type==="SOLID");if(!t.length)throw new Error("[pushFace] No solids created");const r=t[0],n=`${Y0}_S`,o={x:ce/2,z:ce/2},s=Ue(r,n,o.x,o.z),a=r.clone(),c=Ue(a,n,o.x,o.z);r.pushFace(n,a0);const l=Ue(r,n,o.x,o.z);a.pushFace(n,-a0);const d=Ue(a,n,o.x,o.z),p=go(r,n,o.x,o.z),u=1e-6;if(p===0){const f=l.avg-s.avg,m=d.avg-c.avg;if(!(f>u&&m<-u||f<-u&&m>u))throw new Error(`[pushFace] Expected opposite radial motion, got ${f} and ${m}`)}else if(p>0){if(!(l.avg>s.avg+u&&l.min>s.min+u/10))throw new Error(`[pushFace] Positive distance failed to move face outward (avg ${s.avg} → ${l.avg})`);if(!(d.avg<c.avg-u&&d.max<c.max-u/10))throw new Error(`[pushFace] Negative distance failed to move face inward (avg ${c.avg} → ${d.avg})`)}else{if(!(l.avg<s.avg-u&&l.max<s.max-u/10))throw new Error(`[pushFace] Positive distance failed to move face inward (avg ${s.avg} → ${l.avg})`);if(!(d.avg>c.avg+u&&d.min>c.min+u/10))throw new Error(`[pushFace] Negative distance failed to move face outward (avg ${c.avg} → ${d.avg})`)}}i(wo,"afterRun_pushFace");const L={featureID:"SM.F18",rootTransform:[.9870838724223062,.16020433453494515,0,0,.16020433453494515,-.9870838724223062,0,0,0,0,-1,0,0,22.933513,-.5,1],tree:{thickness:1,root:{kind:"flat",id:"SM.TAB3:flat_root",outline:[[15.922428663832992,.9999749363773027],[15.922428130833659,23344293011793158e-24],[24.936370606145015,11.702328425575034],[18.434726468756253,26.225568879708764],[18.91524076397385,23.264301242008393],[-.23208485945451912,20.156678227058677],[-.7127965714465971,23.117913823892142],[2.961251617266918,.4806130036048354],[0,0],[3.7223959386598526,-7392522398809269e-32],[3.7223953502335427,.9999757282964694],[3.7223955449672324,3.999975728296463],[15.92242885856668,3.9999749363772965]],edges:[{id:"SM.TAB3:flat_root:edge",polyline:[[15.922428663832992,.9999749363773027],[15.922428130833659,23344293011793158e-24]]},{id:"SM.TAB3:flat_root:e2",polyline:[[15.922428130833659,23344293011793158e-24],[24.936370606145015,11.702328425575034]]},{id:"SM.TAB3:flat_root:e3",polyline:[[24.936370606145015,11.702328425575034],[18.434726468756253,26.225568879708764]]},{id:"SM.TAB3:flat_root:e4:bridge",polyline:[[18.434726468756253,26.225568879708764],[18.91524076397385,23.264301242008393]]},{id:"SM.TAB3:flat_root:e4",polyline:[[18.91524076397385,23.264301242008393],[-.23208485945451912,20.156678227058677]],bend:{kind:"bend",id:"SM.F9:bend_2",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F9:flat_2",outline:[[0,0],[19.39787100000002,0],[19.39787100000002,3.0000000000000004],[0,3.0000000000000004]],edges:[{id:"SM.F9:flat_2:attach",isAttachEdge:!0,polyline:[[0,0],[19.39787100000002,0]]},{id:"SM.F9:flat_2:top",polyline:[[19.39787100000002,3.0000000000000004],[0,3.0000000000000004]]},{id:"SM.F9:flat_2:left",polyline:[[0,3.0000000000000004],[0,0]]},{id:"SM.F9:flat_2:right",polyline:[[19.39787100000002,0],[19.39787100000002,3.0000000000000004]]}]},attachEdgeId:"SM.F9:flat_2:attach"}]}},{id:"SM.TAB3:flat_root:e4:bridge_1",polyline:[[-.23208485945451912,20.156678227058677],[-.7127965714465971,23.117913823892142]]},{id:"SM.TAB3:flat_root:e5",polyline:[[-.7127965714465971,23.117913823892142],[2.961251617266918,.4806130036048354]],bend:{kind:"bend",id:"SM.F9:bend_1",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F9:flat_1",outline:[[0,0],[22.933513000000012,0],[22.933513000000012,3.0000000000000004],[0,3.0000000000000004]],edges:[{id:"SM.F9:flat_1:attach",isAttachEdge:!0,polyline:[[0,0],[22.933513000000012,0]]},{id:"SM.F9:flat_1:top",polyline:[[22.933513000000012,3.0000000000000004],[0,3.0000000000000004]]},{id:"SM.F9:flat_1:left",polyline:[[0,3.0000000000000004],[0,0]]},{id:"SM.F9:flat_1:right",polyline:[[22.933513000000012,0],[22.933513000000012,3.0000000000000004]]}]},attachEdgeId:"SM.F9:flat_1:attach"}]}},{id:"SM.TAB3:flat_root:e5:bridge",polyline:[[2.961251617266918,.4806130036048354],[0,0]]},{id:"SM.TAB3:flat_root:e1",polyline:[[0,0],[3.7223959386598526,-7392522398809269e-32]]},{id:"SM.TAB3:flat_root:edge_1",polyline:[[3.7223959386598526,-7392522398809269e-32],[3.7223953502335427,.9999757282964694]]},{id:"SM.TAB3:flat_root:edge_2:bridge",polyline:[[3.7223953502335427,.9999757282964694],[3.7223955449672324,3.999975728296463]]},{id:"SM.TAB3:flat_root:edge_2",polyline:[[3.7223955449672324,3.999975728296463],[15.92242885856668,3.9999749363772965]],bend:{kind:"bend",id:"SM.F13:bend",angleDeg:-90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F13:flat",outline:[[12.200033313599473,3.842297553313296],[12.164703568668596,3.8559257397886375],[11.939068265109665,3.9559429911138198],[11.718612363070678,4.066911166871344],[11.503866960129303,4.188562935006142],[11.295349396770701,4.320605225753343],[11.093562010068986,4.462719937668994],[10.898990923513209,4.614564703964317],[10.712104875893294,4.775773717297265],[10.533354092067249,4.945958611034492],[10.363169198330029,5.124709394860537],[10.201960184997075,5.3115954424804555],[10.05011541870176,5.506166529036235],[9.908000706786106,5.707953915737954],[9.77595841603891,5.916471479096555],[9.654306647904113,6.131216882037934],[9.54333847214659,6.351672784076922],[9.443321220821407,6.577308087635854],[9.35449584424748,6.807579217505141],[9.2909286040375,7],[4.937461666166767,7],[1.6458205553889218,5.043522969406299],[0,4.065284454109448],[0,0],[12.200033313599473,0]],edges:[{id:"SM.F13:flat:edge_1",polyline:[[12.200033313599473,3.842297553313296],[12.164703568668596,3.8559257397886375],[11.939068265109665,3.9559429911138198],[11.718612363070678,4.066911166871344],[11.503866960129303,4.188562935006142],[11.295349396770701,4.320605225753343],[11.093562010068986,4.462719937668994],[10.898990923513209,4.614564703964317],[10.712104875893294,4.775773717297265],[10.533354092067249,4.945958611034492],[10.363169198330029,5.124709394860537],[10.201960184997075,5.3115954424804555],[10.05011541870176,5.506166529036235],[9.908000706786106,5.707953915737954],[9.77595841603891,5.916471479096555],[9.654306647904113,6.131216882037934],[9.54333847214659,6.351672784076922],[9.443321220821407,6.577308087635854],[9.35449584424748,6.807579217505141],[9.2909286040375,7]]},{id:"SM.F13:flat:top",polyline:[[9.2909286040375,7],[4.937461666166767,7]]},{id:"SM.F13:flat:edge",polyline:[[4.937461666166767,7],[0,4.065284454109448]],bend:{kind:"bend",id:"SM.F18:bend",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F18:flat",outline:[[0,0],[5.743786472368031,0],[5.743786472368031,7],[0,7]],edges:[{id:"SM.F18:flat:attach",isAttachEdge:!0,polyline:[[0,0],[5.743786472368031,0]]},{id:"SM.F18:flat:top",polyline:[[5.743786472368031,7],[0,7]]},{id:"SM.F18:flat:left",polyline:[[0,7],[0,0]]},{id:"SM.F18:flat:right",polyline:[[5.743786472368031,0],[5.743786472368031,7]]}]},attachEdgeId:"SM.F18:flat:attach"}]}},{id:"SM.F13:flat:left",polyline:[[0,4.065284454109448],[0,0]]},{id:"SM.F13:flat:attach",isAttachEdge:!0,polyline:[[0,0],[12.200033313599473,0]]},{id:"SM.F13:flat:right",polyline:[[12.200033313599473,0],[12.200033313599473,3.842297553313296]]}]},attachEdgeId:"SM.F13:flat:attach"}]}},{id:"SM.TAB3:flat_root:edge_2:bridge_1",polyline:[[15.92242885856668,3.9999749363772965],[15.922428663832992,.9999749363773027]]},{id:"SM.TAB3:flat_root:hole:SM.TAB3:flat_root:hole_1:edge:3",isInternalCutoutEdge:!0,holeId:"SM.TAB3:flat_root:hole_1",holeEdgeIndex:3,holeEdgeSignature:"15.85720,8.55661;8.61462,7.38113",polyline:[[15.857195247283375,8.556605622027856],[8.614620331384927,7.381131149049549]],bend:{kind:"bend",id:"SM.F9:bend",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F9:flat",outline:[[0,0],[7.337345000000003,0],[7.337345000000003,3.0000000000000004],[0,3.0000000000000004]],edges:[{id:"SM.F9:flat:attach",isAttachEdge:!0,polyline:[[0,0],[7.337345000000003,0]]},{id:"SM.F9:flat:top",polyline:[[7.337345000000003,3.0000000000000004],[0,3.0000000000000004]]},{id:"SM.F9:flat:left",polyline:[[0,3.0000000000000004],[0,0]]},{id:"SM.F9:flat:right",polyline:[[7.337345000000003,0],[7.337345000000003,3.0000000000000004]]}]},attachEdgeId:"SM.F9:flat:attach"}]}}],holes:[{id:"SM.TAB3:flat_root:hole_1",outline:[[15.857195247283375,8.556605622027856],[8.614620331384927,7.381131149049549],[7.035448718059691,17.111047857142367],[14.278023633958139,18.286522330120675]]}]},__sheetMeta:{baseType:"FLANGE",defaultInsideRadius:2,defaultKFactor:.5,lastFeatureID:"SM.F18",defaultFlangeLengthReference:"outside",defaultInsetMode:"material_inside"}}};function yo(e){return JSON.parse(JSON.stringify(e))}i(yo,"clone$4");function At(e,t){if(!e)return null;if(String(e.id)===String(t))return e;const r=Array.isArray(e.edges)?e.edges:[];for(const n of r){const o=n?.bend,s=Array.isArray(o?.children)?o.children:[];for(const a of s){const c=At(a?.flat,t);if(c)return c}}return null}i(At,"findFlatById$2");function i0(e,t){return(Array.isArray(e?.edges)?e.edges:[]).find(n=>String(n?.id)===String(t))||null}i(i0,"findEdgeById$1");function c0(e){const t=Array.isArray(e)?e:[];let r=0;for(let n=1;n<t.length;n+=1){const o=t[n-1],s=t[n];r+=Math.hypot(s[0]-o[0],s[1]-o[1])}return r}i(c0,"polylineLength2$1");async function vo(e){await e.reset(),e.features=[];const t=yo(L.tree),r=At(t.root,"SM.TAB3:flat_root");if(!r)throw new Error("Fixture flat SM.TAB3:flat_root not found.");const n=i0(r,"SM.TAB3:flat_root:e2");if(!n)throw new Error("Fixture edge SM.TAB3:flat_root:e2 not found.");const o=Array.isArray(r.outline)?r.outline.length:0,s=c0(n.polyline),a=F0({tree:t,featureID:"SM.TEST.FILLET",targets:[{flatId:"SM.TAB3:flat_root",edgeId:"SM.TAB3:flat_root:e2"}],radius:1.2,resolution:48});if(Number(a?.summary?.applied||0)<1)throw new Error("Corner fillet did not apply to the target edge.");if(Number(a?.summary?.appliedCorners||0)<1)throw new Error("Corner fillet did not apply to any corners.");const c=At(a.tree?.root,"SM.TAB3:flat_root");if(!c)throw new Error("Updated fixture flat SM.TAB3:flat_root not found.");if(!((Array.isArray(c.outline)?c.outline.length:0)>o))throw new Error("Corner fillet did not add rounded vertices to the flat outline.");const d=i0(c,"SM.TAB3:flat_root:e2");if(!d)throw new Error("Updated edge SM.TAB3:flat_root:e2 not found.");if(!(c0(d.polyline)+1e-6<s))throw new Error("Corner fillet did not trim the selected edge span.");const f=Se({featureID:"SM.TEST.FILLET",tree:a.tree,rootMatrix:L.rootTransform,showFlatPattern:!1})?.root||null;if(!f||typeof f._manifoldize!="function")throw new Error("Corner-fillet tree did not build a manifold-capable solid.");try{f._manifoldize()}catch(m){const h=String(m?.message||m||"Unknown manifold error");throw new Error(`sheetMetal_corner_fillet failed manifoldization: ${h}`)}return e}i(vo,"test_sheetMetal_corner_fillet");function _o(e){return JSON.parse(JSON.stringify(e))}i(_o,"clone$3");function Ie(e,t,r=.001){return Math.abs(Number(e)-Number(t))<=r}i(Ie,"approxEqual$3");function V0(e,t){if(!e)return null;if(String(e.id)===String(t))return e;const r=Array.isArray(e.edges)?e.edges:[];for(const n of r){const o=Array.isArray(n?.bend?.children)?n.bend.children:[];for(const s of o){const a=V0(s?.flat,t);if(a)return a}}return null}i(V0,"findFlatById$1");function Eo(e,t,r){const n=e[0]-t[0],o=e[1]-t[1],s=e[2]-t[2],a=n*r[0]+o*r[1]+s*r[2],c=n-r[0]*a,l=o-r[1]*a,d=s-r[2]*a;return Math.hypot(c,l,d)}i(Eo,"distancePointToAxis$1");function So(e,t,r,n){const o=e?._faceNameToID?.get(t),s=Array.isArray(e?._triVerts)?e._triVerts:[],a=Array.isArray(e?._triIDs)?e._triIDs:[],c=Array.isArray(e?._vertProperties)?e._vertProperties:[];if(o==null||!s.length||!a.length||!c.length)return[];const l=[];for(let d=0;d<a.length;d+=1){if(a[d]!==o)continue;const p=d*3;for(let u=0;u<3;u+=1){const f=s[p+u]*3;l.push(Eo([c[f],c[f+1],c[f+2]],r,n))}}return l}i(So,"collectFaceVertexAxisDistances$1");function l0(e,t){return Math.hypot(Number(e?.[0]||0)-Number(t?.[0]||0),Number(e?.[1]||0)-Number(t?.[1]||0),Number(e?.[2]||0)-Number(t?.[2]||0))}i(l0,"distance3");async function bo(e){await e.reset(),e.features=[];const t=1.2,r=_o(L.tree),n=F0({tree:r,featureID:"SM.TEST.FILLET",targets:[{flatId:"SM.TAB3:flat_root",edgeId:"SM.TAB3:flat_root:e2"}],radius:t,resolution:48}),o=V0(n?.tree?.root,"SM.TAB3:flat_root");if(!o)throw new Error("Updated flat for sheet-metal corner fillet metadata test was not found.");const s=(Array.isArray(o.edges)?o.edges:[]).filter(d=>Array.isArray(d?.polyline)&&d.polyline.length>2);if(!s.length)throw new Error("Corner fillet test did not create a rounded thickness edge.");const c=Se({featureID:"SM.TEST.FILLET",tree:n.tree,rootMatrix:L.rootTransform,showFlatPattern:!1})?.root||null;if(!c||typeof c.getFaceMetadata!="function")throw new Error("Corner fillet metadata test did not build a readable solid.");let l=!1;for(const d of s){const p=`SM.TEST.FILLET:FLAT:${o.id}:SIDE:${d.id}`,u=c.getFaceMetadata(p);if(!u||u.type!=="cylindrical")throw new Error(`Expected cylindrical metadata on corner-fillet thickness face "${p}".`);if(!Array.isArray(u.axis)||u.axis.length!==3||!Array.isArray(u.center)||u.center.length!==3)throw new Error(`Corner-fillet thickness face "${p}" is missing axis/center metadata.`);if(!Number.isFinite(u.radius)||u.radius<=0)throw new Error(`Corner-fillet thickness face "${p}" is missing a positive radius.`);if(!Ie(u.height,L.tree.thickness,.001))throw new Error(`Corner-fillet thickness face "${p}" should use the sheet thickness as cylinder height.`);const f=So(c,p,u.center,u.axis);if(!f.length)throw new Error(`Could not sample corner-fillet thickness face "${p}".`);const m=f.reduce((v,E)=>v+E,0)/f.length,h=Math.min(...f),x=Math.max(...f);if(!Ie(m,u.radius,.001))throw new Error(`Corner-fillet thickness face "${p}" radius metadata does not match geometry.`);if(x-h>.002)throw new Error(`Corner-fillet thickness face "${p}" is not geometrically cylindrical.`);Ie(u.radius,t,.001)&&(l=!0);const g=(Array.isArray(c._auxEdges)?c._auxEdges:[]).find(v=>v?.name===`${p}:CENTERLINE`);if(!g||!g.centerline)throw new Error(`Expected a centerline auxiliary edge for corner-fillet thickness face "${p}".`);if(!Array.isArray(g.points)||g.points.length!==2)throw new Error(`Corner-fillet centerline "${p}:CENTERLINE" should contain exactly 2 points.`);const y=[(g.points[0][0]+g.points[1][0])*.5,(g.points[0][1]+g.points[1][1])*.5,(g.points[0][2]+g.points[1][2])*.5];if(!Ie(l0(y,u.center),0,1e-4))throw new Error(`Corner-fillet centerline "${p}:CENTERLINE" is not centered on the cylindrical face axis.`);if(!Ie(l0(g.points[0],g.points[1]),u.height,.001))throw new Error(`Corner-fillet centerline "${p}:CENTERLINE" should span the cylindrical face height.`)}if(!l)throw new Error("No corner-fillet thickness face preserved the requested fillet radius.");return e}i(bo,"test_sheetMetal_corner_fillet_face_cylindrical_metadata");function Ce(e,t,r=.001){return Math.abs(Number(e)-Number(t))<=r}i(Ce,"approxEqual$2");function Po(e,t,r){const n=e[0]-t[0],o=e[1]-t[1],s=e[2]-t[2],a=n*r[0]+o*r[1]+s*r[2],c=n-r[0]*a,l=o-r[1]*a,d=s-r[2]*a;return Math.hypot(c,l,d)}i(Po,"distancePointToAxis");function Fo(e,t,r,n){const o=e?._faceNameToID?.get(t),s=Array.isArray(e?._triVerts)?e._triVerts:[],a=Array.isArray(e?._triIDs)?e._triIDs:[],c=Array.isArray(e?._vertProperties)?e._vertProperties:[];if(o==null||!s.length||!a.length||!c.length)return[];const l=[];for(let d=0;d<a.length;d+=1){if(a[d]!==o)continue;const p=d*3;for(let u=0;u<3;u+=1){const f=s[p+u]*3,m=[c[f],c[f+1],c[f+2]];l.push(Po(m,r,n))}}return l}i(Fo,"collectFaceVertexAxisDistances");function d0(e,t,r){if(!r||r.type!=="cylindrical")throw new Error(`Expected cylindrical metadata on bend face "${t}".`);if(!Number.isFinite(r.radius)||r.radius<=0)throw new Error(`Expected positive bend-face radius on "${t}".`);if(!Number.isFinite(r.height)||r.height<=0)throw new Error(`Expected positive bend-face height on "${t}".`);if(!Array.isArray(r.axis)||r.axis.length!==3)throw new Error(`Expected axis triplet on bend face "${t}".`);if(!Array.isArray(r.center)||r.center.length!==3)throw new Error(`Expected center triplet on bend face "${t}".`);const n=Math.hypot(r.axis[0],r.axis[1],r.axis[2]);if(!Ce(n,1,.001))throw new Error(`Expected normalized bend-face axis on "${t}".`);const o=Fo(e,t,r.center,r.axis);if(!o.length)throw new Error(`Could not sample bend-face vertices for "${t}".`);const s=o.reduce((l,d)=>l+d,0)/o.length,a=Math.min(...o),c=Math.max(...o);if(!Ce(s,r.radius,.001))throw new Error(`Bend-face radius metadata does not match geometry on "${t}".`);if(c-a>.002)throw new Error(`Bend-face vertices are not consistent with a cylindrical radius on "${t}".`)}i(d0,"validateBendFaceMetadata");async function Ao(e){await e.reset(),e.features=[];const{featureID:t,tree:r,rootTransform:n}=L,o=Se({featureID:t,tree:r,rootMatrix:n,showFlatPattern:!1}),s=o?.root||null,a=Array.isArray(o?.evaluated?.bends3D)?o.evaluated.bends3D:[];if(!s||typeof s.getFaceMetadata!="function")throw new Error("Sheet-metal bend metadata test did not produce a readable solid.");if(!a.length)throw new Error("Sheet-metal bend metadata test did not produce any bends.");const c=a.find(x=>x?.bend?.id&&Number.isFinite(x?.midRadius));if(!c)throw new Error("Could not find a bend placement with radius data.");const l=`${t}:BEND:${c.bend.id}:A`,d=`${t}:BEND:${c.bend.id}:B`,p=s.getFaceMetadata(l),u=s.getFaceMetadata(d);d0(s,l,p),d0(s,d,u);const f=Number(r?.thickness);if(!Number.isFinite(f)||f<=0)throw new Error("Sheet-metal test fixture has invalid thickness.");const m=[Math.max(1e-6,c.midRadius-f*.5),c.midRadius+f*.5].sort((x,w)=>x-w),h=[p.radius,u.radius].sort((x,w)=>x-w);if(!Ce(h[0],m[0],.001)||!Ce(h[1],m[1],.001))throw new Error(`Bend-face metadata radii do not match inside/outside bend radii for bend "${c.bend.id}".`);if(!Ce(Math.abs(p.radius-u.radius),f,.001))throw new Error(`Bend-face metadata radii should differ by sheet thickness for bend "${c.bend.id}".`);return e}i(Ao,"test_sheetMetal_bend_face_cylindrical_metadata");function Q(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(Q,"assert$d");async function Io(){const e=st(0,0);Q(e.widthIn===Lt,"Expected missing media width to use the default insert width."),Q(e.heightIn===q0,"Expected missing media height to use the default insert height.");const t=st(1600,800);Q(Math.abs(t.widthIn-Lt)<1e-9,"Expected wide images to keep the default insert width."),Q(Math.abs(t.heightIn-1.6)<1e-9,"Expected wide images to preserve aspect ratio.");const r=st(600,1600);Q(Math.abs(r.heightIn-2.4)<1e-9,"Expected tall images to clamp to the maximum insert height."),Q(Math.abs(r.widthIn-.9)<1e-9,"Expected tall images to preserve aspect ratio after clamping.");const n=at({html:'<div><img alt="clip" src="data:image/png;base64,AAAA" /></div>',plainText:""});Q(n==="data:image/png;base64,AAAA","Expected image clipboard HTML to expose its data URL.");const o=at({html:"<div>not an image</div>",plainText:"data:image/jpeg;base64,BBBB"});Q(o==="data:image/jpeg;base64,BBBB","Expected plain-text data URLs to be accepted as clipboard images.");const s=at({html:'<img src="https://example.com/example.png" />',plainText:""});Q(s==="","Expected remote clipboard image URLs to be ignored so pasted sheets stay self-contained.")}i(Io,"test_sheet_clipboard_image_utils");function To(e){return JSON.parse(JSON.stringify(e))}i(To,"clone$2");async function No(e){await e.reset(),e.features=[];const r=Se({featureID:"SM.TAB3",tree:To(L.tree),rootMatrix:L.rootTransform,showFlatPattern:!0})?.root||null;if(!r)throw new Error("Failed to build source sheet-metal solid for fillet stub test.");e.scene.add(r);const o={type:"EDGE",name:"SM.TAB3:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e2|SM.TAB3:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e3[0]",userData:{}},s=new Kt;s.inputParams=await e.sanitizeInputParams(Kt.inputParamsSchema,{id:"F19",edges:[o],radius:"2.5",direction:"INSET"}),s.inputParams.id="F19",s.inputParams.featureID="F19";const a=await s.run(e),c=Array.isArray(a?.added)?a.added.length:0,l=Array.isArray(a?.removed)?a.removed.length:0;if(c!==0||l!==0)throw new Error("Fillet stub should skip unresolved compound-reference selection without scene edits.");const d=s?.persistentData||{};if(d.stubbed!==!0)throw new Error("Fillet stub should set persistentData.stubbed=true.");if(String(d.strategy||"")!=="miter_tangent_boolean")throw new Error("Fillet stub should report miter_tangent_boolean strategy.");if(String(d.skipped||"")!=="no_edges")throw new Error(`Fillet stub should mark no_edges skip reason, got ${String(d.skipped||"")}.`)}i(No,"test_sheetMetal_corner_fillet_compound_reference");function Do(e){return JSON.parse(JSON.stringify(e))}i(Do,"clone$1");async function Mo(e){await e.reset(),e.features=[];const r=Se({featureID:L.featureID,tree:Do(L.tree),rootMatrix:L.rootTransform,showFlatPattern:!1})?.root||null;if(!r)throw new Error("Failed to build source carrier for corner-fillet selection test.");const s=gr({sourceCarrier:r,selections:[{type:"FACE",name:"SM.F18:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e2",parentSolid:r,userData:{}},{type:"FACE",name:"SM.F18:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e3",parentSolid:r,userData:{}}],edgeSelections:[],radius:1,resolution:32,featureID:"SM.TEST.FILLET.SELECT",showFlatPattern:!1});if(!s?.handled)throw new Error("runSheetMetalCornerFillet did not handle a valid sheet-metal source.");if(Number(s?.summary?.applied||0)<1)throw new Error("Selection-based corner fillet did not resolve selected side faces.");if(!s?.root||typeof s.root._manifoldize!="function")throw new Error("Selection-based corner fillet did not produce a manifold-capable result.");try{s.root._manifoldize()}catch(a){const c=String(a?.message||a||"Unknown manifold error");throw new Error(`sheetMetal_corner_fillet_selection_resolution manifoldization failed: ${c}`)}return e}i(Mo,"test_sheetMetal_corner_fillet_selection_resolution");function se(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(se,"assert$c");async function Co(){const e={type:"SKETCH",name:"SK_001"},t={type:"FACE",name:"SK_001:PROFILE",parent:e},r={type:"SOLID",name:"CUTTER_001"},n={type:"FACE",name:"BODY:FACE_1"},o=Fe.showContexButton([e]);se(o?.field==="profile","Sketch selection should target the profile field."),se(o?.value==="SK_001","Sketch selection should seed the sketch name.");const s=Fe.showContexButton([t]);se(s?.field==="profile","Sketch-face selection should target the profile field."),se(s?.value==="SK_001:PROFILE","Sketch-face selection should seed the selected face name.");const a=Fe.showContexButton([r]);se(a?.field==="profile","Solid selection should target the profile field."),se(a?.value==="CUTTER_001","Solid selection should seed the selected solid name."),se(Fe.showContexButton([n])===!1,"Non-sketch faces should not show the sheet metal cutout context button."),se(Fe.showContexButton([t,r])===!1,"Multiple selections should not show the sheet metal cutout context button.")}i(Co,"test_sheetMetal_cutout_context_button");function ko(e){return JSON.parse(JSON.stringify(e))}i(ko,"clone");function Qe(e,t){if(!e)return null;if(String(e.id)===String(t))return e;const r=Array.isArray(e.edges)?e.edges:[];for(const n of r){const o=n?.bend,s=Array.isArray(o?.children)?o.children:[];for(const a of s){const c=Qe(a?.flat,t);if(c)return c}}return null}i(Qe,"findFlatById");function It(e,t){return(Array.isArray(e?.edges)?e.edges:[]).find(n=>String(n?.id)===String(t))||null}i(It,"findEdgeById");function u0(e){const t=Array.isArray(e)?e:[];let r=0;for(let n=1;n<t.length;n+=1){const o=t[n-1],s=t[n];r+=Math.hypot(s[0]-o[0],s[1]-o[1])}return r}i(u0,"polylineLength2");function Ro(){const e=ko(L.tree),t=Qe(e.root,"SM.F13:flat");if(!t)throw new Error("Fixture flat SM.F13:flat not found.");const r=It(t,"SM.F13:flat:edge");if(!r)throw new Error("Fixture edge SM.F13:flat:edge not found.");if(delete r.bend,Array.isArray(r.polyline)&&r.polyline.length===2){const n=Array.isArray(t.outline)?t.outline:[],o=r.polyline[0],s=r.polyline[1],a=Math.hypot(s[0]-o[0],s[1]-o[1]);if(a>1e-8){let c=null,l=Number.POSITIVE_INFINITY;for(const d of n){if(!Array.isArray(d)||d.length<2)continue;const p=s[0]-o[0],u=s[1]-o[1],f=((d[0]-o[0])*p+(d[1]-o[1])*u)/(a*a);if(!(f>1e-6&&f<1-1e-6))continue;const m=[o[0]+p*f,o[1]+u*f],h=Math.hypot(d[0]-m[0],d[1]-m[1]);h<l&&(l=h,c=[d[0],d[1]])}c&&l<.001&&(r.polyline=[o,c,s])}}return e}i(Ro,"buildPreF18TreeWithMultiPointCutoutEdge");function ge(e){const t=Ro();return wr({tree:t,featureID:"SM.TEST",targets:[{flatId:"SM.F13:flat",edgeId:"SM.F13:flat:edge"}],options:{angleDeg:90,midRadius:2.5,kFactor:.5,legLength:7,requestedLegLength:10,legLengthReference:"outside",legLengthReferenceSetback:3,thickness:1,insideRadius:2,...e}})}i(ge,"applyFlange");async function $o(e){await e.reset(),e.features=[];const t=ge({insetMode:"material_inside",offset:0,edgeStartSetback:0,edgeEndSetback:0});if(t?.summary?.applied!==1)throw new Error("Baseline cutout-edge flange was not applied.");const r=ge({insetMode:"material_inside",offset:0,edgeStartSetback:2,edgeEndSetback:2});if(r?.summary?.applied!==1)throw new Error("Setback cutout-edge flange was not applied.");const n=r.summary.appliedTargets?.[0]||{};if(!(Number(n.edgeStartSetbackApplied)>1.5&&Number(n.edgeEndSetbackApplied)>1.5))throw new Error("Cutout-edge start/end setback were not applied.");const o=Qe(t.tree.root,"SM.F13:flat"),s=Qe(r.tree.root,"SM.F13:flat"),a=It(o,"SM.F13:flat:edge"),c=It(s,"SM.F13:flat:edge"),l=u0(a?.polyline);if(!(u0(c?.polyline)+1e-6<l))throw new Error("Cutout-edge setbacks did not shorten the parent edge span.");const p=ge({insetMode:"bend_outside",offset:0,edgeStartSetback:0,edgeEndSetback:0}),u=ge({insetMode:"material_inside",offset:0,edgeStartSetback:0,edgeEndSetback:0}),f=Number(p.summary.appliedTargets?.[0]?.edgeShiftApplied||0),m=Number(u.summary.appliedTargets?.[0]?.edgeShiftApplied||0);if(!(Math.abs(f-m)>.5))throw new Error("Cutout-edge inset mode did not change edge shift.");const h=ge({insetMode:"material_inside",offset:0,edgeStartSetback:0,edgeEndSetback:0}),x=ge({insetMode:"material_inside",offset:1,edgeStartSetback:0,edgeEndSetback:0}),w=Number(h.summary.appliedTargets?.[0]?.edgeShiftApplied||0),g=Number(x.summary.appliedTargets?.[0]?.edgeShiftApplied||0);if(!(Math.abs(w-g)>.5))throw new Error("Cutout-edge offset did not change edge shift.");return e}i($o,"test_sheetMetal_cutoutEdge_flange_controls");async function Oo(e){await e.reset(),e.features=[];const{featureID:t,tree:r,rootTransform:n}=L,s=Se({featureID:t,tree:r,rootMatrix:n,showFlatPattern:!1})?.root||null;if(!s||typeof s._manifoldize!="function")throw new Error("Sheet-metal fixture did not produce a solid with manifold support.");try{s._manifoldize()}catch(c){const l=String(c?.message||c||"Unknown manifold error");throw new Error(`sheetMetal_nonManifold_sm_f18 failed manifoldization: ${l}`)}const a=typeof s.getFaces=="function"?s.getFaces(!1)||[]:[];if(!Array.isArray(a)||a.length===0)throw new Error("sheetMetal_nonManifold_sm_f18 produced an empty solid.");return e}i(Oo,"test_sheetMetal_nonManifold_sm_f18");async function Lo(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:20,y:0,fixed:!1},{id:2,x:20,y:15,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}}i(Lo,"test_sketch_openLoop");async function Bo(e){const t=e.features.find(s=>s?.type==="S");if(!t)throw new Error("Sketch feature missing from history");const r=e.scene.getObjectByName(t.inputParams.featureID);if(!r)throw new Error("Sketch group not found in scene");let n=0,o=0;if(r.traverse(s=>{s&&(s.type==="FACE"?n++:s.type==="EDGE"&&o++)}),n!==0)throw new Error(`Open sketch generated ${n} face(s)`);if(o===0)throw new Error("Open sketch should expose at least one EDGE")}i(Bo,"afterRun_sketch_openLoop");const We=.001,f0=.001;function p0(e){const r=e?.geometry?.getAttribute?.("position");if(!r||r.itemSize!==3)return[];const n=[],o=new V;for(let s=0;s<r.count;s++)o.set(r.getX(s),r.getY(s),r.getZ(s)).applyMatrix4(e.matrixWorld),n.push([o.x,o.y,o.z]);return n}i(p0,"collectFaceVerticesWorld");function zo(e){if(!Array.isArray(e)||e.length===0)return null;const t=new V;for(const r of e)t.x+=Number(r[0])||0,t.y+=Number(r[1])||0,t.z+=Number(r[2])||0;return t.multiplyScalar(1/e.length)}i(zo,"computeCentroid");function jo(e,t,r){let n=Number.POSITIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=0,a=0;for(const c of e){const l=(Number(c[0])||0)-t.x,d=(Number(c[1])||0)-t.y,p=(Number(c[2])||0)-t.z,u=l*r.x+d*r.y+p*r.z;u<n&&(n=u),u>o&&(o=u),s+=u,a+=1}return{count:a,mean:a?s/a:0,spread:a?o-n:0}}i(jo,"computeSignedDistanceStats");async function Uo(e){const t=await e.newFeature("S");Object.assign(t.inputParams,{id:"S1",sketchPlane:null,curveResolution:32}),t.persistentData={sketch:{points:[{id:0,x:0,y:0,fixed:!0,construction:!0,externalReference:!1},{id:1,x:-10.96543,y:26.783322,fixed:!1,construction:!1,externalReference:!1},{id:2,x:0,y:0,fixed:!0,construction:!1,externalReference:!1},{id:3,x:8.905728,y:14.930946,fixed:!1,construction:!1,externalReference:!1},{id:4,x:8.905728,y:14.930946,fixed:!1,construction:!1,externalReference:!1},{id:5,x:-10.96543,y:26.783322,fixed:!1,construction:!1,externalReference:!1},{id:6,x:-19.87116,y:11.852382,fixed:!1,construction:!1,externalReference:!1},{id:7,x:-19.87116,y:11.852382,fixed:!1,construction:!1,externalReference:!1}],geometries:[{id:1,type:"line",points:[0,3],construction:!1},{id:2,type:"line",points:[4,1],construction:!1},{id:3,type:"line",points:[5,6],construction:!1},{id:4,type:"line",points:[7,2],construction:!1}],constraints:[{id:0,type:"⏚",points:[0],status:"solved"},{id:1,type:"≡",points:[0,2],status:"solved"},{id:2,type:"≡",points:[3,4],status:"solved"},{id:3,type:"≡",points:[1,5],status:"solved"},{id:4,type:"≡",points:[6,7],status:"solved"},{id:5,type:"⟂",points:[0,3,4,1],status:"solved",value:270},{id:6,type:"⟂",points:[4,1,5,6],status:"solved",value:270},{id:7,type:"⟂",points:[5,6,7,2],status:"solved",value:270}]}};const r=await e.newFeature("E");Object.assign(r.inputParams,{id:"E2",profile:"S1:PROFILE",consumeProfileSketch:!0,distance:43.1,distanceBack:73.4,boolean:{targets:[],operation:"NONE"}});const n=await e.newFeature("F");Object.assign(n.inputParams,{id:"F3",edges:["E2:S1:G2_SW|E2:S1:G3_SW[0]","E2:S1:G1_SW|E2:S1:G2_SW[0]"],radius:"9.5",resolution:32,inflate:.1,direction:"AUTO",debug:"NONE",showTangentOverlays:!1});const o=await e.newFeature("R");Object.assign(o.inputParams,{id:"R4",profile:"E2:S1:PROFILE_END",consumeProfileSketch:!0,axis:"E2:S1:G4_SW|E2:S1:PROFILE_END[0]",angle:144,resolution:64,boolean:{targets:["E2"],operation:"UNION"}});const s=await e.newFeature("E");Object.assign(s.inputParams,{id:"E5",profile:"E2:S1:PROFILE_END_END",consumeProfileSketch:!0,distance:0,distanceBack:111.2,boolean:{targets:["E2"],operation:"UNION"}});const a=await e.newFeature("S");return Object.assign(a.inputParams,{id:"S6",sketchPlane:"E5:E2:S1:PROFILE_END_END_START",curveResolution:32}),a.persistentData={sketch:{points:[{id:0,x:0,y:0,fixed:!0,construction:!0,externalReference:!1},{id:1,x:1.282171,y:3.606104,fixed:!1,construction:!1,externalReference:!1},{id:2,x:20.40159,y:3.342124,fixed:!1,construction:!1,externalReference:!1}],geometries:[{id:1,type:"circle",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0],status:"solved"}]}},e}i(Uo,"test_sketch_face_attachment_alignment");async function Wo(e){const t="E5:E2:S1:PROFILE_END_END_START",n="S6:PROFILE",o=e.scene.getObjectByName(t);if(!o||o.type!=="FACE")throw new Error(`[sketch-face-attach] Missing reference face ${t}`);const s=e.scene.getObjectByName("S6");if(!s)throw new Error("[sketch-face-attach] Sketch group S6 not found");let a=null;if(s.traverse(v=>{!a&&v?.type==="FACE"&&v?.name===n&&(a=v)}),!a)throw new Error(`[sketch-face-attach] Missing sketch profile face ${n}`);const c=typeof o.getAverageNormal=="function"?o.getAverageNormal().clone():null;if(!c||c.lengthSq()<1e-12)throw new Error("[sketch-face-attach] Reference face normal is invalid");c.normalize();const l=p0(o),d=p0(a);if(!l.length||!d.length)throw new Error("[sketch-face-attach] Missing reference/sketch vertices");const p=zo(l);if(!p)throw new Error("[sketch-face-attach] Failed to compute reference face centroid");const u=jo(d,p,c);if(Math.abs(u.mean)>We)throw new Error(`[sketch-face-attach] Sketch profile is offset from target face plane (mean=${u.mean}, tol=${We})`);if(u.spread>We)throw new Error(`[sketch-face-attach] Sketch profile is not planar on target face plane (spread=${u.spread}, tol=${We})`);const f=e.features.find(v=>v?.inputParams?.featureID==="S6"),m=Array.isArray(f?.persistentData?.basis?.origin)?new V().fromArray(f.persistentData.basis.origin):null;if(!m)throw new Error("[sketch-face-attach] Sketch basis origin is missing from persistent data");o.updateWorldMatrix(!0,!0);const h=new yr().setFromObject(o).getCenter(new V),x=l[0],w=new V(x[0],x[1],x[2]),g=h.clone().sub(c.clone().multiplyScalar(h.clone().sub(w).dot(c))),y=m.distanceTo(g);if(y>f0)throw new Error(`[sketch-face-attach] Sketch basis origin is not centered on target face (distance=${y}, tol=${f0})`)}i(Wo,"afterRun_sketch_face_attachment_alignment");const k=.01;function I(e,t){if(!e)throw new Error(t)}i(I,"assert$b");function P(e,t,r,n){if(!Number.isFinite(e)||!Number.isFinite(t)||Math.abs(e-t)>r)throw new Error(`${n}. Expected ${t}, got ${e}`)}i(P,"assertNear");function ne(e,t){const r=e.points.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Point ${t} not found`);return r}i(ne,"getPoint");function fe(e,t){const r=e.constraints.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Constraint ${t} not found`);return r}i(fe,"getConstraint");function b(e,t,r){const n=ne(e,t),o=ne(e,r);return Math.hypot(o.x-n.x,o.y-n.y)}i(b,"dist");function Yo(e,t,r,n){const o=ne(e,t),s=ne(e,r),a=ne(e,n),c=s.x-o.x,l=s.y-o.y,d=c*c+l*l;return d>1e-12?Math.abs(((a.x-o.x)*-l+(a.y-o.y)*c)/Math.sqrt(d)):Math.hypot(a.x-o.x,a.y-o.y)}i(Yo,"pointToLineDistance");function Ee(e,t){const r=t.map(o=>ne(e,o));let n=0;for(let o=0;o<r.length;o++){const s=r[o],a=r[(o+1)%r.length];n+=s.x*a.y-a.x*s.y}return n*.5}i(Ee,"signedArea");function m0(e){const t=e.points.map(o=>Number(o.id)).sort((o,s)=>o-s),r=(e.geometries||[]).map(o=>({id:Number(o.id),type:String(o.type),construction:!!o.construction,points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id),n=(e.constraints||[]).map(o=>({id:Number(o.id),type:String(o.type),points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id);return{pointIds:t,geometries:r,constraints:n}}i(m0,"topologySnapshot");function Vo(e,t){const r=new Set((e.points||[]).map(n=>Number(n.id)));for(const n of e.geometries||[])for(const o of n.points||[])I(r.has(Number(o)),`${t}: geometry ${n.id} references missing point ${o}`);for(const n of e.constraints||[])for(const o of n.points||[])I(r.has(Number(o)),`${t}: constraint ${n.id} references missing point ${o}`)}i(Vo,"assertTopologyIntegrity");function be(e,t,r){Vo(t,r);const n=JSON.stringify(m0(e)),o=JSON.stringify(m0(t));I(n===o,`${r}: topology changed`)}i(be,"assertTopologyUnchanged");function he(e){const t=new b0({sketch:JSON.parse(JSON.stringify(e))});return t.solveSketch("full"),t}i(he,"solveSketch");async function Go(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:20,fixed:!1},{id:3,x:0,y:20,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1},{id:102,type:"line",points:[2,3],construction:!1},{id:103,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:1,type:"━",points:[0,1]},{id:2,type:"━",points:[2,3]},{id:3,type:"│",points:[1,2]},{id:4,type:"│",points:[3,0]},{id:5,type:"⟺",points:[0,1],value:40},{id:6,type:"⟺",points:[1,2],value:20}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=Ee(r,[0,1,2,3]);fe(t.sketchObject,5).value=130,t.solveSketch("full");const o=t.sketchObject;be(r,o,"shared-point rectangle width edit"),P(b(o,0,1),130,.05,"shared-point rectangle width changed unexpectedly"),P(b(o,1,2),20,.05,"shared-point rectangle height drifted");const s=ne(o,0);P(s.x,0,k,"shared-point rectangle anchor x moved"),P(s.y,0,k,"shared-point rectangle anchor y moved");const a=Ee(o,[0,1,2,3]);I(Math.abs(a)>1,"shared-point rectangle collapsed"),I(Math.sign(a)===Math.sign(n),"shared-point rectangle flipped orientation")}i(Go,"test_sketch_solver_topology_rect_shared_points");async function Xo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:0,fixed:!1},{id:3,x:40,y:30,fixed:!1},{id:4,x:40,y:30,fixed:!1},{id:5,x:80,y:30,fixed:!1}],geometries:[{id:200,type:"line",points:[0,1],construction:!1},{id:201,type:"line",points:[2,3],construction:!1},{id:202,type:"line",points:[4,5],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:10,type:"≡",points:[1,2]},{id:11,type:"≡",points:[3,4]},{id:12,type:"━",points:[0,1]},{id:13,type:"│",points:[2,3]},{id:14,type:"━",points:[4,5]},{id:15,type:"⟺",points:[0,1],value:40},{id:16,type:"⟺",points:[2,3],value:30},{id:17,type:"⟺",points:[4,5],value:40}]}),r=JSON.parse(JSON.stringify(t.sketchObject));fe(t.sketchObject,16).value=90,t.solveSketch("full");const n=t.sketchObject;be(r,n,"coincident-chain vertical edit"),P(b(n,2,3),90,.05,"coincident-chain edited segment length incorrect"),P(b(n,0,1),40,.05,"coincident-chain left segment length drifted"),P(b(n,4,5),40,.05,"coincident-chain right segment length drifted"),I(b(n,1,2)<k,"coincident-chain joint 1 broke"),I(b(n,3,4)<k,"coincident-chain joint 2 broke")}i(Xo,"test_sketch_solver_topology_coincident_chain");async function Jo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:0,fixed:!1},{id:3,x:40,y:20,fixed:!1},{id:4,x:40,y:20,fixed:!1},{id:5,x:0,y:20,fixed:!1},{id:6,x:0,y:20,fixed:!1},{id:7,x:0,y:0,fixed:!1}],geometries:[{id:300,type:"line",points:[0,1],construction:!1},{id:301,type:"line",points:[2,3],construction:!1},{id:302,type:"line",points:[4,5],construction:!1},{id:303,type:"line",points:[6,7],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:20,type:"≡",points:[1,2]},{id:21,type:"≡",points:[3,4]},{id:22,type:"≡",points:[5,6]},{id:23,type:"≡",points:[7,0]},{id:24,type:"━",points:[0,1]},{id:25,type:"│",points:[2,3]},{id:26,type:"━",points:[4,5]},{id:27,type:"│",points:[6,7]},{id:28,type:"⟺",points:[0,1],value:40},{id:29,type:"⟺",points:[2,3],value:20}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=Ee(r,[0,1,3,5]);fe(t.sketchObject,28).value=120,fe(t.sketchObject,29).value=55,t.solveSketch("full");const o=t.sketchObject;be(r,o,"coincident-loop dual edit"),P(b(o,0,1),120,.06,"coincident-loop width incorrect after edit"),P(b(o,2,3),55,.06,"coincident-loop height incorrect after edit"),I(b(o,1,2)<k,"coincident-loop corner 1 broke"),I(b(o,3,4)<k,"coincident-loop corner 2 broke"),I(b(o,5,6)<k,"coincident-loop corner 3 broke"),I(b(o,7,0)<k,"coincident-loop corner 4 broke");const s=Ee(o,[0,1,3,5]);I(Math.abs(s)>1,"coincident-loop collapsed"),I(Math.sign(s)===Math.sign(n),"coincident-loop flipped orientation")}i(Jo,"test_sketch_solver_topology_coincident_loop_no_flip");async function Ko(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:20,fixed:!1},{id:3,x:0,y:20,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1},{id:102,type:"line",points:[2,3],construction:!1},{id:103,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:1,type:"━",points:[0,1]},{id:2,type:"━",points:[2,3]},{id:3,type:"│",points:[1,2]},{id:4,type:"│",points:[3,0]},{id:5,type:"⟺",points:[0,1],value:40},{id:6,type:"⟺",points:[1,2],value:20}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=Ee(r,[0,1,2,3]),o=[130,25,80,60];for(const s of o){fe(t.sketchObject,5).value=s,t.solveSketch("full");const a=t.sketchObject;be(r,a,"shared-point rectangle round-trip edits"),P(b(a,0,1),s,.07,"shared-point rectangle round-trip width mismatch"),P(b(a,1,2),20,.07,"shared-point rectangle round-trip height drift");const c=ne(a,0);P(c.x,0,k,"shared-point rectangle round-trip anchor x moved"),P(c.y,0,k,"shared-point rectangle round-trip anchor y moved");const l=Ee(a,[0,1,2,3]);I(Math.abs(l)>1,"shared-point rectangle round-trip collapsed"),I(Math.sign(l)===Math.sign(n),"shared-point rectangle round-trip flipped orientation")}}i(Ko,"test_sketch_solver_topology_rect_round_trip_sequence");async function qo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:0,fixed:!1},{id:3,x:40,y:30,fixed:!1},{id:4,x:40,y:30,fixed:!1},{id:5,x:80,y:30,fixed:!1}],geometries:[{id:200,type:"line",points:[0,1],construction:!1},{id:201,type:"line",points:[2,3],construction:!1},{id:202,type:"line",points:[4,5],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:10,type:"≡",points:[1,2]},{id:11,type:"≡",points:[3,4]},{id:12,type:"━",points:[0,1]},{id:13,type:"│",points:[2,3]},{id:14,type:"━",points:[4,5]},{id:15,type:"⟺",points:[0,1],value:40},{id:16,type:"⟺",points:[2,3],value:30},{id:17,type:"⟺",points:[4,5],value:40}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=[90,15,60,45];for(const o of n){fe(t.sketchObject,16).value=o,t.solveSketch("full");const s=t.sketchObject;be(r,s,"coincident-chain multi-step edits"),P(b(s,2,3),o,.08,"coincident-chain multi-step edited segment mismatch"),P(b(s,0,1),40,.08,"coincident-chain multi-step left segment drift"),P(b(s,4,5),40,.08,"coincident-chain multi-step right segment drift"),I(b(s,1,2)<k,"coincident-chain multi-step joint 1 broke"),I(b(s,3,4)<k,"coincident-chain multi-step joint 2 broke")}}i(qo,"test_sketch_solver_topology_coincident_chain_multi_step");async function Zo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:1e3,y:0,fixed:!1}],geometries:[{id:400,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:30,type:"⟺",points:[0,1],value:1e3}]}),r=fe(t.sketchObject,30);r.value=1,t.solveSketch("full");const n=t.sketchObject;P(b(n,0,1),1,.01,"distance slide large-drop did not settle in one solve")}i(Zo,"test_sketch_solver_distance_slide_large_drop_settles_single_solve");async function Ho(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:120,y:0,fixed:!1},{id:2,x:30,y:30,fixed:!1}],geometries:[{id:500,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:1,type:"━",points:[0,1]},{id:2,type:"↥",points:[0,1,2],value:30}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=fe(t.sketchObject,2);n.value=80,t.solveSketch("full");const o=t.sketchObject;be(r,o,"line-to-point distance edit"),P(Yo(o,0,1,2),80,.08,"line-to-point distance mismatch");const s=ne(o,0);P(s.x,0,k,"line-to-point distance anchor x moved"),P(s.y,0,k,"line-to-point distance anchor y moved")}i(Ho,"test_sketch_solver_line_to_point_distance_constraint");async function Qo(e){const t=await e.newFeature("P.CU");return t.inputParams.sizeX=2,t.inputParams.sizeY=3,t.inputParams.sizeZ=4,e}i(Qo,"test_solidMetrics");async function es(e){try{const t=(e.scene?.children||[]).filter(g=>g&&g.type==="SOLID");if(!t.length){console.warn("[solidMetrics] No solids in scene.");return}const r=t[0],n=2,o=3,s=4,a=n*o*s,c=2*(n*o+o*s+s*n),l=typeof r.volume=="function"?r.volume():NaN,d=typeof r.surfaceArea=="function"?r.surfaceArea():NaN,p=i((g,y,v=1e-6)=>Math.abs(g-y)<=v*Math.max(1,Math.abs(y)),"approx"),u=i(g=>Number.isFinite(g)?g.toFixed(6):String(g),"fmt");console.log(`[solidMetrics] Solid volume: ${u(l)} (expected ${u(a)}) ${p(l,a)?"OK":"MISMATCH"}`),console.log(`[solidMetrics] Solid surface area: ${u(d)} (expected ${u(c)}) ${p(d,c)?"OK":"MISMATCH"}`);const f=r.children.filter(g=>g?.type==="FACE"),m=new Map([["_NX",o*s],["_PX",o*s],["_NY",n*s],["_PY",n*s],["_NZ",n*o],["_PZ",n*o]]);for(const g of f){const y=String(g?.name||""),v=Array.from(m.keys()).find(R=>y.endsWith(R));if(!v)continue;const E=typeof g.surfaceArea=="function"?g.surfaceArea():NaN,D=m.get(v);console.log(`[solidMetrics] Face ${y} area: ${u(E)} (expected ${u(D)}) ${p(E,D)?"OK":"MISMATCH"}`)}const x=r.children.filter(g=>g?.type==="EDGE").map(g=>typeof g.length=="function"?g.length():NaN),w=Array.from(new Set(x.map(g=>Number.isFinite(g)?g.toFixed(6):String(g))));console.log(`[solidMetrics] Distinct edge lengths: ${w.join(", ")} (expected ${[n,o,s].map(g=>g.toFixed(6)).join(", ")})`)}catch(t){console.warn("[solidMetrics] afterRun error:",t?.message||t)}}i(es,"afterRun_solidMetrics");async function ts(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.fileToImport=rs,t.inputParams.deflectionAngle=15,e}i(ts,"test_stlLoader");const rs=`
|
|
3
|
+
`)}n.options.maxEntries!==1/0&&n._logs.length>=n.options.maxEntries&&n._logs.shift(),n._logs.push(d)}try{if(typeof r=="function")return r.apply(n._orig,a)}catch(d){try{return n._orig&&typeof n._orig.log=="function"?n._orig.log("ConsoleCapture forwarding error:",d):void 0}catch{}}}i(s,"wrapped");try{Object.defineProperty(s,"name",{value:`capture_${t}`})}catch{}return s}};i(kt,"ConsoleCapture");let wt=kt;const C=!!globalThis?.process?.versions?.node&&typeof window>"u";function $(){if(!C)return A;if(typeof require=="function"){const t=require("node:path");return(t.default??t).posix}const e=globalThis?.module;if(e&&typeof e.createRequire=="function"){const r=e.createRequire(globalThis.__filename||globalThis.process.cwd())("node:path");return(r.default??r).posix}return A}i($,"getNodePosixSync");const N0="/",br=":";function oe(e){if(typeof e!="string")throw new TypeError("Path must be a string")}i(oe,"_assertString");function rt(e){return e.startsWith(N0)}i(rt,"_isAbsolute");function D0(e,t){const r=e.split("/"),n=[];for(let o=0;o<r.length;o++){const s=r[o];!s||s==="."||(s===".."?n.length&&n[n.length-1]!==".."?n.pop():t&&n.push(".."):n.push(s))}return n.join("/")}i(D0,"_normalizeString");function M0(e){if(oe(e),e==="")return".";const t=rt(e),r=e.endsWith("/");let n=D0(e,!t);return n===""&&!t&&(n="."),n!==""&&r&&(n+="/"),(t?"/":"")+n}i(M0,"_normalize");function Pr(...e){let t="";for(let r=0;r<e.length;r++){const n=e[r];oe(n),n!==""&&(t===""?t=n:t+="/"+n)}return M0(t)}i(Pr,"_join");function vt(...e){let t="",r=!1;for(let o=e.length-1;o>=0;o--){const s=e[o];if(s!==void 0&&(oe(s),s!==""&&(t=s+"/"+t,s.startsWith("/")))){r=!0;break}}r||(t="/"+t,r=!0);const n=D0(t,!1);return(r?"/":"")+n||(r?"/":".")}i(vt,"_resolve");function Fr(e,t){if(oe(e),oe(t),e===t||(e=vt(e),t=vt(t),e===t))return"";const r=e.slice(1).split("/").filter(Boolean),n=t.slice(1).split("/").filter(Boolean);let o=0;const s=Math.min(r.length,n.length);for(;o<s&&r[o]===n[o];o++);const a=r.slice(o).map(()=>".."),c=n.slice(o);return a.concat(c).join("/")||""}i(Fr,"_relative");function C0(e){if(oe(e),e.length===0)return".";const t=rt(e),r=e.endsWith("/")?e.length-1:e.length;let n=-1;for(let o=r-1;o>=0;--o)if(e.charCodeAt(o)===47){n=o;break}return n===-1?t?"/":".":t&&n===0?"/":e.slice(0,n)}i(C0,"_dirname");function R0(e,t=""){if(oe(e),t!==void 0&&typeof t!="string")throw new TypeError("ext must be a string");let r=e.length;if(r===0)return"";for(;r>0&&e.charCodeAt(r-1)===47;)r--;if(r===0)return"/";let n=0;for(let s=r-1;s>=0;--s)if(e.charCodeAt(s)===47){n=s+1;break}let o=e.slice(n,r);return t&&o.endsWith(t)&&t!==""&&t!==o&&(o=o.slice(0,o.length-t.length)),o}i(R0,"_basename");function k0(e){oe(e);let t=-1,r=0,n=-1,o=0;for(let s=e.length-1;s>=0;--s){const a=e.charCodeAt(s);if(a===47){if(n!==-1){r=s+1;break}continue}n===-1&&(n=s+1),a===46?t===-1?t=s:o!==1&&(o=1):t!==-1&&(o=-1)}return t===-1||n===-1||o===0||t===r?"":e.slice(t,n)}i(k0,"_extname");function Ar(e){oe(e);const t=rt(e)?"/":"",r=C0(e),n=R0(e),o=k0(n),s=o?n.slice(0,n.length-o.length):n;return{root:t,dir:r,base:n,ext:o,name:s}}i(Ar,"_parse");function Ir(e){const t=e.dir||e.root||"",r=e.base||(e.name||"")+(e.ext||"");return t?t.endsWith("/")?t+r:t+"/"+r:r||"."}i(Ir,"_format");function Tr(e){return e}i(Tr,"_toNamespacedPath");const A={sep:N0,delimiter:br,normalize:M0,join:Pr,resolve:vt,isAbsolute:rt,relative:Fr,dirname:C0,basename:R0,extname:k0,parse:Ar,format:Ir,toNamespacedPath:Tr,posix:null,win32:null};A.posix=A;const qe={get sep(){return C?$().sep:A.sep},get delimiter(){return C?$().delimiter:A.delimiter},normalize:i((...e)=>C?$().normalize(...e):A.normalize(...e),"normalize"),join:i((...e)=>C?$().join(...e):A.join(...e),"join"),resolve:i((...e)=>C?$().resolve(...e):A.resolve(...e),"resolve"),isAbsolute:i((...e)=>C?$().isAbsolute(...e):A.isAbsolute(...e),"isAbsolute"),relative:i((...e)=>C?$().relative(...e):A.relative(...e),"relative"),dirname:i((...e)=>C?$().dirname(...e):A.dirname(...e),"dirname"),basename:i((...e)=>C?$().basename(...e):A.basename(...e),"basename"),extname:i((...e)=>C?$().extname(...e):A.extname(...e),"extname"),parse:i((...e)=>C?$().parse(...e):A.parse(...e),"parse"),format:i((...e)=>C?$().format(...e):A.format(...e),"format"),toNamespacedPath:i((...e)=>C?$().toNamespacedPath(...e):A.toNamespacedPath(...e),"toNamespacedPath"),posix:null,win32:null};qe.posix=qe;const Nr=new Proxy({},{get(){throw new Error("POSIX-only path module: use the named export `posix` with your VFS.")}});qe.win32=Nr;const G=qe,lt=.01,Zt="src/tests/fixtures/sketchSolverTopology";function Y(e,t){if(!e)throw new Error(t)}i(Y,"assert$h");function Ze(e,t){const r=e.points.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Point ${t} not found`);return r}i(Ze,"getPoint$1");function Dr(e,t){const r=e.constraints.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Constraint ${t} not found`);return r}i(Dr,"getConstraint$1");function Ht(e,t,r){const n=Ze(e,t),o=Ze(e,r);return Math.hypot(o.x-n.x,o.y-n.y)}i(Ht,"dist$1");function Qt(e,t){const r=t.map(o=>Ze(e,o));let n=0;for(let o=0;o<r.length;o++){const s=r[o],a=r[(o+1)%r.length];n+=s.x*a.y-a.x*s.y}return n*.5}i(Qt,"signedArea$1");function e0(e){const t=(e.points||[]).map(o=>Number(o.id)).sort((o,s)=>o-s),r=(e.geometries||[]).map(o=>({id:Number(o.id),type:String(o.type),construction:!!o.construction,points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id),n=(e.constraints||[]).map(o=>({id:Number(o.id),type:String(o.type),points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id);return{pointIds:t,geometries:r,constraints:n}}i(e0,"topologySnapshot$1");function Mr(e,t){const r=new Set((e.points||[]).map(n=>Number(n.id)));for(const n of e.geometries||[])for(const o of n.points||[])Y(r.has(Number(o)),`${t}: geometry ${n.id} references missing point ${o}`);for(const n of e.constraints||[])for(const o of n.points||[])Y(r.has(Number(o)),`${t}: constraint ${n.id} references missing point ${o}`)}i(Mr,"assertTopologyIntegrity$1");function Cr(e,t="fixture"){const r=String(e||"").trim();return r&&r.toLowerCase().replace(/[^a-z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").slice(0,120)||t}i(Cr,"sanitizeName");function dt(e,t,r,n){if(!Number.isFinite(e)||!Number.isFinite(t)||Math.abs(e-t)>r)throw new Error(`${n}. Expected ${t}, got ${e}`)}i(dt,"assertNear$1");function Rr(e,t={}){if(typeof e!="string"||!e.trim())return null;const r=Object.keys(t||{}),n=r.map(o=>Number(t[o]));if(!n.every(o=>Number.isFinite(o)))return null;try{const s=Function(...r,`"use strict"; return (${e});`)(...n),a=Number(s);return Number.isFinite(a)?a:null}catch{return null}}i(Rr,"evaluateExpressionWithVars");function $0(e,{maxPasses:t=8,stopWhenConstraintsClear:r=!0}={}){const n=Math.max(1,Number(t)||1);let o=null;for(let s=0;s<n;s++){e.solveSketch("full");const a=e.sketchObject,c=JSON.stringify(a?.points||[]),l=Array.isArray(a?.constraints)?a.constraints.some(d=>typeof d?.error=="string"&&d.error.length>0):!1;if(r&&!l||c===o)break;o=c}}i($0,"solveWithSettle");function kr(e,t,r){const n=t&&typeof t=="object"?t:null;if(!n)throw new Error(`${r}: expressionValues edit requires an object`);for(const o of e.sketchObject?.constraints||[]){if(typeof o?.valueExpr!="string"||!o.valueExpr.length)continue;const s=Rr(o.valueExpr,n);if(!Number.isFinite(s))continue;const a=o?.type==="⟺"&&o?.displayStyle==="diameter"&&o?.valueExprMode==="diameter";o.value=a?Number(s)*.5:Number(s)}}i(kr,"applyExpressionValuesToConstraints");function $r(e,t,r){for(const n of t||[])if(!(!n||typeof n!="object")){if(Object.prototype.hasOwnProperty.call(n,"expressionValues"))kr(e,n.expressionValues,r);else{const o=Number(n.constraintId),s=Number(n.value);if(!Number.isFinite(o))throw new Error(`${r}: edit has invalid constraintId`);if(!Number.isFinite(s))throw new Error(`${r}: edit for constraint ${o} has invalid value`);const a=Dr(e.sketchObject,o);a.value=s}$0(e,{maxPasses:Number.isFinite(Number(n.maxPasses))?Number(n.maxPasses):8,stopWhenConstraintsClear:n.stopWhenConstraintsClear!==!1})}}i($r,"applyEdits");function Or({before:e,after:t,expect:r,contextLabel:n}){if(Mr(t,n),r?.topologyUnchanged!==!1){const s=JSON.stringify(e0(e)),a=JSON.stringify(e0(t));Y(s===a,`${n}: topology changed`)}for(const s of r?.distances||[]){const a=Number(s.a),c=Number(s.b),l=Number(s.value),d=Number.isFinite(Number(s.tol))?Number(s.tol):lt;Y(Number.isFinite(a)&&Number.isFinite(c),`${n}: invalid distance pair`),dt(Ht(t,a,c),l,d,`${n}: distance [${a},${c}] mismatch`)}for(const s of r?.anchors||[]){const a=Number(s.pointId),c=Number.isFinite(Number(s.tol))?Number(s.tol):lt,l=Ze(t,a);Number.isFinite(Number(s.x))&&dt(l.x,Number(s.x),c,`${n}: anchor ${a} x drift`),Number.isFinite(Number(s.y))&&dt(l.y,Number(s.y),c,`${n}: anchor ${a} y drift`)}for(const s of r?.coincidentPairs||[]){const a=Number(s.a),c=Number(s.b),l=Number.isFinite(Number(s.tol))?Number(s.tol):lt;Y(Number.isFinite(a)&&Number.isFinite(c),`${n}: invalid coincident pair`),Y(Ht(t,a,c)<=l,`${n}: coincident pair [${a},${c}] broke`)}for(const s of r?.orientationLoops||[]){const a=Array.isArray(s?.pointIds)?s.pointIds.map(f=>Number(f)):[];Y(a.length>=3,`${n}: orientation loop needs at least 3 points`);const c=Number.isFinite(Number(s.minAbsArea))?Number(s.minAbsArea):1,l=Qt(e,a),d=Qt(t,a);Y(Math.abs(d)>c,`${n}: loop collapsed`),s.preserveSign!==!1&&Y(Math.sign(d)===Math.sign(l),`${n}: loop orientation flipped`)}}i(Or,"runExpectations");async function Lr(e,t){const n=`sketch fixture ${e?.name||G.basename(t)}`;Y(e&&typeof e=="object",`${n}: fixture is not an object`),Y(Array.isArray(e.edits),`${n}: missing edits array`);let o=null;if(e.sketch&&typeof e.sketch=="object")o=e.sketch;else if(typeof e.sourcePartFile=="string"&&e.sourcePartFile.trim().length){const l=e.sourcePartFile.trim(),d=await S.promises.readFile(l,"utf8"),f=JSON.parse(d),u=Array.isArray(f?.features)?f.features:[];let p=null;if(e.sourceFeatureId!=null){const m=String(e.sourceFeatureId);p=u.find(h=>h?.type==="S"&&(String(h?.inputParams?.id??"")===m||String(h?.inputParams?.featureID??"")===m))||null}p||(p=u.find(m=>m?.type==="S"&&m?.persistentData?.sketch)||null),o=p?.persistentData?.sketch||null}Y(o&&typeof o=="object",`${n}: missing sketch or sourcePartFile sketch`);const s=new b0({sketch:JSON.parse(JSON.stringify(o))});$0(s,{maxPasses:Number.isFinite(Number(e.initialSolvePasses))?Number(e.initialSolvePasses):4,stopWhenConstraintsClear:!0});const a=JSON.parse(JSON.stringify(s.sketchObject));$r(s,e.edits,n);const c=s.sketchObject;Or({before:a,after:c,expect:e.expect||{},contextLabel:n})}i(Lr,"runFixture");async function Br(e){if(!(typeof process<"u"&&process.versions&&process.versions.node))return 0;const t=new Set((e||[]).map(s=>s?._sourceFile).filter(s=>typeof s=="string"&&s.length>0));let r=[];try{r=await S.promises.readdir(Zt)}catch{return 0}const n=r.filter(s=>typeof s=="string"&&s.toLowerCase().endsWith(".json")).sort((s,a)=>s.localeCompare(a));let o=0;for(const s of n){const a=G.join(Zt,s);if(t.has(a))continue;let c=null;try{const p=await S.promises.readFile(a,"utf8");c=JSON.parse(p)}catch(p){const m=p?.message||String(p);throw new Error(`Failed to read sketch fixture ${a}: ${m}`)}const l=String(s).replace(/\.[^.]+$/,""),f=`test_sketch_solver_fixture_${Cr(c?.name||l,`fixture_${o}`)}`,u=i(async function(){await Lr(c,a)},"sketchSolverFixtureTest");try{Object.defineProperty(u,"name",{value:f,configurable:!0})}catch{}e.push({test:u,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0,_sourceFile:a}),t.add(a),o+=1}return o}i(Br,"registerSketchSolverTopologyFixtureTests");async function zr(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20;const r=await e.newFeature("P.CU");r.inputParams.sizeX=2,r.inputParams.sizeY=2,r.inputParams.sizeZ=20;const n=await e.newFeature("B");return n.inputParams.targetSolid=r.inputParams.featureID,n.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(zr,"test_boolean_subtract");async function jr(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=10,t.inputParams.sizeY=10,t.inputParams.sizeZ=10;const r=await e.newFeature("P.CU");return r.inputParams.sizeX=10,r.inputParams.sizeY=10,r.inputParams.sizeZ=10,r.inputParams.transform={position:[5,0,0],rotationEuler:[0,0,0],scale:[1,1,1]},r.inputParams.boolean={operation:"UNION",targets:[t.inputParams.featureID]},e}i(jr,"test_boolean_operation_target_name_preserved");async function Ur(e){const t=(e?.features||[]).filter(c=>c?.type==="P.CU");if(t.length<2)throw new Error("[boolean target name] Expected at least two primitive cube features.");const r=t[0]?.inputParams?.featureID,n=t[1]?.inputParams?.featureID;if(!r||!n)throw new Error("[boolean target name] Missing feature IDs for cube setup.");const o=(e?.scene?.children||[]).filter(c=>c?.type==="SOLID");if(o.length!==1)throw new Error(`[boolean target name] Expected exactly one solid after union, got ${o.length}.`);const s=o[0];if(String(s?.name||"")!==String(r))throw new Error(`[boolean target name] Expected result name "${r}", got "${String(s?.name||"")}".`);if(e?.scene?.getObjectByName?.(n))throw new Error(`[boolean target name] Tool-named solid "${n}" should have been replaced.`)}i(Ur,"afterRun_boolean_operation_target_name_preserved");function ut(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ut,"assert$g");async function Wr(){const e=new Ge.Cube({x:10,y:10,z:10,name:"BASE"}),t=new Ge.Cube({x:6,y:6,z:6,name:"TOOL"});e.setFaceMetadata("BASE_NX",{sourceFeatureId:"BASE_FEATURE",marker:"base-nx"}),t.setFaceMetadata("TOOL_PX",{sourceFeatureId:"TOOL_FEATURE",marker:"tool-px"}),t.bakeTRS({position:[7,2,2],rotationEuler:[0,0,0],scale:[1,1,1]}),e.union=()=>{throw new Error("forced union failure for metadata fallback test")};const r={scene:{getObjectByName(){return null}}},n=await Ge.applyBooleanOperation(r,e,{operation:"UNION",targets:[t]},"BOOL_META"),o=Array.isArray(n?.added)?n.added[0]:null;ut(o,"Expected boolean to return a result solid.");const s=o.getFaceMetadata("BASE_NX");ut(s&&s.sourceFeatureId==="BASE_FEATURE","Base face provenance should survive union fallback."),ut(s&&s.marker==="base-nx","Base face custom metadata should survive union fallback.")}i(Wr,"test_boolean_face_metadata_preserved");async function Yr(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=20,t.inputParams.sizeY=20,t.inputParams.sizeZ=20;const r=await e.newFeature("CHAMFER");return r.inputParams.edges=[`${t.inputParams.featureID}_PZ`],r.inputParams.distance=3,r.inputParams.inflate=5e-4,r.inputParams.direction="INSET",e}i(Yr,"test_Chamfer");function nt(e,t){const r=e[0]-t[0],n=e[1]-t[1],o=e[2]-t[2];return r*r+n*n+o*o}i(nt,"pointDistanceSq");function le(e,t,r=1e-12){if(nt(e,t)>r*r)throw new Error(`Expected point ${JSON.stringify(e)} near ${JSON.stringify(t)} (eps=${r})`)}i(le,"assertPointNear");async function Vr(){const e=[[0,0,0],[1,.35,0],[2,-.5,0],[3,.45,0],[4,0,0]],t=Xt(e,{fitStrength:1});if(!Array.isArray(t)||t.length!==e.length)throw new Error("Curve fit should return one output point per input point.");if(le(t[0],e[0],1e-12),le(t[t.length-1],e[e.length-1],1e-12),Dt(e,t))throw new Error("Fitted polyline should not locally reverse direction against the source edge.");let r=0;for(let o=1;o<e.length-1;o++)nt(e[o],t[o])>1e-12&&r++;if(r<=0)throw new Error("At least one interior point should be snapped to the fitted curve.");const n=Xt(e,{fitStrength:0});for(let o=0;o<e.length;o++)le(n[o],e[o],1e-12)}i(Vr,"test_edge_smooth_curve_fit");function Gr(e,t){const r=Array.isArray(e)?e:[],n=Array.isArray(t)?t:[],o=r.length;if(o!==n.length||o<3)return!0;for(let s=0;s<o;s++){const a=(s+1)%o,c=r[s],l=r[a],d=n[s],f=n[a],u=[l[0]-c[0],l[1]-c[1],l[2]-c[2]],p=[f[0]-d[0],f[1]-d[1],f[2]-d[2]],m=Math.hypot(u[0],u[1],u[2]),h=Math.hypot(p[0],p[1],p[2]);if(!(m>1e-12)||!(h>1e-12))continue;if((u[0]*p[0]+u[1]*p[1]+u[2]*p[2])/(m*h)<-1e-6)return!0}return!1}i(Gr,"hasLocalBacktrackingAgainstSourceClosed");async function Xr(){const e=[[1,0,0],[.45,.95,.02],[-.55,.8,-.03],[-1,0,0],[-.45,-.85,.03],[.55,-.75,-.02]],t=Jt(e,{fitStrength:1});if(!Array.isArray(t)||t.length!==e.length)throw new Error("Closed-loop curve fit should return one output point per input point.");if(Gr(e,t))throw new Error("Closed-loop fitted polyline should not locally reverse direction.");let r=0;for(let o=0;o<e.length;o++)nt(e[o],t[o])>1e-12&&r++;if(r<=0)throw new Error("Closed-loop fit should move at least one loop point.");const n=Jt(e,{fitStrength:0});for(let o=0;o<e.length;o++)le(n[o],e[o],1e-12)}i(Xr,"test_edge_smooth_curve_fit_closed_loop");async function Jr(){const e=[0,0,0,1,0,0,1,1,0,0,1,0],t=[0,1,2,0,2,3],r=new Map;r.set(1,{x:.2,y:.8,z:0,count:1});const n=mr(e,t,r,{minArea2Ratio:.04,minNormalDot:.1,minArea2Abs:1e-24});if((Number(n?.movedVertices)||0)!==1)throw new Error("Constrained edge smoothing should move the target vertex.");if((Number(n?.constrainedVertices)||0)<=0)throw new Error("Constrained edge smoothing should scale back fold-causing targets.");const o=e[3],s=e[4];if(!Number.isFinite(o)||!Number.isFinite(s))throw new Error("Constrained edge smoothing produced invalid coordinates.");if(Math.abs(o-.2)<1e-9&&Math.abs(s-.8)<1e-9)throw new Error("Constrained smoothing should not apply a fold-causing target at full displacement.");const a=i((c,l,d)=>{const f=c*3,u=l*3,p=d*3,m=e[u+0]-e[f+0],h=e[u+1]-e[f+1],x=e[p+0]-e[f+0],y=e[p+1]-e[f+1];return m*y-h*x},"triNormalZ");if(!(a(0,1,2)>0))throw new Error("Primary triangle normal flipped after constrained smoothing.");if(!(a(0,2,3)>0))throw new Error("Adjacent triangle normal flipped after constrained smoothing.")}i(Jr,"test_edge_smooth_constraints_prevent_triangle_foldback");function O0(e){const t=Array.isArray(e)?e.map(o=>[o[0],o[1],o[2]]):[],r=[];for(const o of t)r.push(o[0],o[1],o[2]);const n=i((o,s)=>{const a=o.slice(),c=s.map(u=>[u[0],u[1],u[2]]),l={type:"EDGE",name:"MOCK_EDGE_0",userData:{faceA:"FACE_A",faceB:"FACE_B",polylineLocal:c.map(u=>[u[0],u[1],u[2]])},parent:null,parentSolid:null},d={type:"SOLID",name:"MOCK_SOLID",_vertProperties:a,_triVerts:[0,1,2,0,2,3,0,3,4],_triIDs:[0,0,0],_vertKeyToIndex:new Map,_dirty:!1,_faceIndex:null,_manifold:null,traverse(u){typeof u=="function"&&u(l)},visualize(){},_manifoldize(){},getBoundaryEdgePolylines(){return[{name:l.name,faceA:l.userData.faceA,faceB:l.userData.faceB,indices:[0,1,2,3,4],positions:c.map(u=>[u[0],u[1],u[2]]),closedLoop:!1}]},clone(){return n(a,c)}},f={type:"FACE",name:"MOCK_FACE_0",edges:[l],parent:d,parentSolid:d,userData:{faceName:"MOCK_FACE_0"}};return l.parent=d,l.parentSolid=d,d.__testFace=f,d},"createSolid");return n(r,t)}i(O0,"makeMockSolidFromPolyline");async function Kr(){const e=[[0,0,0],[1,.4,0],[2,-.45,0],[3,.35,0],[4,0,0]],t=O0(e),r=new P0;r.inputParams={edges:[t],fitStrength:1,id:"EDGE_SMOOTH_SOLID_TEST"};const n=await r.run();if(!n||!Array.isArray(n.added)||n.added.length!==1)throw new Error("EdgeSmoothFeature should add one smoothed solid when selecting a whole solid.");if(!Array.isArray(n.removed)||n.removed.length!==1||n.removed[0]!==t)throw new Error("EdgeSmoothFeature should remove exactly the selected source solid.");const o=n.added[0],s=Array.isArray(o?._vertProperties)?o._vertProperties:[];if(s.length<15)throw new Error("Smoothed solid is missing expected vertex properties.");const a=[];for(let l=0;l<5;l++){const d=l*3;a.push([s[d+0],s[d+1],s[d+2]])}if(le(a[0],e[0],1e-12),le(a[a.length-1],e[e.length-1],1e-12),Dt(e,a))throw new Error("Whole-solid smoothing should not create local edge backtracking.");let c=0;for(let l=1;l<e.length-1;l++)nt(e[l],a[l])>1e-12&&c++;if(c<=0)throw new Error("Whole-solid smoothing should move at least one interior edge point.");if(!r.persistentData||r.persistentData.selectedSolidCount<1)throw new Error("Feature metadata should record at least one selected solid.")}i(Kr,"test_edge_smooth_whole_solid_selection");async function qr(){const e=[[0,0,0],[1,.3,0],[2,-.35,0],[3,.25,0],[4,0,0]],t=O0(e),r=t.__testFace;if(!r||r.type!=="FACE")throw new Error("Mock setup should provide a face selection target.");const n=new P0;n.inputParams={edges:[r],fitStrength:1,id:"EDGE_SMOOTH_FACE_TEST"};const o=await n.run();if(!o||!Array.isArray(o.added)||o.added.length!==1)throw new Error("EdgeSmoothFeature should add one smoothed solid when selecting a face.");if(!Array.isArray(o.removed)||o.removed.length!==1||o.removed[0]!==t)throw new Error("EdgeSmoothFeature should remove exactly the source solid for face selection.");const s=o.added[0],a=Array.isArray(s?._vertProperties)?s._vertProperties:[];if(a.length<15)throw new Error("Face-selected smoothing output is missing expected vertex properties.");const c=[];for(let l=0;l<5;l++){const d=l*3;c.push([a[d+0],a[d+1],a[d+2]])}if(le(c[0],e[0],1e-12),le(c[c.length-1],e[e.length-1],1e-12),Dt(e,c))throw new Error("Face-selected smoothing should not create local edge backtracking.");if(!n.persistentData||n.persistentData.selectedFaceCount<1)throw new Error("Feature metadata should record at least one selected face.")}i(qr,"test_edge_smooth_face_selection");const _t=-4,Et=15.7,ze=1e-4,Zr=i(()=>({points:[{id:0,x:-2,y:-2,fixed:!0},{id:1,x:2,y:-2,fixed:!1},{id:2,x:2,y:2,fixed:!1},{id:3,x:-2,y:2,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1},{id:102,type:"line",points:[2,3],construction:!1},{id:103,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}),"makeRectSketch$1");function Hr(e){const r=e?.geometry?.getAttribute?.("position");if(!r)return[];const n=[],o=new V;for(let s=0;s<r.count;s++)o.set(r.getX(s),r.getY(s),r.getZ(s)).applyMatrix4(e.matrixWorld),n.push([o.x,o.y,o.z]);return n}i(Hr,"collectWorldVerticesFromFaceObject");function t0(e){const t=[];for(const r of Array.isArray(e)?e:[])Array.isArray(r?.p1)&&t.push(r.p1),Array.isArray(r?.p2)&&t.push(r.p2),Array.isArray(r?.p3)&&t.push(r.p3);return t}i(t0,"collectTriangleVertices");function r0(e,t,r){let n=Number.POSITIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=0,a=0;for(const c of e){const d=c[0]*t.x+c[1]*t.y+c[2]*t.z-r;d<n&&(n=d),d>o&&(o=d),s+=d,a+=1}return{mean:a?s/a:0,spread:a?o-n:0,count:a}}i(r0,"analyzeProjectedOffsets");async function Qr(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch=Zr();const n=await e.newFeature("E");return n.inputParams.profile=r.inputParams.featureID,n.inputParams.consumeProfileSketch=!1,n.inputParams.distance=_t,n.inputParams.distanceBack=Et,e}i(Qr,"test_extrude_negative_distance_cap_alignment");async function en(e){const t=e.features.find(h=>h?.type==="E"),r=e.features.find(h=>h?.type==="S");if(!t?.inputParams?.featureID)throw new Error("[extrude-negative] missing extrude feature id");if(!r?.inputParams?.featureID)throw new Error("[extrude-negative] missing sketch feature id");const n=e.scene.getObjectByName(t.inputParams.featureID);if(!n||typeof n.getFaces!="function")throw new Error("[extrude-negative] extrude solid missing from scene");const o=e.scene.getObjectByName(r.inputParams.featureID),s=o?.children?.find?.(h=>h?.type==="FACE")||o?.children?.find?.(h=>h?.userData?.faceName);if(!s||typeof s.getAverageNormal!="function")throw new Error("[extrude-negative] profile face missing from sketch");const a=s.getAverageNormal().clone();if(a.lengthSq()<1e-20)throw new Error("[extrude-negative] profile normal is degenerate");a.normalize();const c=Hr(s);if(!c.length)throw new Error("[extrude-negative] profile face has no vertices");const l=c.reduce((h,x)=>h+x[0]*a.x+x[1]*a.y+x[2]*a.z,0)/c.length,d=n.getFaces(!1),f=d.find(h=>String(h?.faceName||"").endsWith("_START")),u=d.find(h=>String(h?.faceName||"").endsWith("_END"));if(!f||!u){const h=d.map(x=>String(x?.faceName||""));throw new Error(`[extrude-negative] missing start/end caps. Faces: ${h.join(", ")}`)}const p=r0(t0(f.triangles),a,l),m=r0(t0(u.triangles),a,l);if(p.count===0||m.count===0)throw new Error("[extrude-negative] cap triangles are empty");if(p.spread>ze)throw new Error(`[extrude-negative] start cap not planar on expected axis (spread=${p.spread})`);if(m.spread>ze)throw new Error(`[extrude-negative] end cap not planar on expected axis (spread=${m.spread})`);if(Math.abs(p.mean- -Et)>ze)throw new Error(`[extrude-negative] start cap offset mismatch: got ${p.mean}, expected ${-Et}`);if(Math.abs(m.mean-_t)>ze)throw new Error(`[extrude-negative] end cap offset mismatch: got ${m.mean}, expected ${_t}`)}i(en,"afterRun_extrude_negative_distance_cap_alignment");function Ae(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(Ae,"assert$f");async function tn(e){const t=await e.newFeature("P.CY"),r=String(t?.inputParams?.featureID||"");Ae(r,"Cylinder feature should have a featureID."),await e.runHistory();const n=e?.scene?.getObjectByName?.(r)||null;Ae(n,"Expected cylinder solid in the scene."),Ae(typeof n.getFaceMetadata=="function","Expected solid face metadata API.");const o=n.getFaceMetadata(`${r}_S`),s=n.getFaceMetadata(`${r}_T`);Ae(o?.sourceFeatureId===r,"Cylinder side face should be seeded with sourceFeatureId."),Ae(s?.sourceFeatureId===r,"Cylinder top face should be seeded with sourceFeatureId.")}i(tn,"test_face_source_feature_seed");async function rn(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20;const r=await e.newFeature("E");return r.inputParams.profile=`${t.inputParams.featureID}_T`,r.inputParams.distance=0,r.inputParams.distanceBack=5,r.inputParams.boolean={targets:[t.inputParams.featureID],operation:"UNION"},e}i(rn,"test_ExtrudeFace");async function nn(e){const t=await e.newFeature("P.CY");t.inputParams.radius=5,t.inputParams.height=10,t.inputParams.resolution=48;const r=await e.newFeature("F");return r.inputParams.edges=[`${t.inputParams.featureID}_T`],r.inputParams.radius=1,r.inputParams.direction="INSET",e}i(nn,"test_Fillet");const on="src/tests/partFiles/fillet_angle_test.BREP.json";async function sn(e){const t=await S.promises.readFile(on,"utf8");return await e.reset(),await e.fromJSON(t),e.expressions=`//Examples:
|
|
4
|
+
angle = 35;`,e.currentHistoryStepId="E2",e}i(sn,"test_fillet_angle");async function an(e){const t=e.features.find(l=>l?.type==="F");if(!t)throw new Error("Fillet feature missing from part file.");const n=(Array.isArray(t.inputParams?.edges)?t.inputParams.edges:[]).find(l=>typeof l=="string")||null;if(!n)throw new Error("Fillet edge name not found in test part file.");const o=e.getObjectByName(n);if(!o)throw new Error(`Edge object "${n}" not found after extrude.`);const s=Number(t.inputParams?.radius)||2,a=String(t.inputParams?.direction||"INSET").toUpperCase(),c=T0(o,s,a);if(!Array.isArray(c.points)||!Array.isArray(c.edge))throw new Error("computeFilletCenterline should return point arrays.");if(!Array.isArray(c.tangentA)||!Array.isArray(c.tangentB))throw new Error("computeFilletCenterline should return tangent arrays.");console.log(`✓ Fillet angle tangent test passed: points=${c.points.length}, tangentsA=${c.tangentA.length}, tangentsB=${c.tangentB.length}`)}i(an,"afterRun_fillet_angle");const cn="src/tests/partFiles/fillet_test.BREP.json";async function ln(e){const t=await S.promises.readFile(cn,"utf8");return await e.reset(),await e.fromJSON(t),e.currentHistoryStepId="E7",e}i(ln,"test_fillet_edge_degenerate_segment");async function dn(e){const t=e.features.find(l=>l?.type==="F");if(!t)throw new Error("Fillet feature missing from part file.");const r=Array.isArray(t.inputParams?.edges)?t.inputParams.edges[0]:null;if(!r)throw new Error("Fillet edge reference missing from part file.");const n=typeof r=="object"?r:e.getObjectByName(String(r));if(!n)throw new Error(`Fillet source edge not found: ${String(r)}`);const o=[.5,.675,1],s=o.map(l=>({radius:l,res:T0(n,l,"INSET")}));for(const{radius:l,res:d}of s){if(!Array.isArray(d.points)||!Array.isArray(d.edge))throw new Error(`computeFilletCenterline should provide point arrays (radius=${l}).`);if(d.points.length<2)throw new Error(`computeFilletCenterline should produce at least 2 points (radius=${l}).`);let f=1/0,u=0;for(let p=1;p<d.points.length;p++){const m=d.points[p-1],h=d.points[p],x=Math.hypot(h.x-m.x,h.y-m.y,h.z-m.z);x<f&&(f=x),x>u&&(u=x)}if(Number.isFinite(f)&&Number.isFinite(u)&&u>0&&f<u*1e-5)throw new Error(`Fillet centerline contains a degenerate segment at radius=${l}: minSeg=${f}, maxSeg=${u}`)}const a=s.map(({res:l})=>l.points.length);if(!a.every(l=>l===a[0]))throw new Error(`Degenerate-edge sampling changed with radius: pointCounts=${JSON.stringify(a)}`);console.log(`✓ Fillet degenerate-edge tangent test passed: radii=${o.join(", ")}, points=${a.join(",")}`)}i(dn,"afterRun_fillet_edge_degenerate_segment");const un="src/tests/partFiles/medium_fillets.BREP.json",St="E2",fn="F3";function n0(e,t){const r=(e?.scene?.children||[]).filter(n=>n?.type==="SOLID");return r.find(n=>String(n?.name||"")===String(t))||r[0]||null}i(n0,"getSolidByName$1");async function pn(e){const t=await S.promises.readFile(un,"utf8");return await e.reset(),await e.fromJSON(t),e.currentHistoryStepId=St,e}i(pn,"test_fillet_preserves_original_face_names");async function mn(e){const t=n0(e,St);if(!t||typeof t.getFaceNames!="function")throw new Error("[fillet face-name preserve] Failed to resolve pre-fillet solid.");const r=(t.getFaceNames()||[]).map(a=>String(a||"").trim()).filter(a=>a.length>0);if(r.length===0)throw new Error("[fillet face-name preserve] Pre-fillet solid has no face names.");e.currentHistoryStepId=fn,await e.runHistory();const n=n0(e,St);if(!n||typeof n.getFaceNames!="function")throw new Error("[fillet face-name preserve] Failed to resolve post-fillet solid.");const o=new Set((n.getFaceNames()||[]).map(a=>String(a||"").trim()).filter(a=>a.length>0)),s=r.filter(a=>!o.has(a));if(s.length>0)throw new Error(`[fillet face-name preserve] Lost ${s.length} original face names: ${s.join(", ")}`);console.log(`✓ Fillet preserved ${r.length} original face names on ${String(n?.name||"solid")}.`)}i(mn,"afterRun_fillet_preserves_original_face_names");async function hn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=10,t.inputParams.sizeY=10,t.inputParams.sizeZ=10;const r=await e.newFeature("F");return r.inputParams.edges=[`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_NY[0]`,`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_PZ[0]`,`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_PY[0]`],r.inputParams.radius=1.2,r.inputParams.direction="INSET",r.inputParams.resolution=32,e}i(hn,"test_fillet_corner_bridge");async function xn(e){const t=e.features.find(s=>s?.type==="F");if(!t)throw new Error("Fillet feature missing from history.");const r=t?.persistentData?.miterSummary||null;if(!r||typeof r!="object")throw new Error("Fillet corner bridge summary metadata missing.");const n=Number(r?.cornerBridgeCount||0);if(!Number.isFinite(n)||n<0)throw new Error(`Fillet corner bridge count should be a non-negative number, received ${n}.`);let o=null;for(const s of e.scene?.children||[]){if(s?.owningFeatureID===t.inputParams.featureID&&s?.type==="SOLID"){o=s;break}if(typeof s?.traverse=="function"&&s.traverse(a=>{!o&&a?.owningFeatureID===t.inputParams.featureID&&a?.type==="SOLID"&&(o=a)}),o)break}if(!o||typeof o._manifoldize!="function")throw new Error("Corner-bridge fillet did not produce a manifold-capable solid.");try{o._manifoldize()}catch(s){const a=String(s?.message||s||"Unknown manifold error");throw new Error(`Corner-bridge fillet manifoldization failed: ${a}`)}console.log(`✓ Fillet corner bridge test passed: bridges=${n}`)}i(xn,"afterRun_fillet_corner_bridge");async function gn(e){const t=await e.newFeature("P.CU");t.inputParams.width=10,t.inputParams.height=10,t.inputParams.depth=10;const r=await e.newFeature("F");return r.inputParams.edges=[`${t.inputParams.featureID}_NX|${t.inputParams.featureID}_NY[0]`],r.inputParams.radius=.5,r.inputParams.direction="INSET",e}i(gn,"test_Fillet_NonClosed");async function yn(e){const t=e.features.find(s=>s?.type==="F");if(!t)throw new Error("Fillet feature missing from history");const r=t?.persistentData?.usedSheetMetalPath===!0,n=t?.persistentData?.edgeDirectionDecision||null;if(r||!n||Number(n.totalEdges||0)!==1)throw new Error("Non-closed fillet should produce a normal single-edge fillet result.");let o=0;for(const s of e.scene?.children||[])s?.owningFeatureID===t.inputParams.featureID&&s?.type==="SOLID"&&o++,typeof s?.traverse=="function"&&s.traverse(a=>{a?.owningFeatureID===t.inputParams.featureID&&a?.type==="SOLID"&&o++});if(o===0)throw new Error("Fillet feature should produce at least one solid");console.log(`✓ Non-closed fillet test passed: ${o} solid(s) created`)}i(yn,"afterRun_Fillet_NonClosed");async function wn(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution="128";const r=await e.newFeature("P.CU");r.inputParams.sizeX=5,r.inputParams.sizeY=2,r.inputParams.sizeZ=10,r.inputParams.boolean.targets=[t.inputParams.featureID],r.inputParams.boolean.operation="UNION";const n=await e.newFeature("F");n.inputParams.edges=[`${t.inputParams.featureID}_S|${r.inputParams.featureID}_PY[0]`],n.inputParams.radius=1,n.inputParams.direction="AUTO";const o=await e.newFeature("F");return o.inputParams.edges=[`${t.inputParams.featureID}_S|${t.inputParams.featureID}_T[0]`],o.inputParams.radius=1,o.inputParams.direction="AUTO",e}i(wn,"test_filletsMoreDifficult");async function vn(e){return wn(e)}i(vn,"test_fillets_more_dificult");async function _n(e){await e.newFeature("P.CU")}i(_n,"test_history_expand_does_not_dirty");async function En(e){const t=Array.isArray(e?.features)?e.features[0]:null;if(!t)throw new Error("Expand-state dirty test requires one feature in history.");const r=Number(t.timestamp);if(!Number.isFinite(r))throw new Error("Feature timestamp missing after initial run.");const n=t.lastRun;if(!n||n.ok!==!0)throw new Error("Feature lastRun state missing after initial run.");if(t.inputParams=t.inputParams||{},t.inputParams.__open=!0,await new Promise(o=>setTimeout(o,5)),await e.runHistory(),Number(t.timestamp)!==r)throw new Error("Expanding a feature should not mark it dirty or rerun.");if(t.lastRun!==n)throw new Error("Expanding a feature should not replace lastRun metadata.");if(t.inputParams.__open=!1,await new Promise(o=>setTimeout(o,5)),await e.runHistory(),Number(t.timestamp)!==r)throw new Error("Collapsing a feature should not mark it dirty or rerun.");if(t.lastRun!==n)throw new Error("Collapsing a feature should not replace lastRun metadata.")}i(En,"afterRun_history_expand_does_not_dirty");const ft=i((e,t,r,n,o=100)=>({points:[{id:0,x:e,y:t,fixed:!0},{id:1,x:r,y:t,fixed:!1},{id:2,x:r,y:n,fixed:!1},{id:3,x:e,y:n,fixed:!1}],geometries:[{id:o+0,type:"line",points:[0,1],construction:!1},{id:o+1,type:"line",points:[1,2],construction:!1},{id:o+2,type:"line",points:[2,3],construction:!1},{id:o+3,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}),"makeRectSketch"),Sn=i((e,t,r,n,o=200)=>({points:[{id:0,x:e,y:t,fixed:!0},{id:1,x:r,y:n,fixed:!1}],geometries:[{id:o,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}),"makeLineSketch");async function bn(e){const t=await e.newFeature("D");t.inputParams.transform={position:[1,2,3],rotationEuler:[0,30,0],scale:[1,1,1]};const r=await e.newFeature("SP");r.persistentData.spline={points:[{id:"p0",position:[0,0,0],forwardDistance:1,backwardDistance:1,flipDirection:!1},{id:"p1",position:[5,2,0],forwardDistance:1,backwardDistance:1,flipDirection:!1},{id:"p2",position:[10,0,0],forwardDistance:1,backwardDistance:1,flipDirection:!1}]};const n=await e.newFeature("HX");n.inputParams.radius=2,n.inputParams.endRadius=1.5,n.inputParams.height=8,n.inputParams.turns=2,n.inputParams.resolution=32;const o=await e.newFeature("P");o.inputParams.orientation="XY";const s=await e.newFeature("P");s.inputParams.orientation="XY",s.inputParams.offset_distance=5;const a=await e.newFeature("S");a.inputParams.sketchPlane=o.inputParams.featureID,a.persistentData.sketch=ft(0,0,10,10,100);const c=await e.newFeature("S");c.inputParams.sketchPlane=s.inputParams.featureID,c.persistentData.sketch=ft(2,2,8,8,200);const l=await e.newFeature("LOFT");l.inputParams.profiles=[a.inputParams.featureID,c.inputParams.featureID];const d=await e.newFeature("S");d.inputParams.sketchPlane=o.inputParams.featureID,d.persistentData.sketch=Sn(0,-5,0,5,300);const f=`${d.inputParams.featureID}:G300`,u=await e.newFeature("S");u.inputParams.sketchPlane=o.inputParams.featureID,u.persistentData.sketch=ft(4,-2,7,2,400);const p=await e.newFeature("R");p.inputParams.profile=u.inputParams.featureID,p.inputParams.axis=f,p.inputParams.angle=180,p.inputParams.resolution=32;const m=await e.newFeature("P.CU"),h=await e.newFeature("RM");h.inputParams.targetSolid=m.inputParams.featureID,h.inputParams.mode="Simplify",h.inputParams.tolerance=.05;const x=await e.newFeature("P.CU"),y=await e.newFeature("SWS");y.inputParams.targetSolid=x.inputParams.featureID,y.inputParams.subdivisionLoops=1;const g=await e.newFeature("P.CU"),w=await e.newFeature("XFORM");w.inputParams.solids=[g.inputParams.featureID],w.inputParams.translate=[2,0,0],w.inputParams.rotateEulerDeg=[0,0,45],w.inputParams.copy=!0;const v=await e.newFeature("P.CU"),E=await e.newFeature("OVL");E.inputParams.targetSolid=v.inputParams.featureID,E.inputParams.distance=5e-4;const D=await e.newFeature("P.CU"),k=await e.newFeature("PATLIN");k.inputParams.solids=[D.inputParams.featureID],k.inputParams.count=3,k.inputParams.offset={position:[3,0,0],rotationEuler:[0,0,0],scale:[1,1,1]};const q=await e.newFeature("P.CU"),T=await e.newFeature("PATRAD");T.inputParams.solids=[q.inputParams.featureID],T.inputParams.axisRef=f,T.inputParams.count=4,T.inputParams.totalAngleDeg=180;const Z=await e.newFeature("P.CU"),X=await e.newFeature("PATTERN");X.inputParams.solids=[Z.inputParams.featureID],X.inputParams.mode="LINEAR",X.inputParams.count=2;const M=await e.newFeature("ACOMP");M.inputParams.componentName="missing_component";const H=await e.newFeature("IMAGE");H.inputParams.fileToImport="";const Pe=await e.newFeature("HEIGHTMAP");return Pe.inputParams.fileToImport="",e}i(bn,"test_history_features_basic");async function Pn(e){const t=i((r,n)=>{const o=e.features.find(c=>c?.type===r);if(!o)throw new Error(`${n} feature missing from history`);const s=o?.inputParams?.featureID;if(!s)throw new Error(`${n} feature missing featureID`);if(!e.scene.getObjectByName(s))throw new Error(`${n} object not found in scene`)},"requireFeatureObject");t("D","Datium"),t("SP","Spline"),t("HX","Helix"),t("LOFT","Loft"),t("R","Revolve")}i(Pn,"afterRun_history_features_basic");const _=12,Mt=2,L0=6,B0=90,Fn=6,z0=6,bt=3,j0=7,An=1e-6;function $e(e,t){const r=[{id:0,x:0,y:0,fixed:!0},...t.map((n,o)=>({id:o+1,x:n[0],y:n[1],fixed:!1}))];e.persistentData.sketch={points:r,geometries:[],constraints:[{id:0,type:"⏚",points:[0]}]}}i($e,"buildSketchWithPoints");function Oe(e){return e.features.find(t=>t?.type==="H")||null}i(Oe,"findHoleFeature");function Ct(e){let t=0;for(const r of e){const n=r?._faceMetadata;if(n instanceof Map)for(const o of n.values())o?.hole&&t++}return t}i(Ct,"countHoleMetadata");function Rt(e){return(e.scene?.children||[]).filter(r=>r?.type==="SOLID")[0]||null}i(Rt,"getPrimarySolid");function Me(e,t,r){if(!Number.isFinite(e)||Math.abs(e-t)>An)throw new Error(`${r} expected ${t}, got ${e}`)}i(Me,"expectApprox$1");async function In(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.5,_*.5]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="SIMPLE",o.inputParams.diameter=Mt,o.inputParams.throughAll=!0,o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(In,"test_hole_through");async function Tn(e){const t=Oe(e);if(!t)throw new Error("[hole_through] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_through] No hole records");if(!r[0]?.throughAll)throw new Error("[hole_through] Expected through-all hole record");if(String(r[0]?.type||"").toUpperCase()!=="SIMPLE")throw new Error(`[hole_through] Expected SIMPLE hole, got ${r[0]?.type}`);const n=Rt(e);if(!n)throw new Error("[hole_through] No solids created");if((n._faceNameToID?.size||0)<=6)throw new Error("[hole_through] Hole did not add faces");if(Ct([n])===0)throw new Error("[hole_through] No hole face metadata found")}i(Tn,"afterRun_hole_through");async function Nn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.35,_*.35]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="THREADED",o.inputParams.depth=6,o.inputParams.throughAll=!1,o.inputParams.threadStandard="ISO_METRIC",o.inputParams.threadDesignation="M6x1",o.inputParams.threadMode="SYMBOLIC",o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(Nn,"test_hole_thread_symbolic");async function Dn(e){const t=Oe(e);if(!t)throw new Error("[hole_thread_symbolic] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_thread_symbolic] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="THREADED")throw new Error(`[hole_thread_symbolic] Expected THREADED hole, got ${n?.type}`);if(!n?.thread||String(n.thread.mode||"").toUpperCase()!=="SYMBOLIC")throw new Error("[hole_thread_symbolic] Expected symbolic thread metadata")}i(Dn,"afterRun_hole_thread_symbolic");async function Mn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.65,_*.35]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="THREADED",o.inputParams.depth=4,o.inputParams.throughAll=!1,o.inputParams.threadStandard="ISO_METRIC",o.inputParams.threadDesignation="M6x1",o.inputParams.threadMode="MODELED",o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(Mn,"test_hole_thread_modeled");async function Cn(e){const t=Oe(e);if(!t)throw new Error("[hole_thread_modeled] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_thread_modeled] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="THREADED")throw new Error(`[hole_thread_modeled] Expected THREADED hole, got ${n?.type}`);if(!n?.thread||String(n.thread.mode||"").toUpperCase()!=="MODELED")throw new Error("[hole_thread_modeled] Expected modeled thread metadata")}i(Cn,"afterRun_hole_thread_modeled");async function Rn(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.25,_*.65]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="COUNTERSINK",o.inputParams.diameter=Mt,o.inputParams.depth=Fn,o.inputParams.throughAll=!1,o.inputParams.countersinkDiameter=L0,o.inputParams.countersinkAngle=B0,o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i(Rn,"test_hole_countersink");async function kn(e){const t=Oe(e);if(!t)throw new Error("[hole_countersink] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_countersink] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="COUNTERSINK")throw new Error(`[hole_countersink] Expected COUNTERSINK hole, got ${n?.type}`);if(n?.throughAll)throw new Error("[hole_countersink] Expected non-through hole");if(!(n?.countersinkHeight>0))throw new Error("[hole_countersink] Expected countersink height > 0");Me(n?.countersinkDiameter,L0,"[hole_countersink] countersink diameter"),Me(n?.countersinkAngle,B0,"[hole_countersink] countersink angle");const o=Rt(e);if(!o)throw new Error("[hole_countersink] No solids created");if(Ct([o])===0)throw new Error("[hole_countersink] No hole face metadata found")}i(kn,"afterRun_hole_countersink");async function $n(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=_,t.inputParams.sizeY=_,t.inputParams.sizeZ=_;const r=await e.newFeature("P");r.inputParams.orientation="XY";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,$e(n,[[_*.75,_*.65]]);const o=await e.newFeature("H");return o.inputParams.face=n.inputParams.featureID,o.inputParams.holeType="COUNTERBORE",o.inputParams.diameter=Mt,o.inputParams.depth=j0,o.inputParams.throughAll=!1,o.inputParams.counterboreDiameter=z0,o.inputParams.counterboreDepth=bt,o.inputParams.boolean={targets:[t.inputParams.featureID],operation:"SUBTRACT"},e}i($n,"test_hole_counterbore");async function On(e){const t=Oe(e);if(!t)throw new Error("[hole_counterbore] Hole feature missing");const r=t.persistentData?.holes||[];if(!r.length)throw new Error("[hole_counterbore] No hole records");const n=r[0];if(String(n?.type||"").toUpperCase()!=="COUNTERBORE")throw new Error(`[hole_counterbore] Expected COUNTERBORE hole, got ${n?.type}`);if(n?.throughAll)throw new Error("[hole_counterbore] Expected non-through hole");Me(n?.counterboreDiameter,z0,"[hole_counterbore] counterbore diameter"),Me(n?.counterboreDepth,bt,"[hole_counterbore] counterbore depth"),Me(n?.straightDepth,j0-bt,"[hole_counterbore] straight depth");const o=Rt(e);if(!o)throw new Error("[hole_counterbore] No solids created");if(Ct([o])===0)throw new Error("[hole_counterbore] No hole face metadata found")}i(On,"afterRun_hole_counterbore");const U0="IMPORT3D_DECIMATION_BASELINE",W0="IMPORT3D_DECIMATION_REDUCED",Pt="IMPORT3D_DECIMATION_STABILITY",Te="IMPORT3D_DECIMATION_RESTORE",Ne="IMPORT3D_DECIMATION_LEGACY_CACHE",Xe="IMPORT3D_DECIMATION_SNAPSHOT_CLONE_RESILIENCE";function B(e){const t=Number(e);return!Number.isFinite(t)||Math.abs(t)<1e-16?"0":t.toFixed(9)}i(B,"formatNumber$2");function je(e,t,r,n){const o=r[0]-t[0],s=r[1]-t[1],a=r[2]-t[2],c=n[0]-t[0],l=n[1]-t[1],d=n[2]-t[2];let f=s*d-a*l,u=a*c-o*d,p=o*l-s*c;const m=Math.hypot(f,u,p)||1;f/=m,u/=m,p/=m,e.push(` facet normal ${B(f)} ${B(u)} ${B(p)}`),e.push(" outer loop"),e.push(` vertex ${B(t[0])} ${B(t[1])} ${B(t[2])}`),e.push(` vertex ${B(r[0])} ${B(r[1])} ${B(r[2])}`),e.push(` vertex ${B(n[0])} ${B(n[1])} ${B(n[2])}`),e.push(" endloop"),e.push(" endfacet")}i(je,"appendFacet$2");function Le({radius:e=6,height:t=10,segments:r=96}={}){const n=Math.max(24,Number(r)|0),o=Math.max(.1,Number(e)||1),a=Math.max(.1,Number(t)||1)*.5,c=[0,a,0],l=[0,-a,0],d=["solid import3d_decimation_cylinder"];for(let f=0;f<n;f+=1){const u=f/n*Math.PI*2,p=(f+1)/n*Math.PI*2,m=Math.cos(u),h=Math.sin(u),x=Math.cos(p),y=Math.sin(p),g=[o*m,-a,o*h],w=[o*x,-a,o*y],v=[o*m,a,o*h],E=[o*x,a,o*y];je(d,g,w,E),je(d,g,E,v),je(d,c,E,v),je(d,l,g,w)}return d.push("endsolid import3d_decimation_cylinder"),d.join(`
|
|
5
|
+
`)}i(Le,"buildCylinderStl");function _e(e,t,r){e.inputParams.id=e.inputParams.featureID,e.inputParams.fileToImport=t,e.inputParams.centerMesh=!1,e.inputParams.deflectionAngle=8,e.inputParams.decimationLevel=r,e.inputParams.meshRepairLevel="NONE",e.inputParams.extractPlanarFaces=!1,e.inputParams.segmentAnalyticPrimitives=!1}i(_e,"createImportFeature");function He(e){const t=Array.isArray(e?._vertProperties)?e._vertProperties:[],r=Array.isArray(e?._triVerts)?e._triVerts:[];let n=2166136261;const o=i(a=>{n^=a>>>0,n=Math.imul(n,16777619)>>>0},"mix"),s=i(a=>{const c=String(a||"");for(let l=0;l<c.length;l+=1)o(c.charCodeAt(l)&255),o(c.charCodeAt(l)>>>8&255)},"mixString");for(let a=0;a<t.length;a+=1){const c=Number(t[a]);s(Number.isFinite(c)?c.toFixed(9):"NaN")}for(let a=0;a<r.length;a+=1)o(Number(r[a])|0);return n>>>0}i(He,"hashSolidMeshSignature");function de(e,t){return(e.scene?.children||[]).filter(n=>n?.type==="SOLID").find(n=>String(n?.name||"")===t)||null}i(de,"getSolidByName");function ue(e){return Math.floor((Array.isArray(e?._triVerts)?e._triVerts.length:0)/3)}i(ue,"getSolidTriangleCount");function ot(e,t){return(e.features||[]).find(r=>r&&String(r?.type||"").toUpperCase()==="IMPORT3D"&&(String(r?.inputParams?.featureID||"")===t||String(r?.inputParams?.id||"")===t))||null}i(ot,"getImportFeatureById$1");async function Ln(e){const t=Le({radius:5,height:10,segments:96}),r=await e.newFeature("IMPORT3D");r.inputParams.featureID=U0,_e(r,t,100);const n=await e.newFeature("IMPORT3D");return n.inputParams.featureID=W0,_e(n,t,55),e}i(Ln,"test_import3d_decimation_reduces_triangle_count");async function Bn(e){const t=(e.scene?.children||[]).filter(a=>a?.type==="SOLID");if(!t.length)throw new Error("[import3d decimation] No solids were generated");const r=t.find(a=>String(a?.name||"")===U0),n=t.find(a=>String(a?.name||"")===W0);if(!r||!n)throw new Error("[import3d decimation] Could not locate baseline and reduced solids");const o=Math.floor((Array.isArray(r._triVerts)?r._triVerts.length:0)/3),s=Math.floor((Array.isArray(n._triVerts)?n._triVerts.length:0)/3);if(!(o>0))throw new Error("[import3d decimation] Baseline triangle count is invalid");if(!(s>0))throw new Error("[import3d decimation] Reduced triangle count is invalid");if(!(s<o))throw new Error(`[import3d decimation] Expected reduced triangle count < baseline (${s} >= ${o})`)}i(Bn,"afterRun_import3d_decimation_reduces_triangle_count");async function zn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Pt,_e(t,Le({radius:5,height:10,segments:96}),80),e}i(zn,"test_import3d_decimation_reapplies_from_cached_source_mesh");async function jn(e){const t=ot(e,Pt);if(!t)throw new Error("[import3d decimation stability] Import feature not found");const r=i(()=>{const a=(e.scene?.children||[]).filter(c=>c?.type==="SOLID").find(c=>String(c?.name||"")===Pt);return a?Math.floor((Array.isArray(a._triVerts)?a._triVerts.length:0)/3):0},"getSolidTriangleCount");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=55,await e.runHistory();const n=r();if(!(n>0))throw new Error("[import3d decimation stability] First decimated rebuild produced no triangles");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=55,await e.runHistory();const o=r();if(!(o>0))throw new Error("[import3d decimation stability] Second decimated rebuild produced no triangles");if(n!==o)throw new Error(`[import3d decimation stability] Expected stable triangle count when re-running same decimation level (${n} !== ${o})`)}i(jn,"afterRun_import3d_decimation_reapplies_from_cached_source_mesh");async function Un(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Te,_e(t,Le({radius:5,height:10,segments:96}),100),e}i(Un,"test_import3d_decimation_100_restores_original_geometry");async function Wn(e){const t=ot(e,Te);if(!t)throw new Error("[import3d decimation restore] Import feature not found");const r=de(e,Te);if(!r)throw new Error("[import3d decimation restore] Baseline solid not found");const n=ue(r),o=He(r);t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory();const s=de(e,Te);if(!s)throw new Error("[import3d decimation restore] Decimated solid not found");const a=ue(s);if(!(a<n))throw new Error(`[import3d decimation restore] Expected 90% decimation to reduce triangles (${a} >= ${n})`);t.inputParams.fileToImport=null,t.inputParams.decimationLevel=100,await e.runHistory();const c=de(e,Te);if(!c)throw new Error("[import3d decimation restore] Restored solid not found");const l=ue(c),d=He(c);if(l!==n||d!==o)throw new Error(`[import3d decimation restore] 100% did not restore original mesh (baseline triangles=${n}, restored triangles=${l}, baseline hash=${o}, restored hash=${d})`)}i(Wn,"afterRun_import3d_decimation_100_restores_original_geometry");async function Yn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Ne,_e(t,Le({radius:5,height:10,segments:96}),100),e}i(Yn,"test_import3d_decimation_seeds_source_snapshot_for_legacy_cache");async function Vn(e){const t=ot(e,Ne);if(!t)throw new Error("[import3d decimation legacy cache] Import feature not found");if(!t?.persistentData?.importCache)throw new Error("[import3d decimation legacy cache] Missing import cache");delete t.persistentData.importCache.sourceMeshSnapshot,t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory();const r=ue(de(e,Ne));if(!(r>0))throw new Error("[import3d decimation legacy cache] 90% pass produced no triangles");if(!t?.persistentData?.importCache?.sourceMeshSnapshot)throw new Error("[import3d decimation legacy cache] Expected source mesh snapshot to be seeded");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=80,await e.runHistory();const n=ue(de(e,Ne));if(!(n>0&&n<r))throw new Error(`[import3d decimation legacy cache] Expected 80% to reduce triangles (${n} !< ${r})`);t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory();const o=ue(de(e,Ne));if(o!==r)throw new Error(`[import3d decimation legacy cache] 90% result changed after intermediate edit (${r} !== ${o})`)}i(Vn,"afterRun_import3d_decimation_seeds_source_snapshot_for_legacy_cache");async function Gn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.featureID=Xe,_e(t,Le({radius:5,height:10,segments:96}),100),e}i(Gn,"test_import3d_decimation_preserves_source_snapshot_without_json_clone");async function Xn(e){const t=ot(e,Xe);if(!t)throw new Error("[import3d decimation snapshot resilience] Import feature not found");const r=de(e,Xe);if(!r)throw new Error("[import3d decimation snapshot resilience] Baseline solid not found");const n=ue(r),o=He(r),s=t?.persistentData?.importCache?.sourceMeshSnapshot;if(!s||!Array.isArray(s.position))throw new Error("[import3d decimation snapshot resilience] Initial source mesh snapshot missing");const a=JSON.stringify;try{JSON.stringify=i(function(p,m,h){if(p&&typeof p=="object"&&Array.isArray(p.position)&&p.position.length>0&&(Array.isArray(p.index)||Array.isArray(p.normal)))throw new Error("Synthetic stringify failure for mesh snapshot");return a.call(JSON,p,m,h)},"patchedStringify"),t.inputParams.fileToImport=null,t.inputParams.decimationLevel=90,await e.runHistory()}finally{JSON.stringify=a}const c=t?.persistentData?.importCache?.sourceMeshSnapshot;if(!c||!Array.isArray(c.position))throw new Error("[import3d decimation snapshot resilience] Source snapshot was dropped after cached rebuild");t.inputParams.fileToImport=null,t.inputParams.decimationLevel=100,await e.runHistory();const l=de(e,Xe);if(!l)throw new Error("[import3d decimation snapshot resilience] Restored solid not found");const d=ue(l),f=He(l);if(d!==n||f!==o)throw new Error(`[import3d decimation snapshot resilience] 100% did not restore baseline mesh (baseline triangles=${n}, restored triangles=${d}, baseline hash=${o}, restored hash=${f})`)}i(Xn,"afterRun_import3d_decimation_preserves_source_snapshot_without_json_clone");const te="IMPORT3D_MULTI_SOLIDS";function z(e){const t=Number(e);return!Number.isFinite(t)||Math.abs(t)<1e-16?"0":t.toFixed(9)}i(z,"formatNumber$1");function j(e,t,r,n){const o=r[0]-t[0],s=r[1]-t[1],a=r[2]-t[2],c=n[0]-t[0],l=n[1]-t[1],d=n[2]-t[2];let f=s*d-a*l,u=a*c-o*d,p=o*l-s*c;const m=Math.hypot(f,u,p)||1;f/=m,u/=m,p/=m,e.push(` facet normal ${z(f)} ${z(u)} ${z(p)}`),e.push(" outer loop"),e.push(` vertex ${z(t[0])} ${z(t[1])} ${z(t[2])}`),e.push(` vertex ${z(r[0])} ${z(r[1])} ${z(r[2])}`),e.push(` vertex ${z(n[0])} ${z(n[1])} ${z(n[2])}`),e.push(" endloop"),e.push(" endfacet")}i(j,"appendFacet$1");function o0(e,t,r){const[n,o,s]=t,[a,c,l]=r;j(e,[n,o,s],[a,o,l],[a,o,s]),j(e,[n,o,s],[n,o,l],[a,o,l]),j(e,[n,c,s],[a,c,s],[a,c,l]),j(e,[n,c,s],[a,c,l],[n,c,l]),j(e,[n,o,s],[n,c,s],[n,c,l]),j(e,[n,o,s],[n,c,l],[n,o,l]),j(e,[a,o,s],[a,o,l],[a,c,l]),j(e,[a,o,s],[a,c,l],[a,c,s]),j(e,[n,o,s],[a,c,s],[a,o,s]),j(e,[n,o,s],[n,c,s],[a,c,s]),j(e,[n,o,l],[a,o,l],[a,c,l]),j(e,[n,o,l],[a,c,l],[n,c,l])}i(o0,"appendCube");function Jn(){const e=["solid import3d_multiple_solids"];return o0(e,[-1,0,-1],[1,2,1]),o0(e,[8,0,-1],[10,2,1]),e.push("endsolid import3d_multiple_solids"),e.join(`
|
|
6
|
+
`)}i(Jn,"buildTwoCubeIslandsStl");function Kn(e,t){return(e.features||[]).find(r=>r&&String(r?.type||"").toUpperCase()==="IMPORT3D"&&(String(r?.inputParams?.featureID||"")===t||String(r?.inputParams?.id||"")===t))||null}i(Kn,"getImportFeatureById");function pt(e,t){const r=`${t}_SOLID_`;return(e.scene?.children||[]).filter(n=>n?.type==="SOLID"&&(String(n?.name||"")===t||String(n?.name||"").startsWith(r)))}i(pt,"listFeatureSolids");async function qn(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.id=te,t.inputParams.featureID=te,t.inputParams.fileToImport=Jn(),t.inputParams.centerMesh=!1,t.inputParams.deflectionAngle=8,t.inputParams.decimationLevel=100,t.inputParams.meshRepairLevel="NONE",t.inputParams.extractMultipleSolids=!1,t.inputParams.extractPlanarFaces=!1,t.inputParams.segmentAnalyticPrimitives=!1,e}i(qn,"test_import3d_extract_multiple_solids_toggle");async function Zn(e){const t=Kn(e,te);if(!t)throw new Error("[import3d multi solids] Import feature not found");const r=pt(e,te);if(r.length!==1)throw new Error(`[import3d multi solids] Expected 1 solid with checkbox off, got ${r.length}`);t.inputParams.fileToImport=null,t.inputParams.extractMultipleSolids=!0,await e.runHistory();const n=pt(e,te);if(n.length!==2)throw new Error(`[import3d multi solids] Expected 2 solids with checkbox on, got ${n.length}`);const o=new Set(n.map(a=>String(a?.name||"")));if(!o.has(`${te}_SOLID_01`)||!o.has(`${te}_SOLID_02`))throw new Error(`[import3d multi solids] Unexpected split solid names: ${Array.from(o).join(", ")}`);t.inputParams.fileToImport=null,t.inputParams.extractMultipleSolids=!1,await e.runHistory();const s=pt(e,te);if(s.length!==1)throw new Error(`[import3d multi solids] Expected 1 solid after turning checkbox off again, got ${s.length}`);if(String(s[0]?.name||"")!==te)throw new Error(`[import3d multi solids] Expected merged solid name to reset to feature ID, got ${String(s[0]?.name||"")}`)}i(Zn,"afterRun_import3d_extract_multiple_solids_toggle");const Ft="IMPORT3D_PLANAR_SLIVER_BRIDGE";function U(e){const t=Number(e);return!Number.isFinite(t)||Math.abs(t)<1e-16?"0":t.toFixed(9)}i(U,"formatNumber");function Hn(e,t,r,n){const o=r[0]-t[0],s=r[1]-t[1],a=r[2]-t[2],c=n[0]-t[0],l=n[1]-t[1],d=n[2]-t[2];let f=s*d-a*l,u=a*c-o*d,p=o*l-s*c;const m=Math.hypot(f,u,p)||1;f/=m,u/=m,p/=m,e.push(` facet normal ${U(f)} ${U(u)} ${U(p)}`),e.push(" outer loop"),e.push(` vertex ${U(t[0])} ${U(t[1])} ${U(t[2])}`),e.push(` vertex ${U(r[0])} ${U(r[1])} ${U(r[2])}`),e.push(` vertex ${U(n[0])} ${U(n[1])} ${U(n[2])}`),e.push(" endloop"),e.push(" endfacet")}i(Hn,"appendFacet");function Qn(){const e=["solid import3d_planar_sliver_bridge"],t=i((r,n,o)=>Hn(e,r,n,o),"addTri");return t([1,0,0],[1.000001,.5,0],[1,1,0]),t([0,0,0],[1,0,0],[1,1,0]),t([0,0,0],[1,1,0],[0,1,0]),t([1.000001,0,0],[2.000001,0,0],[2.000001,1,0]),t([1.000001,0,0],[2.000001,1,0],[1.000001,1,0]),e.push("endsolid import3d_planar_sliver_bridge"),e.join(`
|
|
7
|
+
`)}i(Qn,"buildCoplanarSliverBridgeStl");async function eo(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.id=Ft,t.inputParams.featureID=Ft,t.inputParams.fileToImport=Qn(),t.inputParams.centerMesh=!1,t.inputParams.deflectionAngle=5,t.inputParams.extractPlanarFaces=!0,t.inputParams.planarFaceMinAreaPercent=20,t.inputParams.segmentAnalyticPrimitives=!1,e}i(eo,"test_import3d_planar_extraction_merges_sliver_bridge");async function to(e){const t=(e.scene?.children||[]).filter(d=>d?.type==="SOLID");if(!t.length)throw new Error("[import3d planar sliver] No solids were generated");const r=t.find(d=>String(d?.name||"")===Ft)||t[0];if(!r)throw new Error("[import3d planar sliver] Could not find imported solid");const o=(Array.isArray(r._triVerts)?r._triVerts:[]).length/3|0;if(o!==5)throw new Error(`[import3d planar sliver] Expected 5 triangles, got ${o}`);const s=Array.isArray(r._triIDs)?r._triIDs:[],a=r._idToFaceName instanceof Map?r._idToFaceName:new Map;if(s.length!==o)throw new Error("[import3d planar sliver] Triangle IDs missing or inconsistent");const c=s.map(d=>String(a.get(d)||"")),l=new Set(c);if(l.size!==1)throw new Error(`[import3d planar sliver] Expected one merged planar face, got ${l.size} (${Array.from(l).join(", ")})`)}i(to,"afterRun_import3d_planar_extraction_merges_sliver_bridge");async function ro(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=4,t.inputParams.sizeY=3,t.inputParams.sizeZ=2;const r=await e.newFeature("M");return r.inputParams.solids=[t.inputParams.featureID],r.inputParams.mirrorPlane=`${t.inputParams.featureID}_PX`,r.inputParams.offsetDistance=0,e}i(ro,"test_mirror");function O(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(O,"assert$e");function s0(e,t){return(e?.features||[]).find(r=>r?.type===t)||null}i(s0,"getFeatureEntry");async function no(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=5,t.inputParams.sizeY=5,t.inputParams.sizeZ=5;const r=String(t?.inputParams?.featureID||"");O(r,"Cube feature should have a featureID.");const n=await e.newFeature("O.F");n.inputParams.faces=[`${r}_PZ`],n.inputParams.distance=2}i(no,"test_offsetFace_preserves_individual_edges");async function oo(e){const t=s0(e,"P.CU"),r=s0(e,"O.F"),n=String(t?.inputParams?.featureID||""),o=String(r?.inputParams?.featureID||"");O(n,"Cube featureID missing after history run."),O(o,"Offset-face featureID missing after history run.");const s=`${o}:${n}_PZ`,a=e?.scene?.getObjectByName?.(s)||null;O(a&&a.type==="SKETCH",`Expected offset-face sketch group "${s}".`);const c=(a.children||[]).filter(p=>p?.type==="EDGE"),l=(a.children||[]).filter(p=>p?.type==="VERTEX"),d=(a.children||[]).find(p=>p?.type==="FACE"&&p?.name===`${s}:PROFILE`)||null;O(d,"Expected offset-face profile face."),O(c.length===4,`Expected 4 individual offset edges for square face, got ${c.length}.`),O(l.length===4,`Expected 4 boundary points for square face, got ${l.length}.`),O(Array.isArray(d.edges)&&d.edges.length===4,"Profile face should retain the 4 individual edges."),O(c.every(p=>p?.closedLoop===!1),"Square-face offset edges should remain individual open edges.");const f=Array.isArray(d?.userData?.boundaryLoopsWorld)?d.userData.boundaryLoopsWorld:[];O(f.length===1,`Expected one outer boundary loop, got ${f.length}.`);const u=Array.isArray(d?.userData?.boundaryEdgeGroups)?d.userData.boundaryEdgeGroups:[];O(u.length===4,`Expected 4 boundary edge groups, got ${u.length}.`),O(u.every(p=>Array.isArray(p?.pts)&&p.pts.length===2),"Cube edge groups should remain 2-point segments."),O(c.every(p=>p?.userData?.isHole===!1),"Outer square edges should not be marked as holes.")}i(oo,"afterRun_offsetFace_preserves_individual_edges");async function so(e){const s=new hr({radius:2,height:4,resolution:16,name:"CYL"}),c=xr.generate(s,-1,{newSolidName:"CYL_shell",featureId:"TEST"}).getFaces(!1),l="CYL_shell_CYL_T",d="CYL_shell_CYL_B",f=c.find(y=>y.faceName===l),u=c.find(y=>y.faceName===d);if(!f)throw new Error(`Offset shell test could not find expected top face "${l}".`);if(!u)throw new Error(`Offset shell test could not find expected bottom face "${d}".`);if(!Array.isArray(f.triangles)||!f.triangles.length)throw new Error(`Offset shell top face "${l}" has no triangles to validate.`);if(!Array.isArray(u.triangles)||!u.triangles.length)throw new Error(`Offset shell bottom face "${d}" has no triangles to validate.`);const p=-1,m=.35,h=i(y=>(y.p1[1]+y.p2[1]+y.p3[1])/3,"centroidY"),x=i((y,g,w,v)=>{const[E,D,k]=g,[q,T,Z]=w;let X=0;for(const M of y.triangles){const H=(M.p1[0]+M.p2[0]+M.p3[0])/3,Pe=h(M),xe=(M.p1[2]+M.p2[2]+M.p3[2])/3,J0=E*(H-q)+D*(Pe-T)+k*(xe-Z),Ot=Math.abs(J0-p);Ot>X&&(X=Ot)}if(X>m){const M=y.triangles.reduce((H,Pe)=>{const xe=h(Pe);return xe<H.min&&(H.min=xe),xe>H.max&&(H.max=xe),H},{min:1/0,max:-1/0});throw new Error([`Offset shell ${v} face deviated ${X.toFixed(3)} (tolerance ${m}).`,`Centroid Y range: ${M.min.toFixed(3)} .. ${M.max.toFixed(3)}`,`Triangles: ${y.triangles.length}`].join(" "))}},"checkFaceOffsets");return x(f,[0,1,0],[0,4,0],"top"),x(u,[0,-1,0],[0,0,0],"bottom"),e}i(so,"test_offsetShellGrouping");async function ao(e){return await e.newFeature("PLANE"),e}i(ao,"test_plane");async function io(e){const t=await e.newFeature("P.CO");return t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20,e}i(io,"test_primitiveCone");async function co(e){const t=await e.newFeature("P.CU");return t.inputParams.sizeX=5,t.inputParams.sizeY=10,t.inputParams.sizeZ=15,e}i(co,"test_primitiveCube");async function lo(e){const t=await e.newFeature("P.CY");return t.inputParams.radius=1,t.inputParams.height=5,t.inputParams.resolution=30,e}i(lo,"test_primitiveCylinder");async function uo(e){const t=await e.newFeature("P.PY");return t.inputParams.baseSideLength=5,t.inputParams.height=8,t.inputParams.sides=4,e}i(uo,"test_primitivePyramid");async function fo(e){const t=await e.newFeature("P.S");return t.inputParams.radius=5,t.inputParams.resolution=10,e}i(fo,"test_primitiveSphere");async function po(e){const t=await e.newFeature("P.T");t.inputParams.majorRadius=20,t.inputParams.tubeRadius=5,t.inputParams.resolution=10,t.inputParams.arc=300;const r=await e.newFeature("P.T");return r.inputParams.majorRadius=5,r.inputParams.tubeRadius=3,r.inputParams.resolution=30,r.inputParams.arc=360,e}i(po,"test_primitiveTorus");const mo="PUSHFACE_CUBE",Y0="PUSHFACE_CYL",ce=6,ho=1.25,a0=.5;async function xo(e){const t=await e.newFeature("P.CU");t.inputParams.id=mo,t.inputParams.sizeX=ce,t.inputParams.sizeY=ce,t.inputParams.sizeZ=ce;const r=await e.newFeature("P.CY");r.inputParams.id=Y0,r.inputParams.radius=ho,r.inputParams.height=ce,r.inputParams.transform={position:[ce/2,0,ce/2],rotationEuler:[0,0,0],scale:[1,1,1]};const n=await e.newFeature("B");return n.inputParams.targetSolid=t.inputParams.featureID,n.inputParams.boolean={operation:"SUBTRACT",targets:[r.inputParams.featureID]},e}i(xo,"test_pushFace");function Ue(e,t,r,n){if(typeof e.getFace!="function")throw new Error("Solid missing getFace()");const o=e.getFace(t);if(!Array.isArray(o)||o.length===0)throw new Error(`Face "${t}" not found on solid ${e.name||""}`);const s=new Set,a=[];for(const u of o)for(const p of[u.p1,u.p2,u.p3]){if(!Array.isArray(p)||p.length<3)continue;const m=`${p[0]},${p[1]},${p[2]}`;if(s.has(m))continue;s.add(m);const h=p[0],x=p[2];a.push(Math.hypot(h-r,x-n))}if(!a.length)throw new Error(`No vertices collected for face "${t}"`);const l=a.reduce((u,p)=>u+p,0)/a.length,d=Math.min(...a),f=Math.max(...a);return{avg:l,min:d,max:f}}i(Ue,"measureRadialExtent");function go(e,t,r,n){const o=e.getFace(t);if(!Array.isArray(o)||o.length===0)return 0;let s=0;for(const a of o){if(!Array.isArray(a.p1)||!Array.isArray(a.p2)||!Array.isArray(a.p3))continue;const c=a.p1[0],l=a.p1[1],d=a.p1[2],f=a.p2[0],u=a.p2[1],p=a.p2[2],m=a.p3[0],h=a.p3[1],x=a.p3[2],y=f-c,g=u-l,w=p-d,v=m-c,E=h-l,D=x-d,k=g*D-w*E,q=y*E-g*v,T=(c+f+m)/3,Z=(d+p+x)/3,X=T-r,M=Z-n;s+=X*k+M*q}return s===0?0:s>0?1:-1}i(go,"estimateRadialNormalSign");async function yo(e){const t=(e.scene?.children||[]).filter(p=>p?.type==="SOLID");if(!t.length)throw new Error("[pushFace] No solids created");const r=t[0],n=`${Y0}_S`,o={x:ce/2,z:ce/2},s=Ue(r,n,o.x,o.z),a=r.clone(),c=Ue(a,n,o.x,o.z);r.pushFace(n,a0);const l=Ue(r,n,o.x,o.z);a.pushFace(n,-a0);const d=Ue(a,n,o.x,o.z),f=go(r,n,o.x,o.z),u=1e-6;if(f===0){const p=l.avg-s.avg,m=d.avg-c.avg;if(!(p>u&&m<-u||p<-u&&m>u))throw new Error(`[pushFace] Expected opposite radial motion, got ${p} and ${m}`)}else if(f>0){if(!(l.avg>s.avg+u&&l.min>s.min+u/10))throw new Error(`[pushFace] Positive distance failed to move face outward (avg ${s.avg} → ${l.avg})`);if(!(d.avg<c.avg-u&&d.max<c.max-u/10))throw new Error(`[pushFace] Negative distance failed to move face inward (avg ${c.avg} → ${d.avg})`)}else{if(!(l.avg<s.avg-u&&l.max<s.max-u/10))throw new Error(`[pushFace] Positive distance failed to move face inward (avg ${s.avg} → ${l.avg})`);if(!(d.avg>c.avg+u&&d.min>c.min+u/10))throw new Error(`[pushFace] Negative distance failed to move face outward (avg ${c.avg} → ${d.avg})`)}}i(yo,"afterRun_pushFace");const L={featureID:"SM.F18",rootTransform:[.9870838724223062,.16020433453494515,0,0,.16020433453494515,-.9870838724223062,0,0,0,0,-1,0,0,22.933513,-.5,1],tree:{thickness:1,root:{kind:"flat",id:"SM.TAB3:flat_root",outline:[[15.922428663832992,.9999749363773027],[15.922428130833659,23344293011793158e-24],[24.936370606145015,11.702328425575034],[18.434726468756253,26.225568879708764],[18.91524076397385,23.264301242008393],[-.23208485945451912,20.156678227058677],[-.7127965714465971,23.117913823892142],[2.961251617266918,.4806130036048354],[0,0],[3.7223959386598526,-7392522398809269e-32],[3.7223953502335427,.9999757282964694],[3.7223955449672324,3.999975728296463],[15.92242885856668,3.9999749363772965]],edges:[{id:"SM.TAB3:flat_root:edge",polyline:[[15.922428663832992,.9999749363773027],[15.922428130833659,23344293011793158e-24]]},{id:"SM.TAB3:flat_root:e2",polyline:[[15.922428130833659,23344293011793158e-24],[24.936370606145015,11.702328425575034]]},{id:"SM.TAB3:flat_root:e3",polyline:[[24.936370606145015,11.702328425575034],[18.434726468756253,26.225568879708764]]},{id:"SM.TAB3:flat_root:e4:bridge",polyline:[[18.434726468756253,26.225568879708764],[18.91524076397385,23.264301242008393]]},{id:"SM.TAB3:flat_root:e4",polyline:[[18.91524076397385,23.264301242008393],[-.23208485945451912,20.156678227058677]],bend:{kind:"bend",id:"SM.F9:bend_2",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F9:flat_2",outline:[[0,0],[19.39787100000002,0],[19.39787100000002,3.0000000000000004],[0,3.0000000000000004]],edges:[{id:"SM.F9:flat_2:attach",isAttachEdge:!0,polyline:[[0,0],[19.39787100000002,0]]},{id:"SM.F9:flat_2:top",polyline:[[19.39787100000002,3.0000000000000004],[0,3.0000000000000004]]},{id:"SM.F9:flat_2:left",polyline:[[0,3.0000000000000004],[0,0]]},{id:"SM.F9:flat_2:right",polyline:[[19.39787100000002,0],[19.39787100000002,3.0000000000000004]]}]},attachEdgeId:"SM.F9:flat_2:attach"}]}},{id:"SM.TAB3:flat_root:e4:bridge_1",polyline:[[-.23208485945451912,20.156678227058677],[-.7127965714465971,23.117913823892142]]},{id:"SM.TAB3:flat_root:e5",polyline:[[-.7127965714465971,23.117913823892142],[2.961251617266918,.4806130036048354]],bend:{kind:"bend",id:"SM.F9:bend_1",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F9:flat_1",outline:[[0,0],[22.933513000000012,0],[22.933513000000012,3.0000000000000004],[0,3.0000000000000004]],edges:[{id:"SM.F9:flat_1:attach",isAttachEdge:!0,polyline:[[0,0],[22.933513000000012,0]]},{id:"SM.F9:flat_1:top",polyline:[[22.933513000000012,3.0000000000000004],[0,3.0000000000000004]]},{id:"SM.F9:flat_1:left",polyline:[[0,3.0000000000000004],[0,0]]},{id:"SM.F9:flat_1:right",polyline:[[22.933513000000012,0],[22.933513000000012,3.0000000000000004]]}]},attachEdgeId:"SM.F9:flat_1:attach"}]}},{id:"SM.TAB3:flat_root:e5:bridge",polyline:[[2.961251617266918,.4806130036048354],[0,0]]},{id:"SM.TAB3:flat_root:e1",polyline:[[0,0],[3.7223959386598526,-7392522398809269e-32]]},{id:"SM.TAB3:flat_root:edge_1",polyline:[[3.7223959386598526,-7392522398809269e-32],[3.7223953502335427,.9999757282964694]]},{id:"SM.TAB3:flat_root:edge_2:bridge",polyline:[[3.7223953502335427,.9999757282964694],[3.7223955449672324,3.999975728296463]]},{id:"SM.TAB3:flat_root:edge_2",polyline:[[3.7223955449672324,3.999975728296463],[15.92242885856668,3.9999749363772965]],bend:{kind:"bend",id:"SM.F13:bend",angleDeg:-90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F13:flat",outline:[[12.200033313599473,3.842297553313296],[12.164703568668596,3.8559257397886375],[11.939068265109665,3.9559429911138198],[11.718612363070678,4.066911166871344],[11.503866960129303,4.188562935006142],[11.295349396770701,4.320605225753343],[11.093562010068986,4.462719937668994],[10.898990923513209,4.614564703964317],[10.712104875893294,4.775773717297265],[10.533354092067249,4.945958611034492],[10.363169198330029,5.124709394860537],[10.201960184997075,5.3115954424804555],[10.05011541870176,5.506166529036235],[9.908000706786106,5.707953915737954],[9.77595841603891,5.916471479096555],[9.654306647904113,6.131216882037934],[9.54333847214659,6.351672784076922],[9.443321220821407,6.577308087635854],[9.35449584424748,6.807579217505141],[9.2909286040375,7],[4.937461666166767,7],[1.6458205553889218,5.043522969406299],[0,4.065284454109448],[0,0],[12.200033313599473,0]],edges:[{id:"SM.F13:flat:edge_1",polyline:[[12.200033313599473,3.842297553313296],[12.164703568668596,3.8559257397886375],[11.939068265109665,3.9559429911138198],[11.718612363070678,4.066911166871344],[11.503866960129303,4.188562935006142],[11.295349396770701,4.320605225753343],[11.093562010068986,4.462719937668994],[10.898990923513209,4.614564703964317],[10.712104875893294,4.775773717297265],[10.533354092067249,4.945958611034492],[10.363169198330029,5.124709394860537],[10.201960184997075,5.3115954424804555],[10.05011541870176,5.506166529036235],[9.908000706786106,5.707953915737954],[9.77595841603891,5.916471479096555],[9.654306647904113,6.131216882037934],[9.54333847214659,6.351672784076922],[9.443321220821407,6.577308087635854],[9.35449584424748,6.807579217505141],[9.2909286040375,7]]},{id:"SM.F13:flat:top",polyline:[[9.2909286040375,7],[4.937461666166767,7]]},{id:"SM.F13:flat:edge",polyline:[[4.937461666166767,7],[0,4.065284454109448]],bend:{kind:"bend",id:"SM.F18:bend",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F18:flat",outline:[[0,0],[5.743786472368031,0],[5.743786472368031,7],[0,7]],edges:[{id:"SM.F18:flat:attach",isAttachEdge:!0,polyline:[[0,0],[5.743786472368031,0]]},{id:"SM.F18:flat:top",polyline:[[5.743786472368031,7],[0,7]]},{id:"SM.F18:flat:left",polyline:[[0,7],[0,0]]},{id:"SM.F18:flat:right",polyline:[[5.743786472368031,0],[5.743786472368031,7]]}]},attachEdgeId:"SM.F18:flat:attach"}]}},{id:"SM.F13:flat:left",polyline:[[0,4.065284454109448],[0,0]]},{id:"SM.F13:flat:attach",isAttachEdge:!0,polyline:[[0,0],[12.200033313599473,0]]},{id:"SM.F13:flat:right",polyline:[[12.200033313599473,0],[12.200033313599473,3.842297553313296]]}]},attachEdgeId:"SM.F13:flat:attach"}]}},{id:"SM.TAB3:flat_root:edge_2:bridge_1",polyline:[[15.92242885856668,3.9999749363772965],[15.922428663832992,.9999749363773027]]},{id:"SM.TAB3:flat_root:hole:SM.TAB3:flat_root:hole_1:edge:3",isInternalCutoutEdge:!0,holeId:"SM.TAB3:flat_root:hole_1",holeEdgeIndex:3,holeEdgeSignature:"15.85720,8.55661;8.61462,7.38113",polyline:[[15.857195247283375,8.556605622027856],[8.614620331384927,7.381131149049549]],bend:{kind:"bend",id:"SM.F9:bend",angleDeg:90,midRadius:2.5,kFactor:.5,children:[{flat:{kind:"flat",id:"SM.F9:flat",outline:[[0,0],[7.337345000000003,0],[7.337345000000003,3.0000000000000004],[0,3.0000000000000004]],edges:[{id:"SM.F9:flat:attach",isAttachEdge:!0,polyline:[[0,0],[7.337345000000003,0]]},{id:"SM.F9:flat:top",polyline:[[7.337345000000003,3.0000000000000004],[0,3.0000000000000004]]},{id:"SM.F9:flat:left",polyline:[[0,3.0000000000000004],[0,0]]},{id:"SM.F9:flat:right",polyline:[[7.337345000000003,0],[7.337345000000003,3.0000000000000004]]}]},attachEdgeId:"SM.F9:flat:attach"}]}}],holes:[{id:"SM.TAB3:flat_root:hole_1",outline:[[15.857195247283375,8.556605622027856],[8.614620331384927,7.381131149049549],[7.035448718059691,17.111047857142367],[14.278023633958139,18.286522330120675]]}]},__sheetMeta:{baseType:"FLANGE",defaultInsideRadius:2,defaultKFactor:.5,lastFeatureID:"SM.F18",defaultFlangeLengthReference:"outside",defaultInsetMode:"material_inside"}}};function wo(e){return JSON.parse(JSON.stringify(e))}i(wo,"clone$4");function At(e,t){if(!e)return null;if(String(e.id)===String(t))return e;const r=Array.isArray(e.edges)?e.edges:[];for(const n of r){const o=n?.bend,s=Array.isArray(o?.children)?o.children:[];for(const a of s){const c=At(a?.flat,t);if(c)return c}}return null}i(At,"findFlatById$2");function i0(e,t){return(Array.isArray(e?.edges)?e.edges:[]).find(n=>String(n?.id)===String(t))||null}i(i0,"findEdgeById$1");function c0(e){const t=Array.isArray(e)?e:[];let r=0;for(let n=1;n<t.length;n+=1){const o=t[n-1],s=t[n];r+=Math.hypot(s[0]-o[0],s[1]-o[1])}return r}i(c0,"polylineLength2$1");async function vo(e){await e.reset(),e.features=[];const t=wo(L.tree),r=At(t.root,"SM.TAB3:flat_root");if(!r)throw new Error("Fixture flat SM.TAB3:flat_root not found.");const n=i0(r,"SM.TAB3:flat_root:e2");if(!n)throw new Error("Fixture edge SM.TAB3:flat_root:e2 not found.");const o=Array.isArray(r.outline)?r.outline.length:0,s=c0(n.polyline),a=F0({tree:t,featureID:"SM.TEST.FILLET",targets:[{flatId:"SM.TAB3:flat_root",edgeId:"SM.TAB3:flat_root:e2"}],radius:1.2,resolution:48});if(Number(a?.summary?.applied||0)<1)throw new Error("Corner fillet did not apply to the target edge.");if(Number(a?.summary?.appliedCorners||0)<1)throw new Error("Corner fillet did not apply to any corners.");const c=At(a.tree?.root,"SM.TAB3:flat_root");if(!c)throw new Error("Updated fixture flat SM.TAB3:flat_root not found.");if(!((Array.isArray(c.outline)?c.outline.length:0)>o))throw new Error("Corner fillet did not add rounded vertices to the flat outline.");const d=i0(c,"SM.TAB3:flat_root:e2");if(!d)throw new Error("Updated edge SM.TAB3:flat_root:e2 not found.");if(!(c0(d.polyline)+1e-6<s))throw new Error("Corner fillet did not trim the selected edge span.");const p=Se({featureID:"SM.TEST.FILLET",tree:a.tree,rootMatrix:L.rootTransform,showFlatPattern:!1})?.root||null;if(!p||typeof p._manifoldize!="function")throw new Error("Corner-fillet tree did not build a manifold-capable solid.");try{p._manifoldize()}catch(m){const h=String(m?.message||m||"Unknown manifold error");throw new Error(`sheetMetal_corner_fillet failed manifoldization: ${h}`)}return e}i(vo,"test_sheetMetal_corner_fillet");function _o(e){return JSON.parse(JSON.stringify(e))}i(_o,"clone$3");function Ie(e,t,r=.001){return Math.abs(Number(e)-Number(t))<=r}i(Ie,"approxEqual$3");function V0(e,t){if(!e)return null;if(String(e.id)===String(t))return e;const r=Array.isArray(e.edges)?e.edges:[];for(const n of r){const o=Array.isArray(n?.bend?.children)?n.bend.children:[];for(const s of o){const a=V0(s?.flat,t);if(a)return a}}return null}i(V0,"findFlatById$1");function Eo(e,t,r){const n=e[0]-t[0],o=e[1]-t[1],s=e[2]-t[2],a=n*r[0]+o*r[1]+s*r[2],c=n-r[0]*a,l=o-r[1]*a,d=s-r[2]*a;return Math.hypot(c,l,d)}i(Eo,"distancePointToAxis$1");function So(e,t,r,n){const o=e?._faceNameToID?.get(t),s=Array.isArray(e?._triVerts)?e._triVerts:[],a=Array.isArray(e?._triIDs)?e._triIDs:[],c=Array.isArray(e?._vertProperties)?e._vertProperties:[];if(o==null||!s.length||!a.length||!c.length)return[];const l=[];for(let d=0;d<a.length;d+=1){if(a[d]!==o)continue;const f=d*3;for(let u=0;u<3;u+=1){const p=s[f+u]*3;l.push(Eo([c[p],c[p+1],c[p+2]],r,n))}}return l}i(So,"collectFaceVertexAxisDistances$1");function l0(e,t){return Math.hypot(Number(e?.[0]||0)-Number(t?.[0]||0),Number(e?.[1]||0)-Number(t?.[1]||0),Number(e?.[2]||0)-Number(t?.[2]||0))}i(l0,"distance3");async function bo(e){await e.reset(),e.features=[];const t=1.2,r=_o(L.tree),n=F0({tree:r,featureID:"SM.TEST.FILLET",targets:[{flatId:"SM.TAB3:flat_root",edgeId:"SM.TAB3:flat_root:e2"}],radius:t,resolution:48}),o=V0(n?.tree?.root,"SM.TAB3:flat_root");if(!o)throw new Error("Updated flat for sheet-metal corner fillet metadata test was not found.");const s=(Array.isArray(o.edges)?o.edges:[]).filter(d=>Array.isArray(d?.polyline)&&d.polyline.length>2);if(!s.length)throw new Error("Corner fillet test did not create a rounded thickness edge.");const c=Se({featureID:"SM.TEST.FILLET",tree:n.tree,rootMatrix:L.rootTransform,showFlatPattern:!1})?.root||null;if(!c||typeof c.getFaceMetadata!="function")throw new Error("Corner fillet metadata test did not build a readable solid.");let l=!1;for(const d of s){const f=`SM.TEST.FILLET:FLAT:${o.id}:SIDE:${d.id}`,u=c.getFaceMetadata(f);if(!u||u.type!=="cylindrical")throw new Error(`Expected cylindrical metadata on corner-fillet thickness face "${f}".`);if(!Array.isArray(u.axis)||u.axis.length!==3||!Array.isArray(u.center)||u.center.length!==3)throw new Error(`Corner-fillet thickness face "${f}" is missing axis/center metadata.`);if(!Number.isFinite(u.radius)||u.radius<=0)throw new Error(`Corner-fillet thickness face "${f}" is missing a positive radius.`);if(!Ie(u.height,L.tree.thickness,.001))throw new Error(`Corner-fillet thickness face "${f}" should use the sheet thickness as cylinder height.`);const p=So(c,f,u.center,u.axis);if(!p.length)throw new Error(`Could not sample corner-fillet thickness face "${f}".`);const m=p.reduce((v,E)=>v+E,0)/p.length,h=Math.min(...p),x=Math.max(...p);if(!Ie(m,u.radius,.001))throw new Error(`Corner-fillet thickness face "${f}" radius metadata does not match geometry.`);if(x-h>.002)throw new Error(`Corner-fillet thickness face "${f}" is not geometrically cylindrical.`);Ie(u.radius,t,.001)&&(l=!0);const g=(Array.isArray(c._auxEdges)?c._auxEdges:[]).find(v=>v?.name===`${f}:CENTERLINE`);if(!g||!g.centerline)throw new Error(`Expected a centerline auxiliary edge for corner-fillet thickness face "${f}".`);if(!Array.isArray(g.points)||g.points.length!==2)throw new Error(`Corner-fillet centerline "${f}:CENTERLINE" should contain exactly 2 points.`);const w=[(g.points[0][0]+g.points[1][0])*.5,(g.points[0][1]+g.points[1][1])*.5,(g.points[0][2]+g.points[1][2])*.5];if(!Ie(l0(w,u.center),0,1e-4))throw new Error(`Corner-fillet centerline "${f}:CENTERLINE" is not centered on the cylindrical face axis.`);if(!Ie(l0(g.points[0],g.points[1]),u.height,.001))throw new Error(`Corner-fillet centerline "${f}:CENTERLINE" should span the cylindrical face height.`)}if(!l)throw new Error("No corner-fillet thickness face preserved the requested fillet radius.");return e}i(bo,"test_sheetMetal_corner_fillet_face_cylindrical_metadata");function Ce(e,t,r=.001){return Math.abs(Number(e)-Number(t))<=r}i(Ce,"approxEqual$2");function Po(e,t,r){const n=e[0]-t[0],o=e[1]-t[1],s=e[2]-t[2],a=n*r[0]+o*r[1]+s*r[2],c=n-r[0]*a,l=o-r[1]*a,d=s-r[2]*a;return Math.hypot(c,l,d)}i(Po,"distancePointToAxis");function Fo(e,t,r,n){const o=e?._faceNameToID?.get(t),s=Array.isArray(e?._triVerts)?e._triVerts:[],a=Array.isArray(e?._triIDs)?e._triIDs:[],c=Array.isArray(e?._vertProperties)?e._vertProperties:[];if(o==null||!s.length||!a.length||!c.length)return[];const l=[];for(let d=0;d<a.length;d+=1){if(a[d]!==o)continue;const f=d*3;for(let u=0;u<3;u+=1){const p=s[f+u]*3,m=[c[p],c[p+1],c[p+2]];l.push(Po(m,r,n))}}return l}i(Fo,"collectFaceVertexAxisDistances");function d0(e,t,r){if(!r||r.type!=="cylindrical")throw new Error(`Expected cylindrical metadata on bend face "${t}".`);if(!Number.isFinite(r.radius)||r.radius<=0)throw new Error(`Expected positive bend-face radius on "${t}".`);if(!Number.isFinite(r.height)||r.height<=0)throw new Error(`Expected positive bend-face height on "${t}".`);if(!Array.isArray(r.axis)||r.axis.length!==3)throw new Error(`Expected axis triplet on bend face "${t}".`);if(!Array.isArray(r.center)||r.center.length!==3)throw new Error(`Expected center triplet on bend face "${t}".`);const n=Math.hypot(r.axis[0],r.axis[1],r.axis[2]);if(!Ce(n,1,.001))throw new Error(`Expected normalized bend-face axis on "${t}".`);const o=Fo(e,t,r.center,r.axis);if(!o.length)throw new Error(`Could not sample bend-face vertices for "${t}".`);const s=o.reduce((l,d)=>l+d,0)/o.length,a=Math.min(...o),c=Math.max(...o);if(!Ce(s,r.radius,.001))throw new Error(`Bend-face radius metadata does not match geometry on "${t}".`);if(c-a>.002)throw new Error(`Bend-face vertices are not consistent with a cylindrical radius on "${t}".`)}i(d0,"validateBendFaceMetadata");async function Ao(e){await e.reset(),e.features=[];const{featureID:t,tree:r,rootTransform:n}=L,o=Se({featureID:t,tree:r,rootMatrix:n,showFlatPattern:!1}),s=o?.root||null,a=Array.isArray(o?.evaluated?.bends3D)?o.evaluated.bends3D:[];if(!s||typeof s.getFaceMetadata!="function")throw new Error("Sheet-metal bend metadata test did not produce a readable solid.");if(!a.length)throw new Error("Sheet-metal bend metadata test did not produce any bends.");const c=a.find(x=>x?.bend?.id&&Number.isFinite(x?.midRadius));if(!c)throw new Error("Could not find a bend placement with radius data.");const l=`${t}:BEND:${c.bend.id}:A`,d=`${t}:BEND:${c.bend.id}:B`,f=s.getFaceMetadata(l),u=s.getFaceMetadata(d);d0(s,l,f),d0(s,d,u);const p=Number(r?.thickness);if(!Number.isFinite(p)||p<=0)throw new Error("Sheet-metal test fixture has invalid thickness.");const m=[Math.max(1e-6,c.midRadius-p*.5),c.midRadius+p*.5].sort((x,y)=>x-y),h=[f.radius,u.radius].sort((x,y)=>x-y);if(!Ce(h[0],m[0],.001)||!Ce(h[1],m[1],.001))throw new Error(`Bend-face metadata radii do not match inside/outside bend radii for bend "${c.bend.id}".`);if(!Ce(Math.abs(f.radius-u.radius),p,.001))throw new Error(`Bend-face metadata radii should differ by sheet thickness for bend "${c.bend.id}".`);return e}i(Ao,"test_sheetMetal_bend_face_cylindrical_metadata");function Q(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(Q,"assert$d");async function Io(){const e=st(0,0);Q(e.widthIn===Lt,"Expected missing media width to use the default insert width."),Q(e.heightIn===q0,"Expected missing media height to use the default insert height.");const t=st(1600,800);Q(Math.abs(t.widthIn-Lt)<1e-9,"Expected wide images to keep the default insert width."),Q(Math.abs(t.heightIn-1.6)<1e-9,"Expected wide images to preserve aspect ratio.");const r=st(600,1600);Q(Math.abs(r.heightIn-2.4)<1e-9,"Expected tall images to clamp to the maximum insert height."),Q(Math.abs(r.widthIn-.9)<1e-9,"Expected tall images to preserve aspect ratio after clamping.");const n=at({html:'<div><img alt="clip" src="data:image/png;base64,AAAA" /></div>',plainText:""});Q(n==="data:image/png;base64,AAAA","Expected image clipboard HTML to expose its data URL.");const o=at({html:"<div>not an image</div>",plainText:"data:image/jpeg;base64,BBBB"});Q(o==="data:image/jpeg;base64,BBBB","Expected plain-text data URLs to be accepted as clipboard images.");const s=at({html:'<img src="https://example.com/example.png" />',plainText:""});Q(s==="","Expected remote clipboard image URLs to be ignored so pasted sheets stay self-contained.")}i(Io,"test_sheet_clipboard_image_utils");function To(e){return JSON.parse(JSON.stringify(e))}i(To,"clone$2");async function No(e){await e.reset(),e.features=[];const r=Se({featureID:"SM.TAB3",tree:To(L.tree),rootMatrix:L.rootTransform,showFlatPattern:!0})?.root||null;if(!r)throw new Error("Failed to build source sheet-metal solid for fillet stub test.");e.scene.add(r);const o={type:"EDGE",name:"SM.TAB3:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e2|SM.TAB3:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e3[0]",userData:{}},s=new Kt;s.inputParams=await e.sanitizeInputParams(Kt.inputParamsSchema,{id:"F19",edges:[o],radius:"2.5",direction:"INSET"}),s.inputParams.id="F19",s.inputParams.featureID="F19";const a=await s.run(e),c=Array.isArray(a?.added)?a.added.length:0,l=Array.isArray(a?.removed)?a.removed.length:0;if(c!==1||l!==1)throw new Error("Compound corner reference should resolve to a sheet-metal fillet replacement.");const d=s?.persistentData||{};if(d.usedSheetMetalPath!==!0)throw new Error("Compound corner reference should use the sheet-metal fillet path.");const f=d.sheetMetalFilletSummary||{};if(f.applied!==1||f.appliedCorners!==1)throw new Error("Compound corner reference should apply exactly one sheet-metal corner fillet.");const u=Array.isArray(f.appliedTargets)?f.appliedTargets[0]:null,p=Array.isArray(u?.cornerEdgeIds)?u.cornerEdgeIds.join(","):"";if(p!=="SM.TAB3:flat_root:e2,SM.TAB3:flat_root:e3")throw new Error(`Compound corner reference should preserve both corner edges, got ${p}.`)}i(No,"test_sheetMetal_corner_fillet_compound_reference");function Do(e){return JSON.parse(JSON.stringify(e))}i(Do,"clone$1");async function Mo(e){await e.reset(),e.features=[];const r=Se({featureID:L.featureID,tree:Do(L.tree),rootMatrix:L.rootTransform,showFlatPattern:!1})?.root||null;if(!r)throw new Error("Failed to build source carrier for corner-fillet selection test.");const s=gr({sourceCarrier:r,selections:[{type:"FACE",name:"SM.F18:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e2",parentSolid:r,userData:{}},{type:"FACE",name:"SM.F18:FLAT:SM.TAB3:flat_root:SIDE:SM.TAB3:flat_root:e3",parentSolid:r,userData:{}}],edgeSelections:[],radius:1,resolution:32,featureID:"SM.TEST.FILLET.SELECT",showFlatPattern:!1});if(!s?.handled)throw new Error("runSheetMetalCornerFillet did not handle a valid sheet-metal source.");if(Number(s?.summary?.applied||0)<1)throw new Error("Selection-based corner fillet did not resolve selected side faces.");if(!s?.root||typeof s.root._manifoldize!="function")throw new Error("Selection-based corner fillet did not produce a manifold-capable result.");try{s.root._manifoldize()}catch(a){const c=String(a?.message||a||"Unknown manifold error");throw new Error(`sheetMetal_corner_fillet_selection_resolution manifoldization failed: ${c}`)}return e}i(Mo,"test_sheetMetal_corner_fillet_selection_resolution");function se(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(se,"assert$c");async function Co(){const e={type:"SKETCH",name:"SK_001"},t={type:"FACE",name:"SK_001:PROFILE",parent:e},r={type:"SOLID",name:"CUTTER_001"},n={type:"FACE",name:"BODY:FACE_1"},o=Fe.showContexButton([e]);se(o?.field==="profile","Sketch selection should target the profile field."),se(o?.value==="SK_001","Sketch selection should seed the sketch name.");const s=Fe.showContexButton([t]);se(s?.field==="profile","Sketch-face selection should target the profile field."),se(s?.value==="SK_001:PROFILE","Sketch-face selection should seed the selected face name.");const a=Fe.showContexButton([r]);se(a?.field==="profile","Solid selection should target the profile field."),se(a?.value==="CUTTER_001","Solid selection should seed the selected solid name."),se(Fe.showContexButton([n])===!1,"Non-sketch faces should not show the sheet metal cutout context button."),se(Fe.showContexButton([t,r])===!1,"Multiple selections should not show the sheet metal cutout context button.")}i(Co,"test_sheetMetal_cutout_context_button");function Ro(e){return JSON.parse(JSON.stringify(e))}i(Ro,"clone");function Qe(e,t){if(!e)return null;if(String(e.id)===String(t))return e;const r=Array.isArray(e.edges)?e.edges:[];for(const n of r){const o=n?.bend,s=Array.isArray(o?.children)?o.children:[];for(const a of s){const c=Qe(a?.flat,t);if(c)return c}}return null}i(Qe,"findFlatById");function It(e,t){return(Array.isArray(e?.edges)?e.edges:[]).find(n=>String(n?.id)===String(t))||null}i(It,"findEdgeById");function u0(e){const t=Array.isArray(e)?e:[];let r=0;for(let n=1;n<t.length;n+=1){const o=t[n-1],s=t[n];r+=Math.hypot(s[0]-o[0],s[1]-o[1])}return r}i(u0,"polylineLength2");function ko(){const e=Ro(L.tree),t=Qe(e.root,"SM.F13:flat");if(!t)throw new Error("Fixture flat SM.F13:flat not found.");const r=It(t,"SM.F13:flat:edge");if(!r)throw new Error("Fixture edge SM.F13:flat:edge not found.");if(delete r.bend,Array.isArray(r.polyline)&&r.polyline.length===2){const n=Array.isArray(t.outline)?t.outline:[],o=r.polyline[0],s=r.polyline[1],a=Math.hypot(s[0]-o[0],s[1]-o[1]);if(a>1e-8){let c=null,l=Number.POSITIVE_INFINITY;for(const d of n){if(!Array.isArray(d)||d.length<2)continue;const f=s[0]-o[0],u=s[1]-o[1],p=((d[0]-o[0])*f+(d[1]-o[1])*u)/(a*a);if(!(p>1e-6&&p<1-1e-6))continue;const m=[o[0]+f*p,o[1]+u*p],h=Math.hypot(d[0]-m[0],d[1]-m[1]);h<l&&(l=h,c=[d[0],d[1]])}c&&l<.001&&(r.polyline=[o,c,s])}}return e}i(ko,"buildPreF18TreeWithMultiPointCutoutEdge");function ge(e){const t=ko();return yr({tree:t,featureID:"SM.TEST",targets:[{flatId:"SM.F13:flat",edgeId:"SM.F13:flat:edge"}],options:{angleDeg:90,midRadius:2.5,kFactor:.5,legLength:7,requestedLegLength:10,legLengthReference:"outside",legLengthReferenceSetback:3,thickness:1,insideRadius:2,...e}})}i(ge,"applyFlange");async function $o(e){await e.reset(),e.features=[];const t=ge({insetMode:"material_inside",offset:0,edgeStartSetback:0,edgeEndSetback:0});if(t?.summary?.applied!==1)throw new Error("Baseline cutout-edge flange was not applied.");const r=ge({insetMode:"material_inside",offset:0,edgeStartSetback:2,edgeEndSetback:2});if(r?.summary?.applied!==1)throw new Error("Setback cutout-edge flange was not applied.");const n=r.summary.appliedTargets?.[0]||{};if(!(Number(n.edgeStartSetbackApplied)>1.5&&Number(n.edgeEndSetbackApplied)>1.5))throw new Error("Cutout-edge start/end setback were not applied.");const o=Qe(t.tree.root,"SM.F13:flat"),s=Qe(r.tree.root,"SM.F13:flat"),a=It(o,"SM.F13:flat:edge"),c=It(s,"SM.F13:flat:edge"),l=u0(a?.polyline);if(!(u0(c?.polyline)+1e-6<l))throw new Error("Cutout-edge setbacks did not shorten the parent edge span.");const f=ge({insetMode:"bend_outside",offset:0,edgeStartSetback:0,edgeEndSetback:0}),u=ge({insetMode:"material_inside",offset:0,edgeStartSetback:0,edgeEndSetback:0}),p=Number(f.summary.appliedTargets?.[0]?.edgeShiftApplied||0),m=Number(u.summary.appliedTargets?.[0]?.edgeShiftApplied||0);if(!(Math.abs(p-m)>.5))throw new Error("Cutout-edge inset mode did not change edge shift.");const h=ge({insetMode:"material_inside",offset:0,edgeStartSetback:0,edgeEndSetback:0}),x=ge({insetMode:"material_inside",offset:1,edgeStartSetback:0,edgeEndSetback:0}),y=Number(h.summary.appliedTargets?.[0]?.edgeShiftApplied||0),g=Number(x.summary.appliedTargets?.[0]?.edgeShiftApplied||0);if(!(Math.abs(y-g)>.5))throw new Error("Cutout-edge offset did not change edge shift.");return e}i($o,"test_sheetMetal_cutoutEdge_flange_controls");async function Oo(e){await e.reset(),e.features=[];const{featureID:t,tree:r,rootTransform:n}=L,s=Se({featureID:t,tree:r,rootMatrix:n,showFlatPattern:!1})?.root||null;if(!s||typeof s._manifoldize!="function")throw new Error("Sheet-metal fixture did not produce a solid with manifold support.");try{s._manifoldize()}catch(c){const l=String(c?.message||c||"Unknown manifold error");throw new Error(`sheetMetal_nonManifold_sm_f18 failed manifoldization: ${l}`)}const a=typeof s.getFaces=="function"?s.getFaces(!1)||[]:[];if(!Array.isArray(a)||a.length===0)throw new Error("sheetMetal_nonManifold_sm_f18 produced an empty solid.");return e}i(Oo,"test_sheetMetal_nonManifold_sm_f18");async function Lo(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:20,y:0,fixed:!1},{id:2,x:20,y:15,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]}}i(Lo,"test_sketch_openLoop");async function Bo(e){const t=e.features.find(s=>s?.type==="S");if(!t)throw new Error("Sketch feature missing from history");const r=e.scene.getObjectByName(t.inputParams.featureID);if(!r)throw new Error("Sketch group not found in scene");let n=0,o=0;if(r.traverse(s=>{s&&(s.type==="FACE"?n++:s.type==="EDGE"&&o++)}),n!==0)throw new Error(`Open sketch generated ${n} face(s)`);if(o===0)throw new Error("Open sketch should expose at least one EDGE")}i(Bo,"afterRun_sketch_openLoop");const We=.001,f0=.001;function p0(e){const r=e?.geometry?.getAttribute?.("position");if(!r||r.itemSize!==3)return[];const n=[],o=new V;for(let s=0;s<r.count;s++)o.set(r.getX(s),r.getY(s),r.getZ(s)).applyMatrix4(e.matrixWorld),n.push([o.x,o.y,o.z]);return n}i(p0,"collectFaceVerticesWorld");function zo(e){if(!Array.isArray(e)||e.length===0)return null;const t=new V;for(const r of e)t.x+=Number(r[0])||0,t.y+=Number(r[1])||0,t.z+=Number(r[2])||0;return t.multiplyScalar(1/e.length)}i(zo,"computeCentroid");function jo(e,t,r){let n=Number.POSITIVE_INFINITY,o=Number.NEGATIVE_INFINITY,s=0,a=0;for(const c of e){const l=(Number(c[0])||0)-t.x,d=(Number(c[1])||0)-t.y,f=(Number(c[2])||0)-t.z,u=l*r.x+d*r.y+f*r.z;u<n&&(n=u),u>o&&(o=u),s+=u,a+=1}return{count:a,mean:a?s/a:0,spread:a?o-n:0}}i(jo,"computeSignedDistanceStats");async function Uo(e){const t=await e.newFeature("S");Object.assign(t.inputParams,{id:"S1",sketchPlane:null,curveResolution:32}),t.persistentData={sketch:{points:[{id:0,x:0,y:0,fixed:!0,construction:!0,externalReference:!1},{id:1,x:-10.96543,y:26.783322,fixed:!1,construction:!1,externalReference:!1},{id:2,x:0,y:0,fixed:!0,construction:!1,externalReference:!1},{id:3,x:8.905728,y:14.930946,fixed:!1,construction:!1,externalReference:!1},{id:4,x:8.905728,y:14.930946,fixed:!1,construction:!1,externalReference:!1},{id:5,x:-10.96543,y:26.783322,fixed:!1,construction:!1,externalReference:!1},{id:6,x:-19.87116,y:11.852382,fixed:!1,construction:!1,externalReference:!1},{id:7,x:-19.87116,y:11.852382,fixed:!1,construction:!1,externalReference:!1}],geometries:[{id:1,type:"line",points:[0,3],construction:!1},{id:2,type:"line",points:[4,1],construction:!1},{id:3,type:"line",points:[5,6],construction:!1},{id:4,type:"line",points:[7,2],construction:!1}],constraints:[{id:0,type:"⏚",points:[0],status:"solved"},{id:1,type:"≡",points:[0,2],status:"solved"},{id:2,type:"≡",points:[3,4],status:"solved"},{id:3,type:"≡",points:[1,5],status:"solved"},{id:4,type:"≡",points:[6,7],status:"solved"},{id:5,type:"⟂",points:[0,3,4,1],status:"solved",value:270},{id:6,type:"⟂",points:[4,1,5,6],status:"solved",value:270},{id:7,type:"⟂",points:[5,6,7,2],status:"solved",value:270}]}};const r=await e.newFeature("E");Object.assign(r.inputParams,{id:"E2",profile:"S1:PROFILE",consumeProfileSketch:!0,distance:43.1,distanceBack:73.4,boolean:{targets:[],operation:"NONE"}});const n=await e.newFeature("F");Object.assign(n.inputParams,{id:"F3",edges:["E2:S1:G2_SW|E2:S1:G3_SW[0]","E2:S1:G1_SW|E2:S1:G2_SW[0]"],radius:"9.5",resolution:32,inflate:.1,direction:"AUTO",debug:"NONE",showTangentOverlays:!1});const o=await e.newFeature("R");Object.assign(o.inputParams,{id:"R4",profile:"E2:S1:PROFILE_END",consumeProfileSketch:!0,axis:"E2:S1:G4_SW|E2:S1:PROFILE_END[0]",angle:144,resolution:64,boolean:{targets:["E2"],operation:"UNION"}});const s=await e.newFeature("E");Object.assign(s.inputParams,{id:"E5",profile:"E2:S1:PROFILE_END_END",consumeProfileSketch:!0,distance:0,distanceBack:111.2,boolean:{targets:["E2"],operation:"UNION"}});const a=await e.newFeature("S");return Object.assign(a.inputParams,{id:"S6",sketchPlane:"E5:E2:S1:PROFILE_END_END_START",curveResolution:32}),a.persistentData={sketch:{points:[{id:0,x:0,y:0,fixed:!0,construction:!0,externalReference:!1},{id:1,x:1.282171,y:3.606104,fixed:!1,construction:!1,externalReference:!1},{id:2,x:20.40159,y:3.342124,fixed:!1,construction:!1,externalReference:!1}],geometries:[{id:1,type:"circle",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0],status:"solved"}]}},e}i(Uo,"test_sketch_face_attachment_alignment");async function Wo(e){const t="E5:E2:S1:PROFILE_END_END_START",n="S6:PROFILE",o=e.scene.getObjectByName(t);if(!o||o.type!=="FACE")throw new Error(`[sketch-face-attach] Missing reference face ${t}`);const s=e.scene.getObjectByName("S6");if(!s)throw new Error("[sketch-face-attach] Sketch group S6 not found");let a=null;if(s.traverse(v=>{!a&&v?.type==="FACE"&&v?.name===n&&(a=v)}),!a)throw new Error(`[sketch-face-attach] Missing sketch profile face ${n}`);const c=typeof o.getAverageNormal=="function"?o.getAverageNormal().clone():null;if(!c||c.lengthSq()<1e-12)throw new Error("[sketch-face-attach] Reference face normal is invalid");c.normalize();const l=p0(o),d=p0(a);if(!l.length||!d.length)throw new Error("[sketch-face-attach] Missing reference/sketch vertices");const f=zo(l);if(!f)throw new Error("[sketch-face-attach] Failed to compute reference face centroid");const u=jo(d,f,c);if(Math.abs(u.mean)>We)throw new Error(`[sketch-face-attach] Sketch profile is offset from target face plane (mean=${u.mean}, tol=${We})`);if(u.spread>We)throw new Error(`[sketch-face-attach] Sketch profile is not planar on target face plane (spread=${u.spread}, tol=${We})`);const p=e.features.find(v=>v?.inputParams?.featureID==="S6"),m=Array.isArray(p?.persistentData?.basis?.origin)?new V().fromArray(p.persistentData.basis.origin):null;if(!m)throw new Error("[sketch-face-attach] Sketch basis origin is missing from persistent data");o.updateWorldMatrix(!0,!0);const h=new wr().setFromObject(o).getCenter(new V),x=l[0],y=new V(x[0],x[1],x[2]),g=h.clone().sub(c.clone().multiplyScalar(h.clone().sub(y).dot(c))),w=m.distanceTo(g);if(w>f0)throw new Error(`[sketch-face-attach] Sketch basis origin is not centered on target face (distance=${w}, tol=${f0})`)}i(Wo,"afterRun_sketch_face_attachment_alignment");const R=.01;function I(e,t){if(!e)throw new Error(t)}i(I,"assert$b");function P(e,t,r,n){if(!Number.isFinite(e)||!Number.isFinite(t)||Math.abs(e-t)>r)throw new Error(`${n}. Expected ${t}, got ${e}`)}i(P,"assertNear");function ne(e,t){const r=e.points.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Point ${t} not found`);return r}i(ne,"getPoint");function fe(e,t){const r=e.constraints.find(n=>Number(n.id)===Number(t));if(!r)throw new Error(`Constraint ${t} not found`);return r}i(fe,"getConstraint");function b(e,t,r){const n=ne(e,t),o=ne(e,r);return Math.hypot(o.x-n.x,o.y-n.y)}i(b,"dist");function Yo(e,t,r,n){const o=ne(e,t),s=ne(e,r),a=ne(e,n),c=s.x-o.x,l=s.y-o.y,d=c*c+l*l;return d>1e-12?Math.abs(((a.x-o.x)*-l+(a.y-o.y)*c)/Math.sqrt(d)):Math.hypot(a.x-o.x,a.y-o.y)}i(Yo,"pointToLineDistance");function Ee(e,t){const r=t.map(o=>ne(e,o));let n=0;for(let o=0;o<r.length;o++){const s=r[o],a=r[(o+1)%r.length];n+=s.x*a.y-a.x*s.y}return n*.5}i(Ee,"signedArea");function m0(e){const t=e.points.map(o=>Number(o.id)).sort((o,s)=>o-s),r=(e.geometries||[]).map(o=>({id:Number(o.id),type:String(o.type),construction:!!o.construction,points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id),n=(e.constraints||[]).map(o=>({id:Number(o.id),type:String(o.type),points:(o.points||[]).map(s=>Number(s))})).sort((o,s)=>o.id-s.id);return{pointIds:t,geometries:r,constraints:n}}i(m0,"topologySnapshot");function Vo(e,t){const r=new Set((e.points||[]).map(n=>Number(n.id)));for(const n of e.geometries||[])for(const o of n.points||[])I(r.has(Number(o)),`${t}: geometry ${n.id} references missing point ${o}`);for(const n of e.constraints||[])for(const o of n.points||[])I(r.has(Number(o)),`${t}: constraint ${n.id} references missing point ${o}`)}i(Vo,"assertTopologyIntegrity");function be(e,t,r){Vo(t,r);const n=JSON.stringify(m0(e)),o=JSON.stringify(m0(t));I(n===o,`${r}: topology changed`)}i(be,"assertTopologyUnchanged");function he(e){const t=new b0({sketch:JSON.parse(JSON.stringify(e))});return t.solveSketch("full"),t}i(he,"solveSketch");async function Go(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:20,fixed:!1},{id:3,x:0,y:20,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1},{id:102,type:"line",points:[2,3],construction:!1},{id:103,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:1,type:"━",points:[0,1]},{id:2,type:"━",points:[2,3]},{id:3,type:"│",points:[1,2]},{id:4,type:"│",points:[3,0]},{id:5,type:"⟺",points:[0,1],value:40},{id:6,type:"⟺",points:[1,2],value:20}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=Ee(r,[0,1,2,3]);fe(t.sketchObject,5).value=130,t.solveSketch("full");const o=t.sketchObject;be(r,o,"shared-point rectangle width edit"),P(b(o,0,1),130,.05,"shared-point rectangle width changed unexpectedly"),P(b(o,1,2),20,.05,"shared-point rectangle height drifted");const s=ne(o,0);P(s.x,0,R,"shared-point rectangle anchor x moved"),P(s.y,0,R,"shared-point rectangle anchor y moved");const a=Ee(o,[0,1,2,3]);I(Math.abs(a)>1,"shared-point rectangle collapsed"),I(Math.sign(a)===Math.sign(n),"shared-point rectangle flipped orientation")}i(Go,"test_sketch_solver_topology_rect_shared_points");async function Xo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:0,fixed:!1},{id:3,x:40,y:30,fixed:!1},{id:4,x:40,y:30,fixed:!1},{id:5,x:80,y:30,fixed:!1}],geometries:[{id:200,type:"line",points:[0,1],construction:!1},{id:201,type:"line",points:[2,3],construction:!1},{id:202,type:"line",points:[4,5],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:10,type:"≡",points:[1,2]},{id:11,type:"≡",points:[3,4]},{id:12,type:"━",points:[0,1]},{id:13,type:"│",points:[2,3]},{id:14,type:"━",points:[4,5]},{id:15,type:"⟺",points:[0,1],value:40},{id:16,type:"⟺",points:[2,3],value:30},{id:17,type:"⟺",points:[4,5],value:40}]}),r=JSON.parse(JSON.stringify(t.sketchObject));fe(t.sketchObject,16).value=90,t.solveSketch("full");const n=t.sketchObject;be(r,n,"coincident-chain vertical edit"),P(b(n,2,3),90,.05,"coincident-chain edited segment length incorrect"),P(b(n,0,1),40,.05,"coincident-chain left segment length drifted"),P(b(n,4,5),40,.05,"coincident-chain right segment length drifted"),I(b(n,1,2)<R,"coincident-chain joint 1 broke"),I(b(n,3,4)<R,"coincident-chain joint 2 broke")}i(Xo,"test_sketch_solver_topology_coincident_chain");async function Jo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:0,fixed:!1},{id:3,x:40,y:20,fixed:!1},{id:4,x:40,y:20,fixed:!1},{id:5,x:0,y:20,fixed:!1},{id:6,x:0,y:20,fixed:!1},{id:7,x:0,y:0,fixed:!1}],geometries:[{id:300,type:"line",points:[0,1],construction:!1},{id:301,type:"line",points:[2,3],construction:!1},{id:302,type:"line",points:[4,5],construction:!1},{id:303,type:"line",points:[6,7],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:20,type:"≡",points:[1,2]},{id:21,type:"≡",points:[3,4]},{id:22,type:"≡",points:[5,6]},{id:23,type:"≡",points:[7,0]},{id:24,type:"━",points:[0,1]},{id:25,type:"│",points:[2,3]},{id:26,type:"━",points:[4,5]},{id:27,type:"│",points:[6,7]},{id:28,type:"⟺",points:[0,1],value:40},{id:29,type:"⟺",points:[2,3],value:20}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=Ee(r,[0,1,3,5]);fe(t.sketchObject,28).value=120,fe(t.sketchObject,29).value=55,t.solveSketch("full");const o=t.sketchObject;be(r,o,"coincident-loop dual edit"),P(b(o,0,1),120,.06,"coincident-loop width incorrect after edit"),P(b(o,2,3),55,.06,"coincident-loop height incorrect after edit"),I(b(o,1,2)<R,"coincident-loop corner 1 broke"),I(b(o,3,4)<R,"coincident-loop corner 2 broke"),I(b(o,5,6)<R,"coincident-loop corner 3 broke"),I(b(o,7,0)<R,"coincident-loop corner 4 broke");const s=Ee(o,[0,1,3,5]);I(Math.abs(s)>1,"coincident-loop collapsed"),I(Math.sign(s)===Math.sign(n),"coincident-loop flipped orientation")}i(Jo,"test_sketch_solver_topology_coincident_loop_no_flip");async function Ko(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:20,fixed:!1},{id:3,x:0,y:20,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:101,type:"line",points:[1,2],construction:!1},{id:102,type:"line",points:[2,3],construction:!1},{id:103,type:"line",points:[3,0],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:1,type:"━",points:[0,1]},{id:2,type:"━",points:[2,3]},{id:3,type:"│",points:[1,2]},{id:4,type:"│",points:[3,0]},{id:5,type:"⟺",points:[0,1],value:40},{id:6,type:"⟺",points:[1,2],value:20}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=Ee(r,[0,1,2,3]),o=[130,25,80,60];for(const s of o){fe(t.sketchObject,5).value=s,t.solveSketch("full");const a=t.sketchObject;be(r,a,"shared-point rectangle round-trip edits"),P(b(a,0,1),s,.07,"shared-point rectangle round-trip width mismatch"),P(b(a,1,2),20,.07,"shared-point rectangle round-trip height drift");const c=ne(a,0);P(c.x,0,R,"shared-point rectangle round-trip anchor x moved"),P(c.y,0,R,"shared-point rectangle round-trip anchor y moved");const l=Ee(a,[0,1,2,3]);I(Math.abs(l)>1,"shared-point rectangle round-trip collapsed"),I(Math.sign(l)===Math.sign(n),"shared-point rectangle round-trip flipped orientation")}}i(Ko,"test_sketch_solver_topology_rect_round_trip_sequence");async function qo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:40,y:0,fixed:!1},{id:2,x:40,y:0,fixed:!1},{id:3,x:40,y:30,fixed:!1},{id:4,x:40,y:30,fixed:!1},{id:5,x:80,y:30,fixed:!1}],geometries:[{id:200,type:"line",points:[0,1],construction:!1},{id:201,type:"line",points:[2,3],construction:!1},{id:202,type:"line",points:[4,5],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:10,type:"≡",points:[1,2]},{id:11,type:"≡",points:[3,4]},{id:12,type:"━",points:[0,1]},{id:13,type:"│",points:[2,3]},{id:14,type:"━",points:[4,5]},{id:15,type:"⟺",points:[0,1],value:40},{id:16,type:"⟺",points:[2,3],value:30},{id:17,type:"⟺",points:[4,5],value:40}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=[90,15,60,45];for(const o of n){fe(t.sketchObject,16).value=o,t.solveSketch("full");const s=t.sketchObject;be(r,s,"coincident-chain multi-step edits"),P(b(s,2,3),o,.08,"coincident-chain multi-step edited segment mismatch"),P(b(s,0,1),40,.08,"coincident-chain multi-step left segment drift"),P(b(s,4,5),40,.08,"coincident-chain multi-step right segment drift"),I(b(s,1,2)<R,"coincident-chain multi-step joint 1 broke"),I(b(s,3,4)<R,"coincident-chain multi-step joint 2 broke")}}i(qo,"test_sketch_solver_topology_coincident_chain_multi_step");async function Zo(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:1e3,y:0,fixed:!1}],geometries:[{id:400,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:30,type:"⟺",points:[0,1],value:1e3}]}),r=fe(t.sketchObject,30);r.value=1,t.solveSketch("full");const n=t.sketchObject;P(b(n,0,1),1,.01,"distance slide large-drop did not settle in one solve")}i(Zo,"test_sketch_solver_distance_slide_large_drop_settles_single_solve");async function Ho(){const t=he({points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:120,y:0,fixed:!1},{id:2,x:30,y:30,fixed:!1}],geometries:[{id:500,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]},{id:1,type:"━",points:[0,1]},{id:2,type:"↥",points:[0,1,2],value:30}]}),r=JSON.parse(JSON.stringify(t.sketchObject)),n=fe(t.sketchObject,2);n.value=80,t.solveSketch("full");const o=t.sketchObject;be(r,o,"line-to-point distance edit"),P(Yo(o,0,1,2),80,.08,"line-to-point distance mismatch");const s=ne(o,0);P(s.x,0,R,"line-to-point distance anchor x moved"),P(s.y,0,R,"line-to-point distance anchor y moved")}i(Ho,"test_sketch_solver_line_to_point_distance_constraint");async function Qo(e){const t=await e.newFeature("P.CU");return t.inputParams.sizeX=2,t.inputParams.sizeY=3,t.inputParams.sizeZ=4,e}i(Qo,"test_solidMetrics");async function es(e){try{const t=(e.scene?.children||[]).filter(g=>g&&g.type==="SOLID");if(!t.length){console.warn("[solidMetrics] No solids in scene.");return}const r=t[0],n=2,o=3,s=4,a=n*o*s,c=2*(n*o+o*s+s*n),l=typeof r.volume=="function"?r.volume():NaN,d=typeof r.surfaceArea=="function"?r.surfaceArea():NaN,f=i((g,w,v=1e-6)=>Math.abs(g-w)<=v*Math.max(1,Math.abs(w)),"approx"),u=i(g=>Number.isFinite(g)?g.toFixed(6):String(g),"fmt");console.log(`[solidMetrics] Solid volume: ${u(l)} (expected ${u(a)}) ${f(l,a)?"OK":"MISMATCH"}`),console.log(`[solidMetrics] Solid surface area: ${u(d)} (expected ${u(c)}) ${f(d,c)?"OK":"MISMATCH"}`);const p=r.children.filter(g=>g?.type==="FACE"),m=new Map([["_NX",o*s],["_PX",o*s],["_NY",n*s],["_PY",n*s],["_NZ",n*o],["_PZ",n*o]]);for(const g of p){const w=String(g?.name||""),v=Array.from(m.keys()).find(k=>w.endsWith(k));if(!v)continue;const E=typeof g.surfaceArea=="function"?g.surfaceArea():NaN,D=m.get(v);console.log(`[solidMetrics] Face ${w} area: ${u(E)} (expected ${u(D)}) ${f(E,D)?"OK":"MISMATCH"}`)}const x=r.children.filter(g=>g?.type==="EDGE").map(g=>typeof g.length=="function"?g.length():NaN),y=Array.from(new Set(x.map(g=>Number.isFinite(g)?g.toFixed(6):String(g))));console.log(`[solidMetrics] Distinct edge lengths: ${y.join(", ")} (expected ${[n,o,s].map(g=>g.toFixed(6)).join(", ")})`)}catch(t){console.warn("[solidMetrics] afterRun error:",t?.message||t)}}i(es,"afterRun_solidMetrics");async function ts(e){const t=await e.newFeature("IMPORT3D");return t.inputParams.fileToImport=rs,t.inputParams.deflectionAngle=15,e}i(ts,"test_stlLoader");const rs=`
|
|
8
8
|
solid Test_Cube
|
|
9
9
|
facet normal -4.258667930292975e-15 -9.729514454520843e-16 -1.0
|
|
10
10
|
outer loop
|
|
@@ -1883,13 +1883,13 @@ facet normal 0.0 0.0 1.0
|
|
|
1883
1883
|
endloop
|
|
1884
1884
|
endfacet
|
|
1885
1885
|
endsolid Test_Cube
|
|
1886
|
-
`;function N(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(N,"assert$a");async function ns(){const e={type:"SOLID",owningFeatureID:"BODY_OWNER",getFaceMetadata(p){return p==="FACE_FROM_META"?{sourceFeatureId:"FACE_OWNER"}:null},getEdgeMetadata(p){return p==="EDGE_FROM_META"?{sourceFeatureId:"EDGE_OWNER"}:null}},t={type:"FACE",name:"FACE_FROM_META",parentSolid:e,parent:e},r={type:"EDGE",name:"EDGE_FROM_META",parentSolid:e,parent:e},n={type:"FACE",name:"FACE_FALLBACK",parentSolid:e,parent:e},o={type:"EDGE",userData:{splineFeatureId:"SP_001"}},s={type:"LINE2",userData:{},parent:o},c={type:"EDGE",name:"SK_001:G5",userData:{},parent:{type:"SKETCH",name:"SK_001",userData:{sketchFeatureId:"SK_001"}}},d={type:"FACE",name:"OF_001:FACE_A:PROFILE",userData:{},parent:{type:"SKETCH",name:"OF_001:FACE_A",userData:{sketchFeatureId:"OF_001"}}};N(it(t)==="FACE_OWNER","Face metadata owner should win."),N(it(r)==="EDGE_OWNER","Edge metadata owner should use precise edge metadata."),N(it(n)==null,"Faces without precise provenance should not fall back to solid owner."),N(Bt(o)==="SP_001","Spline owner should resolve from spline metadata."),N(Bt(s)==="SP_001","Spline owner should resolve through parent geometry."),N(zt(c)==="SK_001","Sketch owner should resolve through sketch group metadata."),N(zt(d)==="OF_001","Sketch-like owner should resolve from group metadata."),N(Z0([{object:t}])==="FACE_OWNER","Single-selection resolution should unwrap selection objects."),N(H0([{object:s}])==="SP_001","Spline selection resolution should unwrap selection objects."),N(Q0([{object:c}])==="SK_001","Sketch-like selection resolution should unwrap selection objects."),N(ct([{object:t}],["FACE"])===!0,"Single face selection should match FACE."),N(jt([{object:o}])===!0,"Single spline selection should match spline helper."),N(er([{object:d}])===!0,"Single sketch-like selection should match sketch-like helper."),N(ct([{object:r}],["FACE","PLANE"])===!1,"Edge selection should not match the face-only toolbar filter."),N(jt([{object:o},{object:r}])===!1,"Multiple selections should not match spline helper."),N(ct([{object:t},{object:r}],["FACE","EDGE"])===!1,"Multiple selections should not match.")}i(ns,"test_selection_owning_feature_resolution");async function os(e){const t=await e.newFeature("P.CY");t.inputParams.id="SMOOTH_SRC",t.inputParams.radius=5,t.inputParams.height=12,t.inputParams.resolution=16;const r=await e.newFeature("SWS");return r.inputParams.targetSolid=t.inputParams.featureID,r.inputParams.subdivisionLoops=1,e}i(os,"test_smooth_with_subdivision_replaces_source_solid");async function ss(e){const t=(e.features||[]).find(d=>String(d?.type||"").toUpperCase()==="SWS");if(!t)throw new Error("[smooth with subdivision] Feature entry was not created.");const r=t.persistentData||{};if(!(Number(r.sourceTriangleCount)>0))throw new Error("[smooth with subdivision] Source triangle count was not captured.");if(!(Number(r.outputTriangleCount)>Number(r.sourceTriangleCount)))throw new Error("[smooth with subdivision] Expected subdivision to increase triangle count.");const n=(e.scene?.children||[]).filter(d=>d?.type==="SOLID");if(n.length!==1)throw new Error(`[smooth with subdivision] Expected one replacement solid, found ${n.length}.`);const o=n[0],s=o?.userData?.smoothWithSubdivision||null;if(!s)throw new Error("[smooth with subdivision] Output solid is missing feature metadata.");if(Number(s.subdivisionLoops)!==1)throw new Error("[smooth with subdivision] Output solid metadata has the wrong subdivision loop count.");if(!(Number(s.outputTriangleCount)>Number(s.sourceTriangleCount)))throw new Error("[smooth with subdivision] Output solid metadata did not record increased triangle count.");const a=new Set(["SMOOTH_SRC_B","SMOOTH_SRC_T","SMOOTH_SRC_S"]),c=new Set((typeof o.getFaceNames=="function"?o.getFaceNames():[]).map(d=>String(d||"").trim()).filter(d=>d.length>0));if(c.size!==a.size)throw new Error(`[smooth with subdivision] Expected ${a.size} retained face names, found ${c.size}.`);for(const d of a)if(!c.has(d))throw new Error(`[smooth with subdivision] Missing retained face name "${d}".`);const l=typeof o.getFaceMetadata=="function"?o.getFaceMetadata("SMOOTH_SRC_S"):null;if(!l||l.type!=="cylindrical")throw new Error("[smooth with subdivision] Cylindrical side face metadata was not preserved.");if(Number(l.radius)!==5||Number(l.height)!==12)throw new Error("[smooth with subdivision] Cylindrical side face metadata has the wrong dimensions.")}i(ss,"afterRun_smooth_with_subdivision_replaces_source_solid");async function as(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20;const r=await e.newFeature("P");r.inputParams.orientation="YZ";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,n.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:8,y:20,fixed:!1},{id:10,x:-.5,y:-.5,fixed:!1},{id:11,x:.5,y:-.5,fixed:!1},{id:12,x:.5,y:.5,fixed:!1},{id:13,x:-.5,y:.5,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:200,type:"line",points:[10,11],construction:!1},{id:201,type:"line",points:[11,12],construction:!1},{id:202,type:"line",points:[12,13],construction:!1},{id:203,type:"line",points:[13,10],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]};const o=await e.newFeature("SW");o.inputParams.profile=`${t.inputParams.featureID}_T`,o.inputParams.path=[`${n.inputParams.featureID}:G100`],o.inputParams.orientationMode="translate";const s=await e.newFeature("B");return s.inputParams.targetSolid=t.inputParams.featureID,s.inputParams.boolean={targets:[o.inputParams.featureID],operation:"UNION"},e}i(as,"test_SweepFace");async function is(e){const t=await e.newFeature("P");t.inputParams.orientation="YZ";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch={points:[{id:10,x:-5,y:-5,fixed:!1},{id:11,x:5,y:-5,fixed:!1},{id:12,x:5,y:5,fixed:!1},{id:13,x:-5,y:5,fixed:!1},{id:20,x:-3,y:-3,fixed:!1},{id:21,x:3,y:-3,fixed:!1},{id:22,x:3,y:3,fixed:!1},{id:23,x:-3,y:3,fixed:!1},{id:30,x:-1,y:-1,fixed:!1},{id:31,x:1,y:-1,fixed:!1},{id:32,x:1,y:1,fixed:!1},{id:33,x:-1,y:1,fixed:!1}],geometries:[{id:200,type:"line",points:[10,11],construction:!1},{id:201,type:"line",points:[11,12],construction:!1},{id:202,type:"line",points:[12,13],construction:!1},{id:203,type:"line",points:[13,10],construction:!1},{id:210,type:"line",points:[20,21],construction:!1},{id:211,type:"line",points:[21,22],construction:!1},{id:212,type:"line",points:[22,23],construction:!1},{id:213,type:"line",points:[23,20],construction:!1},{id:220,type:"line",points:[30,31],construction:!1},{id:221,type:"line",points:[31,32],construction:!1},{id:222,type:"line",points:[32,33],construction:!1},{id:223,type:"line",points:[33,30],construction:!1}],constraints:[]};const n=await e.newFeature("P");n.inputParams.orientation="XY";const o=await e.newFeature("S");o.inputParams.sketchPlane=n.inputParams.featureID,o.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:12,y:0,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]};const s=await e.newFeature("SW");return s.inputParams.profile=r.inputParams.featureID,s.inputParams.path=[`${o.inputParams.featureID}:G100`],s.inputParams.orientationMode="pathAlign",s.inputParams.consumeProfileSketch=!1,e}i(is,"test_SweepFace_pathAlign_multi_loop_islands");async function cs(e){const t=(e?.features||[]).find(l=>String(l?.type||"").toUpperCase()==="SW"),r=Number(t?.persistentData?.profileIslandCount)||0,n=Array.isArray(t?.persistentData?.profileIslandEdgeCounts)?t.persistentData.profileIslandEdgeCounts:[];if(r>1){if(n.length!==r)throw new Error("[sweep_path_align_multi_loop_islands] Missing per-island edge counts.");if(n.some(l=>!(Number(l)>0)))throw new Error("[sweep_path_align_multi_loop_islands] One or more profile islands lost edge data.")}const o=(e?.scene?.children||[]).filter(l=>String(l?.type||"").toUpperCase()==="SOLID");if(!o.length)throw new Error("[sweep_path_align_multi_loop_islands] Expected sweep to produce a solid.");const s=o.find(l=>String(l?.name||"").startsWith("SW"))||o[0];let a=null;try{a=s.getMesh()}catch(l){throw new Error(`[sweep_path_align_multi_loop_islands] Sweep solid is not manifold: ${l?.message||l}`)}let c=0;try{c=typeof a?.numTri=="function"?Number(a.numTri())||0:(a?.triVerts?.length||0)/3}finally{try{a&&typeof a.delete=="function"&&a.delete()}catch{}}if(!(c>0))throw new Error("[sweep_path_align_multi_loop_islands] Sweep solid has no triangles.")}i(cs,"afterRun_sweepFace_pathAlign_multi_loop_islands");const ls=1e-6;function h0(e){return(Array.isArray(e?.children)?e.children:[]).find(r=>r&&r.type==="SKETCH")||null}i(h0,"findSketchGroup");function x0(e){return e&&(Array.isArray(e.children)?e.children:[]).find(r=>r&&r.type==="FACE")||null}i(x0,"findSketchFace");function ds(e){return e?(Array.isArray(e.children)?e.children:[]).filter(r=>r&&r.type==="EDGE"):[]}i(ds,"findSketchEdges");function we(e,t){if(!e)throw new Error(t)}i(we,"expectTruthy");function us(e,t,r){if(!Number.isFinite(e)||Math.abs(e-t)>ls)throw new Error(`${r} expected ${t}, got ${e}`)}i(us,"expectApprox");async function fs(e){e.expressions='textLabel = "BREP";';const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("TEXT");return r.inputParams.text="textLabel",r.inputParams.textHeight=10,r.inputParams.curveResolution=12,r.inputParams.placementPlane=t.inputParams.featureID,e}i(fs,"test_textToFace");async function ps(e){const t=Array.isArray(e.features)?e.features.find(x=>x&&x.type==="TEXT"):null;if(we(t,"[text_to_face] No TEXT feature found"),String(t?.previouseExpressions?.text??"")!=="BREP")throw new Error("[text_to_face] Text expression did not resolve to BREP");const r=t?.persistentData?.fontFile;if(typeof r!="string"||!r.startsWith("data:"))throw new Error("[text_to_face] Missing persistentData.fontFile data URL");const n=t?.persistentData?.fontFileKey;if(typeof n!="string"||!n.startsWith("font:"))throw new Error("[text_to_face] Missing or invalid persistentData.fontFileKey");const o=h0(e.scene);we(o,"[text_to_face] No SKETCH group found");const s=x0(o);we(s,"[text_to_face] No FACE found on SKETCH group");const a=ds(o);if(!a.length)throw new Error("[text_to_face] No sketch edges found");const c=s?.userData?.boundaryLoopsWorld;if(!Array.isArray(c)||!c.length)throw new Error("[text_to_face] Missing boundaryLoopsWorld on face");const l=s?.userData?.profileGroups;if(!Array.isArray(l)||!l.length)throw new Error("[text_to_face] Missing profileGroups on face");const d=qt.getBaseMaterial(s)||s.material;if(!d)throw new Error("[text_to_face] Face has no material");if(d.side!==Ge.THREE.DoubleSide)throw new Error("[text_to_face] Face material is not DoubleSide");if(!d.polygonOffset)throw new Error("[text_to_face] Face material polygonOffset not enabled");const p=qt.getBaseMaterial(a[0])||a[0].material;if(!p)throw new Error("[text_to_face] Edge has no material");if(p.depthTest!==!1)throw new Error("[text_to_face] Edge material depthTest should be false");if(a[0].renderOrder<2)throw new Error("[text_to_face] Edge renderOrder should be elevated");try{if(typeof s.getAverageNormal=="function"){const x=s.getAverageNormal();us(Number(x.length()),1,"[text_to_face] Face normal not normalized")}}catch{}const u="__text_to_face_removed_font_test__";t.inputParams.font=u,t.inputParams.fontFile="",t.persistentData=t.persistentData||{},t.persistentData.fontFile=r,t.persistentData.fontFileKey=`font:${u}`,await e.runHistory();const f=Array.isArray(e.features)?e.features.find(x=>x&&x.type==="TEXT"):null;if(we(f,"[text_to_face] No TEXT feature found after persisted-font fallback run"),f?.persistentData?.fontFileKey!==`font:${u}`)throw new Error("[text_to_face] Persisted font fallback did not preserve missing font id key");if(typeof f?.persistentData?.fontFile!="string"||!f.persistentData.fontFile.startsWith("data:"))throw new Error("[text_to_face] Persisted font fallback lost font data URL");const m=h0(e.scene);we(m,"[text_to_face] No SKETCH group found after persisted-font fallback run");const h=x0(m);we(h,"[text_to_face] No FACE found after persisted-font fallback run")}i(ps,"afterRun_textToFace");async function ms(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:0,y:40,fixed:!1},{id:2,x:25,y:40,fixed:!1},{id:10,x:-2,y:-2,fixed:!1},{id:11,x:2,y:-2,fixed:!1},{id:12,x:2,y:2,fixed:!1},{id:13,x:-2,y:2,fixed:!1}],geometries:[{id:200,type:"line",points:[0,1],construction:!1},{id:201,type:"line",points:[1,2],construction:!1},{id:300,type:"line",points:[10,11],construction:!1},{id:301,type:"line",points:[11,12],construction:!1},{id:302,type:"line",points:[12,13],construction:!1},{id:303,type:"line",points:[13,10],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]};const n=`${r.inputParams.featureID}:`,o=await e.newFeature("TU");o.inputParams.path=[`${n}G200`,`${n}G201`],o.inputParams.radius=4,o.inputParams.innerRadius=0,o.inputParams.resolution=32;const s=await e.newFeature("TU");return s.inputParams.path=[`${n}G200`,`${n}G201`],s.inputParams.radius=5,s.inputParams.innerRadius=2,s.inputParams.resolution=48,e}i(ms,"test_tube");async function hs(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID;const n=6,o=20,s=[],a=[];for(let p=0;p<n;p++){const u=p/n*2*Math.PI;s.push({id:p,x:Math.cos(u)*o,y:Math.sin(u)*o,fixed:!1})}for(let p=0;p<n;p++){const u=(p+1)%n;a.push({id:200+p,type:"line",points:[p,u],construction:!1})}r.persistentData.sketch={points:s,geometries:a,constraints:[{id:0,type:"⏚",points:[0]}]};const c=`${r.inputParams.featureID}:`,l=await e.newFeature("TU");l.inputParams.path=a.map(p=>`${c}G${p.id}`),l.inputParams.radius=3,l.inputParams.innerRadius=0,l.inputParams.resolution=24;const d=await e.newFeature("TU");return d.inputParams.path=a.map(p=>`${c}G${p.id}`),d.inputParams.radius=4,d.inputParams.innerRadius=1,d.inputParams.resolution=24,d.inputParams.transform={position:[0,0,10],rotation:[0,0,0],scale:[1,1,1]},e}i(hs,"test_tube_closedLoop");function Ye(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(Ye,"assert$9");function xs(){const e={id:"sheet-1",name:"Instruction Sheet",elements:[{id:"table-1",type:"table"}]},t={id:"sheet-2",name:"Formboard Sheet",elements:[{id:"line-1",type:"line",groupId:"wire-harness-formboard:abc"}]};Ye(Be([e],"")===null,"Expected unrelated sheets to be ignored when reusing a formboard target."),Ye(Be([e,t],"")?.id==="sheet-2","Expected an existing formboard sheet to be reused."),Ye(Be([e,t],"sheet-1")?.id==="sheet-2","Expected preferred non-formboard sheets to be ignored in favor of the actual formboard sheet."),Ye(Be([e,t],"sheet-2")?.id==="sheet-2","Expected the preferred sheet to be reused when it already contains a formboard.")}i(xs,"test_wire_harness_formboard_reuses_only_formboard_sheet");function W(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(W,"assert$8");function g0(e,t){return{name:e,userData:{isPortRoot:!0,portData:{objectName:e,name:t,kind:"termination",point:[0,0,0],direction:[1,0,0]}}}}i(g0,"createFakePort");async function gs(){const e=g0("PORT_A","Port A"),t=g0("PORT_B","Port B"),r=new Map([[e.name,e],[t.name,t]]),n={scene:{traverse(c){c(e),c(t)},getObjectByName(c){return r.get(String(c))||null}},getObjectByName(c){return r.get(String(c))||null}},o=Ut(n,{from:"Port A",to:"Port B"});W(o.ok===!0,"Expected endpoint labels to resolve."),W(o.fromRef==="PORT_A","Expected from endpoint to resolve to PORT_A."),W(o.toRef==="PORT_B","Expected to endpoint to resolve to PORT_B."),W(Array.isArray(o.portRefs)&&o.portRefs.length===2,"Expected both endpoint refs to be returned.");const s=Ut(n,{from:"Port A",to:"Missing"});W(s.ok===!1,"Expected unresolved endpoint lookup to fail."),W(s.portRefs.length===1&&s.portRefs[0]==="PORT_A","Expected partial resolution to preserve the valid endpoint.");const a=tr(n,[{id:"WIRE1",from:"Port A",to:"Port B",diameter:1.25}]);W(Array.isArray(a.segments)&&a.segments.length===0,"Expected payload to expose a segments array."),W(Array.isArray(a.connections)&&a.connections.length===1,"Expected payload to expose one valid connection."),W(a.connections[0].id==="WIRE1","Expected payload connection id to match."),W(a.connections[0].startPoint==="PORT_A","Expected payload startPoint to use resolved port object names."),W(a.connections[0].endPoint==="PORT_B","Expected payload endPoint to use resolved port object names."),W(a.connections[0].wireInfo?.diameter===1.25,"Expected payload diameter to match input.")}i(gs,"test_wire_harness_connection_endpoint_resolution");function F(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(F,"assert$7");function Ve(e,t,r="termination",n=[0,0,0],o=[1,0,0]){return{name:e,userData:{isPortRoot:!0,portData:{objectName:e,name:t,kind:r,point:n.slice(),direction:o.slice(),extension:1,displayLength:1}}}}i(Ve,"createPort$1");function mt(e,t,r,n,o){return{type:"SP",inputParams:{featureID:e,curveResolution:4,bendRadius:1},persistentData:{spline:{points:[{id:"p0",position:[0,0,0],attachment:{type:"port",portRef:t,side:r}},{id:"p1",position:[1,0,0],attachment:{type:"port",portRef:n,side:o}}]}}}}i(mt,"createSplineFeature$1");function ws(){const e=Ve("START","Start","termination",[-6,0,0],[1,0,0]),t=Ve("SPLIT","Split","waypoint",[0,0,0],[1,0,0]),r=Ve("BRANCHA","Branch A","termination",[5,4,0],[1,0,0]),n=Ve("BRANCHB","Branch B","termination",[5,-4,0],[1,0,0]),o=new Map([[e.name,e],[t.name,t],[r.name,r],[n.name,n]]);return{scene:{traverse(s){s(e),s(t),s(r),s(n)},getObjectByName(s){return o.get(String(s))||null}},getObjectByName(s){return o.get(String(s))||null},features:[mt("SP1","START","A","SPLIT","A"),mt("SP2","SPLIT","B","BRANCHA","A"),mt("SP3","SPLIT","A","BRANCHB","A")]}}i(ws,"createSimpleHarnessPartHistory");function ys(){const e=new Nt(null),t=e.createSheet({name:"Large Formboard",sizeKey:"CUSTOM",orientation:"landscape",customWidthIn:72,customHeightIn:36,elements:[]});F(t.sizeKey==="CUSTOM","Expected custom sheet size key to persist."),F(t.widthIn===72,"Expected custom sheet width to persist."),F(t.heightIn===36,"Expected custom sheet height to persist.");const r=e.updateSheet(t.id,{sizeKey:"CUSTOM",orientation:"portrait",customWidthIn:36,customHeightIn:72});F(r.sizeKey==="CUSTOM","Expected custom size key to survive sheet updates."),F(r.widthIn===36,"Expected portrait custom sheet width to persist."),F(r.heightIn===72,"Expected portrait custom sheet height to persist.")}i(ys,"test_sheet_custom_size_persists");function vs(){const e=ws(),t=rr(e,{includeTitle:!0});F(t.ok===!0,"Expected harness formboard definition to succeed.");const r=t.elements.filter(p=>p?.type==="line"),n=t.elements.filter(p=>p?.type==="text");F(r.length===3,"Expected three flattened line segments for the Y harness."),F(r.every(p=>p?.formboard?.exactGeometry===!0),"Expected generated line segments to be marked as exact formboard geometry."),F(r.every(p=>p?.formboard?.fromNodeId&&p?.formboard?.toNodeId),"Expected generated line segments to carry directed node metadata."),F(n.some(p=>String(p?.text||"")==="Start"),"Expected endpoint labels to be included."),F(n.some(p=>String(p?.text||"").includes("in")),"Expected segment length labels to be included.");const o=new Nt(null),s=o.createSheet({name:"Instruction Sheet",sizeKey:"A",orientation:"landscape",elements:[]}),a=nr(o,s.id,e,{includeTitle:!0,resizeSheetToFit:!0});F(a.ok===!0,"Expected formboard insertion to succeed."),F(a.sheet?.sizeKey==="CUSTOM","Expected insertion to promote the target sheet to custom sizing."),F((a.insertedElements||[]).filter(p=>p?.type==="line").length===3,"Expected inserted formboard to contain three line elements."),F((a.sheet?.elements||[]).filter(p=>p?.type==="line").every(p=>p?.formboard?.exactGeometry===!0),"Expected exact formboard metadata to persist through sheet normalization."),F((a.sheet?.elements||[]).every(p=>String(p?.groupId||"").startsWith("wire-harness-formboard:")),"Expected inserted sheet elements to belong to the generated formboard group.");const c=or(a.sheet?.elements||[]),l=(a.sheet?.elements||[]).find(p=>p?.type==="line"&&String(p?.formboard?.fromNodeId||"")==="SPLIT"),d=sr(c,"SPLIT",l?.id);F(d.size===1,"Expected pivot branch resolution to isolate one downstream branch."),F(d.has(String(l?.formboard?.toNodeId||"")),"Expected branch resolution to include the segment endpoint opposite the pivot.")}i(vs,"test_wire_harness_formboard_insert");function w0(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(w0,"assert$6");function _s(){const e=new pr(null),t=e.addView({viewName:"Sheet View",camera:{},annotations:[],viewSettings:{pmiTextSizePt:"18.5"}});w0(t?.viewSettings?.pmiTextSizePt===18.5,"Expected PMI text size to normalize to a finite number.");const r=e.updateView(0,n=>(n.viewSettings.pmiTextSizePt=-4,n));w0(!Object.prototype.hasOwnProperty.call(r?.viewSettings||{},"pmiTextSizePt"),"Expected invalid PMI text size to be removed during normalization.")}i(_s,"test_pmi_view_text_size_setting_normalizes");function ee(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ee,"assert$5");function J(e,t,r=1e-9){return Math.abs(Number(e)-Number(t))<=r}i(J,"approxEqual$1");function Es(){ee(ar("PORT")===!0,"Expected feature dimension overlay to support port features.")}i(Es,"test_feature_dimension_overlay_supports_port");function Ss(){const e=Wt({point:[1,2,3],direction:[0,0,10],extension:4},.5);ee(e,"Expected positive port extension geometry to resolve."),ee(J(e.value,4),"Expected extension value to be preserved."),ee(J(e.dragPlaneValue,4),"Expected drag plane value to match positive extension."),ee(J(e.pointB.x,1)&&J(e.pointB.y,2)&&J(e.pointB.z,7),"Expected endpoint to follow normalized port direction.");const t=Wt({point:[0,0,0],direction:[5,0,0],extension:0},.75);ee(t,"Expected zero-length port extension geometry to resolve."),ee(J(t.value,0),"Expected displayed extension value to remain zero."),ee(J(t.dragPlaneValue,.75),"Expected zero-length extension to keep a visible drag handle offset."),ee(J(t.pointB.x,.75)&&J(t.pointB.y,0)&&J(t.pointB.z,0),"Expected visible tip offset to follow the normalized direction.")}i(Ss,"test_port_extension_annotation_geometry_preserves_extension_value");function K(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(K,"assert$4");function Je(e,t,r=1e-9){return Math.abs(Number(e)-Number(t))<=r}i(Je,"approxEqual");function et(e,t,r){const n=[e.x,e.y,e.z];for(let o=0;o<3;o+=1)if(!Je(n[o],t[o]))throw new Error(r||`Expected vector ${t.join(", ")}, got ${n.join(", ")}.`)}i(et,"assertVec3");function bs(){const e=vr({position:[1,2,3],rotationEuler:[4,5,6],scale:[1,1,1],reference:{name:"FACE_REF",type:"FACE",pickPoint:[7,8,9],faceIndex:3}});K(e.reference&&typeof e.reference=="object","Expected transform reference metadata to be preserved."),K(e.reference.name==="FACE_REF","Expected transform reference name to persist."),K(e.reference.type==="FACE","Expected transform reference type to persist."),K(Array.isArray(e.reference.pickPoint),"Expected pick point metadata to persist."),K(e.reference.faceIndex===3,"Expected face index metadata to persist.")}i(bs,"test_transform_reference_sanitize_preserves_metadata");function Ps(){const e=new Re,t=new ke;t.name="FACE_PICK",t.type="FACE",e.add(t);const r=_r({name:"FACE_PICK",type:"FACE",pickPoint:[3,4,5]},e,{},I0),n=new V(Number(r.position[0])||0,Number(r.position[1])||0,Number(r.position[2])||0);et(n,[3,4,5],"Expected face reference base to use the stored pick point.")}i(Ps,"test_transform_reference_base_uses_face_pick_point");function Fs(){const e=new Re,t=new ke;t.name="VERTEX_REF",t.type="VERTEX",t.position.set(10,20,30),e.add(t);const r=Er({position:[1,2,3],rotationEuler:[0,0,0],scale:[1,1,1],reference:{name:"VERTEX_REF",type:"VERTEX"}},e,{},I0),n=new V,o=new Sr,s=new V;r.decompose(n,o,s),et(n,[11,22,33],"Expected referenced transform to offset from the selected vertex.")}i(Fs,"test_referenced_transform_matrix_uses_vertex_reference_origin");function As(){const e=new Re,t=new ke;t.name="PORT_VERTEX",t.type="VERTEX",t.position.set(5,0,0),e.add(t);const r=A0({featureId:"PORT_REF_TEST",inputParams:{portName:"Port Ref Test",transform:{position:[2,0,0],rotationEuler:[0,0,0],scale:[1,1,1],reference:{name:"PORT_VERTEX",type:"VERTEX"}},extension:2,displayLength:4},referenceSource:e});K(Array.isArray(r.point),"Expected port definition point to resolve."),K(Je(r.point[0],7),"Expected port point to be offset from the transform reference."),K(Je(r.point[1],0)&&Je(r.point[2],0),"Expected port point to remain aligned to the reference origin."),K(r.anchorName==="PORT_VERTEX","Expected anchorName compatibility field to reflect the transform reference."),K(r.transform?.reference,"Expected normalized port transform to retain its reference.")}i(As,"test_port_definition_uses_transform_reference_without_anchor");function Is(){const e=new Re,t=new ke;t.name="PORT_VERTEX_DIR",t.type="VERTEX",t.position.set(1,2,3),e.add(t);const r=new ke;r.name="PORT_DATUM_DIR",r.type="DATUM",r.quaternion.setFromAxisAngle(new V(0,0,1),Math.PI/2),e.add(r),e.updateMatrixWorld(!0);const n=A0({featureId:"PORT_DIR_TEST",inputParams:{transform:{position:[2,0,0],rotationEuler:[0,0,0],scale:[1,1,1],reference:{name:"PORT_VERTEX_DIR",type:"VERTEX"}},directionRef:[{name:"PORT_DATUM_DIR",type:"DATUM"}]},referenceSource:e});et(new V(...n.point),[1,4,3],"Expected point reference to inherit the direction reference basis."),et(new V(...n.direction),[0,1,0],"Expected port direction to align with the direction reference.")}i(Is,"test_port_definition_uses_transform_reference_and_direction_reference");function ae(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ae,"assert$3");async function Ts(){const e=new Nt(null),t=e.createSheet({name:"Harness Sheet",sizeKey:"A",orientation:"landscape",elements:[]}),r=[{id:"WIRE1",name:"Wire 1",from:"PORT_A",to:"PORT_B",diameter:1.25},{id:"WIRE2",name:"Wire 2",from:"PORT_C",to:"PORT_D",diameter:.8}],n=[{connectionId:"WIRE1",feasible:!0,distance:123.456},{connectionId:"WIRE2",feasible:!1,error:"No route found."}],o=ir(r,n);ae(o.cells[0][0].text==="Wire","Expected first header cell to be Wire."),ae(o.cells[1][1].text==="123.46","Expected routed length to be formatted into the sheet table."),ae(o.cells[2][5].text==="Failed","Expected failed routes to use a compact sheet status label.");const s=cr(e,t.id,r,n);ae(!!s?.sheet,"Expected sheet insertion to return an updated sheet."),ae(!!s?.element,"Expected sheet insertion to return the inserted element."),ae(s.element.type==="table","Expected inserted element to be a table."),ae(s.element.tableData?.cells?.[1]?.[0]?.text==="Wire 1","Expected first wire name to appear in the inserted table."),ae(s.element.tableData?.cells?.[2]?.[5]?.text==="Failed","Expected compact status text to persist on the inserted table element.")}i(Ts,"test_wire_harness_sheet_table_insert");function ht(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ht,"assert$2");function xt(e,t,r="termination",n=[0,0,0],o=[1,0,0]){return{name:e,userData:{isPortRoot:!0,portData:{objectName:e,name:t,kind:r,point:n.slice(),direction:o.slice(),extension:1,displayLength:1}}}}i(xt,"createPort");function y0(e,t,r,n,o){return{type:"SP",inputParams:{featureID:e,curveResolution:4,bendRadius:1},persistentData:{spline:{points:[{id:"p0",position:[0,0,0],attachment:{type:"port",portRef:t,side:r}},{id:"p1",position:[1,0,0],attachment:{type:"port",portRef:n,side:o}}]}}}}i(y0,"createSplineFeature");async function Ns(){const e=xt("START","Start","termination",[-4,0,0],[1,0,0]),t=xt("END","End","termination",[4,0,0],[1,0,0]),r=xt("WAYPOINT","Waypoint","waypoint",[0,0,0],[1,0,0]),n=new Map([[e.name,e],[t.name,t],[r.name,r]]),o={scene:{traverse(l){l(e),l(t),l(r)},getObjectByName(l){return n.get(String(l))||null}},getObjectByName(l){return n.get(String(l))||null},features:[y0("SP1","START","A","WAYPOINT","A"),y0("SP2","WAYPOINT","B","END","A")]},{network:s,routes:a}=await lr(o,[{id:"WIRE3",name:"Wire 3",from:"Start",to:"End",diameter:1}]),c=(s?.splineSegments||[]).filter(l=>l.firstPoint==="WAYPOINT"||l.secondPoint==="WAYPOINT").map(l=>l.firstPoint==="WAYPOINT"?l.firstSide:l.secondSide);ht(c.length===2,"Expected two waypoint segment attachments."),ht(c.every(l=>l==="B"),"Expected both spline branches to occupy waypoint side B."),ht(a[0].feasible===!1,"Expected route to fail when both branches use the same physical waypoint side.")}i(Ns,"test_wire_harness_infers_endpoint_side_from_spline_direction");function pe(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(pe,"assert$1");function Ds(){const e=new Re;dr(e,[],[{segmentId:"SEG1",featureId:"SP1",polyline:[[0,0,0],[15,0,0],[15,10,0]],diameter:2.5,wireCount:3,wireDiameters:[1,1,1.5],connectionIds:["WIRE-1"],connectionNames:["Wire 1"]}]);const t=e.getObjectByName("__WireHarnessRoutes");pe(t,"Expected the wire harness route group to be added to the scene.");const r=[];e.traverse(a=>{a?.type==="SOLID"&&a?.userData?.isWireHarnessRoute&&r.push(a)}),pe(r.length===1,`Expected one routed harness solid, got ${r.length}.`),pe(typeof r[0].toSTL=="function","Expected routed harness geometry to be a real BREP solid."),pe(ur(e,"WIRE-1").length===1,"Expected connection hover lookup to resolve the routed harness solid.");const n=r[0].getMesh(),o=(n?.triVerts?.length||0)/3|0;try{n?.delete?.()}catch{}pe(o>0,"Expected routed harness solid to contribute triangulated geometry for export."),fr(e),pe(e.getObjectByName("__WireHarnessRoutes")==null,"Expected clear to remove the wire harness route group.");const s=[];e.traverse(a=>{a?.type==="SOLID"&&a?.userData?.isWireHarnessRoute&&s.push(a)}),pe(s.length===0,"Expected clear to remove routed harness solids from the scene.")}i(Ds,"test_wire_harness_routes_render_as_scene_solids");function ie(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ie,"assert");async function Ms(){const e=new Ke;e.wireHarnessManager.addConnection({id:"wire-1",name:"Wire 1",from:"PORT-A",to:"PORT-B",diameter:1.5}),e.wireHarnessManager.setRouteResults([{connectionId:"wire-1",connectionName:"Wire 1",feasible:!0,error:"",distance:42.5,polyline:[[0,0,0],[10,0,0],[10,5,0]],segmentIds:["SP1","SP2"],nodePath:["PORT-A:A","MID:A","PORT-B:A"],reusesHarnessPoint:!1,diameter:1.5,from:"PORT-A",to:"PORT-B"}]);const t=await e.toJSON(),r=new Ke;await r.fromJSON(t);const n=r.wireHarnessManager.getConnections();ie(n.length===1,`Expected one restored connection, got ${n.length}.`),ie(n[0].id==="wire-1","Expected the connection ID to persist.");const o=r.wireHarnessManager.getRouteResults();ie(o.length===1,`Expected one restored route result, got ${o.length}.`),ie(o[0].feasible===!0,"Expected the restored route result to remain feasible."),ie(o[0].distance===42.5,`Expected the restored route distance to persist, got ${o[0].distance}.`),ie(o[0].segmentIds.join(",")==="SP1,SP2","Expected restored route segment IDs to persist.");const s=r.wireHarnessManager.consumePendingRestoredRouteResults();ie(Array.isArray(s)&&s.length===1,"Expected the restored model to queue route geometry restoration."),ie(r.wireHarnessManager.consumePendingRestoredRouteResults()==null,"Expected pending restored routes to be consumed only once.")}i(Ms,"test_wire_harness_route_results_persist_in_model_json");async function Cs(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=8,t.inputParams.sizeY=6,t.inputParams.sizeZ=4}i(Cs,"test_visibility_hidden_state_persistence");async function ks(e){const r=(Array.isArray(e?.features)?e.features[0]:null)?.inputParams?.featureID;if(!r)throw new Error("Visibility persistence test requires a first feature with featureID.");const n=e.scene?.getObjectByName?.(r);if(!n||n.type!=="SOLID")throw new Error("Visibility persistence test could not find initial cube solid.");const o=(Array.isArray(n.children)?n.children:[]).find(f=>f?.type==="FACE")||null,s=(Array.isArray(n.children)?n.children:[]).find(f=>f?.type==="EDGE")||null;if(!o||!s)throw new Error("Visibility persistence test requires one face and one edge on the cube.");const a=String(o?.userData?.faceName||o?.name||""),c=String(s?.name||"");if(!a||!c)throw new Error("Visibility persistence test requires named face and edge.");n.visible=!1,o.visible=!1,s.visible=!1,await e.newFeature("P.CU"),await e.runHistory();const l=e.scene?.getObjectByName?.(r);if(!l)throw new Error("Visibility persistence test could not find rebuilt cube.");if(l.visible!==!1)throw new Error("Hidden solid visibility state was not preserved after history run.");const d=Array.isArray(l.children)?l.children:[],p=d.find(f=>f?.type!=="FACE"?!1:String(f?.userData?.faceName||f?.name||"")===a)||null,u=d.find(f=>f?.type==="EDGE"&&String(f?.name||"")===c)||null;if(!p)throw new Error("Hidden face could not be resolved after history run.");if(!u)throw new Error("Hidden edge could not be resolved after history run.");if(p.visible!==!1)throw new Error("Hidden face visibility state was not preserved after history run.");if(u.visible!==!1)throw new Error("Hidden edge visibility state was not preserved after history run.")}i(ks,"afterRun_visibility_hidden_state_persistence");const re=typeof process<"u"&&process.versions&&process.versions.node&&typeof window>"u",G0=G.join("tests","test-run.log"),me=[{test:ao,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:co,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:uo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:lo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:tn,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:no,afterRun:oo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:io,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:po,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:fo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Es,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ss,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:bs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ps,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Fs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:As,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Is,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:zr,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Wr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:jr,afterRun:Ur,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ts,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Ln,afterRun:Bn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:zn,afterRun:jn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Un,afterRun:Wn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Yn,afterRun:Vn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Gn,afterRun:Xn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:eo,afterRun:to,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:qn,afterRun:Zn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:as,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:is,afterRun:cs,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ms,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:hs,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:xs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:gs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:ys,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:_s,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Io,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:vs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ts,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ns,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ds,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ms,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Lo,afterRun:Bo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Uo,afterRun:Wo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Go,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Xo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Jo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ko,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:qo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Zo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ho,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:so,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Qr,afterRun:en,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:rn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:nn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:sn,afterRun:an,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:hn,afterRun:xn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ln,afterRun:dn,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:pn,afterRun:mn,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:gn,afterRun:wn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:vn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Yr,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Vr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Xr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Jr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Kr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:qr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:os,afterRun:ss,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:In,afterRun:Tn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:kn,afterRun:Rn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:$n,afterRun:On,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Nn,afterRun:Dn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Mn,afterRun:Cn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:xo,afterRun:wo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ro,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:bn,afterRun:Pn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:_n,afterRun:En,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:ns,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Cs,afterRun:ks,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:fs,afterRun:ps,printArtifacts:!1,exportFaces:!0,exportSolids:!1,resetHistory:!0},{test:Oo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ao,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Co,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:$o,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:vo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:bo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Mo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:No,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Qo,afterRun:es,printArtifacts:!0,exportFaces:!0,exportSolids:!0,resetHistory:!0}];async function Rs(){if(typeof process<"u"&&process.versions&&process.versions.node)try{const e="src/tests/partFiles";try{const r=(await S.promises.readdir(e)).filter(n=>typeof n=="string"&&n.toLowerCase().endsWith(".json"));for(const n of r){const o=G.join(e,n),c=`import_part_${String(n).replace(/\.[^.]+$/,"").replace(/[^a-zA-Z0-9._-]+/g,"_").substring(0,100)}`,l=i(async function(d){const p=await S.promises.readFile(o,"utf8");let u=p;try{const f=JSON.parse(p);f&&typeof f=="object"&&(Array.isArray(f.features)?u=JSON.stringify(f):f.data&&(u=typeof f.data=="string"?f.data:JSON.stringify(f.data)))}catch{}await d.reset(),await d.fromJSON(u)},"importTest");try{Object.defineProperty(l,"name",{value:c,configurable:!0})}catch{}me.push({test:l,afterRun:null,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0,allowErrors:!0,_sourceFile:o})}}catch{}}catch(e){console.warn("Failed to register part file import tests:",e?.message||e)}}i(Rs,"registerPartFileTests");re&&X0().then(()=>{process.exit(0)}).catch(e=>{console.error(e),process.exit(1)});async function X0(e=new Ke,t=null){typeof process<"u"&&process.versions&&process.versions.node,re&&(Ls(),ye("Test run started")),await S.promises.rm("./tests/results",{recursive:!0,force:!0}),await Rs(),await Br(me);for(const r of me){const n=r===me[me.length-1];await e.reset(),r.resetHistory&&(e.features=[]);const o=r?.test?.name&&String(r.test.name)||"unnamed_test";re&&ye(`Starting ${o}`);let s=null;try{s=await Tt(r,e)}catch(a){throw re&&ye(`Test ${o} failed: ${v0(a)}`),a}if(re&&ye(s?`Test ${o} completed with handled error: ${v0(s)}`:`Test ${o} completed successfully`),typeof window<"u")typeof t=="function"&&await t(e,n);else{const a=r.test.name,c=`./tests/results/${a}/`;S.existsSync(c)||S.mkdirSync(c,{recursive:!0});const l=(e.scene?.children||[]).filter(d=>d&&d.type==="SOLID"&&typeof d.toSTL=="function");(r.exportSolids||r.printArtifacts)&&l.forEach((d,p)=>{const u=d.name&&String(d.name).trim().length?String(d.name):`solid_${p}`,f=ve(u);let m="";try{m=d.toSTL(f,6)}catch(x){console.warn(`[runTests] toSTL failed for solid ${u}:`,x?.message||x);return}const h=G.join(c,`${f}.stl`);tt(h,m)}),r.exportFaces&&l.forEach((d,p)=>{const u=d.name&&String(d.name).trim().length?String(d.name):`solid_${p}`,f=ve(u);let m=[];try{m=typeof d.getFaces=="function"?d.getFaces(!1):[]}catch{m=[]}m.forEach(({faceName:h,triangles:x},w)=>{if(!x||x.length===0)return;const g=h||`face_${w}`,y=ve(g),v=Bs(`${f}_${y}`,x),E=G.join(c,`${f}_${y}.stl`);tt(E,v)})}),await zs({partHistory:e,exportName:a,exportPath:c,solids:l})}}re&&ye("Test run finished")}i(X0,"runTests");async function Tt(e,t=new Ke){let r=null;try{if(await e.test(t),await t.runHistory(),typeof e.afterRun=="function")try{await e.afterRun(t)}catch(n){console.warn("afterRun failed:",n?.message||n)}}catch(n){if(r=n,e.allowErrors){const o=e.test&&e.test.name?e.test.name:"unnamed_test",s=`./tests/results/${o}/`;try{S.existsSync(s)||S.mkdirSync(s,{recursive:!0})}catch{}const a=`${n?.message||n}
|
|
1886
|
+
`;function N(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(N,"assert$a");async function ns(){const e={type:"SOLID",owningFeatureID:"BODY_OWNER",getFaceMetadata(f){return f==="FACE_FROM_META"?{sourceFeatureId:"FACE_OWNER"}:null},getEdgeMetadata(f){return f==="EDGE_FROM_META"?{sourceFeatureId:"EDGE_OWNER"}:null}},t={type:"FACE",name:"FACE_FROM_META",parentSolid:e,parent:e},r={type:"EDGE",name:"EDGE_FROM_META",parentSolid:e,parent:e},n={type:"FACE",name:"FACE_FALLBACK",parentSolid:e,parent:e},o={type:"EDGE",userData:{splineFeatureId:"SP_001"}},s={type:"LINE2",userData:{},parent:o},c={type:"EDGE",name:"SK_001:G5",userData:{},parent:{type:"SKETCH",name:"SK_001",userData:{sketchFeatureId:"SK_001"}}},d={type:"FACE",name:"OF_001:FACE_A:PROFILE",userData:{},parent:{type:"SKETCH",name:"OF_001:FACE_A",userData:{sketchFeatureId:"OF_001"}}};N(it(t)==="FACE_OWNER","Face metadata owner should win."),N(it(r)==="EDGE_OWNER","Edge metadata owner should use precise edge metadata."),N(it(n)==null,"Faces without precise provenance should not fall back to solid owner."),N(Bt(o)==="SP_001","Spline owner should resolve from spline metadata."),N(Bt(s)==="SP_001","Spline owner should resolve through parent geometry."),N(zt(c)==="SK_001","Sketch owner should resolve through sketch group metadata."),N(zt(d)==="OF_001","Sketch-like owner should resolve from group metadata."),N(Z0([{object:t}])==="FACE_OWNER","Single-selection resolution should unwrap selection objects."),N(H0([{object:s}])==="SP_001","Spline selection resolution should unwrap selection objects."),N(Q0([{object:c}])==="SK_001","Sketch-like selection resolution should unwrap selection objects."),N(ct([{object:t}],["FACE"])===!0,"Single face selection should match FACE."),N(jt([{object:o}])===!0,"Single spline selection should match spline helper."),N(er([{object:d}])===!0,"Single sketch-like selection should match sketch-like helper."),N(ct([{object:r}],["FACE","PLANE"])===!1,"Edge selection should not match the face-only toolbar filter."),N(jt([{object:o},{object:r}])===!1,"Multiple selections should not match spline helper."),N(ct([{object:t},{object:r}],["FACE","EDGE"])===!1,"Multiple selections should not match.")}i(ns,"test_selection_owning_feature_resolution");async function os(e){const t=await e.newFeature("P.CY");t.inputParams.id="SMOOTH_SRC",t.inputParams.radius=5,t.inputParams.height=12,t.inputParams.resolution=16;const r=await e.newFeature("SWS");return r.inputParams.targetSolid=t.inputParams.featureID,r.inputParams.subdivisionLoops=1,e}i(os,"test_smooth_with_subdivision_replaces_source_solid");async function ss(e){const t=(e.features||[]).find(d=>String(d?.type||"").toUpperCase()==="SWS");if(!t)throw new Error("[smooth with subdivision] Feature entry was not created.");const r=t.persistentData||{};if(!(Number(r.sourceTriangleCount)>0))throw new Error("[smooth with subdivision] Source triangle count was not captured.");if(!(Number(r.outputTriangleCount)>Number(r.sourceTriangleCount)))throw new Error("[smooth with subdivision] Expected subdivision to increase triangle count.");const n=(e.scene?.children||[]).filter(d=>d?.type==="SOLID");if(n.length!==1)throw new Error(`[smooth with subdivision] Expected one replacement solid, found ${n.length}.`);const o=n[0],s=o?.userData?.smoothWithSubdivision||null;if(!s)throw new Error("[smooth with subdivision] Output solid is missing feature metadata.");if(Number(s.subdivisionLoops)!==1)throw new Error("[smooth with subdivision] Output solid metadata has the wrong subdivision loop count.");if(!(Number(s.outputTriangleCount)>Number(s.sourceTriangleCount)))throw new Error("[smooth with subdivision] Output solid metadata did not record increased triangle count.");const a=new Set(["SMOOTH_SRC_B","SMOOTH_SRC_T","SMOOTH_SRC_S"]),c=new Set((typeof o.getFaceNames=="function"?o.getFaceNames():[]).map(d=>String(d||"").trim()).filter(d=>d.length>0));if(c.size!==a.size)throw new Error(`[smooth with subdivision] Expected ${a.size} retained face names, found ${c.size}.`);for(const d of a)if(!c.has(d))throw new Error(`[smooth with subdivision] Missing retained face name "${d}".`);const l=typeof o.getFaceMetadata=="function"?o.getFaceMetadata("SMOOTH_SRC_S"):null;if(!l||l.type!=="cylindrical")throw new Error("[smooth with subdivision] Cylindrical side face metadata was not preserved.");if(Number(l.radius)!==5||Number(l.height)!==12)throw new Error("[smooth with subdivision] Cylindrical side face metadata has the wrong dimensions.")}i(ss,"afterRun_smooth_with_subdivision_replaces_source_solid");async function as(e){const t=await e.newFeature("P.CO");t.inputParams.radiusTop=3,t.inputParams.radiusBottom=.5,t.inputParams.height=5.2,t.inputParams.resolution=20;const r=await e.newFeature("P");r.inputParams.orientation="YZ";const n=await e.newFeature("S");n.inputParams.sketchPlane=r.inputParams.featureID,n.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:8,y:20,fixed:!1},{id:10,x:-.5,y:-.5,fixed:!1},{id:11,x:.5,y:-.5,fixed:!1},{id:12,x:.5,y:.5,fixed:!1},{id:13,x:-.5,y:.5,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1},{id:200,type:"line",points:[10,11],construction:!1},{id:201,type:"line",points:[11,12],construction:!1},{id:202,type:"line",points:[12,13],construction:!1},{id:203,type:"line",points:[13,10],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]};const o=await e.newFeature("SW");o.inputParams.profile=`${t.inputParams.featureID}_T`,o.inputParams.path=[`${n.inputParams.featureID}:G100`],o.inputParams.orientationMode="translate";const s=await e.newFeature("B");return s.inputParams.targetSolid=t.inputParams.featureID,s.inputParams.boolean={targets:[o.inputParams.featureID],operation:"UNION"},e}i(as,"test_SweepFace");async function is(e){const t=await e.newFeature("P");t.inputParams.orientation="YZ";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch={points:[{id:10,x:-5,y:-5,fixed:!1},{id:11,x:5,y:-5,fixed:!1},{id:12,x:5,y:5,fixed:!1},{id:13,x:-5,y:5,fixed:!1},{id:20,x:-3,y:-3,fixed:!1},{id:21,x:3,y:-3,fixed:!1},{id:22,x:3,y:3,fixed:!1},{id:23,x:-3,y:3,fixed:!1},{id:30,x:-1,y:-1,fixed:!1},{id:31,x:1,y:-1,fixed:!1},{id:32,x:1,y:1,fixed:!1},{id:33,x:-1,y:1,fixed:!1}],geometries:[{id:200,type:"line",points:[10,11],construction:!1},{id:201,type:"line",points:[11,12],construction:!1},{id:202,type:"line",points:[12,13],construction:!1},{id:203,type:"line",points:[13,10],construction:!1},{id:210,type:"line",points:[20,21],construction:!1},{id:211,type:"line",points:[21,22],construction:!1},{id:212,type:"line",points:[22,23],construction:!1},{id:213,type:"line",points:[23,20],construction:!1},{id:220,type:"line",points:[30,31],construction:!1},{id:221,type:"line",points:[31,32],construction:!1},{id:222,type:"line",points:[32,33],construction:!1},{id:223,type:"line",points:[33,30],construction:!1}],constraints:[]};const n=await e.newFeature("P");n.inputParams.orientation="XY";const o=await e.newFeature("S");o.inputParams.sketchPlane=n.inputParams.featureID,o.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:12,y:0,fixed:!1}],geometries:[{id:100,type:"line",points:[0,1],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]};const s=await e.newFeature("SW");return s.inputParams.profile=r.inputParams.featureID,s.inputParams.path=[`${o.inputParams.featureID}:G100`],s.inputParams.orientationMode="pathAlign",s.inputParams.consumeProfileSketch=!1,e}i(is,"test_SweepFace_pathAlign_multi_loop_islands");async function cs(e){const t=(e?.features||[]).find(l=>String(l?.type||"").toUpperCase()==="SW"),r=Number(t?.persistentData?.profileIslandCount)||0,n=Array.isArray(t?.persistentData?.profileIslandEdgeCounts)?t.persistentData.profileIslandEdgeCounts:[];if(r>1){if(n.length!==r)throw new Error("[sweep_path_align_multi_loop_islands] Missing per-island edge counts.");if(n.some(l=>!(Number(l)>0)))throw new Error("[sweep_path_align_multi_loop_islands] One or more profile islands lost edge data.")}const o=(e?.scene?.children||[]).filter(l=>String(l?.type||"").toUpperCase()==="SOLID");if(!o.length)throw new Error("[sweep_path_align_multi_loop_islands] Expected sweep to produce a solid.");const s=o.find(l=>String(l?.name||"").startsWith("SW"))||o[0];let a=null;try{a=s.getMesh()}catch(l){throw new Error(`[sweep_path_align_multi_loop_islands] Sweep solid is not manifold: ${l?.message||l}`)}let c=0;try{c=typeof a?.numTri=="function"?Number(a.numTri())||0:(a?.triVerts?.length||0)/3}finally{try{a&&typeof a.delete=="function"&&a.delete()}catch{}}if(!(c>0))throw new Error("[sweep_path_align_multi_loop_islands] Sweep solid has no triangles.")}i(cs,"afterRun_sweepFace_pathAlign_multi_loop_islands");const ls=1e-6;function h0(e){return(Array.isArray(e?.children)?e.children:[]).find(r=>r&&r.type==="SKETCH")||null}i(h0,"findSketchGroup");function x0(e){return e&&(Array.isArray(e.children)?e.children:[]).find(r=>r&&r.type==="FACE")||null}i(x0,"findSketchFace");function ds(e){return e?(Array.isArray(e.children)?e.children:[]).filter(r=>r&&r.type==="EDGE"):[]}i(ds,"findSketchEdges");function ye(e,t){if(!e)throw new Error(t)}i(ye,"expectTruthy");function us(e,t,r){if(!Number.isFinite(e)||Math.abs(e-t)>ls)throw new Error(`${r} expected ${t}, got ${e}`)}i(us,"expectApprox");async function fs(e){e.expressions='textLabel = "BREP";';const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("TEXT");return r.inputParams.text="textLabel",r.inputParams.textHeight=10,r.inputParams.curveResolution=12,r.inputParams.placementPlane=t.inputParams.featureID,e}i(fs,"test_textToFace");async function ps(e){const t=Array.isArray(e.features)?e.features.find(x=>x&&x.type==="TEXT"):null;if(ye(t,"[text_to_face] No TEXT feature found"),String(t?.previouseExpressions?.text??"")!=="BREP")throw new Error("[text_to_face] Text expression did not resolve to BREP");const r=t?.persistentData?.fontFile;if(typeof r!="string"||!r.startsWith("data:"))throw new Error("[text_to_face] Missing persistentData.fontFile data URL");const n=t?.persistentData?.fontFileKey;if(typeof n!="string"||!n.startsWith("font:"))throw new Error("[text_to_face] Missing or invalid persistentData.fontFileKey");const o=h0(e.scene);ye(o,"[text_to_face] No SKETCH group found");const s=x0(o);ye(s,"[text_to_face] No FACE found on SKETCH group");const a=ds(o);if(!a.length)throw new Error("[text_to_face] No sketch edges found");const c=s?.userData?.boundaryLoopsWorld;if(!Array.isArray(c)||!c.length)throw new Error("[text_to_face] Missing boundaryLoopsWorld on face");const l=s?.userData?.profileGroups;if(!Array.isArray(l)||!l.length)throw new Error("[text_to_face] Missing profileGroups on face");const d=qt.getBaseMaterial(s)||s.material;if(!d)throw new Error("[text_to_face] Face has no material");if(d.side!==Ge.THREE.DoubleSide)throw new Error("[text_to_face] Face material is not DoubleSide");if(!d.polygonOffset)throw new Error("[text_to_face] Face material polygonOffset not enabled");const f=qt.getBaseMaterial(a[0])||a[0].material;if(!f)throw new Error("[text_to_face] Edge has no material");if(f.depthTest!==!1)throw new Error("[text_to_face] Edge material depthTest should be false");if(a[0].renderOrder<2)throw new Error("[text_to_face] Edge renderOrder should be elevated");try{if(typeof s.getAverageNormal=="function"){const x=s.getAverageNormal();us(Number(x.length()),1,"[text_to_face] Face normal not normalized")}}catch{}const u="__text_to_face_removed_font_test__";t.inputParams.font=u,t.inputParams.fontFile="",t.persistentData=t.persistentData||{},t.persistentData.fontFile=r,t.persistentData.fontFileKey=`font:${u}`,await e.runHistory();const p=Array.isArray(e.features)?e.features.find(x=>x&&x.type==="TEXT"):null;if(ye(p,"[text_to_face] No TEXT feature found after persisted-font fallback run"),p?.persistentData?.fontFileKey!==`font:${u}`)throw new Error("[text_to_face] Persisted font fallback did not preserve missing font id key");if(typeof p?.persistentData?.fontFile!="string"||!p.persistentData.fontFile.startsWith("data:"))throw new Error("[text_to_face] Persisted font fallback lost font data URL");const m=h0(e.scene);ye(m,"[text_to_face] No SKETCH group found after persisted-font fallback run");const h=x0(m);ye(h,"[text_to_face] No FACE found after persisted-font fallback run")}i(ps,"afterRun_textToFace");async function ms(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID,r.persistentData.sketch={points:[{id:0,x:0,y:0,fixed:!0},{id:1,x:0,y:40,fixed:!1},{id:2,x:25,y:40,fixed:!1},{id:10,x:-2,y:-2,fixed:!1},{id:11,x:2,y:-2,fixed:!1},{id:12,x:2,y:2,fixed:!1},{id:13,x:-2,y:2,fixed:!1}],geometries:[{id:200,type:"line",points:[0,1],construction:!1},{id:201,type:"line",points:[1,2],construction:!1},{id:300,type:"line",points:[10,11],construction:!1},{id:301,type:"line",points:[11,12],construction:!1},{id:302,type:"line",points:[12,13],construction:!1},{id:303,type:"line",points:[13,10],construction:!1}],constraints:[{id:0,type:"⏚",points:[0]}]};const n=`${r.inputParams.featureID}:`,o=await e.newFeature("TU");o.inputParams.path=[`${n}G200`,`${n}G201`],o.inputParams.radius=4,o.inputParams.innerRadius=0,o.inputParams.resolution=32;const s=await e.newFeature("TU");return s.inputParams.path=[`${n}G200`,`${n}G201`],s.inputParams.radius=5,s.inputParams.innerRadius=2,s.inputParams.resolution=48,e}i(ms,"test_tube");async function hs(e){const t=await e.newFeature("P");t.inputParams.orientation="XY";const r=await e.newFeature("S");r.inputParams.sketchPlane=t.inputParams.featureID;const n=6,o=20,s=[],a=[];for(let f=0;f<n;f++){const u=f/n*2*Math.PI;s.push({id:f,x:Math.cos(u)*o,y:Math.sin(u)*o,fixed:!1})}for(let f=0;f<n;f++){const u=(f+1)%n;a.push({id:200+f,type:"line",points:[f,u],construction:!1})}r.persistentData.sketch={points:s,geometries:a,constraints:[{id:0,type:"⏚",points:[0]}]};const c=`${r.inputParams.featureID}:`,l=await e.newFeature("TU");l.inputParams.path=a.map(f=>`${c}G${f.id}`),l.inputParams.radius=3,l.inputParams.innerRadius=0,l.inputParams.resolution=24;const d=await e.newFeature("TU");return d.inputParams.path=a.map(f=>`${c}G${f.id}`),d.inputParams.radius=4,d.inputParams.innerRadius=1,d.inputParams.resolution=24,d.inputParams.transform={position:[0,0,10],rotation:[0,0,0],scale:[1,1,1]},e}i(hs,"test_tube_closedLoop");function Ye(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(Ye,"assert$9");function xs(){const e={id:"sheet-1",name:"Instruction Sheet",elements:[{id:"table-1",type:"table"}]},t={id:"sheet-2",name:"Formboard Sheet",elements:[{id:"line-1",type:"line",groupId:"wire-harness-formboard:abc"}]};Ye(Be([e],"")===null,"Expected unrelated sheets to be ignored when reusing a formboard target."),Ye(Be([e,t],"")?.id==="sheet-2","Expected an existing formboard sheet to be reused."),Ye(Be([e,t],"sheet-1")?.id==="sheet-2","Expected preferred non-formboard sheets to be ignored in favor of the actual formboard sheet."),Ye(Be([e,t],"sheet-2")?.id==="sheet-2","Expected the preferred sheet to be reused when it already contains a formboard.")}i(xs,"test_wire_harness_formboard_reuses_only_formboard_sheet");function W(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(W,"assert$8");function g0(e,t){return{name:e,userData:{isPortRoot:!0,portData:{objectName:e,name:t,kind:"termination",point:[0,0,0],direction:[1,0,0]}}}}i(g0,"createFakePort");async function gs(){const e=g0("PORT_A","Port A"),t=g0("PORT_B","Port B"),r=new Map([[e.name,e],[t.name,t]]),n={scene:{traverse(c){c(e),c(t)},getObjectByName(c){return r.get(String(c))||null}},getObjectByName(c){return r.get(String(c))||null}},o=Ut(n,{from:"Port A",to:"Port B"});W(o.ok===!0,"Expected endpoint labels to resolve."),W(o.fromRef==="PORT_A","Expected from endpoint to resolve to PORT_A."),W(o.toRef==="PORT_B","Expected to endpoint to resolve to PORT_B."),W(Array.isArray(o.portRefs)&&o.portRefs.length===2,"Expected both endpoint refs to be returned.");const s=Ut(n,{from:"Port A",to:"Missing"});W(s.ok===!1,"Expected unresolved endpoint lookup to fail."),W(s.portRefs.length===1&&s.portRefs[0]==="PORT_A","Expected partial resolution to preserve the valid endpoint.");const a=tr(n,[{id:"WIRE1",from:"Port A",to:"Port B",diameter:1.25}]);W(Array.isArray(a.segments)&&a.segments.length===0,"Expected payload to expose a segments array."),W(Array.isArray(a.connections)&&a.connections.length===1,"Expected payload to expose one valid connection."),W(a.connections[0].id==="WIRE1","Expected payload connection id to match."),W(a.connections[0].startPoint==="PORT_A","Expected payload startPoint to use resolved port object names."),W(a.connections[0].endPoint==="PORT_B","Expected payload endPoint to use resolved port object names."),W(a.connections[0].wireInfo?.diameter===1.25,"Expected payload diameter to match input.")}i(gs,"test_wire_harness_connection_endpoint_resolution");function F(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(F,"assert$7");function Ve(e,t,r="termination",n=[0,0,0],o=[1,0,0]){return{name:e,userData:{isPortRoot:!0,portData:{objectName:e,name:t,kind:r,point:n.slice(),direction:o.slice(),extension:1,displayLength:1}}}}i(Ve,"createPort$1");function mt(e,t,r,n,o){return{type:"SP",inputParams:{featureID:e,curveResolution:4,bendRadius:1},persistentData:{spline:{points:[{id:"p0",position:[0,0,0],attachment:{type:"port",portRef:t,side:r}},{id:"p1",position:[1,0,0],attachment:{type:"port",portRef:n,side:o}}]}}}}i(mt,"createSplineFeature$1");function ys(){const e=Ve("START","Start","termination",[-6,0,0],[1,0,0]),t=Ve("SPLIT","Split","waypoint",[0,0,0],[1,0,0]),r=Ve("BRANCHA","Branch A","termination",[5,4,0],[1,0,0]),n=Ve("BRANCHB","Branch B","termination",[5,-4,0],[1,0,0]),o=new Map([[e.name,e],[t.name,t],[r.name,r],[n.name,n]]);return{scene:{traverse(s){s(e),s(t),s(r),s(n)},getObjectByName(s){return o.get(String(s))||null}},getObjectByName(s){return o.get(String(s))||null},features:[mt("SP1","START","A","SPLIT","A"),mt("SP2","SPLIT","B","BRANCHA","A"),mt("SP3","SPLIT","A","BRANCHB","A")]}}i(ys,"createSimpleHarnessPartHistory");function ws(){const e=new Nt(null),t=e.createSheet({name:"Large Formboard",sizeKey:"CUSTOM",orientation:"landscape",customWidthIn:72,customHeightIn:36,elements:[]});F(t.sizeKey==="CUSTOM","Expected custom sheet size key to persist."),F(t.widthIn===72,"Expected custom sheet width to persist."),F(t.heightIn===36,"Expected custom sheet height to persist.");const r=e.updateSheet(t.id,{sizeKey:"CUSTOM",orientation:"portrait",customWidthIn:36,customHeightIn:72});F(r.sizeKey==="CUSTOM","Expected custom size key to survive sheet updates."),F(r.widthIn===36,"Expected portrait custom sheet width to persist."),F(r.heightIn===72,"Expected portrait custom sheet height to persist.")}i(ws,"test_sheet_custom_size_persists");function vs(){const e=ys(),t=rr(e,{includeTitle:!0});F(t.ok===!0,"Expected harness formboard definition to succeed.");const r=t.elements.filter(f=>f?.type==="line"),n=t.elements.filter(f=>f?.type==="text");F(r.length===3,"Expected three flattened line segments for the Y harness."),F(r.every(f=>f?.formboard?.exactGeometry===!0),"Expected generated line segments to be marked as exact formboard geometry."),F(r.every(f=>f?.formboard?.fromNodeId&&f?.formboard?.toNodeId),"Expected generated line segments to carry directed node metadata."),F(n.some(f=>String(f?.text||"")==="Start"),"Expected endpoint labels to be included."),F(n.some(f=>String(f?.text||"").includes("in")),"Expected segment length labels to be included.");const o=new Nt(null),s=o.createSheet({name:"Instruction Sheet",sizeKey:"A",orientation:"landscape",elements:[]}),a=nr(o,s.id,e,{includeTitle:!0,resizeSheetToFit:!0});F(a.ok===!0,"Expected formboard insertion to succeed."),F(a.sheet?.sizeKey==="CUSTOM","Expected insertion to promote the target sheet to custom sizing."),F((a.insertedElements||[]).filter(f=>f?.type==="line").length===3,"Expected inserted formboard to contain three line elements."),F((a.sheet?.elements||[]).filter(f=>f?.type==="line").every(f=>f?.formboard?.exactGeometry===!0),"Expected exact formboard metadata to persist through sheet normalization."),F((a.sheet?.elements||[]).every(f=>String(f?.groupId||"").startsWith("wire-harness-formboard:")),"Expected inserted sheet elements to belong to the generated formboard group.");const c=or(a.sheet?.elements||[]),l=(a.sheet?.elements||[]).find(f=>f?.type==="line"&&String(f?.formboard?.fromNodeId||"")==="SPLIT"),d=sr(c,"SPLIT",l?.id);F(d.size===1,"Expected pivot branch resolution to isolate one downstream branch."),F(d.has(String(l?.formboard?.toNodeId||"")),"Expected branch resolution to include the segment endpoint opposite the pivot.")}i(vs,"test_wire_harness_formboard_insert");function y0(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(y0,"assert$6");function _s(){const e=new pr(null),t=e.addView({viewName:"Sheet View",camera:{},annotations:[],viewSettings:{pmiTextSizePt:"18.5"}});y0(t?.viewSettings?.pmiTextSizePt===18.5,"Expected PMI text size to normalize to a finite number.");const r=e.updateView(0,n=>(n.viewSettings.pmiTextSizePt=-4,n));y0(!Object.prototype.hasOwnProperty.call(r?.viewSettings||{},"pmiTextSizePt"),"Expected invalid PMI text size to be removed during normalization.")}i(_s,"test_pmi_view_text_size_setting_normalizes");function ee(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ee,"assert$5");function J(e,t,r=1e-9){return Math.abs(Number(e)-Number(t))<=r}i(J,"approxEqual$1");function Es(){ee(ar("PORT")===!0,"Expected feature dimension overlay to support port features.")}i(Es,"test_feature_dimension_overlay_supports_port");function Ss(){const e=Wt({point:[1,2,3],direction:[0,0,10],extension:4},.5);ee(e,"Expected positive port extension geometry to resolve."),ee(J(e.value,4),"Expected extension value to be preserved."),ee(J(e.dragPlaneValue,4),"Expected drag plane value to match positive extension."),ee(J(e.pointB.x,1)&&J(e.pointB.y,2)&&J(e.pointB.z,7),"Expected endpoint to follow normalized port direction.");const t=Wt({point:[0,0,0],direction:[5,0,0],extension:0},.75);ee(t,"Expected zero-length port extension geometry to resolve."),ee(J(t.value,0),"Expected displayed extension value to remain zero."),ee(J(t.dragPlaneValue,.75),"Expected zero-length extension to keep a visible drag handle offset."),ee(J(t.pointB.x,.75)&&J(t.pointB.y,0)&&J(t.pointB.z,0),"Expected visible tip offset to follow the normalized direction.")}i(Ss,"test_port_extension_annotation_geometry_preserves_extension_value");function K(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(K,"assert$4");function Je(e,t,r=1e-9){return Math.abs(Number(e)-Number(t))<=r}i(Je,"approxEqual");function et(e,t,r){const n=[e.x,e.y,e.z];for(let o=0;o<3;o+=1)if(!Je(n[o],t[o]))throw new Error(r||`Expected vector ${t.join(", ")}, got ${n.join(", ")}.`)}i(et,"assertVec3");function bs(){const e=vr({position:[1,2,3],rotationEuler:[4,5,6],scale:[1,1,1],reference:{name:"FACE_REF",type:"FACE",pickPoint:[7,8,9],faceIndex:3}});K(e.reference&&typeof e.reference=="object","Expected transform reference metadata to be preserved."),K(e.reference.name==="FACE_REF","Expected transform reference name to persist."),K(e.reference.type==="FACE","Expected transform reference type to persist."),K(Array.isArray(e.reference.pickPoint),"Expected pick point metadata to persist."),K(e.reference.faceIndex===3,"Expected face index metadata to persist.")}i(bs,"test_transform_reference_sanitize_preserves_metadata");function Ps(){const e=new ke,t=new Re;t.name="FACE_PICK",t.type="FACE",e.add(t);const r=_r({name:"FACE_PICK",type:"FACE",pickPoint:[3,4,5]},e,{},I0),n=new V(Number(r.position[0])||0,Number(r.position[1])||0,Number(r.position[2])||0);et(n,[3,4,5],"Expected face reference base to use the stored pick point.")}i(Ps,"test_transform_reference_base_uses_face_pick_point");function Fs(){const e=new ke,t=new Re;t.name="VERTEX_REF",t.type="VERTEX",t.position.set(10,20,30),e.add(t);const r=Er({position:[1,2,3],rotationEuler:[0,0,0],scale:[1,1,1],reference:{name:"VERTEX_REF",type:"VERTEX"}},e,{},I0),n=new V,o=new Sr,s=new V;r.decompose(n,o,s),et(n,[11,22,33],"Expected referenced transform to offset from the selected vertex.")}i(Fs,"test_referenced_transform_matrix_uses_vertex_reference_origin");function As(){const e=new ke,t=new Re;t.name="PORT_VERTEX",t.type="VERTEX",t.position.set(5,0,0),e.add(t);const r=A0({featureId:"PORT_REF_TEST",inputParams:{portName:"Port Ref Test",transform:{position:[2,0,0],rotationEuler:[0,0,0],scale:[1,1,1],reference:{name:"PORT_VERTEX",type:"VERTEX"}},extension:2,displayLength:4},referenceSource:e});K(Array.isArray(r.point),"Expected port definition point to resolve."),K(Je(r.point[0],7),"Expected port point to be offset from the transform reference."),K(Je(r.point[1],0)&&Je(r.point[2],0),"Expected port point to remain aligned to the reference origin."),K(r.anchorName==="PORT_VERTEX","Expected anchorName compatibility field to reflect the transform reference."),K(r.transform?.reference,"Expected normalized port transform to retain its reference.")}i(As,"test_port_definition_uses_transform_reference_without_anchor");function Is(){const e=new ke,t=new Re;t.name="PORT_VERTEX_DIR",t.type="VERTEX",t.position.set(1,2,3),e.add(t);const r=new Re;r.name="PORT_DATUM_DIR",r.type="DATUM",r.quaternion.setFromAxisAngle(new V(0,0,1),Math.PI/2),e.add(r),e.updateMatrixWorld(!0);const n=A0({featureId:"PORT_DIR_TEST",inputParams:{transform:{position:[2,0,0],rotationEuler:[0,0,0],scale:[1,1,1],reference:{name:"PORT_VERTEX_DIR",type:"VERTEX"}},directionRef:[{name:"PORT_DATUM_DIR",type:"DATUM"}]},referenceSource:e});et(new V(...n.point),[1,4,3],"Expected point reference to inherit the direction reference basis."),et(new V(...n.direction),[0,1,0],"Expected port direction to align with the direction reference.")}i(Is,"test_port_definition_uses_transform_reference_and_direction_reference");function ae(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ae,"assert$3");async function Ts(){const e=new Nt(null),t=e.createSheet({name:"Harness Sheet",sizeKey:"A",orientation:"landscape",elements:[]}),r=[{id:"WIRE1",name:"Wire 1",from:"PORT_A",to:"PORT_B",diameter:1.25},{id:"WIRE2",name:"Wire 2",from:"PORT_C",to:"PORT_D",diameter:.8}],n=[{connectionId:"WIRE1",feasible:!0,distance:123.456},{connectionId:"WIRE2",feasible:!1,error:"No route found."}],o=ir(r,n);ae(o.cells[0][0].text==="Wire","Expected first header cell to be Wire."),ae(o.cells[1][1].text==="123.46","Expected routed length to be formatted into the sheet table."),ae(o.cells[2][5].text==="Failed","Expected failed routes to use a compact sheet status label.");const s=cr(e,t.id,r,n);ae(!!s?.sheet,"Expected sheet insertion to return an updated sheet."),ae(!!s?.element,"Expected sheet insertion to return the inserted element."),ae(s.element.type==="table","Expected inserted element to be a table."),ae(s.element.tableData?.cells?.[1]?.[0]?.text==="Wire 1","Expected first wire name to appear in the inserted table."),ae(s.element.tableData?.cells?.[2]?.[5]?.text==="Failed","Expected compact status text to persist on the inserted table element.")}i(Ts,"test_wire_harness_sheet_table_insert");function ht(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ht,"assert$2");function xt(e,t,r="termination",n=[0,0,0],o=[1,0,0]){return{name:e,userData:{isPortRoot:!0,portData:{objectName:e,name:t,kind:r,point:n.slice(),direction:o.slice(),extension:1,displayLength:1}}}}i(xt,"createPort");function w0(e,t,r,n,o){return{type:"SP",inputParams:{featureID:e,curveResolution:4,bendRadius:1},persistentData:{spline:{points:[{id:"p0",position:[0,0,0],attachment:{type:"port",portRef:t,side:r}},{id:"p1",position:[1,0,0],attachment:{type:"port",portRef:n,side:o}}]}}}}i(w0,"createSplineFeature");async function Ns(){const e=xt("START","Start","termination",[-4,0,0],[1,0,0]),t=xt("END","End","termination",[4,0,0],[1,0,0]),r=xt("WAYPOINT","Waypoint","waypoint",[0,0,0],[1,0,0]),n=new Map([[e.name,e],[t.name,t],[r.name,r]]),o={scene:{traverse(l){l(e),l(t),l(r)},getObjectByName(l){return n.get(String(l))||null}},getObjectByName(l){return n.get(String(l))||null},features:[w0("SP1","START","A","WAYPOINT","A"),w0("SP2","WAYPOINT","B","END","A")]},{network:s,routes:a}=await lr(o,[{id:"WIRE3",name:"Wire 3",from:"Start",to:"End",diameter:1}]),c=(s?.splineSegments||[]).filter(l=>l.firstPoint==="WAYPOINT"||l.secondPoint==="WAYPOINT").map(l=>l.firstPoint==="WAYPOINT"?l.firstSide:l.secondSide);ht(c.length===2,"Expected two waypoint segment attachments."),ht(c.every(l=>l==="B"),"Expected both spline branches to occupy waypoint side B."),ht(a[0].feasible===!1,"Expected route to fail when both branches use the same physical waypoint side.")}i(Ns,"test_wire_harness_infers_endpoint_side_from_spline_direction");function pe(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(pe,"assert$1");function Ds(){const e=new ke;dr(e,[],[{segmentId:"SEG1",featureId:"SP1",polyline:[[0,0,0],[15,0,0],[15,10,0]],diameter:2.5,wireCount:3,wireDiameters:[1,1,1.5],connectionIds:["WIRE-1"],connectionNames:["Wire 1"]}]);const t=e.getObjectByName("__WireHarnessRoutes");pe(t,"Expected the wire harness route group to be added to the scene.");const r=[];e.traverse(a=>{a?.type==="SOLID"&&a?.userData?.isWireHarnessRoute&&r.push(a)}),pe(r.length===1,`Expected one routed harness solid, got ${r.length}.`),pe(typeof r[0].toSTL=="function","Expected routed harness geometry to be a real BREP solid."),pe(ur(e,"WIRE-1").length===1,"Expected connection hover lookup to resolve the routed harness solid.");const n=r[0].getMesh(),o=(n?.triVerts?.length||0)/3|0;try{n?.delete?.()}catch{}pe(o>0,"Expected routed harness solid to contribute triangulated geometry for export."),fr(e),pe(e.getObjectByName("__WireHarnessRoutes")==null,"Expected clear to remove the wire harness route group.");const s=[];e.traverse(a=>{a?.type==="SOLID"&&a?.userData?.isWireHarnessRoute&&s.push(a)}),pe(s.length===0,"Expected clear to remove routed harness solids from the scene.")}i(Ds,"test_wire_harness_routes_render_as_scene_solids");function ie(e,t){if(!e)throw new Error(t||"Assertion failed.")}i(ie,"assert");async function Ms(){const e=new Ke;e.wireHarnessManager.addConnection({id:"wire-1",name:"Wire 1",from:"PORT-A",to:"PORT-B",diameter:1.5}),e.wireHarnessManager.setRouteResults([{connectionId:"wire-1",connectionName:"Wire 1",feasible:!0,error:"",distance:42.5,polyline:[[0,0,0],[10,0,0],[10,5,0]],segmentIds:["SP1","SP2"],nodePath:["PORT-A:A","MID:A","PORT-B:A"],reusesHarnessPoint:!1,diameter:1.5,from:"PORT-A",to:"PORT-B"}]);const t=await e.toJSON(),r=new Ke;await r.fromJSON(t);const n=r.wireHarnessManager.getConnections();ie(n.length===1,`Expected one restored connection, got ${n.length}.`),ie(n[0].id==="wire-1","Expected the connection ID to persist.");const o=r.wireHarnessManager.getRouteResults();ie(o.length===1,`Expected one restored route result, got ${o.length}.`),ie(o[0].feasible===!0,"Expected the restored route result to remain feasible."),ie(o[0].distance===42.5,`Expected the restored route distance to persist, got ${o[0].distance}.`),ie(o[0].segmentIds.join(",")==="SP1,SP2","Expected restored route segment IDs to persist.");const s=r.wireHarnessManager.consumePendingRestoredRouteResults();ie(Array.isArray(s)&&s.length===1,"Expected the restored model to queue route geometry restoration."),ie(r.wireHarnessManager.consumePendingRestoredRouteResults()==null,"Expected pending restored routes to be consumed only once.")}i(Ms,"test_wire_harness_route_results_persist_in_model_json");async function Cs(e){const t=await e.newFeature("P.CU");t.inputParams.sizeX=8,t.inputParams.sizeY=6,t.inputParams.sizeZ=4}i(Cs,"test_visibility_hidden_state_persistence");async function Rs(e){const r=(Array.isArray(e?.features)?e.features[0]:null)?.inputParams?.featureID;if(!r)throw new Error("Visibility persistence test requires a first feature with featureID.");const n=e.scene?.getObjectByName?.(r);if(!n||n.type!=="SOLID")throw new Error("Visibility persistence test could not find initial cube solid.");const o=(Array.isArray(n.children)?n.children:[]).find(p=>p?.type==="FACE")||null,s=(Array.isArray(n.children)?n.children:[]).find(p=>p?.type==="EDGE")||null;if(!o||!s)throw new Error("Visibility persistence test requires one face and one edge on the cube.");const a=String(o?.userData?.faceName||o?.name||""),c=String(s?.name||"");if(!a||!c)throw new Error("Visibility persistence test requires named face and edge.");n.visible=!1,o.visible=!1,s.visible=!1,await e.newFeature("P.CU"),await e.runHistory();const l=e.scene?.getObjectByName?.(r);if(!l)throw new Error("Visibility persistence test could not find rebuilt cube.");if(l.visible!==!1)throw new Error("Hidden solid visibility state was not preserved after history run.");const d=Array.isArray(l.children)?l.children:[],f=d.find(p=>p?.type!=="FACE"?!1:String(p?.userData?.faceName||p?.name||"")===a)||null,u=d.find(p=>p?.type==="EDGE"&&String(p?.name||"")===c)||null;if(!f)throw new Error("Hidden face could not be resolved after history run.");if(!u)throw new Error("Hidden edge could not be resolved after history run.");if(f.visible!==!1)throw new Error("Hidden face visibility state was not preserved after history run.");if(u.visible!==!1)throw new Error("Hidden edge visibility state was not preserved after history run.")}i(Rs,"afterRun_visibility_hidden_state_persistence");const re=typeof process<"u"&&process.versions&&process.versions.node&&typeof window>"u",G0=G.join("tests","test-run.log"),me=[{test:ao,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:co,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:uo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:lo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:tn,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:no,afterRun:oo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:io,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:po,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:fo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Es,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ss,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:bs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ps,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Fs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:As,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Is,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:zr,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Wr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:jr,afterRun:Ur,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ts,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Ln,afterRun:Bn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:zn,afterRun:jn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Un,afterRun:Wn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Yn,afterRun:Vn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Gn,afterRun:Xn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:eo,afterRun:to,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:qn,afterRun:Zn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:as,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:is,afterRun:cs,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ms,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:hs,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:xs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:gs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:ws,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:_s,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Io,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:vs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ts,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ns,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ds,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ms,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Lo,afterRun:Bo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Uo,afterRun:Wo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Go,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Xo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Jo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ko,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:qo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Zo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ho,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:so,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Qr,afterRun:en,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:rn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:nn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:sn,afterRun:an,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:hn,afterRun:xn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ln,afterRun:dn,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:pn,afterRun:mn,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:gn,afterRun:yn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:vn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Yr,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Vr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Xr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Jr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Kr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:qr,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:os,afterRun:ss,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:In,afterRun:Tn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Rn,afterRun:kn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:$n,afterRun:On,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Nn,afterRun:Dn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:Mn,afterRun:Cn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:xo,afterRun:yo,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:ro,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:bn,afterRun:Pn,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0},{test:_n,afterRun:En,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:ns,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Cs,afterRun:Rs,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:fs,afterRun:ps,printArtifacts:!1,exportFaces:!0,exportSolids:!1,resetHistory:!0},{test:Oo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Ao,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Co,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:$o,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:vo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:bo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Mo,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:No,printArtifacts:!1,exportFaces:!1,exportSolids:!1,resetHistory:!0},{test:Qo,afterRun:es,printArtifacts:!0,exportFaces:!0,exportSolids:!0,resetHistory:!0}];async function ks(){if(typeof process<"u"&&process.versions&&process.versions.node)try{const e="src/tests/partFiles";try{const r=(await S.promises.readdir(e)).filter(n=>typeof n=="string"&&n.toLowerCase().endsWith(".json"));for(const n of r){const o=G.join(e,n),c=`import_part_${String(n).replace(/\.[^.]+$/,"").replace(/[^a-zA-Z0-9._-]+/g,"_").substring(0,100)}`,l=i(async function(d){const f=await S.promises.readFile(o,"utf8");let u=f;try{const p=JSON.parse(f);p&&typeof p=="object"&&(Array.isArray(p.features)?u=JSON.stringify(p):p.data&&(u=typeof p.data=="string"?p.data:JSON.stringify(p.data)))}catch{}await d.reset(),await d.fromJSON(u)},"importTest");try{Object.defineProperty(l,"name",{value:c,configurable:!0})}catch{}me.push({test:l,afterRun:null,printArtifacts:!1,exportFaces:!0,exportSolids:!0,resetHistory:!0,allowErrors:!0,_sourceFile:o})}}catch{}}catch(e){console.warn("Failed to register part file import tests:",e?.message||e)}}i(ks,"registerPartFileTests");re&&X0().then(()=>{process.exit(0)}).catch(e=>{console.error(e),process.exit(1)});async function X0(e=new Ke,t=null){typeof process<"u"&&process.versions&&process.versions.node,re&&(Ls(),we("Test run started")),await S.promises.rm("./tests/results",{recursive:!0,force:!0}),await ks(),await Br(me);for(const r of me){const n=r===me[me.length-1];await e.reset(),r.resetHistory&&(e.features=[]);const o=r?.test?.name&&String(r.test.name)||"unnamed_test";re&&we(`Starting ${o}`);let s=null;try{s=await Tt(r,e)}catch(a){throw re&&we(`Test ${o} failed: ${v0(a)}`),a}if(re&&we(s?`Test ${o} completed with handled error: ${v0(s)}`:`Test ${o} completed successfully`),typeof window<"u")typeof t=="function"&&await t(e,n);else{const a=r.test.name,c=`./tests/results/${a}/`;S.existsSync(c)||S.mkdirSync(c,{recursive:!0});const l=(e.scene?.children||[]).filter(d=>d&&d.type==="SOLID"&&typeof d.toSTL=="function");(r.exportSolids||r.printArtifacts)&&l.forEach((d,f)=>{const u=d.name&&String(d.name).trim().length?String(d.name):`solid_${f}`,p=ve(u);let m="";try{m=d.toSTL(p,6)}catch(x){console.warn(`[runTests] toSTL failed for solid ${u}:`,x?.message||x);return}const h=G.join(c,`${p}.stl`);tt(h,m)}),r.exportFaces&&l.forEach((d,f)=>{const u=d.name&&String(d.name).trim().length?String(d.name):`solid_${f}`,p=ve(u);let m=[];try{m=typeof d.getFaces=="function"?d.getFaces(!1):[]}catch{m=[]}m.forEach(({faceName:h,triangles:x},y)=>{if(!x||x.length===0)return;const g=h||`face_${y}`,w=ve(g),v=Bs(`${p}_${w}`,x),E=G.join(c,`${p}_${w}.stl`);tt(E,v)})}),await zs({partHistory:e,exportName:a,exportPath:c,solids:l})}}re&&we("Test run finished")}i(X0,"runTests");async function Tt(e,t=new Ke){let r=null;try{if(await e.test(t),await t.runHistory(),typeof e.afterRun=="function")try{await e.afterRun(t)}catch(n){console.warn("afterRun failed:",n?.message||n)}}catch(n){if(r=n,e.allowErrors){const o=e.test&&e.test.name?e.test.name:"unnamed_test",s=`./tests/results/${o}/`;try{S.existsSync(s)||S.mkdirSync(s,{recursive:!0})}catch{}const a=`${n?.message||n}
|
|
1887
1887
|
|
|
1888
|
-
${n?.stack||""}`;try{tt(G.join(s,"error.txt"),a)}catch{}console.error(`Error in test ${o}:`,n)}else throw n}return await new Promise(n=>setTimeout(n,1e3)),r}i(Tt,"runSingleTest");function tt(e,t){if(!(typeof window<"u"))try{const r=G.dirname(e);S.existsSync(r)||S.mkdirSync(r,{recursive:!0}),S.writeFileSync(e,t,"utf8")}catch(r){console.log(`Error writing file ${e}:`,r)}}i(tt,"writeFile");function $s(e,t){if(!(typeof window<"u"))try{const r=G.dirname(e);S.existsSync(r)||S.mkdirSync(r,{recursive:!0}),S.writeFileSync(e,t)}catch(r){console.log(`Error writing file ${e}:`,r)}}i($s,"writeBinaryFile");function Os(e,t){if(re)try{const r=G.dirname(e);S.existsSync(r)||S.mkdirSync(r,{recursive:!0}),S.appendFileSync(e,t,"utf8")}catch(r){console.log(`Error appending file ${e}:`,r)}}i(Os,"appendToFile");function Ls(){re&&tt(G0,"")}i(Ls,"resetTestLog");function
|
|
1889
|
-
`)}i(
|
|
1890
|
-
${e.stack}`:t}i(v0,"stringifyError");function ve(e){return String(e).replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").substring(0,100)||"artifact"}i(ve,"sanitizeFileName");function Bs(e,t){const r=i(o=>Number.isFinite(o)?Math.abs(o)<1e-18?"0":o.toFixed(6):"0","fmt"),n=[];n.push(`solid ${e}`);for(let o=0;o<t.length;o++){const s=t[o],a=s.p1,c=s.p2,l=s.p3,d=c[0]-a[0],
|
|
1891
|
-
`)}i(Bs,"trianglesToAsciiSTL");async function zs({partHistory:e,exportName:t,exportPath:r,solids:n}){let o=null;try{const u=await e?.toJSON?.();u&&(o=typeof u=="string"?u:JSON.stringify(u))}catch(u){console.warn(`[runTests] Failed to serialize feature history for ${t}:`,u?.message||u)}const s=o?{"Metadata/featureHistory.json":o}:void 0,a=o?{featureHistoryPath:"/Metadata/featureHistory.json"}:void 0,c=e?.metadataManager||null,l=[];(n||[]).forEach((u,
|
|
1888
|
+
${n?.stack||""}`;try{tt(G.join(s,"error.txt"),a)}catch{}console.error(`Error in test ${o}:`,n)}else throw n}return await new Promise(n=>setTimeout(n,1e3)),r}i(Tt,"runSingleTest");function tt(e,t){if(!(typeof window<"u"))try{const r=G.dirname(e);S.existsSync(r)||S.mkdirSync(r,{recursive:!0}),S.writeFileSync(e,t,"utf8")}catch(r){console.log(`Error writing file ${e}:`,r)}}i(tt,"writeFile");function $s(e,t){if(!(typeof window<"u"))try{const r=G.dirname(e);S.existsSync(r)||S.mkdirSync(r,{recursive:!0}),S.writeFileSync(e,t)}catch(r){console.log(`Error writing file ${e}:`,r)}}i($s,"writeBinaryFile");function Os(e,t){if(re)try{const r=G.dirname(e);S.existsSync(r)||S.mkdirSync(r,{recursive:!0}),S.appendFileSync(e,t,"utf8")}catch(r){console.log(`Error appending file ${e}:`,r)}}i(Os,"appendToFile");function Ls(){re&&tt(G0,"")}i(Ls,"resetTestLog");function we(e){if(!re)return;const t=new Date().toISOString();Os(G0,`[${t}] ${e}
|
|
1889
|
+
`)}i(we,"logTestEvent");function v0(e){if(!e)return"Unknown error";if(typeof e=="string")return e;const t=e?.message||e?.toString?.()||"Unknown error";return e?.stack?`${t}
|
|
1890
|
+
${e.stack}`:t}i(v0,"stringifyError");function ve(e){return String(e).replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^_+|_+$/g,"").substring(0,100)||"artifact"}i(ve,"sanitizeFileName");function Bs(e,t){const r=i(o=>Number.isFinite(o)?Math.abs(o)<1e-18?"0":o.toFixed(6):"0","fmt"),n=[];n.push(`solid ${e}`);for(let o=0;o<t.length;o++){const s=t[o],a=s.p1,c=s.p2,l=s.p3,d=c[0]-a[0],f=c[1]-a[1],u=c[2]-a[2],p=l[0]-a[0],m=l[1]-a[1],h=l[2]-a[2];let x=f*h-u*m,y=u*p-d*h,g=d*m-f*p;const w=Math.hypot(x,y,g)||1;x/=w,y/=w,g/=w,n.push(` facet normal ${r(x)} ${r(y)} ${r(g)}`),n.push(" outer loop"),n.push(` vertex ${r(a[0])} ${r(a[1])} ${r(a[2])}`),n.push(` vertex ${r(c[0])} ${r(c[1])} ${r(c[2])}`),n.push(` vertex ${r(l[0])} ${r(l[1])} ${r(l[2])}`),n.push(" endloop"),n.push(" endfacet")}return n.push(`endsolid ${e}`),n.join(`
|
|
1891
|
+
`)}i(Bs,"trianglesToAsciiSTL");async function zs({partHistory:e,exportName:t,exportPath:r,solids:n}){let o=null;try{const u=await e?.toJSON?.();u&&(o=typeof u=="string"?u:JSON.stringify(u))}catch(u){console.warn(`[runTests] Failed to serialize feature history for ${t}:`,u?.message||u)}const s=o?{"Metadata/featureHistory.json":o}:void 0,a=o?{featureHistoryPath:"/Metadata/featureHistory.json"}:void 0,c=e?.metadataManager||null,l=[];(n||[]).forEach((u,p)=>{try{const m=u?.getMesh?.(),h=!!(m&&m.vertProperties&&m.triVerts);if(m&&typeof m.delete=="function")try{m.delete()}catch{}if(h)l.push(u);else{const x=ve(u?.name||`solid_${p}`);console.warn(`[runTests] Skipping non-manifold solid for 3MF: ${x}`)}}catch{const m=ve(u?.name||`solid_${p}`);console.warn(`[runTests] Skipping solid that failed to export for 3MF: ${m}`)}});const d=ve(t||"partHistory"),f=G.join(r,`${d}.3mf`);try{let u=null;try{u=await Yt(l,{unit:"millimeter",precision:6,scale:1,additionalFiles:s,modelMetadata:a,metadataManager:c})}catch{u=await Yt([],{unit:"millimeter",precision:6,scale:1,additionalFiles:s,modelMetadata:a,metadataManager:c})}u&&u.length?$s(f,u):console.warn(`[runTests] 3MF export returned empty payload for ${t}`)}catch(u){console.warn(`[runTests] 3MF export failed for ${t}:`,u?.message||u)}}i(zs,"export3mfArtifact");const $t=class $t{constructor({containerEl:t=document.getElementById("viewport"),sidebarEl:r=document.getElementById("sidebar"),exposeEnvOnWindow:n=!0,viewer:o=null}={}){this.autoProgress=window.location.href.includes("autoNext=true"),this.env=o instanceof Vt?o:new Vt({container:t,sidebar:r}),n&&(window.env=this.env),this.testNames=me.map(s=>s.test.name),this.currentIndex=0,this.enabled=new Map(this.testNames.map(s=>[s,!0])),this.status=new Map(this.testNames.map(s=>[s,""])),this.errors=new Map,this.screenshots=new Map,this.logWindow=null,this.logContent=null,this.popupDiv=document.createElement("div"),this.popupDiv.style.padding="10px",this.popupDiv.style.background="#0b0b0e",this.popupDiv.style.color="#e5e7eb",this.popupDiv.style.border="1px solid #2a2a33",this.popupDiv.style.borderRadius="12px",this.popupDiv.style.maxWidth="90vw",this.popupDiv.style.maxHeight="100%",this.popupDiv.style.overflow="auto",this.ui=this._buildUI(),window.browserTesting=this,this.autoProgress&&(this.loggingTool=new wt({captureStack:!1}),this.loggingTool.install())}sleep(t){return new Promise(r=>setTimeout(r,t))}async dumpScreenshot(){return await this.sleep(2e3),this.env.renderer.domElement.toDataURL()}show(){try{this.ui?.window?.root&&(this.ui.window.root.style.display="")}catch{}}hide(){try{this.ui?.window?.root&&(this.ui.window.root.style.display="none")}catch{}}toggle(){try{if(!this.ui?.window?.root)return;const t=this.ui.window.root.style.display;this.ui.window.root.style.display=t==="none"?"":"none"}catch{}}async callBetweenTestsToRender(t,r){this.env.partHistory.features=t.features,this.env.scene=t.scene;try{this.autoProgress&&await this.sleep(1e3),await this.sleep(1e3),await this.env.renderer.render(this.env.scene,this.env.camera);const n=this.env.renderer.domElement.toDataURL(),o=document.createElement("img");o.src=n,o.style.maxWidth="360px",o.style.height="auto",o.style.display="block",o.style.margin="8px 0",this.popupDiv.appendChild(o)}catch(n){console.log("Error occurred while writing to popup:",n)}if(this.autoProgress,!r){const n=window.open("","_blank");n&&n.document&&n.document.body&&(n.document.body.style.background="#0b0b0e",n.document.body.style.color="#e5e7eb",n.document.body.appendChild(this.popupDiv))}}async run(){await X0(this.env.partHistory,this.callBetweenTestsToRender.bind(this))}_buildUI(){const t=new Gt({title:"Browser Testing",width:500,height:700,right:16,top:40,shaded:!1}),r=document.createElement("div");Object.assign(r.style,{padding:"10px 12px",display:"grid",gridTemplateColumns:"repeat(4, 1fr)",gap:"8px",borderBottom:"1px solid #23232b"});const n=De("Run (current)"),o=De("Previous"),s=De("Next"),a=De("Run All");r.appendChild(n),r.appendChild(o),r.appendChild(s),r.appendChild(a);const c=document.createElement("div"),l=document.createElement("table");Object.assign(l.style,{width:"100%",borderCollapse:"collapse"});const d=document.createElement("thead"),f=document.createElement("tr");f.appendChild(gt("Test (enable/disable)","50%")),f.appendChild(gt("Status","20%")),f.appendChild(gt("Actions","30%")),d.appendChild(f),l.appendChild(d);const u=document.createElement("tbody");this._rowRefs=new Map,this.testNames.forEach((m,h)=>{const x=document.createElement("tr");Object.assign(x.style,Ws());const y=document.createElement("td");y.style.height="10px",Object.assign(y.style,yt());const g=document.createElement("label");g.style.display="flex",g.style.alignItems="center",g.style.gap="8px";const w=document.createElement("input");w.type="checkbox",w.checked=!0,w.addEventListener("change",()=>{this.enabled.set(m,w.checked)});const v=document.createElement("span");v.textContent=m,g.appendChild(w),g.appendChild(v),y.appendChild(g);const E=document.createElement("td");Object.assign(E.style,yt()),S0(E,this.status.get(m));const D=document.createElement("td");Object.assign(D.style,yt()),D.style.display="flex",D.style.gap="6px";const k=E0("▷"),q=E0("Show Log");D.appendChild(k),D.appendChild(q),x.addEventListener("click",T=>{const Z=T.target&&T.target.tagName?T.target.tagName.toLowerCase():"";Z==="button"||Z==="input"||Z==="label"||this._selectRow(h)}),k.addEventListener("click",async T=>{T.stopPropagation(),this._selectRow(h),await this._runSingleByName(m)}),q.addEventListener("click",T=>{T.stopPropagation(),this._showErrorLog(m)}),x.appendChild(y),x.appendChild(E),x.appendChild(D),u.appendChild(x),this._rowRefs.set(m,{row:x,checkbox:w,statusCell:E,runBtn:k,logBtn:q})}),l.appendChild(u),c.appendChild(l);const p=document.createElement("div");return Object.assign(p.style,{padding:"10px 12px",borderTop:"1px solid #23232b",color:"#a7aab3",display:"flex",justifyContent:"space-between"}),this._selectionLabel=document.createElement("span"),this._selectionLabel.textContent=this._currentLabel(),p.appendChild(this._selectionLabel),t.content.appendChild(r),t.content.appendChild(c),t.content.appendChild(p),n.addEventListener("click",async()=>{const m=this.testNames[this.currentIndex];await this._runSingleByName(m)}),o.addEventListener("click",()=>this._moveSelection(-1)),s.addEventListener("click",()=>this._moveSelection(1)),a.addEventListener("click",async()=>{await this._runAllEnabled()}),this._applySelectionStyles(),{window:t,controls:r,table:l,tbody:u}}_currentLabel(){return`Selected: ${this.testNames[this.currentIndex]||"(none)"} (${this.currentIndex+1}/${this.testNames.length})`}_moveSelection(t){this.testNames.length&&(this.currentIndex=(this.currentIndex+t+this.testNames.length)%this.testNames.length,this._applySelectionStyles())}_selectRow(t){t<0||t>=this.testNames.length||(this.currentIndex=t,this._applySelectionStyles())}_applySelectionStyles(){this._selectionLabel.textContent=this._currentLabel(),this.testNames.forEach((t,r)=>{const n=this._rowRefs.get(t);n&&(r===this.currentIndex?(n.row.style.outline="2px solid #4155ff88",n.row.style.background="#12131a"):(n.row.style.outline="none",n.row.style.background="transparent"))})}_setStatus(t,r){this.status.set(t,r);const n=this._rowRefs.get(t);n&&S0(n.statusCell,r)}_setError(t,r){if(!r){this.errors.delete(t);return}this.errors.set(t,{message:String(r&&r.message?r.message:r),stack:r&&r.stack?String(r.stack):""})}_showErrorLog(t){const r=this.errors.get(t);if(this._ensureLogWindow(),!this.logWindow||!this.logContent)return;this.logWindow.setTitle(`Log - ${t}`),this.logContent.innerHTML="";const n=this.screenshots.get(t);if(n){const s=document.createElement("div");Object.assign(s.style,{marginBottom:"8px",display:"flex",justifyContent:"center"});const a=document.createElement("img");a.src=n,a.alt=`Canvas snapshot for ${t}`,a.style.maxWidth="100%",a.style.height="auto",a.style.border="1px solid #1e2030",a.style.borderRadius="8px",s.appendChild(a),this.logContent.appendChild(s)}const o=document.createElement("pre");o.style.whiteSpace="pre-wrap",o.style.lineHeight="1.5",o.style.background="#111217",o.style.border="1px solid #1e2030",o.style.padding="3px",o.style.borderRadius="8px",o.textContent=r?`${r.message}
|
|
1892
1892
|
|
|
1893
1893
|
${r.stack}`:"No error captured for this test.",this.logContent.appendChild(o),this.logWindow.root.style.display="flex"}_ensureLogWindow(){if(this.logWindow)return;const t=new Gt({title:"Log",width:720,height:520,right:24,top:80,shaded:!1,onClose:i(()=>this._hideLogWindow(),"onClose")}),r=document.createElement("div");r.style.display="flex",r.style.flexDirection="column",r.style.gap="10px",r.style.width="100%",r.style.height="100%",r.style.boxSizing="border-box",t.content.appendChild(r),this.logWindow=t,this.logContent=r;try{t.root.style.display="none"}catch{}}_hideLogWindow(){if(this.logWindow?.root)try{this.logWindow.root.style.display="none"}catch{}}async _runSingleByName(t){this._setError(t,null),this._setStatus(t,"");const r=this._rowRefs.get(t);r&&(r.row.style.background="#151726");const n=me.find(o=>o.test.name===t);try{if(typeof Tt=="function"){this.startLogging(),this.env.partHistory.reset(),await Tt(n,this.env.partHistory);try{typeof this.env.zoomToFit=="function"?this.env.zoomToFit(1.2):this.env.render(),await new Promise(s=>requestAnimationFrame(s));const o=this.env.renderer?.domElement?.toDataURL?.("image/png");o&&typeof o=="string"&&o.startsWith("data:image")&&this.screenshots.set(t,o)}catch{}this._setError(t,await this.endLogging())}}catch(o){console.error(`Error in test ${t}:`,o),this._setError(t,o),this._setStatus(t,"fail")}finally{const o=this.testNames.indexOf(t);r&&(o===this.currentIndex?r.row.style.background="#12131a":r.row.style.background="transparent")}}async startLogging(){this.loggingTool&&this.loggingTool.clearLogs()}async endLogging(){if(!this.loggingTool)return"";let t="";return this.loggingTool.getLogs().forEach(r=>{console.log(r),r.args.forEach(n=>{if(typeof n=="string"){t+=`${n}
|
|
1894
1894
|
`;return}typeof n=="object"&&n!==null&&(t+=`${JSON.stringify(n,null,2)}
|
|
1895
|
-
`)})}),t}async _runAllEnabled(){this.popupDiv.innerHTML="";for(let t=0;t<this.testNames.length;t++){const r=this.testNames[t];this.enabled.get(r)&&(this._selectRow(t),await this._runSingleByName(r))}}};i($t,"BrowserTesting");let _0=$t;function js(){return{background:"#1f2937",color:"#f9fafb",border:"1px solid #374151",padding:"3px",borderRadius:"8px",cursor:"pointer",fontWeight:"700",outline:"none",transition:"background 120ms ease, transform 60ms ease, box-shadow 120ms ease",userSelect:"none"}}i(js,"darkButtonStyle");function Us(e){e.addEventListener("mouseenter",()=>{e.style.background="#2b3545",e.style.boxShadow="0 3px 10px rgba(0,0,0,0.35)"}),e.addEventListener("mouseleave",()=>{e.style.background="#1f2937",e.style.transform="none",e.style.boxShadow="none"}),e.addEventListener("mousedown",()=>{e.style.transform="translateY(1px)"}),e.addEventListener("mouseup",()=>{e.style.transform="none"})}i(Us,"decorateButtonHover");function De(e){const t=document.createElement("button");return t.type="button",t.textContent=e,Object.assign(t.style,js()),Us(t),t}i(De,"makeButton");function E0(e){const t=De(e);return t.style.padding="3px",t.style.borderRadius="6px",t.style.fontSize="10px",t}i(E0,"miniButton");function gt(e,t){const r=document.createElement("th");return r.textContent=e,r.style.textAlign="left",r.style.padding="10px 12px",r.style.borderBottom="1px solid #23232b",r.style.color="#9aa0aa",r.style.fontWeight="600",t&&(r.style.width=t),r}i(gt,"th");function Ws(){return{borderBottom:"1px solid #1c1d25"}}i(Ws,"rowStyle");function
|
|
1895
|
+
`)})}),t}async _runAllEnabled(){this.popupDiv.innerHTML="";for(let t=0;t<this.testNames.length;t++){const r=this.testNames[t];this.enabled.get(r)&&(this._selectRow(t),await this._runSingleByName(r))}}};i($t,"BrowserTesting");let _0=$t;function js(){return{background:"#1f2937",color:"#f9fafb",border:"1px solid #374151",padding:"3px",borderRadius:"8px",cursor:"pointer",fontWeight:"700",outline:"none",transition:"background 120ms ease, transform 60ms ease, box-shadow 120ms ease",userSelect:"none"}}i(js,"darkButtonStyle");function Us(e){e.addEventListener("mouseenter",()=>{e.style.background="#2b3545",e.style.boxShadow="0 3px 10px rgba(0,0,0,0.35)"}),e.addEventListener("mouseleave",()=>{e.style.background="#1f2937",e.style.transform="none",e.style.boxShadow="none"}),e.addEventListener("mousedown",()=>{e.style.transform="translateY(1px)"}),e.addEventListener("mouseup",()=>{e.style.transform="none"})}i(Us,"decorateButtonHover");function De(e){const t=document.createElement("button");return t.type="button",t.textContent=e,Object.assign(t.style,js()),Us(t),t}i(De,"makeButton");function E0(e){const t=De(e);return t.style.padding="3px",t.style.borderRadius="6px",t.style.fontSize="10px",t}i(E0,"miniButton");function gt(e,t){const r=document.createElement("th");return r.textContent=e,r.style.textAlign="left",r.style.padding="10px 12px",r.style.borderBottom="1px solid #23232b",r.style.color="#9aa0aa",r.style.fontWeight="600",t&&(r.style.width=t),r}i(gt,"th");function Ws(){return{borderBottom:"1px solid #1c1d25"}}i(Ws,"rowStyle");function yt(){return{padding:"3px 12px",verticalAlign:"middle"}}i(yt,"cellStyle");function S0(e,t){e.textContent="";const r=document.createElement("span");r.textContent=t===""?"":t.toUpperCase(),r.style.fontWeight="800",r.style.letterSpacing="0.5px",r.style.padding=t?"2px 8px":"0",r.style.borderRadius="999px",t==="pass"?(r.style.background="#093d2a",r.style.color="#86efac",r.style.border="1px solid #14532d"):t==="fail"?(r.style.background="#3a0b0f",r.style.color="#fca5a5",r.style.border="1px solid #7f1d1d"):(r.style.background="transparent",r.style.color="#a7aab3"),e.appendChild(r)}i(S0,"updateStatusCell");export{_0 as BrowserTesting};
|