diff --git a/README.md b/README.md index f62c1ce..e0b0233 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ 手机 / 平板上的本地 DWG、PDF、EPUB、FB2、XPS/OXPS、CBZ 和图片浏览器。 -- DWG 解析:`@node-projects/acad-ts`(MIT),支持模型空间、块、布局、代理图形数据、图层、文字、填充、样条、尺寸等常见 2D 实体。 +- DWG 渲染:`@flyfish-dev/cad-viewer`(AGPL-3.0,GitHub: `flyfish-dev/cad-viewer`),WebGL 常驻渲染,支持块展开、图层、文字、填充、线型、样条、标注和常用 DWG 2006–2026 版本。 - 文档渲染:MuPDF.js。 -- 前端:Vite + TypeScript + Canvas 2D,解析在 Web Worker 中执行。 +- 前端:Vite + TypeScript;DWG 解析在 Web Worker 中执行,运行时 WASM 位于 `public/wasm/`。 - Android:Kotlin WebView 壳,离线内置全部前端资源,支持文件关联、返回键和 PNG 保存。 -- 默认显示第三方代理对象,可在左侧立方体按钮中关闭;DWG 中代理数据本身没有图形时不会产生可见图元。 +- 代理对象只有在文件中包含代理图形数据时才会显示;没有任何图形数据的字典代理不可见。 ## 开发 diff --git a/android/app/src/main/java/com/phoneviewer/app/MainActivity.kt b/android/app/src/main/java/com/phoneviewer/app/MainActivity.kt index 9b657dd..1675fe4 100644 --- a/android/app/src/main/java/com/phoneviewer/app/MainActivity.kt +++ b/android/app/src/main/java/com/phoneviewer/app/MainActivity.kt @@ -34,8 +34,8 @@ class MainActivity : Activity() { private var lastOpenedMime = "application/octet-stream" private companion object { - const val APP_HOST = "appassets-v5.androidplatform.net" - const val APP_URL = "https://appassets-v5.androidplatform.net/" + const val APP_HOST = "appassets-v6.androidplatform.net" + const val APP_URL = "https://appassets-v6.androidplatform.net/" const val FILE_CHOOSER_REQUEST = 1001 } diff --git a/package-lock.json b/package-lock.json index 79deda5..f6ed1a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "phone-viewer-app", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "phone-viewer-app", - "version": "0.4.0", + "version": "0.5.0", + "hasInstallScript": true, "license": "AGPL-3.0-or-later", "dependencies": { - "@node-projects/acad-ts": "^3.6.0", + "@flyfish-dev/cad-viewer": "^0.8.3", "mupdf": "1.28.1" }, "devDependencies": { - "@mlightcad/libredwg-web": "0.8.2", "adm-zip": "^0.6.1", "playwright-core": "^1.64.1", "typescript": "^5.6.3", @@ -1974,6 +1974,30 @@ "node": ">=12" } }, + "node_modules/@flyfish-dev/cad-viewer": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@flyfish-dev/cad-viewer/-/cad-viewer-0.8.3.tgz", + "integrity": "sha512-HR2+Rt3Pb17QpmzOwz5lG87vamwoYcRrKaeRT+CBvJgK350z3qAV9VtAe2kt0XZjrTLTKYAglGBTjGY0IgDDPg==", + "license": "AGPL-3.0-only", + "dependencies": { + "@mlightcad/libredwg-web": "0.7.9", + "@mlightcad/shx-parser": "^1.4.5", + "dwf-viewer": "^0.6.7", + "dxf-parser": "^1.1.2" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@flyfish-dev/cad-viewer/node_modules/@mlightcad/libredwg-web": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/@mlightcad/libredwg-web/-/libredwg-web-0.7.9.tgz", + "integrity": "sha512-tqjx0eCiR0CNI3TyO3LVYzl4ptuzSFm7asGn/C1YiJaEk7Vneto+lRrVUco85qw+PZDihaFLwWqdYIEse1swbA==", + "license": "GPL-3.0", + "engines": { + "node": ">=20" + } + }, "node_modules/@isaacs/cliui": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", @@ -2045,15 +2069,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mlightcad/libredwg-web": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@mlightcad/libredwg-web/-/libredwg-web-0.8.2.tgz", - "integrity": "sha512-0iX1lbASPEWwhljHAmaB6/mNHAPObCMmlDIza1a6B0egpm0DGfaMjXimwWmVSPE+FNEMhQxB3Tw0mupDwkWHIQ==", - "dev": true, - "license": "GPL-3.0", - "engines": { - "node": ">=20" - } + "node_modules/@mlightcad/shx-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@mlightcad/shx-parser/-/shx-parser-1.4.5.tgz", + "integrity": "sha512-wFY0X7vnuq1IYdP6JRggb7LHcKho1P8zmcWGtXa6XYthwyKZCUoZCswTXr7J26pbzyhBB7KsiCcCL48iwIevdQ==", + "license": "MIT" }, "node_modules/@napi-rs/lzma-linux-x64-gnu": { "version": "1.5.1", @@ -2075,12 +2095,6 @@ "node": "^22.20 || ^24.12 || >=25" } }, - "node_modules/@node-projects/acad-ts": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@node-projects/acad-ts/-/acad-ts-3.6.0.tgz", - "integrity": "sha512-MQadFJafEuKDmzhi7GeoCV/YpPcbSVHMuuRhaQGuC9Xbzo8DdRpE8LWdV/fNT5FqbRXh60oK/B/2pPFR/ATpmw==", - "license": "MIT" - }, "node_modules/@rollup/plugin-babel": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz", @@ -3138,6 +3152,32 @@ "node": ">= 0.4" } }, + "node_modules/dwf-viewer": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/dwf-viewer/-/dwf-viewer-0.6.7.tgz", + "integrity": "sha512-YIUmzt0Ck+TFxFsmuYlRydy15wrqDEaweqUd8uO44Q6r9NgfkIlLKIHbEQFX/CNqMzzjd7lSbNdFPAEXMBx3LQ==", + "license": "AGPL-3.0-only", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "three": ">=0.150.0" + }, + "peerDependenciesMeta": { + "three": { + "optional": true + } + } + }, + "node_modules/dxf-parser": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/dxf-parser/-/dxf-parser-1.1.2.tgz", + "integrity": "sha512-GPTumUvRkounlIazLIyJMmTWt+nlg+ksS0Hdm8jWvejmZKBTz6gvHTam76wRm4PQMma5sgKLThblQyeIJcH79Q==", + "license": "MIT", + "dependencies": { + "loglevel": "^1.7.1" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -4390,6 +4430,19 @@ "dev": true, "license": "MIT" }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", diff --git a/package.json b/package.json index ff2cf8e..9eaac1e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "phone-viewer-app", "private": true, - "version": "0.4.1", + "version": "0.5.0", "type": "module", "license": "AGPL-3.0-or-later", "description": "Mobile/tablet all-in-one viewer: DWG (model & layout space) + PDF/EPUB/XPS/CBZ/images", @@ -14,11 +14,13 @@ "preview": "vite preview", "typecheck": "tsc --noEmit", "icons": "node scripts/make-icons.mjs", - "patch:wasm": "node scripts/patch-wasm-memory.mjs", - "apk": "node scripts/build-apk.mjs" + "apk": "node scripts/build-apk.mjs", + "copy:cad-wasm": "node scripts/copy-cad-wasm.mjs", + "postinstall": "node scripts/copy-cad-wasm.mjs", + "prebuild": "node scripts/copy-cad-wasm.mjs" }, "dependencies": { - "@node-projects/acad-ts": "^3.6.0", + "@flyfish-dev/cad-viewer": "^0.8.3", "mupdf": "1.28.1" }, "devDependencies": { @@ -26,7 +28,6 @@ "playwright-core": "^1.64.1", "typescript": "^5.6.3", "vite": "^5.4.10", - "vite-plugin-pwa": "^0.20.5", - "@mlightcad/libredwg-web": "0.8.2" + "vite-plugin-pwa": "^0.20.5" } } diff --git a/public/wasm/dwfv-render.wasm b/public/wasm/dwfv-render.wasm new file mode 100644 index 0000000..da23d5e Binary files /dev/null and b/public/wasm/dwfv-render.wasm differ diff --git a/public/wasm/dwg-worker.js b/public/wasm/dwg-worker.js new file mode 100644 index 0000000..2eb2a12 --- /dev/null +++ b/public/wasm/dwg-worker.js @@ -0,0 +1,24 @@ +var Ze=Object.defineProperty;var qe=(t,n,o)=>n in t?Ze(t,n,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[n]=o;var M=(t,n,o)=>qe(t,typeof n!="symbol"?n+"":n,o);const Qe="modulepreload",je=function(t){return"/"+t},le={},Ee=function(n,o,e){let a=Promise.resolve();if(o&&o.length>0){let d=function(c){return Promise.all(c.map(E=>Promise.resolve(E).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};var r=d;document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),_=s?.nonce||s?.getAttribute("nonce");a=d(o.map(c=>{if(c=je(c),c in le)return;le[c]=!0;const E=c.endsWith(".css"),u=E?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${u}`))return;const l=document.createElement("link");if(l.rel=E?"stylesheet":Qe,E||(l.as="script"),l.crossOrigin="",l.href=c,_&&l.setAttribute("nonce",_),document.head.appendChild(l),E)return new Promise((I,f)=>{l.addEventListener("load",I),l.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${c}`)))})}))}function i(s){const _=new Event("vite:preloadError",{cancelable:!0});if(_.payload=s,window.dispatchEvent(_),!_.defaultPrevented)throw s}return a.then(s=>{for(const _ of s||[])_.status==="rejected"&&i(_.reason);return n().catch(i)})};function to(t){return{format:t.format,sourceName:t.sourceName,units:t.units,header:t.header??{},layers:t.layers??{},lineTypes:t.lineTypes??{},blocks:t.blocks??{},entities:t.entities??[],pages:t.pages,savedView:t.savedView,dataLinks:t.dataLinks,dataTables:t.dataTables,xrecords:t.xrecords,dictionaries:t.dictionaries,metadata:t.metadata??{},warnings:t.warnings??[],raw:t.raw}}function ue(t){switch(String(t??"").toUpperCase()){case"LINE":return"line";case"CIRCLE":return"circle";case"ARC":return"arc";case"LWPOLYLINE":case"POLYLINE":case"POLYLINE_2D":case"POLYLINE2D":case"POLYLINE_3D":case"POLYLINE3D":case"LEADER":case"MULTILEADER":return"polyline";case"ELLIPSE":return"ellipse";case"TEXT":case"MTEXT":case"ATTRIB":case"ATTDEF":case"DIMENSION":return"text";case"POINT":return"point";case"INSERT":case"MINSERT":return"insert";case"SOLID":case"TRACE":case"3DFACE":return"solid";case"HATCH":return"hatch";case"SPLINE":return"spline";case"PATH":case"XPS_PATH":case"DWF_PATH":return"path";case"IMAGE":case"RASTER_IMAGE":case"DWF_IMAGE":return"image";case"VIEWPORT":return"viewport";case"TABLE":case"ACAD_TABLE":case"ACDBTABLE":return"table";default:return"unsupported"}}function Re(t,n,o={}){const e=n??String(t.type??t.entityType??t.objectName??"UNKNOWN").toUpperCase(),a=o.includeUnknownProperties===!1?{type:e,kind:ue(e)}:{...t,type:e,kind:ue(e)};o.keepRaw&&(a.raw=t),a.handle=y(t.handle??t.id),a.layer=y(t.layer??t.layerName),a.lineType=y(t.lineType??t.linetype),a.lineTypeScale=P(t.lineTypeScale??t.linetypeScale??t.ltscale)??a.lineTypeScale,a.flag=P(t.flag??t.flags)??a.flag,a.constantWidth=P(t.constantWidth??t.constWidth??t.const_width)??a.constantWidth,a.thickness=P(t.thickness)??a.thickness;const i=t.isClosed===!0||t.closed===!0||t.shape===!0,r=/^(POLYLINE|POLYLINE_2D|POLYLINE2D|POLYLINE_3D|POLYLINE3D|SPLINE)$/.test(e),s=e==="LWPOLYLINE";(i||r&&(Number(a.flag??0)&1)===1||s&&(Number(a.flag??0)&512)===512)&&(a.isClosed=!0);const _=o.numericColorMode??"auto",d=P(t.color),c=P(t.colorIndex??t.colorNumber??t.aci??t.aciColor??t.color_index);a.colorIndex=c??(_!=="rgb"&&d!==void 0&&Math.abs(d)<=257?d:void 0);const E=t.trueColor??t.true_color??t.truecolor??t.colorRGB??t.colorRgb??t.rgbColor??t.rgb,u=d!==void 0&&d>=0&&d<=16777215&&(_==="rgb"||_==="auto"&&Math.abs(d)>257);a.trueColor=E??(u?d:void 0),(typeof t.color=="string"||typeof t.color=="number")&&(a.color=t.color),a.colorNumber=P(t.colorNumber)??a.colorNumber,a.colorName=y(t.colorName??t.color_name)??a.colorName,a.fillColor=t.fillColor??t.fill_color,a.fillColorIndex=P(t.fillColorIndex??t.fill_color_index??t.fillColorNumber)??a.fillColorIndex,a.opacity=P(t.opacity??t.alpha)??a.opacity,a.lineweight=P(t.lineweight??t.lineWeight),a.isVisible=!(t.isVisible===!1||t.visible===!1);const l=O(t.text);a.startPoint=b(t.startPoint??l?.startPoint??t.start??t.p0??t.from)??a.startPoint,a.endPoint=b(t.endPoint??l?.endPoint??t.end??t.p1??t.to)??a.endPoint,a.center=b(t.center??t.centerPoint)??a.center,a.insertionPoint=b(t.insertionPoint??l?.startPoint??t.position??t.location??t.point??t.basePoint)??a.insertionPoint,a.radius=P(t.radius)??a.radius,a.startAngle=P(t.startAngle??t.start_angle)??a.startAngle,a.endAngle=P(t.endAngle??t.end_angle)??a.endAngle,a.majorAxisEndPoint=b(t.majorAxisEndPoint??t.majorAxis??t.major)??a.majorAxisEndPoint,a.axisRatio=P(t.axisRatio??t.ratio)??a.axisRatio,a.height=P(t.height??t.textHeight??l?.textHeight)??a.height,a.textHeight=P(t.textHeight??t.height??l?.textHeight)??a.textHeight,a.xScale=P(t.xScale??t.widthFactor??l?.xScale)??a.xScale,a.generationFlag=P(t.generationFlag??t.textGenerationFlag??l?.generationFlag)??a.generationFlag,a.halign=P(t.halign??t.horizontalAlignment??l?.halign)??a.halign,a.valign=P(t.valign??t.verticalAlignment??l?.valign)??a.valign,a.extrusionDirection=b(t.extrusionDirection??t.extrusion??l?.extrusionDirection)??a.extrusionDirection,a.rotation=P(t.rotation??t.angle??l?.rotation)??a.rotation,a.text=y(l?.text??t.text??t.value??t.string??t.contents??t.defaultValue??t.default_value)??a.text,a.value=y(t.value??l?.text??t.text??t.string??t.contents??t.defaultValue??t.default_value)??a.value,a.name=y(t.name??t.blockName)??a.name,a.blockName=y(t.blockName??t.name)??a.blockName,a.effectiveBlockName=y(t.effectiveBlockName??t.effectiveName??t.dynamicBlockName??t.originalBlockName)??a.effectiveBlockName,a.extensionDictionaryHandle=Rt(t.extensionDictionaryHandle??t.ownerDictionaryHardId??t.ownerdictionaryHardId??t.xdicobjhandle)??a.extensionDictionaryHandle,a.ownerBlockRecordHandle=Rt(t.ownerBlockRecordHandle??t.ownerBlockRecordSoftId??t.ownerHandle)??a.ownerBlockRecordHandle,a.attributeTag=y(t.attributeTag??t.tag??t.tagString??t.attrTag)??a.attributeTag,a.attributePrompt=y(t.attributePrompt??t.prompt??t.promptString)??a.attributePrompt,a.attributeFlags=P(t.attributeFlags??t.flags??t.flag)??a.attributeFlags,(e==="INSERT"||e==="MINSERT")&&(a.insertRowCount=j(t.insertRowCount??t.rowCount??t.numRows??t.num_rows)??a.insertRowCount,a.insertColumnCount=j(t.insertColumnCount??t.columnCount??t.numColumns??t.numCols??t.num_cols)??a.insertColumnCount,a.insertRowSpacing=P(t.insertRowSpacing??t.rowSpacing??t.row_spacing)??a.insertRowSpacing,a.insertColumnSpacing=P(t.insertColumnSpacing??t.columnSpacing??t.col_spacing)??a.insertColumnSpacing);const I=Ne(t.xdata??t.extendedData??t.xData);if(I.length>0&&(a.xdata=I),e==="INSERT"||e==="MINSERT"){const p=b(t.scale);if(p)a.scale=p;else{const C=P(t.scaleX??t.xScale),L=P(t.scaleY??t.yScale)??C,R=P(t.scaleZ??t.zScale);C!==void 0&&L!==void 0&&(a.scale=R===void 0?{x:C,y:L}:{x:C,y:L,z:R})}}const f=Dn(t.vertices??t.points);f.length>0&&(a.vertices=f);const A=Dn(t.controlPoints??t.control_points);A.length>0&&(a.controlPoints=A);const h=Dn(t.fitPoints??t.fit_points);h.length>0&&(a.fitPoints=h);const g=t.attribs??t.attributes??t.attributeEntities;return Array.isArray(g)&&(a.attribs=g.filter(p=>!!p&&typeof p=="object").map(p=>Re(p,void 0,o))),(e==="TABLE"||e==="ACAD_TABLE"||e==="ACDBTABLE")&&(a.table=no(t)),a}function no(t){const n=O(t.ldata??t.linkedData),o=O(t.tdata??t.linkedTableData),e=j(t.rowCount??t.rowsCount??t.numRows??t.num_rows??o?.numRows??o?.num_rows)??0,a=j(t.columnCount??t.columnsCount??t.numColumns??t.numCols??t.num_cols??o?.numColumns??o?.numCols??o?.num_cols)??0,i=me(t.cells??t.tableCells??t.cellValues??o?.rows??t.data),r=a||io(i),s=e||(r>0?Math.ceil(i.length/r):0),_=i.map(({cell:d,index:c})=>ro(d,c,r));return{name:y(t.tableName??t.name??n?.name??n?.description),rowCount:s,columnCount:r,cells:_,rowHeights:Ae(t.rowHeights??t.rowHeightArr??t.row_heights),columnWidths:Ae(t.columnWidths??t.columnWidthArr??t.col_widths),dataLinkHandle:Rt(t.dataLinkHandle??t.dataLink??t.data_link),titleSuppressed:nn(t.titleSuppressed??t.title_suppressed),headerSuppressed:nn(t.headerSuppressed??t.header_suppressed)}}function b(t){if(!t||typeof t!="object")return;const n=t,o=Number(n.x??n.X??n[0]),e=Number(n.y??n.Y??n[1]),a=n.z??n.Z??n[2],i=a===void 0?void 0:Number(a);if(!(!Number.isFinite(o)||!Number.isFinite(e)))return Number.isFinite(i)?{x:o,y:e,z:i}:{x:o,y:e}}function Dn(t){if(!Array.isArray(t))return[];const n=[];for(const o of t){const e=b(o);if(!e)continue;const a=o,i=e,r=P(a.bulge);r!==void 0&&(i.bulge=r);const s=P(a.startWidth??a.start_width);s!==void 0&&(i.startWidth=s);const _=P(a.endWidth??a.end_width);_!==void 0&&(i.endWidth=_),n.push(i)}return n}function eo(t,n){n.name&&(t[n.name]=n,t[n.name.toLowerCase()]=n)}function oo(t,n){n.name&&(t[n.name]=n,t[n.name.toLowerCase()]=n)}function ao(t,n){n.name&&(t[n.name]=n,t[n.name.toLowerCase()]=n,n.handle&&(t[n.handle]=n,t[n.handle.toLowerCase()]=n))}function P(t){const n=Number(t);return Number.isFinite(n)?n:void 0}function y(t){if(typeof t!="string"&&typeof t!="number")return;const n=String(t);return n.length>0?n:void 0}function O(t){return t&&typeof t=="object"&&!Array.isArray(t)?t:void 0}function j(t){const n=P(t);if(!(n===void 0||n<=0))return Math.max(1,Math.trunc(n))}function nn(t){if(typeof t=="boolean")return t;if(t===0||t==="0")return!1;if(t===1||t==="1")return!0}function Ae(t){if(!Array.isArray(t))return;const n=t.map(P).filter(o=>o!==void 0);return n.length>0?n:void 0}function Rt(t){if(Array.isArray(t))return y(t[3]??t[2]??t[t.length-1]);if(t&&typeof t=="object"){const n=t;return y(n.absolute_ref??n.absoluteRef??n.handle??n.id??n.value)}return y(t)}function me(t){if(!Array.isArray(t)){const e=O(t),a=e?.rows??e?.values??e?.cells;return Array.isArray(a)?me(a):[]}const n=[];let o=0;for(const e of t){if(Array.isArray(e)){for(const r of e)n.push({cell:r,index:o++});continue}const a=O(e),i=a?.cells??a?.values;if(Array.isArray(i)&&!("text"in(a??{}))&&!("value"in(a??{}))){for(const r of i)n.push({cell:r,index:o++});continue}n.push({cell:e,index:o++})}return n}function io(t){let n=-1;for(const{cell:o}of t){const e=O(o),a=P(e?.column??e?.columnIndex??e?.col??e?.column_index);a!==void 0&&(n=Math.max(n,Math.trunc(a)))}return n>=0?n+1:0}function ro(t,n,o){const e=O(t),a=e?so(e):X(t),r=y(e?e.text??e.textValue??e.text_value??e.valueString??e.value_string??e.displayValue:t)??Se(a),s=Math.max(0,Math.trunc(P(e?.row??e?.rowIndex??e?.row_index)??(o>0?Math.floor(n/o):n))),_=Math.max(0,Math.trunc(P(e?.column??e?.columnIndex??e?.col??e?.column_index)??(o>0?n%o:0))),d=y(e?.attrText??e?.attributeText),c=d?{value:d}:_o(e?.blockAttributes??e?.attributes);return{row:s,column:_,text:r,...a!==void 0?{value:a}:{},...y(e?.dataType??e?.typeName)||P(e?.cellType??e?.type)!==void 0?{dataType:y(e?.dataType??e?.typeName)??P(e?.cellType??e?.type)}:{},...y(e?.formula??e?.expression)?{formula:y(e?.formula??e?.expression)}:{},...j(e?.rowSpan??e?.mergedHeight??e?.merged_height_flag)?{rowSpan:j(e?.rowSpan??e?.mergedHeight??e?.merged_height_flag)}:{},...j(e?.columnSpan??e?.mergedWidth??e?.merged_width_flag)?{columnSpan:j(e?.columnSpan??e?.mergedWidth??e?.merged_width_flag)}:{},...nn(e?.isMerged??e?.mergedValue??e?.is_merged_value)!==void 0?{isMerged:nn(e?.isMerged??e?.mergedValue??e?.is_merged_value)}:{},...P(e?.textHeight??e?.text_height)!==void 0?{textHeight:P(e?.textHeight??e?.text_height)}:{},...Rt(e?.blockTableRecordId??e?.blockRecordHandle??e?.block_handle)?{blockTableRecordId:Rt(e?.blockTableRecordId??e?.blockRecordHandle??e?.block_handle)}:{},...c&&Object.keys(c).length>0?{blockAttributes:c}:{}}}function so(t){const n=O(t.value),o=[n?.valueString,n?.value_string,n?.dataString,n?.data_string,n?.dataLong,n?.data_long,n?.dataDouble,n?.data_double,n?.dataDate,n?.data_date,n?.dataPoint,n?.data_point,n?.data3dPoint,n?.data_3dpoint,t.rawValue,t.value];for(const e of o){const a=X(e);if(a!==void 0&&a!=="")return a}}function Se(t){if(t==null)return"";if(typeof t=="string"||typeof t=="number"||typeof t=="boolean")return String(t);if(!Array.isArray(t)&&typeof t=="object"&&"x"in t&&"y"in t){const n=t;return[n.x,n.y,n.z].filter(o=>o!==void 0).join(", ")}try{return JSON.stringify(t)}catch{return String(t)}}function _o(t){const n=O(t);if(!n)return;const o={};for(const[e,a]of Object.entries(n)){const i=y(a)??Se(X(a));i&&(o[e]=i)}return Object.keys(o).length>0?o:void 0}function Ne(t){if(!t)return[];if(Array.isArray(t)){if(t.some(s=>O(s)?.appName!==void 0||O(s)?.applicationName!==void 0))return t.flatMap(s=>Ne(s));const r=t.map(Qt).filter(s=>!!s);return r.length>0?[{entries:r}]:[]}const n=O(t);if(!n)return[];const o=y(n.appName??n.applicationName??n.app),e=Array.isArray(n.customStrings)?n.customStrings.map(r=>({code:1e3,value:r})):void 0,a=n.entries??n.value??n.values??n.data??e;if(Array.isArray(a)){const r=a.map(Qt).filter(s=>!!s);return r.length>0?[{appName:o,entries:r}]:[]}if(o){const r=Qt(a);return r?[{appName:o,entries:[r]}]:[]}const i=[];for(const[r,s]of Object.entries(n)){const _=(Array.isArray(s)?s:[s]).map(Qt).filter(d=>!!d);_.length>0&&i.push({appName:r,entries:_})}return i}function Qt(t){const n=O(t);if(n){const e=X(n.value??n.data??n.text??n);return e===void 0?void 0:{...P(n.code??n.groupCode)!==void 0?{code:P(n.code??n.groupCode)}:{},...y(n.name??n.key)?{name:y(n.name??n.key)}:{},value:e}}const o=X(t);return o===void 0?void 0:{value:o}}function X(t,n=0,o=new WeakSet){if(t===null)return null;if(typeof t=="string"||typeof t=="boolean")return t;if(typeof t=="number")return Number.isFinite(t)?t:String(t);if(typeof t=="bigint")return t.toString();if(t instanceof Date)return t.toISOString();if(ArrayBuffer.isView(t))return Array.from(new Uint8Array(t.buffer,t.byteOffset,Math.min(t.byteLength,65536)));if(n>=12||!t||typeof t!="object")return;if(o.has(t))return"[Circular]";if(o.add(t),Array.isArray(t)){const a=t.slice(0,1e5).map(i=>X(i,n+1,o)).filter(i=>i!==void 0);return o.delete(t),a}const e={};for(const[a,i]of Object.entries(t).slice(0,1e4)){const r=X(i,n+1,o);r!==void 0&&(e[a]=r)}return o.delete(t),e}function co(t){return t.byteOffset===0&&t.byteLength===t.buffer.byteLength?t.buffer:t.slice().buffer}function lo(t,n){return{a:t.a*n.a+t.c*n.b,b:t.b*n.a+t.d*n.b,c:t.a*n.c+t.c*n.d,d:t.b*n.c+t.d*n.d,e:t.a*n.e+t.c*n.f+t.e,f:t.b*n.e+t.d*n.f+t.f}}function Eo(t){const n=Math.cos(t),o=Math.sin(t);return{a:n,b:o,c:-o,d:n,e:0,f:0}}const uo={AC1009:"R12",AC1012:"R13",AC1014:"R14",AC1015:"R2000",AC1018:"R2004",AC1021:"R2007",AC1024:"R2010",AC1027:"R2013",AC1032:"R2018"};function Ao(t){const n=Array.from(t.slice(0,6),e=>String.fromCharCode(e)).join(""),o=uo[n]??"UNKNOWN";return{signature:n,release:o,supportedByOpenDesignSpec:o!=="UNKNOWN",notes:o==="UNKNOWN"?"Unknown or unsupported DWG signature.":void 0}}const fe=new Map;async function fo(t,n={}){const o=performance.now(),e=n.sourceName;n.onProgress?.({phase:"detect",format:"dwg",message:"Validating DWG header…",total:t.byteLength});const a=Ao(t);if(!a.signature.startsWith("AC"))throw new Error(`Invalid DWG header: ${JSON.stringify(a.signature)}.`);n.onProgress?.({phase:"wasm-init",format:"dwg",message:"Initializing LibreDWG WebAssembly…",total:t.byteLength});const i=await De(n.wasmPath??"/wasm/");n.onProgress?.({phase:"parse",format:"dwg",message:`Decoding ${a.signature} DWG…`,total:t.byteLength,percent:35});const r=co(t),s=i.Dwg_File_Type?.DWG??0,_=i.instance.dwg_read_data(r,s);if(!_)throw new Error("LibreDWG returned an empty DWG result.");if(typeof _.error=="number"&&_.error!==0)throw new Error(`LibreDWG parse error code: ${_.error}.`);n.onProgress?.({phase:"normalize",format:"dwg",message:"Normalizing DWG database…",total:t.byteLength,percent:72});const d=typeof i.instance.convert=="function"?i.instance.convert(_):_;Po(d,To(i.instance,_,i.module.Dwg_Object_Type));try{typeof i.instance.dwg_free=="function"&&i.instance.dwg_free(_)}catch{}const c=ho(d,e,a,{keepRaw:!!n.keepRaw}),E=performance.now()-o;return n.onProgress?.({phase:"done",format:"dwg",message:"DWG loaded.",total:t.byteLength,percent:100,elapsedMs:E}),{document:c,raw:n.keepRaw?d:void 0,bytes:t.byteLength,elapsedMs:E,format:"dwg",warnings:c.warnings}}async function De(t="/wasm/"){const n=Bo(t);let o=fe.get(n);o||(o=Ee(()=>Promise.resolve().then(()=>Le),[]).then(async i=>{const r=i,s=r.LibreDwg;if(!s||typeof s.create!="function")throw new Error("@mlightcad/libredwg-web did not expose LibreDwg.create().");try{if(typeof r.createModule=="function"&&typeof s.createByWasmInstance=="function"){const _=Te(n),d=await Fo(_),c=await r.createModule({wasmBinary:d,locateFile:E=>new URL(E,Ge(n)).href});return Ie(s.createByWasmInstance(c))}return Ie(await s.create(n))}catch(_){const d=_ instanceof Error?_.message:String(_);throw new Error(`Failed to initialize LibreDWG WebAssembly from ${n}. Ensure libredwg-web.wasm is deployed at ${Te(n)} and wasmPath is resolved relative to the page, not the worker script. Run npm run copy:wasm before dev/build. Original error: ${d}`)}}),fe.set(n,o));const e=await o,a=await Ee(()=>Promise.resolve().then(()=>Le),void 0);return{module:a,instance:e,Dwg_File_Type:a.Dwg_File_Type}}function Ie(t){if(!t||t.__cadViewerCompatibilityGuards===!0)return t;const n=t.dwg_ptr_to_ltype_dash_array;if(typeof n=="function")try{t.dwg_ptr_to_ltype_dash_array=(...o)=>{const e=n.apply(t,o);return Array.isArray(e)?e.map(a=>({length:$(a,"length"),complex_shapecode:$(a,"complex_shapecode"),shape_flag:$(a,"shape_flag"),style:Io(t,$(a,"style")),scale:$(a,"scale"),rotation:$(a,"rotation"),x_offset:$(a,"x_offset"),y_offset:$(a,"y_offset"),text:$(a,"text")})):[]}}catch{}try{Object.defineProperty(t,"__cadViewerCompatibilityGuards",{value:!0})}catch{}return t}function $(t,n){if(!(!t||typeof t!="object"))try{return t[n]}catch{return}}function Io(t,n){if(n)try{return t.dwg_ref_get_absref?.(n)==null?void 0:n}catch{return}}function ho(t,n,o,e={}){const a=t&&typeof t=="object"?t:{},i=Go(a,e),r=Do(a),s=Mo(a,e,r),_=vo(a,e),d=Yo(a.header,o),c=No(s),E=Wo(a,d),u=E.view,l=Co(a),I=yo(a,l),f=Ro(a),A=mo(a),h=Array.isArray(a.entities)?a.entities:[],g={keepRaw:!!e.keepRaw,includeUnknownProperties:!!e.keepRaw},p=h.filter(R=>!!R&&typeof R=="object").map(R=>xe(R,g)),C=to({format:"dwg",sourceName:n,header:d,layers:i,lineTypes:s,blocks:_,entities:p,savedView:u,dictionaries:l,xrecords:I,dataLinks:f,dataTables:A,metadata:{parser:"@mlightcad/libredwg-web",parserMode:"wasm",version:o,savedView:u,requiredShxFonts:c,businessData:{dictionaries:l.length,xrecords:I.length,dataLinks:f.length,dataTables:A.length}},warnings:E.warning?[E.warning]:[],raw:e.keepRaw?t:void 0});if(p.length===0&&C.warnings.push("DWG parsed successfully but no model-space entities were exposed by the converter. Check layout/paper-space content or unsupported proxy objects."),[...new Set(Object.values(s))].some(R=>R.pattern.some(D=>Number(D.elementTypeFlag??0)!==0))){const R=c.length>0?` This drawing references external shape font${c.length===1?"":"s"} ${c.join(", ")}, whose outlines are not embedded in the DWG.`:"";C.warnings.push(`Complex SHX linetype glyphs are preserved in the normalized LTYPE definition and rendered as a dash/dot approximation.${R}`)}return C}function To(t,n,o){const e={DATALINK:[],DATATABLE:[],TABLECONTENT:[],TABLES:[]};if(!t||!n||!o)return e;e.DATALINK=xn(t,n,o.DWG_TYPE_DATALINK,(i,r)=>{const s=P(N(t,i,"year")),_=P(N(t,i,"month")),d=P(N(t,i,"day")),c=P(N(t,i,"hour")),E=P(N(t,i,"minute")),u=P(N(t,i,"seconds"));return jt({handle:r,dataAdapter:N(t,i,"data_adapter"),description:N(t,i,"description"),tooltip:N(t,i,"tooltip"),connectionString:N(t,i,"connection_string"),option:N(t,i,"option"),updateOption:N(t,i,"update_option"),pathOption:N(t,i,"path_option"),updateStatus:N(t,i,"update_status"),lastUpdated:go(s,_,d,c,E,u)})}),e.DATATABLE=xn(t,n,o.DWG_TYPE_DATATABLE,(i,r)=>jt({handle:r,flags:N(t,i,"flags"),rowCount:N(t,i,"num_rows"),columnCount:N(t,i,"num_cols"),tableName:N(t,i,"table_name"),columns:_t(N(t,i,"cols"))})),e.TABLECONTENT=xn(t,n,o.DWG_TYPE_TABLECONTENT,(i,r)=>jt({handle:r,linkedData:_t(N(t,i,"ldata")),linkedTableData:_t(N(t,i,"tdata")),formattedTableData:_t(N(t,i,"fdata")),tableStyle:W(N(t,i,"tablestyle"))}));const a=o.DWG_TYPE_TABLE;if(Number.isFinite(a)&&typeof t.dwg_getall_entities_in_model_space=="function")try{for(const i of t.dwg_getall_entities_in_model_space(n)??[]){if(t.dwg_object_get_fixedtype?.(i)!==a)continue;const r=t.dwg_object_to_entity?.(i),s=t.dwg_object_to_entity_tio?.(i);if(!r||!s)continue;const _=W(t.dwg_object_entity_get_handle_object?.(r)?.value);e.TABLES.push(jt({handle:_,linkedData:_t(N(t,s,"ldata")),linkedTableData:_t(N(t,s,"tdata")),formattedTableData:_t(N(t,s,"fdata"))}))}}catch{}return e}function Po(t,n){if(!t||typeof t!="object")return;const o=t,e=o.objects&&typeof o.objects=="object"?o.objects:o.objects={};for(const a of["DATALINK","DATATABLE","TABLECONTENT"]){if(n[a].length===0)continue;const i=Array.isArray(e[a])?e[a]:[];e[a]=po(i,n[a])}if(n.TABLES.length>0&&Array.isArray(o.entities)){const a=new Map(n.TABLES.map(i=>[y(i.handle)?.toLocaleUpperCase(),i]));for(const i of o.entities){if(!i||typeof i!="object")continue;const r=i,s=a.get(y(r.handle)?.toLocaleUpperCase());s&&Object.assign(r,s)}}}function xn(t,n,o,e){if(!Number.isFinite(o)||typeof t.dwg_getall_object_by_type!="function")return[];const a=[];try{for(const i of t.dwg_getall_object_by_type(n,o)??[]){const r=t.dwg_obj_generic_to_object?.(i),s=W(r?t.dwg_object_get_handle_object?.(r)?.value:void 0);a.push(e(i,s))}}catch{}return a}function N(t,n,o){try{return typeof t.dwg_dynapi_entity_data=="function"?t.dwg_dynapi_entity_data(n,o):t.dwg_dynapi_entity_value?.(n,o)?.data}catch{return}}function _t(t){if(!(!t||typeof t!="object"||typeof t=="number"))return X(t)}function W(t){if(typeof t=="bigint")return t.toString(16).toLocaleUpperCase();if(typeof t=="number"&&Number.isFinite(t))return Math.trunc(t).toString(16).toLocaleUpperCase();if(typeof t=="string")return t.trim()||void 0;if(Array.isArray(t))return W(t[3]??t[2]);if(t&&typeof t=="object"){const n=t;return W(n.absolute_ref??n.absoluteRef??n.value??n.handle)}}function jt(t){return Object.fromEntries(Object.entries(t).filter(([,n])=>n!=null&&n!==""))}function go(t,n,o,e=0,a=0,i=0){if(!t||!n||!o)return;const r=new Date(Date.UTC(t,n-1,o,e,a,i));return Number.isNaN(r.getTime())?void 0:r.toISOString()}function po(t,n){const o=[...t],e=new Map;o.forEach((a,i)=>{if(a&&typeof a=="object"){const r=W(a.handle);r&&e.set(r.toLocaleUpperCase(),i)}});for(const a of n){const i=W(a.handle),r=i?e.get(i.toLocaleUpperCase()):void 0;r===void 0?o.push(a):o[r]={...o[r],...a}}return o}function Co(t){const n=t.objects&&typeof t.objects=="object"?t.objects:void 0,o=[n?.DICTIONARY,n?.dictionary,t.dictionaries],e=[],a=new Set;for(const i of o)for(const r of dn(i)){if(!r||typeof r!="object")continue;const s=r,_=W(s.handle??s.id);if(_&&a.has(_.toLocaleUpperCase()))continue;const d=[];if(s.entries&&typeof s.entries=="object"&&!Array.isArray(s.entries))for(const[c,E]of Object.entries(s.entries)){const u=W(E);c&&u&&d.push({name:c,handle:u})}else if(Array.isArray(s.texts)&&Array.isArray(s.itemhandles))for(let c=0;c{const I=l&&typeof l=="object"?l:void 0,f=X(I?.value??I?.data??l);if(f===void 0)return[];const A=P(I?.code??I?.groupCode);return[{...A!==void 0?{code:A}:{},value:f}]}),E=W(_.ownerHandle??_.owner),u=Lo(d,E,n);a.push({handle:d,ownerHandle:E,entryName:y(_.entryName)??u.entryName,dictionaryPath:Array.isArray(_.dictionaryPath)?_.dictionaryPath.map(String):u.path,extensionDictionary:W(_.extensionDictionary),cloning:P(_.cloning),data:c}),d&&i.add(d.toLocaleUpperCase())}return a}function Lo(t,n,o){if(!t)return{};const e=new Map(o.filter(d=>d.handle).map(d=>[d.handle.toLocaleUpperCase(),d])),a=n?e.get(n.toLocaleUpperCase()):void 0,i=a?.entries.find(d=>d.handle.toLocaleUpperCase()===t.toLocaleUpperCase())?.name;if(!a)return{entryName:i};const r=i?[i]:[];let s=a;const _=new Set;for(;s.handle&&s.ownerHandle&&!_.has(s.handle.toLocaleUpperCase());){_.add(s.handle.toLocaleUpperCase());const d=e.get(s.ownerHandle.toLocaleUpperCase());if(!d)break;const c=d.entries.find(E=>E.handle.toLocaleUpperCase()===s.handle.toLocaleUpperCase())?.name;c&&r.unshift(c),s=d}return{entryName:i,path:r.length>0?r:void 0}}function Ro(t){const n=t.objects&&typeof t.objects=="object"?t.objects:void 0,o=[],e=new Set;for(const a of dn(n?.DATALINK??n?.dataLinks??t.dataLinks)){if(!a||typeof a!="object")continue;const i=a,r=W(i.handle??i.id);r&&e.has(r.toLocaleUpperCase())||(o.push({handle:r,dataAdapter:y(i.dataAdapter??i.data_adapter),description:y(i.description),tooltip:y(i.tooltip),connectionString:y(i.connectionString??i.connection_string),updateStatus:y(i.updateStatus??i.update_status),updateOption:P(i.updateOption??i.update_option),pathOption:P(i.pathOption??i.path_option),lastUpdated:y(i.lastUpdated??i.last_updated)}),r&&e.add(r.toLocaleUpperCase()))}return o}function mo(t){const n=t.objects&&typeof t.objects=="object"?t.objects:void 0,o=[n?.DATATABLE,n?.dataTables,t.dataTables,n?.TABLECONTENT],e=[],a=new Set;for(const i of o)for(const r of dn(i)){if(!r||typeof r!="object")continue;const s=r,_=W(s.handle??s.id);if(_&&a.has(_.toLocaleUpperCase()))continue;const d=ut(s.linkedData??s.ldata),c=ut(s.linkedTableData??s.tdata),E=So(s.columns??s.cols,c),u=Math.max(0,Math.trunc(P(s.rowCount??s.numRows??s.num_rows??c?.numRows??c?.num_rows)??0),...E.map(I=>I.values.length)),l=Math.max(E.length,Math.trunc(P(s.columnCount??s.numColumns??s.numCols??s.num_cols??c?.numColumns??c?.numCols??c?.num_cols)??0));E.length===0&&u===0&&l===0||(e.push({handle:_,name:y(s.tableName??s.table_name??s.name??d?.name??d?.description),rowCount:u,columnCount:l,columns:E}),_&&a.add(_.toLocaleUpperCase()))}return e}function So(t,n){const o=Array.isArray(t)?t:Array.isArray(n?.columns)?n.columns:Array.isArray(n?.cols)?n.cols:[];if(o.length>0)return o.map((r,s)=>{const _=ut(r),d=Array.isArray(_?.rows)?_.rows:Array.isArray(_?.values)?_.values:[];return{name:y(_?.name??_?.text)??`Column ${s+1}`,type:y(_?.type)??P(_?.type),values:d.map(he).filter(c=>c!==void 0)}});const a=(Array.isArray(n?.rows)?n.rows:[]).map(r=>{const s=ut(r);return Array.isArray(s?.cells)?s.cells:[]}),i=Math.max(0,...a.map(r=>r.length));return Array.from({length:i},(r,s)=>({name:`Column ${s+1}`,values:a.map(_=>he(_[s])).filter(_=>_!==void 0)}))}function he(t){const n=ut(t),e=(Array.isArray(n?.cellContents)?n.cellContents:Array.isArray(n?.cell_contents)?n.cell_contents:void 0)?.[0]??n?.value??t,a=ut(e);for(const i of[a?.valueString,a?.value_string,a?.dataString,a?.data_string,a?.dataLong,a?.data_long,a?.dataDouble,a?.data_double,a?.dataDate,a?.data_date,a?.dataPoint,a?.data_point,a?.data3dPoint,a?.data_3dpoint,e]){const r=X(i);if(r!=null&&r!==""&&!(typeof r=="object"&&!Array.isArray(r)&&Object.keys(r).length===0))return r}}function dn(t){if(!t)return[];if(Array.isArray(t))return t;if(typeof t!="object")return[];const n=t;for(const o of["entries","records","items","values"])if(Array.isArray(n[o]))return n[o];return Object.values(n).filter(o=>o&&typeof o=="object")}function ut(t){return t&&typeof t=="object"&&!Array.isArray(t)?t:void 0}function No(t){const n=new Set;for(const o of new Set(Object.values(t)))for(const e of o.pattern)Number(e.elementTypeFlag??0)!==0&&e.fontName&&n.add(e.fontName);return[...n].sort((o,e)=>o.localeCompare(e))}function Do(t){const n=t.tables,o=[t.STYLE,t.styles,n?.STYLE,n?.styles],e=new Map,a=new Set;for(const i of o)for(const r of xt(i)){if(!r||typeof r!="object")continue;const s=r,_=y(s.font??s.fontFileName??s.primaryFontFileName);if(!_||!/\.shx$/i.test(_))continue;((P(s.standardFlag??s.flag)??0)&1)===1&&a.add(_);for(const c of[s.handle,s.id,s.name]){const E=Ye(c);E&&e.set(E,_)}}return{byReference:e,shapeFonts:[...a]}}function xe(t,n){const o=Re(t,void 0,{...n,numericColorMode:"rgb"}),e=P(t.color);e!==void 0&&e>=0&&e<=16777215&&(o.color=e,o.trueColor=e),o.colorIndex=P(t.colorIndex??t.colorNumber??t.aci)??o.colorIndex,o.colorName=y(t.colorName??t.color_name)??o.colorName;const a=P(t.flag??t.flags);a!==void 0&&(o.flag=a);const i=String(t.type??t.entityType??"").toUpperCase(),r=/^(POLYLINE|POLYLINE_2D|POLYLINE2D|POLYLINE_3D|POLYLINE3D)$/.test(i);return(t.isClosed===!0||t.closed===!0||r&&(Number(a??0)&1)===1||i==="LWPOLYLINE"&&(Number(a??0)&512)===512)&&(o.isClosed=!0),o}function xo(t){return typeof t=="number"&&Number.isFinite(t)&&Math.abs(Math.trunc(t))>=1&&Math.abs(Math.trunc(t))<=255}function Yo(t,n){const o=t&&typeof t=="object"?{...t}:{};return n&&(o.dwgVersion=n),o}function Go(t,n){const o={},e=[],a=t.tables;for(const i of["LAYER","layer","layers"]){const r=t[i]??a?.[i];r&&e.push(r)}for(const i of e)for(const r of xt(i)){const s=r,_=y(s.name??s.layerName??s.entryName);if(!_)continue;const d=P(s.colorIndex??s.colorNumber),c=P(s.trueColor??s.true_color??s.truecolor??s.colorRGB??s.colorRgb??s.rgbColor??s.rgb),E=P(s.color),u=xo(d),l=c??(!u&&E!==void 0&&E>=0&&E<=16777215?E:void 0),I={name:_,color:u?void 0:s.color??s.colorName,colorIndex:d??(E!==void 0&&Math.abs(E)<=257?E:void 0),trueColor:l,lineType:y(s.lineType??s.linetype),lineweight:P(s.lineweight??s.lineWeight),isVisible:!(s.isVisible===!1||s.off===!0||Number(d??s.color??1)<0),isFrozen:!!(s.isFrozen??s.frozen),isLocked:!!(s.isLocked??s.locked),raw:n.keepRaw?s:void 0};eo(o,I)}return o}function Mo(t,n,o){const e={},a=t.tables,i=[t.LTYPE,t.ltype,t.lineTypes,a?.LTYPE,a?.ltype,a?.lineTypes];for(const r of i)for(const s of xt(r)){const _=s,d=y(_.name??_.lineTypeName??_.entryName);if(!d)continue;const E=(Array.isArray(_.pattern)?_.pattern:Array.isArray(_.dashes)?_.dashes:[]).flatMap(u=>{if(typeof u=="number")return[{length:u}];if(!u||typeof u!="object")return[];const l=u,I=P(l.elementLength??l.length??l.dashLength);if(I===void 0)return[];const f=P(l.elementTypeFlag),A=P(l.shapeNumber),h=f!==void 0&&A!==void 0,g=h?A:P(l.typeFlag??l.elementTypeFlag),p=y(l.styleObjectId??l.styleHandle??l.style),C=(p?o.byReference.get(Ye(p)):void 0)??((Number(g??0)&4)===4&&o.shapeFonts.length===1?o.shapeFonts[0]:void 0);return[{length:I,elementTypeFlag:g,shapeNumber:h?f:P(l.shapeCode??l.shapeNumber),scale:P(l.scale),rotation:P(l.rotation),offsetX:P(l.offsetX),offsetY:P(l.offsetY),text:y(l.text),styleHandle:p,fontName:C}]});ao(e,{name:d,handle:y(_.handle??_.id),description:y(_.description),totalPatternLength:P(_.totalPatternLength??_.patternLength),pattern:E,raw:n.keepRaw?_:void 0})}return e}function Ye(t){return String(t??"").trim().replace(/^0x/i,"").replace(/^0+(?=[0-9a-f])/i,"").toLowerCase()}function Wo(t,n){const o=t.tables,a=xt(t.VPORT??t.vports??o?.VPORT??o?.vports).filter(h=>!!h&&typeof h=="object").find(h=>String(h.name??"").trim().toLowerCase()==="*active"),i=a?"vport":"header-ucs",r=a??n,s=b(r.ucsOrigin??r.UCSORG??n.UCSORG),_=b(r.ucsXAxis??r.UCSXDIR??n.UCSXDIR),d=b(r.ucsYAxis??r.UCSYDIR??n.UCSYDIR),c=P(r.viewTwistAngle??r.twistAngle??r.VIEWTWIST)??0,E=b(r.viewDirectionFromTarget??r.viewDirection??r.direction??r.VIEWDIR),u=!Oo(s,_,d);if(!(!!a||!!E||u||Math.abs(c)>1e-12))return{};const I=wo(E),f=I?bo(s,_,d,c):{a:1,b:0,c:0,d:1,e:0,f:0},A={source:i,name:y(r.name),handle:y(r.handle),center:b(r.center??r.VIEWCTR),target:b(r.viewTarget??r.target??r.TARGET),direction:E,viewHeight:P(r.viewHeight??r.height??r.VIEWSIZE),aspectRatio:P(r.aspectRatio),twistAngle:c,ucsOrigin:s,ucsXAxis:_,ucsYAxis:d,sceneTransformApplied:I,sceneTransform:f};return I?{view:A}:{view:A,warning:"The saved CAD view has a missing, non-finite, or tilted VIEWDIR. File Viewer kept world coordinates instead of applying an unsafe 2D UCS/PLAN rotation."}}function bo(t,n,o,e){const a=Yn(n)??{x:1,y:0},i=Yn(o)??{x:-a.y,y:a.x},r=a.x*i.x+a.y*i.y,s=Yn({x:i.x-a.x*r,y:i.y-a.y*r})??{x:-a.y,y:a.x},_={a:a.x,b:s.x,c:a.y,d:s.y,e:t?-(t.x*a.x+t.y*a.y):0,f:t?-(t.x*s.x+t.y*s.y):0};return lo(Eo(Number.isFinite(e)?e:0),_)}function Yn(t){if(!t||Math.abs(Number(t.z??0))>1e-6)return;const n=Math.hypot(t.x,t.y);if(!(!Number.isFinite(n)||n<1e-12))return{x:t.x/n,y:t.y/n}}function wo(t){if(!t||!Number.isFinite(t.x)||!Number.isFinite(t.y)||!Number.isFinite(t.z))return!1;const n=Math.hypot(t.x,t.y,Number(t.z));return!Number.isFinite(n)||n<1e-12?!1:Math.hypot(t.x,t.y)/n<=1e-4&&Math.abs(Number(t.z))/n>=1-1e-8}function Oo(t,n,o){const e=(s,_)=>Number.isFinite(s)&&Math.abs(Number(s)-_)<=1e-10,a=!t||e(t.x,0)&&e(t.y,0)&&e(t.z??0,0),i=!n||e(n.x,1)&&e(n.y,0)&&e(n.z??0,0),r=!o||e(o.x,0)&&e(o.y,1)&&e(o.z??0,0);return a&&i&&r}function vo(t,n){const o={},e=[t.blocks,t.blockHeaders,t.block_records,t.blockRecords],a=t.tables;e.push(a?.BLOCK,a?.BLOCK_RECORD,a?.blocks);const i={keepRaw:!!n.keepRaw,includeUnknownProperties:!!n.keepRaw};for(const r of e)for(const s of xt(r)){const _=s,d=y(_.name??_.blockName??_.name2);if(!d)continue;const E=(Array.isArray(_.entities)?_.entities:Array.isArray(_.ownedObjects)?_.ownedObjects:[]).filter(u=>!!u&&typeof u=="object").map(u=>xe(u,i));oo(o,{name:d,basePoint:b(_.basePoint??_.origin)??{x:0,y:0},entities:E,raw:n.keepRaw?_:void 0})}return o}function xt(t){if(!t)return[];if(Array.isArray(t))return t;if(typeof t!="object")return[];const n=t,o=Object.values(n).filter(a=>a&&typeof a=="object"),e=["entries","records","items","values","layers","blocks"].flatMap(a=>Array.isArray(n[a])?n[a]:[]);return e.length>0?e:o}function Bo(t){const n=t.trim()||"/wasm";if(n==="/")return Pe();const o=n.replace(/\/+$/,"");return ko(o)?o:o.startsWith("/")?`${Pe()}${o}`:new URL(o,Me()).href.replace(/\/+$/,"")}function Te(t){return new URL("libredwg-web.wasm",Ge(t)).href}function Ge(t){return t.endsWith("/")?t:`${t}/`}function ko(t){return/^[a-z][a-z0-9+.-]*:/i.test(t)}function Me(){return typeof document<"u"&&document.baseURI?document.baseURI:typeof location<"u"&&location.href?location.href:"http://localhost/"}function Pe(){return typeof location<"u"&&location.origin?location.origin:new URL(Me()).origin}async function Fo(t){if(typeof fetch!="function")throw new Error("fetch() is not available, so the LibreDWG WASM binary cannot be loaded.");const n=await fetch(t,{credentials:"same-origin",cache:"force-cache"});if(!n.ok)throw new Error(`WASM asset request failed with HTTP ${n.status} for ${t}.`);const o=new Uint8Array(await n.arrayBuffer());if(!Uo(o)){const e=Vo(o),a=n.headers.get("content-type")??"unknown content-type";throw new Error(`Invalid WASM asset at ${t}. Expected bytes 00 61 73 6d, got ${Ho(o)} (${a}). The server probably returned an HTML fallback page instead of libredwg-web.wasm. Response preview: ${e}`)}return o}function Uo(t){return t.length>=4&&t[0]===0&&t[1]===97&&t[2]===115&&t[3]===109}function Ho(t){return Array.from(t.slice(0,4)).map(n=>n.toString(16).padStart(2,"0")).join(" ")}function Vo(t){const n=Array.from(t.slice(0,32)).map(o=>o>=32&&o<=126?String.fromCharCode(o):".").join("");return JSON.stringify(n)}const Et=self;Et.postMessage({type:"ready"});Et.onmessage=async t=>{const n=t.data;if(!n)return;const o=performance.now(),e=a=>{Et.postMessage({type:"progress",requestId:n.requestId,progress:a})};try{if(n.type==="warmup"){e({phase:"wasm-init",format:"dwg",message:"Warming LibreDWG WebAssembly instance…"}),await De(n.options?.wasmPath??"/wasm/"),Et.postMessage({type:"warmup-result",requestId:n.requestId,elapsedMs:performance.now()-o});return}if(n.type!=="load")return;e({phase:"worker-ready",format:"dwg",message:"DWG worker received file bytes.",total:n.bytes.byteLength});const a=await fo(new Uint8Array(n.bytes),{...n.options,sourceName:n.fileName,keepRaw:!!n.options?.keepRaw,onProgress:e});a.document.metadata.loaderMode="worker",a.elapsedMs=performance.now()-o,Et.postMessage({type:"result",requestId:n.requestId,result:a})}catch(a){const i=Xo(a);Et.postMessage({type:"error",requestId:n.requestId,error:i})}};function Xo(t){return t instanceof Error?{name:t.name,message:t.message,stack:t.stack}:{message:String(t)}}var Wn;(function(t){t[t.CP_UTF8=0]="CP_UTF8",t[t.CP_US_ASCII=1]="CP_US_ASCII",t[t.CP_ISO_8859_1=2]="CP_ISO_8859_1",t[t.CP_ISO_8859_2=3]="CP_ISO_8859_2",t[t.CP_ISO_8859_3=4]="CP_ISO_8859_3",t[t.CP_ISO_8859_4=5]="CP_ISO_8859_4",t[t.CP_ISO_8859_5=6]="CP_ISO_8859_5",t[t.CP_ISO_8859_6=7]="CP_ISO_8859_6",t[t.CP_ISO_8859_7=8]="CP_ISO_8859_7",t[t.CP_ISO_8859_8=9]="CP_ISO_8859_8",t[t.CP_ISO_8859_9=10]="CP_ISO_8859_9",t[t.CP_CP437=11]="CP_CP437",t[t.CP_CP850=12]="CP_CP850",t[t.CP_CP852=13]="CP_CP852",t[t.CP_CP855=14]="CP_CP855",t[t.CP_CP857=15]="CP_CP857",t[t.CP_CP860=16]="CP_CP860",t[t.CP_CP861=17]="CP_CP861",t[t.CP_CP863=18]="CP_CP863",t[t.CP_CP864=19]="CP_CP864",t[t.CP_CP865=20]="CP_CP865",t[t.CP_CP869=21]="CP_CP869",t[t.CP_CP932=22]="CP_CP932",t[t.CP_MACINTOSH=23]="CP_MACINTOSH",t[t.CP_BIG5=24]="CP_BIG5",t[t.CP_CP949=25]="CP_CP949",t[t.CP_JOHAB=26]="CP_JOHAB",t[t.CP_CP866=27]="CP_CP866",t[t.CP_ANSI_1250=28]="CP_ANSI_1250",t[t.CP_ANSI_1251=29]="CP_ANSI_1251",t[t.CP_ANSI_1252=30]="CP_ANSI_1252",t[t.CP_GB2312=31]="CP_GB2312",t[t.CP_ANSI_1253=32]="CP_ANSI_1253",t[t.CP_ANSI_1254=33]="CP_ANSI_1254",t[t.CP_ANSI_1255=34]="CP_ANSI_1255",t[t.CP_ANSI_1256=35]="CP_ANSI_1256",t[t.CP_ANSI_1257=36]="CP_ANSI_1257",t[t.CP_ANSI_874=37]="CP_ANSI_874",t[t.CP_ANSI_932=38]="CP_ANSI_932",t[t.CP_ANSI_936=39]="CP_ANSI_936",t[t.CP_ANSI_949=40]="CP_ANSI_949",t[t.CP_ANSI_950=41]="CP_ANSI_950",t[t.CP_ANSI_1361=42]="CP_ANSI_1361",t[t.CP_UTF16=43]="CP_UTF16",t[t.CP_ANSI_1258=44]="CP_ANSI_1258",t[t.CP_UNDEFINED=255]="CP_UNDEFINED"})(Wn||(Wn={}));const Ko=["utf-8","utf-8","iso-8859-1","iso-8859-2","iso-8859-3","iso-8859-4","iso-8859-5","iso-8859-6","iso-8859-7","iso-8859-8","iso-8859-9","utf-8","utf-8","utf-8","utf-8","utf-8","utf-8","utf-8","utf-8","utf-8","utf-8","utf-8","shift-jis","macintosh","big5","utf-8","utf-8","ibm866","windows-1250","windows-1251","windows-1252","gbk","windows-1253","windows-1254","windows-1255","windows-1256","windows-1257","windows-874","shift-jis","gbk","euc-kr","big5","utf-8","utf-16le","windows-1258"],bn=t=>Ko[t];var wn;(function(t){t[t.Rotated=0]="Rotated",t[t.Aligned=1]="Aligned",t[t.Angular=2]="Angular",t[t.Diameter=3]="Diameter",t[t.Radius=4]="Radius",t[t.Angular3Point=5]="Angular3Point",t[t.Ordinate=6]="Ordinate",t[t.ReferenceIsExclusive=32]="ReferenceIsExclusive",t[t.IsOrdinateXTypeFlag=64]="IsOrdinateXTypeFlag",t[t.IsCustomTextPositionFlag=128]="IsCustomTextPositionFlag"})(wn||(wn={}));var J;(function(t){t[t.TopLeft=1]="TopLeft",t[t.TopCenter=2]="TopCenter",t[t.TopRight=3]="TopRight",t[t.MiddleLeft=4]="MiddleLeft",t[t.MiddleCenter=5]="MiddleCenter",t[t.MiddleRight=6]="MiddleRight",t[t.BottomLeft=7]="BottomLeft",t[t.BottomCenter=8]="BottomCenter",t[t.BottomRight=9]="BottomRight"})(J||(J={}));var On;(function(t){t[t.AtLeast=1]="AtLeast",t[t.Exact=2]="Exact"})(On||(On={}));var vn;(function(t){t[t.Center=0]="Center",t[t.Above=1]="Above",t[t.Outside=2]="Outside",t[t.JIS=3]="JIS",t[t.Below=4]="Below"})(vn||(vn={}));var Bn;(function(t){t[t.Feet=0]="Feet",t[t.None=1]="None",t[t.Inch=2]="Inch",t[t.FeetAndInch=3]="FeetAndInch",t[t.Leading=4]="Leading",t[t.Trailing=8]="Trailing",t[t.LeadingAndTrailing=12]="LeadingAndTrailing"})(Bn||(Bn={}));var kn;(function(t){t[t.None=0]="None",t[t.Leading=1]="Leading",t[t.Trailing=2]="Trailing",t[t.LeadingAndTrailing=3]="LeadingAndTrailing"})(kn||(kn={}));var Fn;(function(t){t[t.Center=0]="Center",t[t.Left=1]="Left",t[t.Right=2]="Right",t[t.OverFirst=3]="OverFirst",t[t.OverSecond=4]="OverSecond"})(Fn||(Fn={}));var Un;(function(t){t[t.Bottom=0]="Bottom",t[t.Center=1]="Center",t[t.Top=2]="Top"})(Un||(Un={}));var mt;(function(t){t[t.PatternFill=0]="PatternFill",t[t.SolidFill=1]="SolidFill"})(mt||(mt={}));var St;(function(t){t[t.NonAssociative=0]="NonAssociative",t[t.Associative=1]="Associative"})(St||(St={}));var Hn;(function(t){t[t.Normal=0]="Normal",t[t.Outer=1]="Outer",t[t.Ignore=2]="Ignore"})(Hn||(Hn={}));var Vn;(function(t){t[t.UserDefined=0]="UserDefined",t[t.Predefined=1]="Predefined",t[t.Custom=2]="Custom"})(Vn||(Vn={}));var Xn;(function(t){t[t.NotAnnotated=0]="NotAnnotated",t[t.Annotated=1]="Annotated"})(Xn||(Xn={}));var en;(function(t){t[t.Solid=0]="Solid",t[t.Gradient=1]="Gradient"})(en||(en={}));var Nt;(function(t){t[t.TwoColor=0]="TwoColor",t[t.OneColor=1]="OneColor"})(Nt||(Nt={}));var Kn;(function(t){t[t.Default=0]="Default",t[t.External=1]="External",t[t.Polyline=2]="Polyline",t[t.Derived=4]="Derived",t[t.Textbox=8]="Textbox",t[t.Outermost=16]="Outermost"})(Kn||(Kn={}));var dt;(function(t){t[t.Line=1]="Line",t[t.Circular=2]="Circular",t[t.Elliptic=3]="Elliptic",t[t.Spline=4]="Spline"})(dt||(dt={}));var zn;(function(t){t[t.CLOSED_POLYLINE=1]="CLOSED_POLYLINE",t[t.CURVE_FIT=2]="CURVE_FIT",t[t.SPLINE_FIT=4]="SPLINE_FIT",t[t.POLYLINE_3D=8]="POLYLINE_3D",t[t.POLYGON_3D=16]="POLYGON_3D",t[t.CLOSED_POLYGON=32]="CLOSED_POLYGON",t[t.POLYFACE=64]="POLYFACE",t[t.CONTINUOUS=128]="CONTINUOUS"})(zn||(zn={}));var $n;(function(t){t[t.NONE=0]="NONE",t[t.QUADRATIC=5]="QUADRATIC",t[t.CUBIC=6]="CUBIC",t[t.BEZIER=8]="BEZIER"})($n||($n={}));var Jn;(function(t){t[t.Dwg=0]="Dwg",t[t.Dxf=1]="Dxf"})(Jn||(Jn={}));var Zn;(function(t){t[t.NONE=0]="NONE",t[t.MIRRORED_X=2]="MIRRORED_X",t[t.MIRRORED_Y=4]="MIRRORED_Y"})(Zn||(Zn={}));var Dt;(function(t){t[t.LEFT=0]="LEFT",t[t.CENTER=1]="CENTER",t[t.RIGHT=2]="RIGHT",t[t.ALIGNED=3]="ALIGNED",t[t.MIDDLE=4]="MIDDLE",t[t.FIT=5]="FIT"})(Dt||(Dt={}));var qn;(function(t){t[t.BASELINE=0]="BASELINE",t[t.BOTTOM=1]="BOTTOM",t[t.MIDDLE=2]="MIDDLE",t[t.TOP=3]="TOP"})(qn||(qn={}));const We=Object.freeze(["ACADMAINTVER","ACADVER","ANGBASE","ANGDIR","ATTMODE","AUNITS","AUPREC","CECOLOR","CELTSCALE","CELTYPE","CELWEIGHT","CEPSNID","CEPSNTYPE","CHAMFERA","CHAMFERB","CHAMFERC","CHAMFERD","CLAYER","CMLJUST","CMLSCALE","CMLSTYLE","CSHADOW","DIMADEC","DIMALT","DIMALTD","DIMALTF","DIMALTRND","DIMALTTD","DIMALTTZ","DIMALTU","DIMALTZ","DIMAPOST","DIMASO","DIMASSOC","DIMASZ","DIMATFIT","DIMAUNIT","DIMAZIN","DIMBLK","DIMBLK1","DIMBLK2","DIMCEN","DIMCLRD","DIMCLRE","DIMCLRT","DIMDEC","DIMDLE","DIMDLI","DIMDSEP","DIMEXE","DIMEXO","DIMFAC","DIMGAP","DIMJUST","DIMLDRBLK","DIMLFAC","DIMLIM","DIMLUNIT","DIMLWD","DIMLWE","DIMPOST","DIMRND","DIMSAH","DIMSCALE","DIMSD1","DIMSD2","DIMSE1","DIMSE2","DIMSHO","DIMSOXD","DIMSTYLE","DIMTAD","DIMTDEC","DIMTFAC","DIMTIH","DIMTIX","DIMTM","DIMTMOVE","DIMTOFL","DIMTOH","DIMTOL","DIMTOLJ","DIMTP","DIMTSZ","DIMTVP","DIMTXSTY","DIMTXT","DIMTZIN","DIMUPT","DIMZIN","DISPSILH","DRAGVS","DWGCODEPAGE","ELEVATION","ENDCAPS","EXTMAX","EXTMIN","EXTNAMES","FILLETRAD","FILLMODE","FINGERPRINTGUID","HALOGAP","HANDSEED","HIDETEXT","HYPERLINKBASE","INDEXCTL","INSBASE","INSUNITS","INTERFERECOLOR","INTERFEREOBJVS","INTERFEREVPVS","INTERSECTIONCOLOR","INTERSECTIONDISPLAY","JOINSTYLE","LIMCHECK","LIMMAX","LIMMIN","LTSCALE","LUNITS","LUPREC","LWDISPLAY","MAXACTVP","MEASUREMENT","MENU","MIRRTEXT","OBSCOLOR","OBSLTYPE","ORTHOMODE","PDMODE","PDSIZE","PELEVATION","PEXTMAX","PEXTMIN","PINSBASE","PLIMCHECK","PLIMMAX","PLIMMIN","PLINEGEN","PLINEWID","PROJECTNAME","PROXYGRAPHICS","PSLTSCALE","PSTYLEMODE","PSVPSCALE","PUCSBASE","PUCSNAME","PUCSORG","PUCSORGBACK","PUCSORGBOTTOM","PUCSORGFRONT","PUCSORGLEFT","PUCSORGRIGHT","PUCSORGTOP","PUCSORTHOREF","PUCSORTHOVIEW","PUCSXDIR","PUCSYDIR","QTEXTMODE","REGENMODE","SHADEDGE","SHADEDIF","SHADOWPLANELOCATION","SKETCHINC","SKPOLY","SORTENTS","SPLINESEGS","SPLINETYPE","SURFTAB1","SURFTAB2","SURFTYPE","SURFU","SURFV","TDCREATE","TDINDWG","TDUCREATE","TDUPDATE","TDUSRTIMER","TDUUPDATE","TEXTSIZE","TEXTSTYLE","THICKNESS","TILEMODE","TRACEWID","TREEDEPTH","UCSBASE","UCSNAME","UCSORG","UCSORGBACK","UCSORGBOTTOM","UCSORGFRONT","UCSORGLEFT","UCSORGRIGHT","UCSORGTOP","UCSORTHOREF","UCSORTHOVIEW","UCSXDIR","UCSYDIR","UNITMODE","USERI1","USERI2","USERI3","USERI4","USERI5","USERR1","USERR2","USERR3","USERR4","USERR5","USRTIMER","VERSIONGUID","VISRETAIN","WORLDVIEW","XCLIPFRAME","XEDIT"]);var Qn;(function(t){t[t.NotApplicable=0]="NotApplicable",t[t.KeepExisting=1]="KeepExisting",t[t.UseClone=2]="UseClone",t[t.XrefName=3]="XrefName",t[t.Name=4]="Name",t[t.UnmangleName=5]="UnmangleName"})(Qn||(Qn={}));const be=[{type:"invalid",hdr:"INVALI",description:"No DWG",version:0},{type:"r1.1",hdr:"MC0.0",description:"MicroCAD Release 1.1",version:0},{type:"r1.2",hdr:"AC1.2",description:"AutoCAD Release 1.2",version:0},{type:"r1.3",hdr:"AC1.3",description:"AutoCAD Release 1.3",version:1},{type:"r1.4",hdr:"AC1.40",description:"AutoCAD Release 1.4",version:2},{type:"r2.0b",hdr:"AC1.50",description:"AutoCAD 2.0 beta",version:3},{type:"r2.0",hdr:"AC1.50",description:"AutoCAD Release 2.0",version:4},{type:"r2.10",hdr:"AC2.10",description:"AutoCAD Release 2.10",version:5},{type:"r2.21",hdr:"AC2.21",description:"AutoCAD Release 2.21",version:6},{type:"r2.22",hdr:"AC2.22",description:"AutoCAD Release 2.22",version:7},{type:"r2.4",hdr:"AC1001",description:"AutoCAD Release 2.4",version:8},{type:"r2.5",hdr:"AC1002",description:"AutoCAD Release 2.5",version:9},{type:"r2.6",hdr:"AC1003",description:"AutoCAD Release 2.6",version:10},{type:"r9",hdr:"AC1004",description:"AutoCAD Release 9",version:11},{type:"r9c1",hdr:"AC1005",description:"AutoCAD Release 9c1",version:12},{type:"r10",hdr:"AC1006",description:"AutoCAD Release 10",version:13},{type:"r11b1",hdr:"AC1007",description:"AutoCAD 11 beta 1",version:14},{type:"r11b2",hdr:"AC1008",description:"AutoCAD 11 beta 2",version:15},{type:"r11",hdr:"AC1009",description:"AutoCAD Release 11/12 (LT R1/R2)",version:16},{type:"r13b1",hdr:"AC1010",description:"AutoCAD pre-R13 a",version:17},{type:"r13b2",hdr:"AC1011",description:"AutoCAD pre-R13 b",version:18},{type:"r13",hdr:"AC1012",description:"AutoCAD Release 13",version:19},{type:"r13c3",hdr:"AC1013",description:"AutoCAD Release 13c3",version:20},{type:"r14",hdr:"AC1014",description:"AutoCAD Release 14",version:21},{type:"r2000b",hdr:"AC1500",description:"AutoCAD 2000 beta",version:22},{type:"r2000",hdr:"AC1015",description:"AutoCAD Release 2000",version:23},{type:"r2000i",hdr:"AC1016",description:"AutoCAD Release 2000i",version:23},{type:"r2002",hdr:"AC1017",description:"AutoCAD Release 2002",version:23},{type:"r2004a",hdr:"AC402a",description:"AutoCAD 2004 alpha a",version:24},{type:"r2004b",hdr:"AC402b",description:"AutoCAD 2004 alpha b",version:24},{type:"r2004c",hdr:"AC1018",description:"AutoCAD 2004 beta",version:24},{type:"r2004",hdr:"AC1018",description:"AutoCAD Release 2004",version:25},{type:"r2007a",hdr:"AC701a",description:"AutoCAD 2007 alpha",version:26},{type:"r2007b",hdr:"AC1021",description:"AutoCAD 2007 beta",version:26},{type:"r2007",hdr:"AC1021",description:"AutoCAD Release 2007",version:27},{type:"r2010b",hdr:"AC1024",description:"AutoCAD 2010 beta",version:28},{type:"r2010",hdr:"AC1024",description:"AutoCAD Release 2010",version:29},{type:"r2013b",hdr:"AC1027",description:"AutoCAD 2013 beta",version:30},{type:"r2013",hdr:"AC1027",description:"AutoCAD Release 2013",version:31},{type:"r2018b",hdr:"AC1032",description:"AutoCAD 2018 beta",version:32},{type:"r2018",hdr:"AC1032",description:"AutoCAD Release 2018",version:33},{type:"r2022b",hdr:"AC103-4",description:"AutoCAD 2022 beta",version:36},{type:"r>2022",hdr:"",description:"AutoCAD Release >2022",version:0}];async function jn(t={}){const n=zo(t),o=await import(n),e=o.default??o.createModule??o;if(typeof e!="function")throw new Error(`LibreDWG runtime module at ${n} did not export a createModule function.`);return e(t)}function zo(t){const n=t.locateFile;if(typeof n=="function")try{const o=n("libredwg-web.js","");if(typeof o=="string"&&o.length>0)return o}catch{}return typeof document<"u"&&document.baseURI?new URL("wasm/libredwg-web.js",document.baseURI).href:typeof location<"u"&&location.href?new URL("/wasm/libredwg-web.js",location.origin).href:"/wasm/libredwg-web.js"}var on;(function(t){t[t.DWG_SUPERTYPE_ENTITY=0]="DWG_SUPERTYPE_ENTITY",t[t.DWG_SUPERTYPE_OBJECT=1]="DWG_SUPERTYPE_OBJECT"})(on||(on={}));var an;(function(t){t[t.WRONGCRC=1]="WRONGCRC",t[t.NOTYETSUPPORTED=2]="NOTYETSUPPORTED",t[t.UNHANDLEDCLASS=4]="UNHANDLEDCLASS",t[t.INVALIDTYPE=8]="INVALIDTYPE",t[t.INVALIDHANDLE=16]="INVALIDHANDLE",t[t.INVALIDEED=32]="INVALIDEED",t[t.VALUEOUTOFBOUNDS=64]="VALUEOUTOFBOUNDS",t[t.CLASSESNOTFOUND=128]="CLASSESNOTFOUND",t[t.SECTIONNOTFOUND=256]="SECTIONNOTFOUND",t[t.PAGENOTFOUND=512]="PAGENOTFOUND",t[t.INTERNALERROR=1024]="INTERNALERROR",t[t.INVALIDDWG=2048]="INVALIDDWG",t[t.IOERROR=4096]="IOERROR",t[t.OUTOFMEM=8192]="OUTOFMEM"})(an||(an={}));var T;(function(t){t[t.DWG_TYPE_UNUSED=0]="DWG_TYPE_UNUSED",t[t.DWG_TYPE_TEXT=1]="DWG_TYPE_TEXT",t[t.DWG_TYPE_ATTRIB=2]="DWG_TYPE_ATTRIB",t[t.DWG_TYPE_ATTDEF=3]="DWG_TYPE_ATTDEF",t[t.DWG_TYPE_BLOCK=4]="DWG_TYPE_BLOCK",t[t.DWG_TYPE_ENDBLK=5]="DWG_TYPE_ENDBLK",t[t.DWG_TYPE_SEQEND=6]="DWG_TYPE_SEQEND",t[t.DWG_TYPE_INSERT=7]="DWG_TYPE_INSERT",t[t.DWG_TYPE_MINSERT=8]="DWG_TYPE_MINSERT",t[t.DWG_TYPE_VERTEX_2D=10]="DWG_TYPE_VERTEX_2D",t[t.DWG_TYPE_VERTEX_3D=11]="DWG_TYPE_VERTEX_3D",t[t.DWG_TYPE_VERTEX_MESH=12]="DWG_TYPE_VERTEX_MESH",t[t.DWG_TYPE_VERTEX_PFACE=13]="DWG_TYPE_VERTEX_PFACE",t[t.DWG_TYPE_VERTEX_PFACE_FACE=14]="DWG_TYPE_VERTEX_PFACE_FACE",t[t.DWG_TYPE_POLYLINE_2D=15]="DWG_TYPE_POLYLINE_2D",t[t.DWG_TYPE_POLYLINE_3D=16]="DWG_TYPE_POLYLINE_3D",t[t.DWG_TYPE_ARC=17]="DWG_TYPE_ARC",t[t.DWG_TYPE_CIRCLE=18]="DWG_TYPE_CIRCLE",t[t.DWG_TYPE_LINE=19]="DWG_TYPE_LINE",t[t.DWG_TYPE_DIMENSION_ORDINATE=20]="DWG_TYPE_DIMENSION_ORDINATE",t[t.DWG_TYPE_DIMENSION_LINEAR=21]="DWG_TYPE_DIMENSION_LINEAR",t[t.DWG_TYPE_DIMENSION_ALIGNED=22]="DWG_TYPE_DIMENSION_ALIGNED",t[t.DWG_TYPE_DIMENSION_ANG3PT=23]="DWG_TYPE_DIMENSION_ANG3PT",t[t.DWG_TYPE_DIMENSION_ANG2LN=24]="DWG_TYPE_DIMENSION_ANG2LN",t[t.DWG_TYPE_DIMENSION_RADIUS=25]="DWG_TYPE_DIMENSION_RADIUS",t[t.DWG_TYPE_DIMENSION_DIAMETER=26]="DWG_TYPE_DIMENSION_DIAMETER",t[t.DWG_TYPE_POINT=27]="DWG_TYPE_POINT",t[t.DWG_TYPE_3DFACE=28]="DWG_TYPE_3DFACE",t[t.DWG_TYPE_POLYLINE_PFACE=29]="DWG_TYPE_POLYLINE_PFACE",t[t.DWG_TYPE_POLYLINE_MESH=30]="DWG_TYPE_POLYLINE_MESH",t[t.DWG_TYPE_SOLID=31]="DWG_TYPE_SOLID",t[t.DWG_TYPE_TRACE=32]="DWG_TYPE_TRACE",t[t.DWG_TYPE_SHAPE=33]="DWG_TYPE_SHAPE",t[t.DWG_TYPE_VIEWPORT=34]="DWG_TYPE_VIEWPORT",t[t.DWG_TYPE_ELLIPSE=35]="DWG_TYPE_ELLIPSE",t[t.DWG_TYPE_SPLINE=36]="DWG_TYPE_SPLINE",t[t.DWG_TYPE_REGION=37]="DWG_TYPE_REGION",t[t.DWG_TYPE_3DSOLID=38]="DWG_TYPE_3DSOLID",t[t.DWG_TYPE_BODY=39]="DWG_TYPE_BODY",t[t.DWG_TYPE_RAY=40]="DWG_TYPE_RAY",t[t.DWG_TYPE_XLINE=41]="DWG_TYPE_XLINE",t[t.DWG_TYPE_DICTIONARY=42]="DWG_TYPE_DICTIONARY",t[t.DWG_TYPE_OLEFRAME=43]="DWG_TYPE_OLEFRAME",t[t.DWG_TYPE_MTEXT=44]="DWG_TYPE_MTEXT",t[t.DWG_TYPE_LEADER=45]="DWG_TYPE_LEADER",t[t.DWG_TYPE_TOLERANCE=46]="DWG_TYPE_TOLERANCE",t[t.DWG_TYPE_MLINE=47]="DWG_TYPE_MLINE",t[t.DWG_TYPE_BLOCK_CONTROL=48]="DWG_TYPE_BLOCK_CONTROL",t[t.DWG_TYPE_BLOCK_HEADER=49]="DWG_TYPE_BLOCK_HEADER",t[t.DWG_TYPE_LAYER_CONTROL=50]="DWG_TYPE_LAYER_CONTROL",t[t.DWG_TYPE_LAYER=51]="DWG_TYPE_LAYER",t[t.DWG_TYPE_STYLE_CONTROL=52]="DWG_TYPE_STYLE_CONTROL",t[t.DWG_TYPE_STYLE=53]="DWG_TYPE_STYLE",t[t.DWG_TYPE_LTYPE_CONTROL=56]="DWG_TYPE_LTYPE_CONTROL",t[t.DWG_TYPE_LTYPE=57]="DWG_TYPE_LTYPE",t[t.DWG_TYPE_VIEW_CONTROL=60]="DWG_TYPE_VIEW_CONTROL",t[t.DWG_TYPE_VIEW=61]="DWG_TYPE_VIEW",t[t.DWG_TYPE_UCS_CONTROL=62]="DWG_TYPE_UCS_CONTROL",t[t.DWG_TYPE_UCS=63]="DWG_TYPE_UCS",t[t.DWG_TYPE_VPORT_CONTROL=64]="DWG_TYPE_VPORT_CONTROL",t[t.DWG_TYPE_VPORT=65]="DWG_TYPE_VPORT",t[t.DWG_TYPE_APPID_CONTROL=66]="DWG_TYPE_APPID_CONTROL",t[t.DWG_TYPE_APPID=67]="DWG_TYPE_APPID",t[t.DWG_TYPE_DIMSTYLE_CONTROL=68]="DWG_TYPE_DIMSTYLE_CONTROL",t[t.DWG_TYPE_DIMSTYLE=69]="DWG_TYPE_DIMSTYLE",t[t.DWG_TYPE_VX_CONTROL=70]="DWG_TYPE_VX_CONTROL",t[t.DWG_TYPE_VX_TABLE_RECORD=71]="DWG_TYPE_VX_TABLE_RECORD",t[t.DWG_TYPE_GROUP=72]="DWG_TYPE_GROUP",t[t.DWG_TYPE_MLINESTYLE=73]="DWG_TYPE_MLINESTYLE",t[t.DWG_TYPE_OLE2FRAME=74]="DWG_TYPE_OLE2FRAME",t[t.DWG_TYPE_DUMMY=75]="DWG_TYPE_DUMMY",t[t.DWG_TYPE_LONG_TRANSACTION=76]="DWG_TYPE_LONG_TRANSACTION",t[t.DWG_TYPE_LWPOLYLINE=77]="DWG_TYPE_LWPOLYLINE",t[t.DWG_TYPE_HATCH=78]="DWG_TYPE_HATCH",t[t.DWG_TYPE_XRECORD=79]="DWG_TYPE_XRECORD",t[t.DWG_TYPE_PLACEHOLDER=80]="DWG_TYPE_PLACEHOLDER",t[t.DWG_TYPE_VBA_PROJECT=81]="DWG_TYPE_VBA_PROJECT",t[t.DWG_TYPE_LAYOUT=82]="DWG_TYPE_LAYOUT",t[t.DWG_TYPE_PROXY_ENTITY=498]="DWG_TYPE_PROXY_ENTITY",t[t.DWG_TYPE_PROXY_OBJECT=499]="DWG_TYPE_PROXY_OBJECT",t[t.DWG_TYPE_ACDSRECORD=500]="DWG_TYPE_ACDSRECORD",t[t.DWG_TYPE_ACDSSCHEMA=501]="DWG_TYPE_ACDSSCHEMA",t[t.DWG_TYPE_ACMECOMMANDHISTORY=502]="DWG_TYPE_ACMECOMMANDHISTORY",t[t.DWG_TYPE_ACMESCOPE=503]="DWG_TYPE_ACMESCOPE",t[t.DWG_TYPE_ACMESTATEMGR=504]="DWG_TYPE_ACMESTATEMGR",t[t.DWG_TYPE_ACSH_BOOLEAN_CLASS=505]="DWG_TYPE_ACSH_BOOLEAN_CLASS",t[t.DWG_TYPE_ACSH_BOX_CLASS=506]="DWG_TYPE_ACSH_BOX_CLASS",t[t.DWG_TYPE_ACSH_BREP_CLASS=507]="DWG_TYPE_ACSH_BREP_CLASS",t[t.DWG_TYPE_ACSH_CHAMFER_CLASS=508]="DWG_TYPE_ACSH_CHAMFER_CLASS",t[t.DWG_TYPE_ACSH_CONE_CLASS=509]="DWG_TYPE_ACSH_CONE_CLASS",t[t.DWG_TYPE_ACSH_CYLINDER_CLASS=510]="DWG_TYPE_ACSH_CYLINDER_CLASS",t[t.DWG_TYPE_ACSH_EXTRUSION_CLASS=511]="DWG_TYPE_ACSH_EXTRUSION_CLASS",t[t.DWG_TYPE_ACSH_FILLET_CLASS=512]="DWG_TYPE_ACSH_FILLET_CLASS",t[t.DWG_TYPE_ACSH_HISTORY_CLASS=513]="DWG_TYPE_ACSH_HISTORY_CLASS",t[t.DWG_TYPE_ACSH_LOFT_CLASS=514]="DWG_TYPE_ACSH_LOFT_CLASS",t[t.DWG_TYPE_ACSH_PYRAMID_CLASS=515]="DWG_TYPE_ACSH_PYRAMID_CLASS",t[t.DWG_TYPE_ACSH_REVOLVE_CLASS=516]="DWG_TYPE_ACSH_REVOLVE_CLASS",t[t.DWG_TYPE_ACSH_SPHERE_CLASS=517]="DWG_TYPE_ACSH_SPHERE_CLASS",t[t.DWG_TYPE_ACSH_SWEEP_CLASS=518]="DWG_TYPE_ACSH_SWEEP_CLASS",t[t.DWG_TYPE_ACSH_TORUS_CLASS=519]="DWG_TYPE_ACSH_TORUS_CLASS",t[t.DWG_TYPE_ACSH_WEDGE_CLASS=520]="DWG_TYPE_ACSH_WEDGE_CLASS",t[t.DWG_TYPE_ALDIMOBJECTCONTEXTDATA=521]="DWG_TYPE_ALDIMOBJECTCONTEXTDATA",t[t.DWG_TYPE_ALIGNMENTPARAMETERENTITY=522]="DWG_TYPE_ALIGNMENTPARAMETERENTITY",t[t.DWG_TYPE_ANGDIMOBJECTCONTEXTDATA=523]="DWG_TYPE_ANGDIMOBJECTCONTEXTDATA",t[t.DWG_TYPE_ANNOTSCALEOBJECTCONTEXTDATA=524]="DWG_TYPE_ANNOTSCALEOBJECTCONTEXTDATA",t[t.DWG_TYPE_ARCALIGNEDTEXT=525]="DWG_TYPE_ARCALIGNEDTEXT",t[t.DWG_TYPE_ARC_DIMENSION=526]="DWG_TYPE_ARC_DIMENSION",t[t.DWG_TYPE_ASSOC2DCONSTRAINTGROUP=527]="DWG_TYPE_ASSOC2DCONSTRAINTGROUP",t[t.DWG_TYPE_ASSOC3POINTANGULARDIMACTIONBODY=528]="DWG_TYPE_ASSOC3POINTANGULARDIMACTIONBODY",t[t.DWG_TYPE_ASSOCACTION=529]="DWG_TYPE_ASSOCACTION",t[t.DWG_TYPE_ASSOCACTIONPARAM=530]="DWG_TYPE_ASSOCACTIONPARAM",t[t.DWG_TYPE_ASSOCALIGNEDDIMACTIONBODY=531]="DWG_TYPE_ASSOCALIGNEDDIMACTIONBODY",t[t.DWG_TYPE_ASSOCARRAYACTIONBODY=532]="DWG_TYPE_ASSOCARRAYACTIONBODY",t[t.DWG_TYPE_ASSOCARRAYMODIFYACTIONBODY=533]="DWG_TYPE_ASSOCARRAYMODIFYACTIONBODY",t[t.DWG_TYPE_ASSOCARRAYMODIFYPARAMETERS=534]="DWG_TYPE_ASSOCARRAYMODIFYPARAMETERS",t[t.DWG_TYPE_ASSOCARRAYPATHPARAMETERS=535]="DWG_TYPE_ASSOCARRAYPATHPARAMETERS",t[t.DWG_TYPE_ASSOCARRAYPOLARPARAMETERS=536]="DWG_TYPE_ASSOCARRAYPOLARPARAMETERS",t[t.DWG_TYPE_ASSOCARRAYRECTANGULARPARAMETERS=537]="DWG_TYPE_ASSOCARRAYRECTANGULARPARAMETERS",t[t.DWG_TYPE_ASSOCASMBODYACTIONPARAM=538]="DWG_TYPE_ASSOCASMBODYACTIONPARAM",t[t.DWG_TYPE_ASSOCBLENDSURFACEACTIONBODY=539]="DWG_TYPE_ASSOCBLENDSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCCOMPOUNDACTIONPARAM=540]="DWG_TYPE_ASSOCCOMPOUNDACTIONPARAM",t[t.DWG_TYPE_ASSOCDEPENDENCY=541]="DWG_TYPE_ASSOCDEPENDENCY",t[t.DWG_TYPE_ASSOCDIMDEPENDENCYBODY=542]="DWG_TYPE_ASSOCDIMDEPENDENCYBODY",t[t.DWG_TYPE_ASSOCEDGEACTIONPARAM=543]="DWG_TYPE_ASSOCEDGEACTIONPARAM",t[t.DWG_TYPE_ASSOCEDGECHAMFERACTIONBODY=544]="DWG_TYPE_ASSOCEDGECHAMFERACTIONBODY",t[t.DWG_TYPE_ASSOCEDGEFILLETACTIONBODY=545]="DWG_TYPE_ASSOCEDGEFILLETACTIONBODY",t[t.DWG_TYPE_ASSOCEXTENDSURFACEACTIONBODY=546]="DWG_TYPE_ASSOCEXTENDSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCEXTRUDEDSURFACEACTIONBODY=547]="DWG_TYPE_ASSOCEXTRUDEDSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCFACEACTIONPARAM=548]="DWG_TYPE_ASSOCFACEACTIONPARAM",t[t.DWG_TYPE_ASSOCFILLETSURFACEACTIONBODY=549]="DWG_TYPE_ASSOCFILLETSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCGEOMDEPENDENCY=550]="DWG_TYPE_ASSOCGEOMDEPENDENCY",t[t.DWG_TYPE_ASSOCLOFTEDSURFACEACTIONBODY=551]="DWG_TYPE_ASSOCLOFTEDSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCMLEADERACTIONBODY=552]="DWG_TYPE_ASSOCMLEADERACTIONBODY",t[t.DWG_TYPE_ASSOCNETWORK=553]="DWG_TYPE_ASSOCNETWORK",t[t.DWG_TYPE_ASSOCNETWORKSURFACEACTIONBODY=554]="DWG_TYPE_ASSOCNETWORKSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCOBJECTACTIONPARAM=555]="DWG_TYPE_ASSOCOBJECTACTIONPARAM",t[t.DWG_TYPE_ASSOCOFFSETSURFACEACTIONBODY=556]="DWG_TYPE_ASSOCOFFSETSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCORDINATEDIMACTIONBODY=557]="DWG_TYPE_ASSOCORDINATEDIMACTIONBODY",t[t.DWG_TYPE_ASSOCOSNAPPOINTREFACTIONPARAM=558]="DWG_TYPE_ASSOCOSNAPPOINTREFACTIONPARAM",t[t.DWG_TYPE_ASSOCPATCHSURFACEACTIONBODY=559]="DWG_TYPE_ASSOCPATCHSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCPATHACTIONPARAM=560]="DWG_TYPE_ASSOCPATHACTIONPARAM",t[t.DWG_TYPE_ASSOCPERSSUBENTMANAGER=561]="DWG_TYPE_ASSOCPERSSUBENTMANAGER",t[t.DWG_TYPE_ASSOCPLANESURFACEACTIONBODY=562]="DWG_TYPE_ASSOCPLANESURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCPOINTREFACTIONPARAM=563]="DWG_TYPE_ASSOCPOINTREFACTIONPARAM",t[t.DWG_TYPE_ASSOCRESTOREENTITYSTATEACTIONBODY=564]="DWG_TYPE_ASSOCRESTOREENTITYSTATEACTIONBODY",t[t.DWG_TYPE_ASSOCREVOLVEDSURFACEACTIONBODY=565]="DWG_TYPE_ASSOCREVOLVEDSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCROTATEDDIMACTIONBODY=566]="DWG_TYPE_ASSOCROTATEDDIMACTIONBODY",t[t.DWG_TYPE_ASSOCSWEPTSURFACEACTIONBODY=567]="DWG_TYPE_ASSOCSWEPTSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCTRIMSURFACEACTIONBODY=568]="DWG_TYPE_ASSOCTRIMSURFACEACTIONBODY",t[t.DWG_TYPE_ASSOCVALUEDEPENDENCY=569]="DWG_TYPE_ASSOCVALUEDEPENDENCY",t[t.DWG_TYPE_ASSOCVARIABLE=570]="DWG_TYPE_ASSOCVARIABLE",t[t.DWG_TYPE_ASSOCVERTEXACTIONPARAM=571]="DWG_TYPE_ASSOCVERTEXACTIONPARAM",t[t.DWG_TYPE_BASEPOINTPARAMETERENTITY=572]="DWG_TYPE_BASEPOINTPARAMETERENTITY",t[t.DWG_TYPE_BLKREFOBJECTCONTEXTDATA=573]="DWG_TYPE_BLKREFOBJECTCONTEXTDATA",t[t.DWG_TYPE_BLOCKALIGNEDCONSTRAINTPARAMETER=574]="DWG_TYPE_BLOCKALIGNEDCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKALIGNMENTGRIP=575]="DWG_TYPE_BLOCKALIGNMENTGRIP",t[t.DWG_TYPE_BLOCKALIGNMENTPARAMETER=576]="DWG_TYPE_BLOCKALIGNMENTPARAMETER",t[t.DWG_TYPE_BLOCKANGULARCONSTRAINTPARAMETER=577]="DWG_TYPE_BLOCKANGULARCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKARRAYACTION=578]="DWG_TYPE_BLOCKARRAYACTION",t[t.DWG_TYPE_BLOCKBASEPOINTPARAMETER=579]="DWG_TYPE_BLOCKBASEPOINTPARAMETER",t[t.DWG_TYPE_BLOCKDIAMETRICCONSTRAINTPARAMETER=580]="DWG_TYPE_BLOCKDIAMETRICCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKFLIPACTION=581]="DWG_TYPE_BLOCKFLIPACTION",t[t.DWG_TYPE_BLOCKFLIPGRIP=582]="DWG_TYPE_BLOCKFLIPGRIP",t[t.DWG_TYPE_BLOCKFLIPPARAMETER=583]="DWG_TYPE_BLOCKFLIPPARAMETER",t[t.DWG_TYPE_BLOCKGRIPLOCATIONCOMPONENT=584]="DWG_TYPE_BLOCKGRIPLOCATIONCOMPONENT",t[t.DWG_TYPE_BLOCKHORIZONTALCONSTRAINTPARAMETER=585]="DWG_TYPE_BLOCKHORIZONTALCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKLINEARCONSTRAINTPARAMETER=586]="DWG_TYPE_BLOCKLINEARCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKLINEARGRIP=587]="DWG_TYPE_BLOCKLINEARGRIP",t[t.DWG_TYPE_BLOCKLINEARPARAMETER=588]="DWG_TYPE_BLOCKLINEARPARAMETER",t[t.DWG_TYPE_BLOCKLOOKUPACTION=589]="DWG_TYPE_BLOCKLOOKUPACTION",t[t.DWG_TYPE_BLOCKLOOKUPGRIP=590]="DWG_TYPE_BLOCKLOOKUPGRIP",t[t.DWG_TYPE_BLOCKLOOKUPPARAMETER=591]="DWG_TYPE_BLOCKLOOKUPPARAMETER",t[t.DWG_TYPE_BLOCKMOVEACTION=592]="DWG_TYPE_BLOCKMOVEACTION",t[t.DWG_TYPE_BLOCKPARAMDEPENDENCYBODY=593]="DWG_TYPE_BLOCKPARAMDEPENDENCYBODY",t[t.DWG_TYPE_BLOCKPOINTPARAMETER=594]="DWG_TYPE_BLOCKPOINTPARAMETER",t[t.DWG_TYPE_BLOCKPOLARGRIP=595]="DWG_TYPE_BLOCKPOLARGRIP",t[t.DWG_TYPE_BLOCKPOLARPARAMETER=596]="DWG_TYPE_BLOCKPOLARPARAMETER",t[t.DWG_TYPE_BLOCKPOLARSTRETCHACTION=597]="DWG_TYPE_BLOCKPOLARSTRETCHACTION",t[t.DWG_TYPE_BLOCKPROPERTIESTABLE=598]="DWG_TYPE_BLOCKPROPERTIESTABLE",t[t.DWG_TYPE_BLOCKPROPERTIESTABLEGRIP=599]="DWG_TYPE_BLOCKPROPERTIESTABLEGRIP",t[t.DWG_TYPE_BLOCKRADIALCONSTRAINTPARAMETER=600]="DWG_TYPE_BLOCKRADIALCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKREPRESENTATION=601]="DWG_TYPE_BLOCKREPRESENTATION",t[t.DWG_TYPE_BLOCKROTATEACTION=602]="DWG_TYPE_BLOCKROTATEACTION",t[t.DWG_TYPE_BLOCKROTATIONGRIP=603]="DWG_TYPE_BLOCKROTATIONGRIP",t[t.DWG_TYPE_BLOCKROTATIONPARAMETER=604]="DWG_TYPE_BLOCKROTATIONPARAMETER",t[t.DWG_TYPE_BLOCKSCALEACTION=605]="DWG_TYPE_BLOCKSCALEACTION",t[t.DWG_TYPE_BLOCKSTRETCHACTION=606]="DWG_TYPE_BLOCKSTRETCHACTION",t[t.DWG_TYPE_BLOCKUSERPARAMETER=607]="DWG_TYPE_BLOCKUSERPARAMETER",t[t.DWG_TYPE_BLOCKVERTICALCONSTRAINTPARAMETER=608]="DWG_TYPE_BLOCKVERTICALCONSTRAINTPARAMETER",t[t.DWG_TYPE_BLOCKVISIBILITYGRIP=609]="DWG_TYPE_BLOCKVISIBILITYGRIP",t[t.DWG_TYPE_BLOCKVISIBILITYPARAMETER=610]="DWG_TYPE_BLOCKVISIBILITYPARAMETER",t[t.DWG_TYPE_BLOCKXYGRIP=611]="DWG_TYPE_BLOCKXYGRIP",t[t.DWG_TYPE_BLOCKXYPARAMETER=612]="DWG_TYPE_BLOCKXYPARAMETER",t[t.DWG_TYPE_CAMERA=613]="DWG_TYPE_CAMERA",t[t.DWG_TYPE_CELLSTYLEMAP=614]="DWG_TYPE_CELLSTYLEMAP",t[t.DWG_TYPE_CONTEXTDATAMANAGER=615]="DWG_TYPE_CONTEXTDATAMANAGER",t[t.DWG_TYPE_CSACDOCUMENTOPTIONS=616]="DWG_TYPE_CSACDOCUMENTOPTIONS",t[t.DWG_TYPE_CURVEPATH=617]="DWG_TYPE_CURVEPATH",t[t.DWG_TYPE_DATALINK=618]="DWG_TYPE_DATALINK",t[t.DWG_TYPE_DATATABLE=619]="DWG_TYPE_DATATABLE",t[t.DWG_TYPE_DBCOLOR=620]="DWG_TYPE_DBCOLOR",t[t.DWG_TYPE_DETAILVIEWSTYLE=621]="DWG_TYPE_DETAILVIEWSTYLE",t[t.DWG_TYPE_DGNDEFINITION=622]="DWG_TYPE_DGNDEFINITION",t[t.DWG_TYPE_DGNUNDERLAY=623]="DWG_TYPE_DGNUNDERLAY",t[t.DWG_TYPE_DICTIONARYVAR=624]="DWG_TYPE_DICTIONARYVAR",t[t.DWG_TYPE_DICTIONARYWDFLT=625]="DWG_TYPE_DICTIONARYWDFLT",t[t.DWG_TYPE_DIMASSOC=626]="DWG_TYPE_DIMASSOC",t[t.DWG_TYPE_DMDIMOBJECTCONTEXTDATA=627]="DWG_TYPE_DMDIMOBJECTCONTEXTDATA",t[t.DWG_TYPE_DWFDEFINITION=628]="DWG_TYPE_DWFDEFINITION",t[t.DWG_TYPE_DWFUNDERLAY=629]="DWG_TYPE_DWFUNDERLAY",t[t.DWG_TYPE_DYNAMICBLOCKPROXYNODE=630]="DWG_TYPE_DYNAMICBLOCKPROXYNODE",t[t.DWG_TYPE_DYNAMICBLOCKPURGEPREVENTER=631]="DWG_TYPE_DYNAMICBLOCKPURGEPREVENTER",t[t.DWG_TYPE_EVALUATION_GRAPH=632]="DWG_TYPE_EVALUATION_GRAPH",t[t.DWG_TYPE_EXTRUDEDSURFACE=633]="DWG_TYPE_EXTRUDEDSURFACE",t[t.DWG_TYPE_FCFOBJECTCONTEXTDATA=634]="DWG_TYPE_FCFOBJECTCONTEXTDATA",t[t.DWG_TYPE_FIELD=635]="DWG_TYPE_FIELD",t[t.DWG_TYPE_FIELDLIST=636]="DWG_TYPE_FIELDLIST",t[t.DWG_TYPE_FLIPPARAMETERENTITY=637]="DWG_TYPE_FLIPPARAMETERENTITY",t[t.DWG_TYPE_FLIPACTIONENTITY=638]="DWG_TYPE_FLIPACTIONENTITY",t[t.DWG_TYPE_GEODATA=639]="DWG_TYPE_GEODATA",t[t.DWG_TYPE_GEOMAPIMAGE=640]="DWG_TYPE_GEOMAPIMAGE",t[t.DWG_TYPE_GEOPOSITIONMARKER=641]="DWG_TYPE_GEOPOSITIONMARKER",t[t.DWG_TYPE_GRADIENT_BACKGROUND=642]="DWG_TYPE_GRADIENT_BACKGROUND",t[t.DWG_TYPE_GROUND_PLANE_BACKGROUND=643]="DWG_TYPE_GROUND_PLANE_BACKGROUND",t[t.DWG_TYPE_HELIX=644]="DWG_TYPE_HELIX",t[t.DWG_TYPE_IBL_BACKGROUND=645]="DWG_TYPE_IBL_BACKGROUND",t[t.DWG_TYPE_IDBUFFER=646]="DWG_TYPE_IDBUFFER",t[t.DWG_TYPE_IMAGE=647]="DWG_TYPE_IMAGE",t[t.DWG_TYPE_IMAGEDEF=648]="DWG_TYPE_IMAGEDEF",t[t.DWG_TYPE_IMAGEDEF_REACTOR=649]="DWG_TYPE_IMAGEDEF_REACTOR",t[t.DWG_TYPE_IMAGE_BACKGROUND=650]="DWG_TYPE_IMAGE_BACKGROUND",t[t.DWG_TYPE_INDEX=651]="DWG_TYPE_INDEX",t[t.DWG_TYPE_LARGE_RADIAL_DIMENSION=652]="DWG_TYPE_LARGE_RADIAL_DIMENSION",t[t.DWG_TYPE_LAYERFILTER=653]="DWG_TYPE_LAYERFILTER",t[t.DWG_TYPE_LAYER_INDEX=654]="DWG_TYPE_LAYER_INDEX",t[t.DWG_TYPE_LAYOUTPRINTCONFIG=655]="DWG_TYPE_LAYOUTPRINTCONFIG",t[t.DWG_TYPE_LEADEROBJECTCONTEXTDATA=656]="DWG_TYPE_LEADEROBJECTCONTEXTDATA",t[t.DWG_TYPE_LIGHT=657]="DWG_TYPE_LIGHT",t[t.DWG_TYPE_LIGHTLIST=658]="DWG_TYPE_LIGHTLIST",t[t.DWG_TYPE_LINEARPARAMETERENTITY=659]="DWG_TYPE_LINEARPARAMETERENTITY",t[t.DWG_TYPE_LOFTEDSURFACE=660]="DWG_TYPE_LOFTEDSURFACE",t[t.DWG_TYPE_MATERIAL=661]="DWG_TYPE_MATERIAL",t[t.DWG_TYPE_MENTALRAYRENDERSETTINGS=662]="DWG_TYPE_MENTALRAYRENDERSETTINGS",t[t.DWG_TYPE_MESH=663]="DWG_TYPE_MESH",t[t.DWG_TYPE_MLEADEROBJECTCONTEXTDATA=664]="DWG_TYPE_MLEADEROBJECTCONTEXTDATA",t[t.DWG_TYPE_MLEADERSTYLE=665]="DWG_TYPE_MLEADERSTYLE",t[t.DWG_TYPE_MOVEACTIONENTITY=666]="DWG_TYPE_MOVEACTIONENTITY",t[t.DWG_TYPE_MOTIONPATH=667]="DWG_TYPE_MOTIONPATH",t[t.DWG_TYPE_MPOLYGON=668]="DWG_TYPE_MPOLYGON",t[t.DWG_TYPE_MTEXTATTRIBUTEOBJECTCONTEXTDATA=669]="DWG_TYPE_MTEXTATTRIBUTEOBJECTCONTEXTDATA",t[t.DWG_TYPE_MTEXTOBJECTCONTEXTDATA=670]="DWG_TYPE_MTEXTOBJECTCONTEXTDATA",t[t.DWG_TYPE_MULTILEADER=671]="DWG_TYPE_MULTILEADER",t[t.DWG_TYPE_NAVISWORKSMODEL=672]="DWG_TYPE_NAVISWORKSMODEL",t[t.DWG_TYPE_NAVISWORKSMODELDEF=673]="DWG_TYPE_NAVISWORKSMODELDEF",t[t.DWG_TYPE_NPOCOLLECTION=674]="DWG_TYPE_NPOCOLLECTION",t[t.DWG_TYPE_NURBSURFACE=675]="DWG_TYPE_NURBSURFACE",t[t.DWG_TYPE_OBJECT_PTR=676]="DWG_TYPE_OBJECT_PTR",t[t.DWG_TYPE_ORDDIMOBJECTCONTEXTDATA=677]="DWG_TYPE_ORDDIMOBJECTCONTEXTDATA",t[t.DWG_TYPE_PARTIAL_VIEWING_INDEX=678]="DWG_TYPE_PARTIAL_VIEWING_INDEX",t[t.DWG_TYPE_PDFDEFINITION=679]="DWG_TYPE_PDFDEFINITION",t[t.DWG_TYPE_PDFUNDERLAY=680]="DWG_TYPE_PDFUNDERLAY",t[t.DWG_TYPE_PERSUBENTMGR=681]="DWG_TYPE_PERSUBENTMGR",t[t.DWG_TYPE_PLANESURFACE=682]="DWG_TYPE_PLANESURFACE",t[t.DWG_TYPE_PLOTSETTINGS=683]="DWG_TYPE_PLOTSETTINGS",t[t.DWG_TYPE_POINTCLOUD=684]="DWG_TYPE_POINTCLOUD",t[t.DWG_TYPE_POINTCLOUDCOLORMAP=685]="DWG_TYPE_POINTCLOUDCOLORMAP",t[t.DWG_TYPE_POINTCLOUDDEF=686]="DWG_TYPE_POINTCLOUDDEF",t[t.DWG_TYPE_POINTCLOUDDEFEX=687]="DWG_TYPE_POINTCLOUDDEFEX",t[t.DWG_TYPE_POINTCLOUDDEF_REACTOR=688]="DWG_TYPE_POINTCLOUDDEF_REACTOR",t[t.DWG_TYPE_POINTCLOUDDEF_REACTOR_EX=689]="DWG_TYPE_POINTCLOUDDEF_REACTOR_EX",t[t.DWG_TYPE_POINTCLOUDEX=690]="DWG_TYPE_POINTCLOUDEX",t[t.DWG_TYPE_POINTPARAMETERENTITY=691]="DWG_TYPE_POINTPARAMETERENTITY",t[t.DWG_TYPE_POINTPATH=692]="DWG_TYPE_POINTPATH",t[t.DWG_TYPE_POLARGRIPENTITY=693]="DWG_TYPE_POLARGRIPENTITY",t[t.DWG_TYPE_RADIMLGOBJECTCONTEXTDATA=694]="DWG_TYPE_RADIMLGOBJECTCONTEXTDATA",t[t.DWG_TYPE_RADIMOBJECTCONTEXTDATA=695]="DWG_TYPE_RADIMOBJECTCONTEXTDATA",t[t.DWG_TYPE_RAPIDRTRENDERSETTINGS=696]="DWG_TYPE_RAPIDRTRENDERSETTINGS",t[t.DWG_TYPE_RASTERVARIABLES=697]="DWG_TYPE_RASTERVARIABLES",t[t.DWG_TYPE_RENDERENTRY=698]="DWG_TYPE_RENDERENTRY",t[t.DWG_TYPE_RENDERENVIRONMENT=699]="DWG_TYPE_RENDERENVIRONMENT",t[t.DWG_TYPE_RENDERGLOBAL=700]="DWG_TYPE_RENDERGLOBAL",t[t.DWG_TYPE_RENDERSETTINGS=701]="DWG_TYPE_RENDERSETTINGS",t[t.DWG_TYPE_REVOLVEDSURFACE=702]="DWG_TYPE_REVOLVEDSURFACE",t[t.DWG_TYPE_ROTATIONPARAMETERENTITY=703]="DWG_TYPE_ROTATIONPARAMETERENTITY",t[t.DWG_TYPE_ROTATEACTIONENTITY=704]="DWG_TYPE_ROTATEACTIONENTITY",t[t.DWG_TYPE_RTEXT=705]="DWG_TYPE_RTEXT",t[t.DWG_TYPE_SCALE=706]="DWG_TYPE_SCALE",t[t.DWG_TYPE_SCALEACTIONENTITY=707]="DWG_TYPE_SCALEACTIONENTITY",t[t.DWG_TYPE_SECTIONOBJECT=708]="DWG_TYPE_SECTIONOBJECT",t[t.DWG_TYPE_SECTIONVIEWSTYLE=709]="DWG_TYPE_SECTIONVIEWSTYLE",t[t.DWG_TYPE_SECTION_MANAGER=710]="DWG_TYPE_SECTION_MANAGER",t[t.DWG_TYPE_SECTION_SETTINGS=711]="DWG_TYPE_SECTION_SETTINGS",t[t.DWG_TYPE_SKYLIGHT_BACKGROUND=712]="DWG_TYPE_SKYLIGHT_BACKGROUND",t[t.DWG_TYPE_SOLID_BACKGROUND=713]="DWG_TYPE_SOLID_BACKGROUND",t[t.DWG_TYPE_SORTENTSTABLE=714]="DWG_TYPE_SORTENTSTABLE",t[t.DWG_TYPE_SPATIAL_FILTER=715]="DWG_TYPE_SPATIAL_FILTER",t[t.DWG_TYPE_SPATIAL_INDEX=716]="DWG_TYPE_SPATIAL_INDEX",t[t.DWG_TYPE_STRETCHACTIONENTITY=717]="DWG_TYPE_STRETCHACTIONENTITY",t[t.DWG_TYPE_SUN=718]="DWG_TYPE_SUN",t[t.DWG_TYPE_SUNSTUDY=719]="DWG_TYPE_SUNSTUDY",t[t.DWG_TYPE_SWEPTSURFACE=720]="DWG_TYPE_SWEPTSURFACE",t[t.DWG_TYPE_TABLE=721]="DWG_TYPE_TABLE",t[t.DWG_TYPE_TABLECONTENT=722]="DWG_TYPE_TABLECONTENT",t[t.DWG_TYPE_TABLEGEOMETRY=723]="DWG_TYPE_TABLEGEOMETRY",t[t.DWG_TYPE_TABLESTYLE=724]="DWG_TYPE_TABLESTYLE",t[t.DWG_TYPE_TEXTOBJECTCONTEXTDATA=725]="DWG_TYPE_TEXTOBJECTCONTEXTDATA",t[t.DWG_TYPE_TVDEVICEPROPERTIES=726]="DWG_TYPE_TVDEVICEPROPERTIES",t[t.DWG_TYPE_VISIBILITYGRIPENTITY=727]="DWG_TYPE_VISIBILITYGRIPENTITY",t[t.DWG_TYPE_VISIBILITYPARAMETERENTITY=728]="DWG_TYPE_VISIBILITYPARAMETERENTITY",t[t.DWG_TYPE_VISUALSTYLE=729]="DWG_TYPE_VISUALSTYLE",t[t.DWG_TYPE_WIPEOUT=730]="DWG_TYPE_WIPEOUT",t[t.DWG_TYPE_WIPEOUTVARIABLES=731]="DWG_TYPE_WIPEOUTVARIABLES",t[t.DWG_TYPE_XREFPANELOBJECT=732]="DWG_TYPE_XREFPANELOBJECT",t[t.DWG_TYPE_XYPARAMETERENTITY=733]="DWG_TYPE_XYPARAMETERENTITY",t[t.DWG_TYPE_BREAKDATA=734]="DWG_TYPE_BREAKDATA",t[t.DWG_TYPE_BREAKPOINTREF=735]="DWG_TYPE_BREAKPOINTREF",t[t.DWG_TYPE_FLIPGRIPENTITY=736]="DWG_TYPE_FLIPGRIPENTITY",t[t.DWG_TYPE_LINEARGRIPENTITY=737]="DWG_TYPE_LINEARGRIPENTITY",t[t.DWG_TYPE_ROTATIONGRIPENTITY=738]="DWG_TYPE_ROTATIONGRIPENTITY",t[t.DWG_TYPE_XYGRIPENTITY=739]="DWG_TYPE_XYGRIPENTITY",t[t.DWG_TYPE__3DLINE=740]="DWG_TYPE__3DLINE",t[t.DWG_TYPE_REPEAT=741]="DWG_TYPE_REPEAT",t[t.DWG_TYPE_ENDREP=742]="DWG_TYPE_ENDREP",t[t.DWG_TYPE_JUMP=743]="DWG_TYPE_JUMP",t[t.DWG_TYPE_LOAD=744]="DWG_TYPE_LOAD",t[t.DWG_TYPE_FREED=65533]="DWG_TYPE_FREED",t[t.DWG_TYPE_UNKNOWN_ENT=65534]="DWG_TYPE_UNKNOWN_ENT",t[t.DWG_TYPE_UNKNOWN_OBJ=65535]="DWG_TYPE_UNKNOWN_OBJ"})(T||(T={}));const $o=Object.fromEntries(Object.entries(T).map(([t,n])=>[n,t]));var rn;(function(t){t[t.DWG=0]="DWG",t[t.DXF=1]="DXF"})(rn||(rn={}));var ct;(function(t){t[t.Line=1]="Line",t[t.CircularArc=2]="CircularArc",t[t.EllipticArc=3]="EllipticArc",t[t.Spline=4]="Spline"})(ct||(ct={}));var Z;(function(t){t[t.ByLayer=192]="ByLayer",t[t.ByBlock=193]="ByBlock",t[t.Entities=194]="Entities",t[t.TrueColor=195]="TrueColor",t[t.ForegroundColor=197]="ForegroundColor",t[t.None=200]="None"})(Z||(Z={}));const ge=192,pe=193,Jo=194,Gn=195,Ce=200;function we(t){if(t==null)return;const n=t.method,o=t.method!=null&&n!==0?n:t.rgb>>>24&255,e=t.index;if(t.rgb!=null&&o>=ge&&o<=Ce&&(t.rgb>>>24&255)===o)return t.rgb>>0;if(o===Z.ByLayer||e===256)return ge<<24>>0;if(o===Z.ByBlock||e===0)return pe<<24>>0;if(o===Z.None)return Ce<<24>>0;if(o===Z.TrueColor){const a=t.rgb!=null?t.rgb&16777215:void 0;if(a!=null)return(Jo<<24|a)>>0}return o===Z.ForegroundColor?(Gn<<24|7)>>0:o===Z.Entities&&e>0&&e<256||e>0&&e<256?(Gn<<24|e&255)>>0:pe<<24>>0}const Zo="*MODEL_SPACE",qo="*PAPER_SPACE",Oe=t=>t&&t.toUpperCase()==Zo,Qo=t=>t&&t.toUpperCase().startsWith(qo),w=t=>t.toString(16).toUpperCase(),Lt=t=>{const n=new Array(t.length);for(let o=0;o{if(!t||t.length<98)return null;const n=new DataView(t.buffer,t.byteOffset,t.byteLength),o=i=>({x:n.getFloat64(i,!0),y:n.getFloat64(i+8,!0),z:n.getFloat64(i+16,!0)}),e=o(2),a=o(50);return[e.x,e.y,e.z,a.x,a.y,a.z].every(Number.isFinite)?{upperLeft:e,lowerRight:a}:null};class ta{constructor(n){M(this,"libredwg");M(this,"layers",new Map);M(this,"ltypes",new Map);M(this,"classes",[]);M(this,"unknownEntityCount");this.libredwg=n,this.unknownEntityCount=0}prepare(n,o=!1){(o||this.layers.size==0)&&(this.layers.clear(),n.tables.LAYER.entries.forEach(e=>{this.layers.set(e.handle,e.name)})),(o||this.ltypes.size==0)&&(this.ltypes.clear(),n.tables.LTYPE.entries.forEach(e=>{this.ltypes.set(e.handle,e.name)})),this.classes=n.classes,this.unknownEntityCount=0}setClasses(n){this.classes=n}clear(){this.layers.clear(),this.ltypes.clear(),this.classes=[],this.unknownEntityCount=0}convert(n){const o=this.libredwg,e=o.dwg_object_to_entity(n),a=o.dwg_object_to_entity_tio(n);if(e&&a){const i=this.getCommonAttrs(e),r=o.dwg_object_get_fixedtype(n);if(r==T.DWG_TYPE_3DFACE)return this.convert3dFace(a,i);if(r==T.DWG_TYPE_3DSOLID)return this.convert3dSolid(a,i);if(r==T.DWG_TYPE_ARC)return this.convertArc(a,i);if(r==T.DWG_TYPE_ATTDEF)return this.convertAttdef(a,i);if(r==T.DWG_TYPE_CIRCLE)return this.convertCircle(a,i);if(r==T.DWG_TYPE_DIMENSION_ALIGNED||r==T.DWG_TYPE_DIMENSION_LINEAR)return this.convertAlignedDimension(a,i);if(r==T.DWG_TYPE_DIMENSION_ANG3PT)return this.convert3PointAngularDimension(a,i);if(r==T.DWG_TYPE_DIMENSION_ANG2LN)return this.convert2LineAngularDimension(a,i);if(r==T.DWG_TYPE_DIMENSION_DIAMETER)return this.convertDiameterDimension(a,i);if(r==T.DWG_TYPE_DIMENSION_ORDINATE)return this.convertOrdinateDimension(a,i);if(r==T.DWG_TYPE_DIMENSION_RADIUS)return this.convertRadiusDimension(a,i);if(r==T.DWG_TYPE_ELLIPSE)return this.convertEllise(a,i);if(r==T.DWG_TYPE_HATCH)return this.convertHatch(a,i);if(r==T.DWG_TYPE_IMAGE)return this.convertImage(a,i);if(r==T.DWG_TYPE_INSERT)return this.convertInsert(a,i);if(r==T.DWG_TYPE_LEADER)return this.convertLeader(a,i);if(r==T.DWG_TYPE_LINE)return this.convertLine(a,i);if(r==T.DWG_TYPE_LWPOLYLINE)return this.convertLWPolyline(a,i);if(r==T.DWG_TYPE_MLINE)return this.convertMLine(a,i);if(r==T.DWG_TYPE_MULTILEADER)return this.convertMultiLeader(a,i);if(r==T.DWG_TYPE_MTEXT)return this.convertMText(a,i);if(r==T.DWG_TYPE_OLE2FRAME)return this.convertOle2Frame(a,i);if(r==T.DWG_TYPE_OLEFRAME)return this.convertOleFrame(a,i);if(r==T.DWG_TYPE_POINT)return this.convertPoint(a,i);if(r==T.DWG_TYPE_POLYLINE_2D)return this.convertPolyline2d(a,i,n);if(r==T.DWG_TYPE_POLYLINE_3D)return this.convertPolyline3d(a,i,n);if(r==T.DWG_TYPE_PROXY_ENTITY)return this.convertProxyEntity(a,i,n);if(r==T.DWG_TYPE_RAY)return this.convertRay(a,i);if(r==T.DWG_TYPE_SECTIONOBJECT)return this.convertSection(a,i);if(r==T.DWG_TYPE_SHAPE)return this.convertShape(a,i);if(r==T.DWG_TYPE_SOLID)return this.convertSolid(a,i);if(r==T.DWG_TYPE_SPLINE)return this.convertSpline(a,i);if(r==T.DWG_TYPE_TABLE)return this.convertTable(a,i);if(r==T.DWG_TYPE_TEXT)return this.convertText(a,i);if(r==T.DWG_TYPE_TOLERANCE)return this.convertTolerance(a,i);if(r==T.DWG_TYPE_VIEWPORT)return this.convertViewport(a,i);if(r==T.DWG_TYPE_WIPEOUT)return this.convertWipeout(a,i);if(r==T.DWG_TYPE_XLINE)return this.convertXline(a,i);r===T.DWG_TYPE_UNKNOWN_ENT&&this.unknownEntityCount++}}convert3dFace(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"corner1"),i=e.dwg_dynapi_entity_data(n,"corner2"),r=e.dwg_dynapi_entity_data(n,"corner3"),s=e.dwg_dynapi_entity_data(n,"corner4"),_=e.dwg_dynapi_entity_data(n,"invis_flags");return{type:"3DFACE",...o,corner1:a,corner2:i,corner3:r,corner4:s,flag:_}}convert3dSolid(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"version"),i=e.dwg_dynapi_entity_data(n,"acis_empty"),r=e.dwg_dynapi_entity_data(n,"acis_data"),s=e.dwg_dynapi_entity_data(n,"has_revision_guid"),_=e.dwg_dynapi_entity_data(n,"history_id"),d=e.dwg_ref_get_id(_),c={type:"3DSOLID",subclassMarker:"AcDb3dSolid",...o};if(a&&(c.version=a),c.satCache=i,r&&(c.data=r),s){const E=e.dwg_dynapi_entity_field_offset(n,"revision_guid");if(E>=0){const u=e.UTF8ToString(n+E,38);u&&(c.guid=u)}}return d&&(c.historyObjectSoftId=d),c}convertArc(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"center"),i=e.dwg_dynapi_entity_data(n,"radius"),r=e.dwg_dynapi_entity_data(n,"thickness"),s=e.dwg_dynapi_entity_data(n,"extrusion"),_=e.dwg_dynapi_entity_data(n,"start_angle"),d=e.dwg_dynapi_entity_data(n,"end_angle");return{type:"ARC",...o,thickness:r,center:a,radius:i,startAngle:_,endAngle:d,extrusionDirection:s}}convertEmbeddedMText(n,o){const e=this.libredwg,a=e.dwg_dynapi_subclass_data(n,o,"attachment"),i=e.dwg_dynapi_subclass_data(n,o,"ins_pt"),r=e.dwg_dynapi_subclass_data(n,o,"x_axis_dir"),s=e.dwg_dynapi_subclass_data(n,o,"rect_height"),_=e.dwg_dynapi_subclass_data(n,o,"rect_width"),d=e.dwg_dynapi_subclass_data(n,o,"extents_height"),c=e.dwg_dynapi_subclass_data(n,o,"extents_width");return{insertionPoint:i,rectHeight:s,rectWidth:_,extentsHeight:d,extentsWidth:c,attachmentPoint:a,direction:r}}convertAttdef(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"default_value"),i=this.convertTextBase(n);i.text=a;const r=e.dwg_dynapi_entity_data(n,"prompt"),s=e.dwg_dynapi_entity_data(n,"tag"),_=e.dwg_dynapi_entity_data(n,"flags"),d=e.dwg_dynapi_entity_data(n,"field_length"),c=e.dwg_dynapi_entity_data(n,"lock_position_flag"),E=e.dwg_dynapi_entity_data(n,"keep_duplicate_records"),u=e.dwg_dynapi_entity_data(n,"is_really_locked"),l=e.dwg_dynapi_entity_data(n,"mtext_type"),I=e.dwg_dynapi_entity_data(n,"alignment_pt");return{type:"ATTDEF",...o,text:this.convertTextBase(n),prompt:r,tag:s,flags:_,fieldLength:d,lockPositionFlag:c>0,duplicateRecordCloningFlag:E>0,mtextFlag:l,isReallyLocked:u>0,alignmentPoint:I,annotationScale:1,attrTag:"",mtext:this.convertEmbeddedMText(n,"ATTDEF_mtext")}}convertAttrib(n,o){const e=this.libredwg,a=this.convertTextBase(n),i=e.dwg_dynapi_entity_data(n,"tag"),r=e.dwg_dynapi_entity_data(n,"flags"),s=e.dwg_dynapi_entity_data(n,"field_length"),_=e.dwg_dynapi_entity_data(n,"lock_position_flag"),d=e.dwg_dynapi_entity_data(n,"keep_duplicate_records"),c=e.dwg_dynapi_entity_data(n,"mtext_type"),E=e.dwg_dynapi_entity_data(n,"is_really_locked"),u=e.dwg_dynapi_entity_data(n,"alignment_pt");return{type:"ATTRIB",...o,text:a,tag:i,flags:r,fieldLength:s,lockPositionFlag:!!_,duplicateRecordCloningFlag:!!d,mtextFlag:c,isReallyLocked:!!E,numberOfSecondaryAttrs:0,secondaryAttrsHardId:"0",alignmentPoint:{...u,z:0},annotationScale:1,attrTag:"",mtext:this.convertEmbeddedMText(n,"ATTDEF_mtext")}}convertCircle(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"center"),i=e.dwg_dynapi_entity_data(n,"radius"),r=e.dwg_dynapi_entity_data(n,"thickness"),s=e.dwg_dynapi_entity_data(n,"extrusion");return{type:"CIRCLE",...o,thickness:r,center:a,radius:i,extrusionDirection:s}}convertAlignedDimension(n,o){const e=this.libredwg,a=this.getDimensionCommonAttrs(n),i=e.dwg_dynapi_entity_data(n,"clone_ins_pt"),r=e.dwg_dynapi_entity_data(n,"xline1_pt"),s=e.dwg_dynapi_entity_data(n,"xline2_pt"),_=e.dwg_dynapi_entity_data(n,"ins_rotation"),d=e.dwg_dynapi_entity_data(n,"oblique_angle");return{subclassMarker:"AcDbAlignedDimension",...o,...a,insertionPoint:i,subDefinitionPoint1:r,subDefinitionPoint2:s,rotationAngle:_??0,obliqueAngle:d}}convert3PointAngularDimension(n,o){const e=this.libredwg,a=this.getDimensionCommonAttrs(n),i=e.dwg_dynapi_entity_data(n,"xline1_pt"),r=e.dwg_dynapi_entity_data(n,"xline2_pt"),s=e.dwg_dynapi_entity_data(n,"center_pt"),_=e.dwg_dynapi_entity_data(n,"xline2end_pt");return{subclassMarker:"AcDb3PointAngularDimension",...o,...a,subDefinitionPoint1:i,subDefinitionPoint2:r,centerPoint:s,arcPoint:_}}convert2LineAngularDimension(n,o){const e=this.libredwg,a=this.getDimensionCommonAttrs(n),i=e.dwg_dynapi_entity_data(n,"xline1start_pt"),r=e.dwg_dynapi_entity_data(n,"xline1end_pt"),s=e.dwg_dynapi_entity_data(n,"xline2start_pt"),_=e.dwg_dynapi_entity_data(n,"xline2end_pt"),d=e.dwg_dynapi_entity_data(n,"def_pt");return _&&(a.definitionPoint=_),{subclassMarker:"AcDb2LineAngularDimension",...o,...a,subDefinitionPoint1:i??r??d??_,subDefinitionPoint2:s??_??d??r,centerPoint:d??s??r??_,arcPoint:_??a.definitionPoint}}convertDiameterDimension(n,o){const e=this.libredwg,a=this.getDimensionCommonAttrs(n),i=e.dwg_dynapi_entity_data(n,"first_arc_pt"),r=e.dwg_dynapi_entity_data(n,"leader_len");return{subclassMarker:"AcDbDiametricDimension",...o,...a,centerPoint:i,leaderLength:r}}convertOrdinateDimension(n,o){const e=this.libredwg,a=this.getDimensionCommonAttrs(n),i=e.dwg_dynapi_entity_data(n,"feature_location_pt"),r=e.dwg_dynapi_entity_data(n,"leader_endpt");return{subclassMarker:"AcDbOrdinateDimension",...o,...a,subDefinitionPoint1:i,subDefinitionPoint2:r}}convertRadiusDimension(n,o){const e=this.libredwg,a=this.getDimensionCommonAttrs(n),i=e.dwg_dynapi_entity_data(n,"first_arc_pt"),r=e.dwg_dynapi_entity_data(n,"leader_len");return{subclassMarker:"AcDbRadialDimension",...o,...a,centerPoint:i,leaderLength:r}}convertEllise(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"center"),i=e.dwg_dynapi_entity_data(n,"sm_axis"),r=e.dwg_dynapi_entity_data(n,"extrusion"),s=e.dwg_dynapi_entity_data(n,"axis_ratio"),_=e.dwg_dynapi_entity_data(n,"start_angle"),d=e.dwg_dynapi_entity_data(n,"end_angle");return{type:"ELLIPSE",...o,center:a,majorAxisEndPoint:i,extrusionDirection:r,axisRatio:s,startAngle:_,endAngle:d}}convertHatch(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"extrusion"),i=e.dwg_dynapi_entity_data(n,"name"),r=e.dwg_dynapi_entity_data(n,"is_solid_fill"),s=e.dwg_dynapi_entity_data(n,"is_associative"),_=e.dwg_dynapi_entity_data(n,"num_paths"),d=e.dwg_dynapi_entity_data(n,"paths"),c=e.dwg_ptr_to_hatch_path_array(d,_),E=e.dwg_dynapi_entity_data(n,"style"),u=e.dwg_dynapi_entity_data(n,"pattern_type"),l=e.dwg_dynapi_entity_data(n,"angle"),I=e.dwg_dynapi_entity_data(n,"scale_spacing"),f=e.dwg_dynapi_entity_data(n,"num_deflines"),A=e.dwg_dynapi_entity_data(n,"deflines"),h=e.dwg_ptr_to_hatch_defline_array(A,f),g=e.dwg_dynapi_entity_data(n,"pixel_size"),p=e.dwg_dynapi_entity_data(n,"num_seeds"),C=e.dwg_dynapi_entity_data(n,"seeds"),L=e.dwg_ptr_to_point2d_array(C,p),R={...o,extrusionDirection:a,patternName:i,solidFill:r?mt.SolidFill:mt.PatternFill,associativity:s?St.Associative:St.NonAssociative,numberOfBoundaryPaths:_,boundaryPaths:this.convertHatchBoundaryPaths(c),hatchStyle:E,patternType:u,patternAngle:l,patternScale:I,numberOfDefinitionLines:f,definitionLines:h.map(Y=>({angle:Y.angle,base:Y.pt0,offset:Y.offset,numberOfDashLengths:Y.dashes.length,dashLengths:Y.dashes})),pixelSize:g,numberOfSeedPoints:p,seedPoints:L};if(e.dwg_dynapi_entity_data(n,"is_gradient_fill")>0){const Y=e.dwg_dynapi_entity_data(n,"gradient_name"),U=e.dwg_dynapi_entity_data(n,"gradient_angle"),H=e.dwg_dynapi_entity_data(n,"gradient_shift"),v=e.dwg_dynapi_entity_data(n,"gradient_tint"),B=e.dwg_dynapi_entity_data(n,"single_color_gradient"),V=e.dwg_dynapi_entity_data(n,"colors"),K=e.dwg_ptr_to_hatch_gradient_color_array(V,B==1?1:2);return{type:"HATCH",...R,gradientFlag:en.Gradient,gradientColorFlag:B==1?Nt.OneColor:Nt.TwoColor,gradientName:Y,gradientRotation:U,gradientDefinition:H,colorTint:v,gradientColors:K}}else return{type:"HATCH",...R}}convertHatchBoundaryPaths(n){return n.filter(e=>e.num_segs_or_paths>0).map(e=>{const a={boundaryPathTypeFlag:e.flag};if(e.flag&2)return{...a,hasBulge:e.bulges_present,isClosed:e.closed,numberOfVertices:e.num_segs_or_paths,vertices:e.polyline_paths.map(i=>({x:i.point.x,y:i.point.y,bulge:i.bulge}))};{const i=e.segs.map(r=>{if(r.curve_type==ct.Line)return{type:dt.Line,start:r.first_endpoint,end:r.second_endpoint};if(r.curve_type==ct.CircularArc)return{type:dt.Circular,center:r.center,radius:r.radius,startAngle:r.start_angle,endAngle:r.end_angle,isCCW:r.is_ccw};if(r.curve_type==ct.EllipticArc)return{type:dt.Elliptic,center:r.center,end:r.endpoint,lengthOfMinorAxis:r.minor_major_ratio,startAngle:r.start_angle,endAngle:r.end_angle,isCCW:r.is_ccw};if(r.curve_type==ct.Spline)return{type:dt.Spline,degree:r.degree,isRational:r.is_rational,isPeriodic:r.is_periodic,numberOfKnots:r.num_knots,numberOfControlPoints:r.num_control_points,knots:r.knots,controlPoints:r.control_points,numberOfFitData:r.num_fitpts,fitDatum:r.fitpts,startTangent:r.start_tangent,endTangent:r.end_tangent}});return{...a,numberOfEdges:e.num_segs_or_paths,edges:i}}})}convertImage(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"class_version"),i=e.dwg_dynapi_entity_data(n,"pt0"),r=e.dwg_dynapi_entity_data(n,"uvec"),s=e.dwg_dynapi_entity_data(n,"vvec"),_=e.dwg_dynapi_entity_data(n,"image_size"),d=e.dwg_dynapi_entity_data(n,"display_props"),c=e.dwg_dynapi_entity_data(n,"clipping"),E=e.dwg_dynapi_entity_data(n,"brightness"),u=e.dwg_dynapi_entity_data(n,"contrast"),l=e.dwg_dynapi_entity_data(n,"fade"),I=e.dwg_dynapi_entity_data(n,"clip_mode"),f=e.dwg_dynapi_entity_data(n,"clip_boundary_type"),A=e.dwg_dynapi_entity_data(n,"num_clip_verts"),h=e.dwg_dynapi_entity_data(n,"clip_verts"),g=e.dwg_ptr_to_point3d_array(h,A),p=e.dwg_dynapi_entity_data(n,"imagedef"),C=e.dwg_ref_get_id(p)??"",L=e.dwg_dynapi_entity_data(n,"imagedefreactor"),R=e.dwg_ref_get_id(L)??"";return{type:"IMAGE",...o,version:a,position:i,uPixel:r,vPixel:s,imageSize:_,imageDefHandle:C,flags:d,clipping:c,brightness:E,contrast:u,fade:l,imageDefReactorHandle:R,clippingBoundaryType:f,countBoundaryPoints:A,clippingBoundaryPath:g,clipMode:I}}convertInsert(n,o){const e=this.libredwg;let a="";const i=e.dwg_dynapi_entity_data(n,"block_header");if(i){const A=e.dwg_ref_get_object(i);if(A){const h=e.dwg_object_to_object_tio(A);h&&(a=e.dwg_entity_block_header_get_block(h).name)}}a||(a=e.dwg_dynapi_entity_data(n,"block_name"));const r=e.dwg_dynapi_entity_data(n,"ins_pt"),s=e.dwg_dynapi_entity_data(n,"scale"),_=e.dwg_dynapi_entity_data(n,"rotation"),d=e.dwg_dynapi_entity_data(n,"num_cols"),c=e.dwg_dynapi_entity_data(n,"num_rows"),E=e.dwg_dynapi_entity_data(n,"col_spacing"),u=e.dwg_dynapi_entity_data(n,"row_spacing"),l=e.dwg_dynapi_entity_data(n,"extrusion"),I=e.dwg_entity_insert_get_attribs(n),f=[];return I.forEach(A=>{const h=e.dwg_object_to_entity(A),g=e.dwg_object_to_entity_tio(A);if(h&&g){const p=this.getCommonAttrs(h);e.dwg_object_get_fixedtype(A)==T.DWG_TYPE_ATTRIB&&f.push(this.convertAttrib(g,p))}}),{type:"INSERT",...o,name:a,insertionPoint:r,xScale:s?s.x:1,yScale:s?s.y:1,zScale:s?s.z:1,rotation:_,columnCount:d,rowCount:c,columnSpacing:E,rowSpacing:u,extrusionDirection:l,attribs:f}}convertLeader(n,o){const e=this.libredwg,a=e.dwg_entity_mtext_get_style_name(n),i=e.dwg_dynapi_entity_data(n,"arrowhead_type"),r=e.dwg_dynapi_entity_data(n,"path_type"),s=e.dwg_dynapi_entity_data(n,"annot_type"),_=e.dwg_dynapi_entity_data(n,"hookline_dir"),d=e.dwg_dynapi_entity_data(n,"hookline_on"),c=e.dwg_dynapi_entity_data(n,"box_height"),E=e.dwg_dynapi_entity_data(n,"box_width"),u=e.dwg_dynapi_entity_data(n,"num_points"),l=e.dwg_dynapi_entity_data(n,"points"),I=u>0?e.dwg_ptr_to_point3d_array(l,u):[],f=e.dwg_dynapi_entity_data(n,"byblock_color"),A=e.dwg_dynapi_entity_data(n,"extrusion"),h=e.dwg_dynapi_entity_data(n,"x_direction"),g=e.dwg_dynapi_entity_data(n,"inspt_offset"),p=e.dwg_dynapi_entity_data(n,"endptproj");return{type:"LEADER",...o,styleName:a,isArrowheadEnabled:i>0,isSpline:r>0,leaderCreationFlag:s,isHooklineSameDirection:_>0,isHooklineExists:d>0,textHeight:c,textWidth:E,numberOfVertices:u,vertices:I,byBlockColor:f,normal:A,horizontalDirection:h,offsetFromBlock:g,offsetFromAnnotation:p}}convertLine(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"start"),i=e.dwg_dynapi_entity_data(n,"end"),r=e.dwg_dynapi_entity_data(n,"thickness"),s=e.dwg_dynapi_entity_data(n,"extrusion");return{type:"LINE",...o,thickness:r,startPoint:a,endPoint:i,extrusionDirection:s}}convertLWPolyline(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"num_points"),i=e.dwg_dynapi_entity_data(n,"flag"),r=e.dwg_dynapi_entity_data(n,"const_width"),s=e.dwg_dynapi_entity_data(n,"elevation"),_=e.dwg_dynapi_entity_data(n,"thickness"),d=e.dwg_dynapi_entity_data(n,"extrusion"),c=[],E=e.dwg_dynapi_entity_data(n,"num_points"),u=e.dwg_dynapi_entity_data(n,"points"),l=e.dwg_ptr_to_point2d_array(u,E),I=e.dwg_dynapi_entity_data(n,"num_bulges"),f=e.dwg_dynapi_entity_data(n,"bulges"),A=e.dwg_ptr_to_double_array(f,I);return l.forEach((h,g)=>{c.push({id:g,x:h.x,y:h.y,bulge:A.length>g?A[g]:0})}),{type:"LWPOLYLINE",...o,numberOfVertices:a,flag:i,constantWidth:r,elevation:s,thickness:_,extrusionDirection:d,vertices:c}}convertMLine(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"scale"),i=e.dwg_dynapi_entity_data(n,"flags"),r=e.dwg_dynapi_entity_data(n,"justification"),s=e.dwg_dynapi_entity_data(n,"base_point"),_=e.dwg_dynapi_entity_data(n,"extrusion"),d=e.dwg_dynapi_entity_data(n,"num_lines"),c=e.dwg_dynapi_entity_data(n,"num_verts"),E=e.dwg_dynapi_entity_data(n,"verts"),u=e.dwg_ptr_to_mline_vertex_array(E,c),l=[];return u.forEach(I=>{l.push({vertex:I.vertex,vertexDirection:I.vertex_direction,miterDirection:I.miter_direction,numberOfLines:I.num_lines,lines:I.lines.map(f=>({numberOfSegmentParams:f.num_segparms,segmentParams:f.segparms,numberOfAreaFillParams:f.num_areafillparms,areaFillParams:f.areafillparms}))})}),{type:"MLINE",...o,scale:a,flags:i,justification:r,startPoint:s,extrusionDirection:_,numberOfLines:d,numberOfVertices:c,vertices:l,mlineStyle:""}}convertMultiLeader(n,o){const e=this.libredwg,a=S=>e.dwg_dynapi_entity_data(n,S),i=(S,x,lt)=>e.dwg_dynapi_subclass_data(S,x,lt),r=S=>e.dwg_ref_get_id(S),s=S=>S>0,_=S=>S!=null?we(S):void 0,d=a("class_version"),c=r(a("mleaderstyle")),E=a("flags"),u=a("type"),l=_(a("line_color")),I=r(a("line_ltype")),f=a("line_linewt"),A=s(a("has_landing")),h=s(a("has_dogleg")),g=a("landing_dist"),p=r(a("arrow_handle")),C=a("arrow_size"),L=a("style_content"),R=r(a("text_style")),D=a("text_left"),Y=a("text_right"),U=a("text_angletype"),H=a("text_alignment"),v=_(a("text_color")),B=s(a("has_text_frame")),V=r(a("block_style")),K=_(a("block_color")),tt=a("block_scale"),nt=a("block_rotation"),et=a("style_attachment"),ot=s(a("is_annotative")),q=s(a("is_neg_textdir")),at=a("ipe_alignment");let z=a("justification");const it=a("attach_dir"),At=a("attach_bottom"),ft=a("attach_top"),It=a("scale_factor"),ht=a("num_arrowheads"),Tt=a("arrowheads"),Pt=e.dwg_dynapi_subclass_size("LEADER_ArrowHead"),rt=[];for(let S=0;S0&&(qt=e.dwg_ptr_to_double_array(lt,x)[0]),oe=s(i(m,"MLEADER_Content_MText","word_break")),ae=i(m,"MLEADER_Content_MText","default_text")}let ie;if(Gt){const S=i(m,"MLEADER_Content_Block","transform");ie={blockContentId:r(i(m,"MLEADER_Content_Block","block_table")),normal:i(m,"MLEADER_Content_Block","normal"),position:i(m,"MLEADER_Content_Block","location"),scale:i(m,"MLEADER_Content_Block","scale"),rotation:i(m,"MLEADER_Content_Block","rotation"),color:_(i(m,"MLEADER_Content_Block","color")),transformationMatrix:S?e.dwg_ptr_to_double_array(S,16):void 0}}const ve=i(G,"MLEADER_AnnotContext","num_leaders"),Be=i(G,"MLEADER_AnnotContext","leaders"),ke=e.dwg_dynapi_subclass_size("LEADER_Node"),Fe=e.dwg_dynapi_subclass_size("LEADER_Line"),re=e.dwg_dynapi_subclass_size("LEADER_Break"),Rn=[];for(let S=0;S0?e.dwg_ptr_to_point3d_array(Ke,de):[],$e=i(Q,"LEADER_Line","num_breaks"),Je=i(Q,"LEADER_Line","breaks"),Sn=[];for(let Nn=0;Nn<$e;Nn++){const ce=Je+Nn*re;Sn.push({start:i(ce,"LEADER_Break","start"),end:i(ce,"LEADER_Break","end")})}_e.push({vertices:ze,leaderLineIndex:i(Q,"LEADER_Line","line_index"),breaks:Sn.length>0?Sn:void 0})}Rn.push({lastLeaderLinePoint:lt?i(x,"LEADER_Node","lastleaderlinepoint"):void 0,lastLeaderLinePointSet:lt,doglegVector:se?i(x,"LEADER_Node","dogleg_vector"):void 0,doglegVectorSet:se,doglegLength:i(x,"LEADER_Node","dogleg_length"),breaks:mn.length>0?mn:void 0,leaderBranchIndex:i(x,"LEADER_Node","branch_index"),leaderLines:_e})}return{type:"MULTILEADER",...o,subclassMarker:"AcDbMLeader",version:d,leaderStyleId:c,propertyOverrideFlag:E,leaderLineType:u,leaderLineColor:l,leaderLineTypeId:I,leaderLineWeight:f,landingEnabled:A,doglegEnabled:h,doglegLength:g,arrowheadId:p,arrowheadSize:C||In,contentType:L,textStyleId:R,textLeftAttachmentType:D||hn,textRightAttachmentType:Y||Tn,textAngleType:U||Pn,textAlignmentType:H||gn,textColor:v,textFrameEnabled:B,landingGap:un,textAttachment:An,textFlowDirection:Mt,blockContentId:V,blockContentColor:K,blockContentScale:tt,blockContentRotation:nt,blockContentConnectionType:et,annotativeScaleEnabled:ot,arrowheadOverrides:rt.length>0?rt:void 0,blockAttributes:Ct.length>0?Ct:void 0,textDirectionNegative:q,textAlignInIPE:at,textAttachmentPoint:z,textAttachmentDirection:it,bottomTextAttachmentDirection:At,topTextAttachmentDirection:ft,contentScale:It||ln,contentBasePosition:En,normal:Wt,textHeight:fn,textRotation:bt,textWidth:wt,textLineSpacingFactor:Ot,textLineSpacingStyle:vt,textAnchor:Bt,textDirection:kt,textBackgroundColor:Ft,textBackgroundScaleFactor:Ut,textBackgroundTransparency:Ht,textBackgroundColorOn:Vt,textFillOn:Xt,textColumnType:Kt,textUseAutoHeight:zt,textColumnWidth:$t,textColumnGutterWidth:Jt,textColumnFlowReversed:Zt,textColumnHeight:qt,textUseWordBreak:oe,textContent:ae,hasMText:Yt,hasBlock:Gt,blockContent:ie,planeOrigin:pn,planeXAxisDirection:Cn,planeYAxisDirection:yn,planeNormalReversed:Ln,leaderSections:Rn.length>0?Rn:void 0}}convertOle2Frame(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"oleversion"),i=e.dwg_dynapi_entity_data(n,"oleclient"),r=e.dwg_dynapi_entity_data(n,"data_size");let s=e.dwg_dynapi_entity_data(n,"pt1"),_=e.dwg_dynapi_entity_data(n,"pt2");const d=e.dwg_dynapi_entity_data(n,"lock_aspect"),c=e.dwg_dynapi_entity_data(n,"type"),E=e.dwg_dynapi_entity_data(n,"mode"),u=e.dwg_entity_ole2frame_get_data(n),l=u?Lt(u):"";if(u){const I=jo(u);I&&(s=I.upperLeft,_=I.lowerRight)}return{type:"OLE2FRAME",...o,oleVersion:a,oleClient:i,dataSize:r,leftUpPoint:s,rightDownPoint:_,lockAspect:d,oleObjectType:c,tileModeDescriptor:E,binaryData:l}}convertOleFrame(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"flag"),i=e.dwg_dynapi_entity_data(n,"mode"),r=e.dwg_dynapi_entity_data(n,"data_size"),s=e.dwg_entity_oleframe_get_data(n),_=s?Lt(s):"";return{type:"OLEFRAME",...o,flag:a,mode:i,dataSize:r,binaryData:_}}convertMText(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"ins_pt"),i=e.dwg_dynapi_entity_data(n,"text_height"),r=e.dwg_dynapi_entity_data(n,"rect_height"),s=e.dwg_dynapi_entity_data(n,"rect_width"),_=e.dwg_dynapi_entity_data(n,"extents_width"),d=e.dwg_dynapi_entity_data(n,"extents_height"),c=e.dwg_dynapi_entity_data(n,"attachment"),E=e.dwg_dynapi_entity_data(n,"flow_dir"),u=e.dwg_dynapi_entity_data(n,"text"),l=e.dwg_entity_mtext_get_style_name(n),I=e.dwg_dynapi_entity_data(n,"extrusion"),f=e.dwg_dynapi_entity_data(n,"x_axis_dir"),A=e.dwg_dynapi_entity_data(n,"linespace_style"),h=e.dwg_dynapi_entity_data(n,"linespace_factor"),g=e.dwg_dynapi_entity_data(n,"bg_fill_flag"),p=e.dwg_dynapi_entity_data(n,"bg_fill_scale"),C=e.dwg_dynapi_entity_data(n,"bg_fill_color"),L=e.dwg_dynapi_entity_data(n,"bg_fill_trans"),R=e.dwg_dynapi_entity_data(n,"column_type"),D=e.dwg_dynapi_entity_data(n,"flow_reversed"),Y=e.dwg_dynapi_entity_data(n,"auto_height"),U=e.dwg_dynapi_entity_data(n,"column_width"),H=e.dwg_dynapi_entity_data(n,"gutter"),v=e.dwg_dynapi_entity_data(n,"num_column_heights"),B=e.dwg_dynapi_entity_data(n,"column_heights"),V=e.dwg_ptr_to_double_array(B,v);return{type:"MTEXT",...o,insertionPoint:a,textHeight:i,rectHeight:r,rectWidth:s,extentsHeight:d,extentsWidth:_,attachmentPoint:c,drawingDirection:E,text:u,styleName:l,extrusionDirection:I,direction:f,rotation:0,lineSpacingStyle:A,lineSpacing:h,backgroundFill:g,fillBoxScale:p,backgroundFillColor:C.rgb,backgroundFillTransparency:L,columnType:R,columnFlowReversed:D,columnAutoHeight:Y,columnWidth:U,columnGutter:H,columnHeightCount:v,columnHeights:V}}convertPoint(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"x"),i=e.dwg_dynapi_entity_data(n,"y"),r=e.dwg_dynapi_entity_data(n,"z"),s=e.dwg_dynapi_entity_data(n,"thickness"),_=e.dwg_dynapi_entity_data(n,"extrusion"),d=e.dwg_dynapi_entity_data(n,"x_ang");return{type:"POINT",...o,position:{x:a,y:i,z:r},thickness:s,extrusionDirection:_,angle:d}}convertPolyline2d(n,o,e){const a=this.libredwg,i=a.dwg_dynapi_entity_data(n,"flag"),r=a.dwg_dynapi_entity_data(n,"curve_type"),s=a.dwg_dynapi_entity_data(n,"start_width"),_=a.dwg_dynapi_entity_data(n,"end_width"),d=a.dwg_dynapi_entity_data(n,"elevation"),c=a.dwg_dynapi_entity_data(n,"thickness"),E=a.dwg_dynapi_entity_data(n,"extrusion"),u=a.dwg_entity_polyline_2d_get_vertices(e);return{type:"POLYLINE2D",...o,flag:i,smoothType:r,startWidth:s,endWidth:_,elevation:d,thickness:c,extrusionDirection:E,vertices:u.map(l=>({x:l.point.x,y:l.point.y,z:l.point.z,startWidth:l.start_width,endWidth:l.end_width,bulge:l.bulge,flag:l.flag,tangentDirection:l.tangent_dir})),meshMVertexCount:0,meshNVertexCount:0,surfaceMDensity:0,surfaceNDensity:0}}convertPolyline3d(n,o,e){const a=this.libredwg,i=a.dwg_dynapi_entity_data(n,"flag"),r=a.dwg_dynapi_entity_data(n,"curve_type"),s=a.dwg_dynapi_entity_data(n,"start_width"),_=a.dwg_dynapi_entity_data(n,"end_width"),d=a.dwg_dynapi_entity_data(n,"extrusion"),c=a.dwg_entity_polyline_3d_get_vertices(e);return{type:"POLYLINE3D",...o,flag:i,smoothType:r,startWidth:s,endWidth:_,extrusionDirection:d,vertices:c.map(E=>({x:E.point.x,y:E.point.y,z:E.point.z,flag:E.flag}))}}convertProxyEntity(n,o,e){const a=this.libredwg,i=a.dwg_dynapi_entity_data(n,"proxy_id"),r=a.dwg_dynapi_entity_data(n,"class_id"),s=a.dwg_dynapi_entity_data(n,"data_numbits"),_=a.dwg_dynapi_entity_data(n,"version"),d=a.dwg_dynapi_entity_data(n,"from_dxf"),c=a.dwg_dynapi_entity_data(n,"num_objids"),E=a.dwg_entity_get_preview(e),u=E?.length??0,l=a.dwg_entity_proxy_entity_get_entity_data(n),I=E?Lt(E):void 0,f=l?Lt(l):void 0;let A;if(c>0){const p=a.dwg_dynapi_entity_data(n,"objids");p&&(A=a.dwg_ptr_to_object_ref_array(p,c).map(L=>w(L.absolute_ref)))}const h=this.getOriginalDxfName(r),g={type:"ACAD_PROXY_ENTITY",subclassMarker:"AcDbProxyEntity",...o,proxyEntityClassId:i||498,applicationEntityClassId:r};return h&&(g.originalDxfName=h),u>0&&(g.graphicsDataSize=u),I&&(g.graphicsData=I),s>0&&(g.entityDataSize=s),f&&(g.entityData=f),A&&A.length>0&&(g.linkedObjectIds=A),_&&(g.objectDrawingFormat=_),(d===0||d===1)&&(g.originalDataFormat=d),g}getOriginalDxfName(n){if(this.classes.length===0||n<0)return;const o=n>=500?n-500:n;if(o>=0&&o0?e.dwg_ptr_to_point3d_array(l,u):[],f=e.dwg_dynapi_entity_data(n,"num_blverts"),A=e.dwg_dynapi_entity_data(n,"blverts"),h=f>0?e.dwg_ptr_to_point3d_array(A,f):[],g=e.dwg_dynapi_entity_data(n,"geometrySettingHardId"),p=e.dwg_ref_get_handle_absolute_ref(g)??0n;return{type:"SECTION",...o,state:a,flags:i,name:r,verticalDirection:s,topHeight:_,bottomHeight:d,indicatorTransparency:c,indicatorColor:E.rgb,numberOfVertices:u,vertices:I,numberOfBackLineVertices:f,backLineVertices:h,geometrySettingHardId:p}}convertShape(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"ins_pt"),i=e.dwg_dynapi_entity_data(n,"scale"),r=e.dwg_dynapi_entity_data(n,"rotation"),s=e.dwg_dynapi_entity_data(n,"width_factor"),_=e.dwg_dynapi_entity_data(n,"oblique_angle"),d=e.dwg_dynapi_entity_data(n,"thickness"),c=e.dwg_dynapi_entity_data(n,"extrusion"),E=e.dwg_dynapi_entity_data(n,"style_id"),u=e.dwg_entity_text_get_style_name(n);return{type:"SHAPE",subclassMarker:"AcDbShape",...o,thickness:d,insertionPoint:a,size:i,shapeNumber:E,styleName:u,rotation:r,xScale:s,obliqueAngle:_,extrusionDirection:c}}convertSolid(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"corner1"),i=e.dwg_dynapi_entity_data(n,"corner2"),r=e.dwg_dynapi_entity_data(n,"corner3"),s=e.dwg_dynapi_entity_data(n,"corner4"),_=e.dwg_dynapi_entity_data(n,"thickness"),d=e.dwg_dynapi_entity_data(n,"extrusion");return{type:"SOLID",...o,corner1:a,corner2:i,corner3:r,corner4:s,thickness:_,extrusionDirection:d}}convertSpline(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"splineflags"),i=e.dwg_dynapi_entity_data(n,"degree"),r=e.dwg_dynapi_entity_data(n,"knot_tol"),s=e.dwg_dynapi_entity_data(n,"num_knots"),_=e.dwg_dynapi_entity_data(n,"knots"),d=e.dwg_ptr_to_double_array(_,s),c=e.dwg_dynapi_entity_data(n,"fit_tol"),E=e.dwg_dynapi_entity_data(n,"num_fit_pts"),u=e.dwg_dynapi_entity_data(n,"fit_pts"),l=e.dwg_ptr_to_point3d_array(u,E),I=e.dwg_dynapi_entity_data(n,"weighted"),f=e.dwg_dynapi_entity_data(n,"ctrl_tol"),A=e.dwg_dynapi_entity_data(n,"num_ctrl_pts"),h=e.dwg_dynapi_entity_data(n,"ctrl_pts"),g=e.dwg_ptr_to_point4d_array(h,A),p=e.dwg_dynapi_entity_data(n,"beg_tan_vec"),C=e.dwg_dynapi_entity_data(n,"end_tan_vec");return{type:"SPLINE",...o,flag:a,degree:i,numberOfKnots:s,numberOfControlPoints:A,numberOfFitPoints:E,knotTolerance:r,controlTolerance:f,fitTolerance:c,startTangent:p,endTangent:C,knots:d,weights:I?g.map(L=>L.w):void 0,controlPoints:g.map(L=>({x:L.x,y:L.y,z:L.z})),fitPoints:l}}convertTable(n,o){const e=this.libredwg,a=e.dwg_dynapi_subclass_data(n,"ldata","name"),i=e.dwg_dynapi_entity_data(n,"ins_pt"),r=e.dwg_dynapi_entity_data(n,"horiz_direction"),s=e.dwg_dynapi_entity_data(n,"flag_for_table_value"),_=e.dwg_dynapi_entity_data(n,"num_rows"),d=e.dwg_dynapi_entity_data(n,"num_cols"),c=e.dwg_dynapi_entity_data(n,"row_heights"),E=e.dwg_ptr_to_double_array(c,_),u=e.dwg_dynapi_entity_data(n,"col_widths"),l=e.dwg_ptr_to_double_array(u,d),I=e.dwg_dynapi_entity_data(n,"tablestyle"),f=e.dwg_ref_get_id(I)??"",A=e.dwg_dynapi_entity_data(n,"block_header"),h=e.dwg_ref_get_id(A)??"",g=e.dwg_dynapi_entity_data(n,"table_flag_override"),p=e.dwg_dynapi_entity_data(n,"border_color_overrides_flag"),C=e.dwg_dynapi_entity_data(n,"border_lineweight_overrides_flag"),L=e.dwg_dynapi_entity_data(n,"border_visibility_overrides_flag"),R=e.dwg_dynapi_entity_data(n,"num_cells"),D=e.dwg_dynapi_entity_data(n,"cells"),Y=e.dwg_ptr_to_table_cell_array(D,R);return{type:"ACAD_TABLE",...o,name:a,startPoint:i,directionVector:r,tableValue:s,rowCount:_,columnCount:d,overrideFlag:g,borderColorOverrideFlag:p,borderLineWeightOverrideFlag:C,borderVisibilityOverrideFlag:L,rowHeightArr:E,columnWidthArr:l,tableStyleId:f,blockRecordHandle:h,cells:this.convertTableCells(Y),bmpPreview:""}}convertTableCells(n){return n.map(o=>({text:o.text_value,attachmentPoint:o.cell_alignment,textStyle:o.text_style?String(o.text_style):void 0,rotation:o.rotation,cellType:o.type,flagValue:o.flags,mergedValue:o.is_merged_value,autoFit:o.is_autofit_flag,topBorderVisibility:!!o.top_visibility,bottomBorderVisibility:!!o.bottom_visibility,leftBorderVisibility:!!o.left_visibility,rightBorderVisibility:!!o.right_visibility,overrideFlag:o.cell_flag_override,virtualEdgeFlag:o.virtual_edge_flag,blockTableRecordId:o.block_handle?String(o.block_handle.absolute_ref??""):void 0,blockScale:o.block_scale,blockAttrNum:o.attr_defs?.length??0,attrDefineId:o.attr_defs?.map(e=>String(e.attdef?.absolute_ref??"")),textHeight:o.text_height??0,extendedCellFlags:o.additional_data_flag}))}convertTextBase(n){const o=this.libredwg,e=o.dwg_dynapi_entity_data(n,"text_value"),a=o.dwg_dynapi_entity_data(n,"thickness"),i=o.dwg_dynapi_entity_data(n,"ins_pt"),r=o.dwg_dynapi_entity_data(n,"alignment_pt"),s=o.dwg_dynapi_entity_data(n,"rotation"),_=o.dwg_dynapi_entity_data(n,"height"),d=o.dwg_dynapi_entity_data(n,"width_factor"),c=o.dwg_dynapi_entity_data(n,"oblique_angle"),E=o.dwg_entity_text_get_style_name(n),u=o.dwg_dynapi_entity_data(n,"generation"),l=o.dwg_dynapi_entity_data(n,"horiz_alignment"),I=o.dwg_dynapi_entity_data(n,"vert_alignment"),f=o.dwg_dynapi_entity_data(n,"extrusion");return{text:e,thickness:a,startPoint:i,endPoint:r,textHeight:_,rotation:s,xScale:d,obliqueAngle:c,styleName:E,generationFlag:u,halign:l,valign:I,extrusionDirection:f}}convertText(n,o){return{type:"TEXT",...o,...this.convertTextBase(n)}}convertTolerance(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"ins_pt"),i=e.dwg_dynapi_entity_data(n,"text_value"),r=e.dwg_dynapi_entity_data(n,"x_direction"),s=e.dwg_dynapi_entity_data(n,"extrusion"),_=e.dwg_entity_get_dimstyle_name(n);return{type:"TOLERANCE",...o,styleName:_,insertionPoint:a,text:i,extrusionDirection:s,xAxisDirection:r}}convertViewport(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"center"),i=e.dwg_dynapi_entity_data(n,"width"),r=e.dwg_dynapi_entity_data(n,"height"),s=e.dwg_dynapi_entity_data(n,"on_off"),_=e.dwg_dynapi_entity_data(n,"VIEWCTR"),d=e.dwg_dynapi_entity_data(n,"SNAPBASE"),c=e.dwg_dynapi_entity_data(n,"SNAPUNIT"),E=e.dwg_dynapi_entity_data(n,"GRIDUNIT"),u=e.dwg_dynapi_entity_data(n,"VIEWDIR"),l=e.dwg_dynapi_entity_data(n,"view_target"),I=e.dwg_dynapi_entity_data(n,"lens_length"),f=e.dwg_dynapi_entity_data(n,"front_clip_z"),A=e.dwg_dynapi_entity_data(n,"back_clip_z"),h=e.dwg_dynapi_entity_data(n,"VIEWSIZE"),g=e.dwg_dynapi_entity_data(n,"SNAPANG"),p=e.dwg_dynapi_entity_data(n,"twist_angle"),C=e.dwg_dynapi_entity_data(n,"circle_zoom"),L=e.dwg_dynapi_entity_data(n,"status_flag"),R=e.dwg_dynapi_entity_data(n,"style_sheet"),D=e.dwg_dynapi_entity_data(n,"render_mode"),Y=e.dwg_dynapi_entity_data(n,"UCSVP"),U=e.dwg_dynapi_entity_data(n,"ucsorg"),H=e.dwg_dynapi_entity_data(n,"ucsxdir"),v=e.dwg_dynapi_entity_data(n,"ucsydir"),B=e.dwg_dynapi_entity_data(n,"named_ucs"),V=e.dwg_ref_get_id(B),K=e.dwg_dynapi_entity_data(n,"base_ucs"),tt=e.dwg_ref_get_id(K),nt=e.dwg_dynapi_entity_data(n,"UCSORTHOVIEW"),et=e.dwg_dynapi_entity_data(n,"ucs_elevation"),ot=e.dwg_dynapi_entity_data(n,"shadeplot_mode"),q=e.dwg_dynapi_entity_data(n,"use_default_lights"),at=e.dwg_dynapi_entity_data(n,"default_lighting_type"),z=e.dwg_dynapi_entity_data(n,"brightness"),it=e.dwg_dynapi_entity_data(n,"contrast"),At=e.dwg_dynapi_entity_data(n,"grid_major"),ft=e.dwg_dynapi_entity_data(n,"background"),It=e.dwg_ref_get_id(ft),ht=e.dwg_dynapi_entity_data(n,"shadeplot"),Tt=e.dwg_ref_get_id(ht),Pt=e.dwg_dynapi_entity_data(n,"visualstyle"),rt=e.dwg_ref_get_id(Pt),gt=e.dwg_dynapi_entity_data(n,"sun"),pt=e.dwg_ref_get_id(gt);return{type:"VIEWPORT",...o,viewportCenter:a,width:i,height:r,status:s,viewportId:0,displayCenter:_,snapBase:d,snapSpacing:c,gridSpacing:E,viewDirection:u,targetPoint:l,perspectiveLensLength:I,frontClipZ:f,backClipZ:A,viewHeight:h,snapAngle:g,viewTwistAngle:p,circleZoomPercent:C,statusBitFlags:L,sheetName:R,renderMode:D,ucsPerViewport:Y,ucsOrigin:U,ucsXAxis:H,ucsYAxis:v,ucsId:V??"",ucsBaseId:tt??"",orthographicType:nt,elevation:et,shadePlotMode:ot,majorGridFrequency:At,backgroundId:It??"",shadePlotId:Tt??"",visualStyleId:rt??"",isDefaultLighting:!!q,defaultLightingType:at,brightness:z,contrast:it,sunId:pt??""}}convertWipeout(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"class_version"),i=e.dwg_dynapi_entity_data(n,"pt0"),r=e.dwg_dynapi_entity_data(n,"uvec"),s=e.dwg_dynapi_entity_data(n,"vvec"),_=e.dwg_dynapi_entity_data(n,"image_size"),d=e.dwg_dynapi_entity_data(n,"display_props"),c=e.dwg_dynapi_entity_data(n,"clipping"),E=e.dwg_dynapi_entity_data(n,"brightness"),u=e.dwg_dynapi_entity_data(n,"contrast"),l=e.dwg_dynapi_entity_data(n,"fade"),I=e.dwg_dynapi_entity_data(n,"clip_mode"),f=e.dwg_dynapi_entity_data(n,"clip_boundary_type"),A=e.dwg_dynapi_entity_data(n,"num_clip_verts"),h=e.dwg_dynapi_entity_data(n,"clip_verts"),g=e.dwg_ptr_to_point2d_array(h,A),p=e.dwg_dynapi_entity_data(n,"imagedef"),C=e.dwg_ref_get_absref(p)??0,L=e.dwg_dynapi_entity_data(n,"imagedefreactor"),R=e.dwg_ref_get_absref(L)??0;return{type:"WIPEOUT",...o,version:a,position:i,uPixel:r,vPixel:s,imageSize:_,imageDefHandle:C,flags:d,clipping:c,brightness:E,contrast:u,fade:l,imageDefReactorHandle:R,clippingBoundaryType:f,countBoundaryPoints:A,clippingBoundaryPath:g,clipMode:I}}convertXline(n,o){const e=this.libredwg,a=e.dwg_dynapi_entity_data(n,"point"),i=e.dwg_dynapi_entity_data(n,"vector");return{type:"XLINE",...o,firstPoint:a,unitDirection:i}}getDimensionCommonAttrs(n){const o=this.libredwg,e=o.dwg_dynapi_entity_data(n,"class_version"),a=o.dwg_entity_get_block_name(n,"block"),i=o.dwg_dynapi_entity_data(n,"def_pt"),r=o.dwg_dynapi_entity_data(n,"text_midpt"),s=o.dwg_dynapi_entity_data(n,"attachmentPoint"),_=o.dwg_dynapi_entity_data(n,"flag"),d=o.dwg_dynapi_entity_data(n,"lspace_factor"),c=o.dwg_dynapi_entity_data(n,"lspace_factor"),E=o.dwg_dynapi_entity_data(n,"act_measurement"),u=o.dwg_dynapi_entity_data(n,"user_text"),l=o.dwg_dynapi_entity_data(n,"text_rotation"),I=o.dwg_dynapi_entity_data(n,"ins_rotation"),f=o.dwg_dynapi_entity_data(n,"extrusion"),A=o.dwg_entity_get_dimstyle_name(n);return{type:"DIMENSION",version:e,name:a,definitionPoint:i,textPoint:r,dimensionType:_,attachmentPoint:s,textLineSpacingStyle:d,textLineSpacingFactor:c||1,measurement:E,text:u,textRotation:l,ocsRotation:I,extrusionDirection:f,styleName:A}}getCommonAttrs(n){const o=this.libredwg,e=o.dwg_object_entity_get_color_object(n),a=e.method,i=e.index;let r;(a==194||(e.rgb>>>24&255)===194)&&(r=e.rgb&16777215);const s=this.getLayerName(n),_=o.dwg_object_entity_get_handle_object(n),d=o.dwg_object_entity_get_ownerhandle_object(n),c=o.dwg_object_entity_get_xdicobjhandle_object(n),E=this.getLtypeName(n),u=o.dwg_object_entity_get_line_weight(n),l=o.dwg_object_entity_get_ltype_scale(n),I=!o.dwg_object_entity_get_invisible(n),f=o.dwg_object_entity_get_xdata(n);return{handle:w(_.value),ownerDictionaryHardId:w(c.absolute_ref),ownerBlockRecordSoftId:w(d.absolute_ref),layer:s,color:r,colorIndex:i,colorName:e.name,lineType:E,lineweight:u,lineTypeScale:l,isVisible:I,transparency:e.alpha,transparencyType:e.alpha_type,xdata:f}}getLayerName(n){const e=this.libredwg.dwg_object_entity_get_layer_object_ref(n);return this.layers.get(w(e.handleref.value))??"0"}getLtypeName(n){const e=this.libredwg.dwg_object_entity_get_ltype_object_ref(n);return this.ltypes.get(w(e.handleref.value))??""}}class ye{constructor(n){M(this,"libredwg");M(this,"entityConverter");this.libredwg=n,this.entityConverter=new ta(n)}convert(n){this.entityConverter.clear();const o={tables:{APPID:{entries:[]},BLOCK_RECORD:{entries:[]},DIMSTYLE:{entries:[]},LAYER:{entries:[]},LTYPE:{entries:[]},STYLE:{entries:[]},VPORT:{entries:[]}},objects:{DICTIONARY:[],IMAGEDEF:[],LAYER_FILTER:[],LAYER_INDEX:[],LAYOUT:[],MLEADERSTYLE:[],SPATIAL_FILTER:[],XRECORD:[]},header:{},entities:[],classes:[]},e=this.libredwg;this.convertHeader(n,o.header),this.convertClasses(n,o.classes),this.entityConverter.setClasses(o.classes);const a=e.dwg_get_num_objects(n);for(let s=0;s{o.entities.push(u),u.type==="INSERT"&&u.attribs.forEach(l=>{o.entities.push(l)})})}break;case T.DWG_TYPE_DIMSTYLE:o.tables.DIMSTYLE.entries.push(this.convertDimStyle(d,_));break;case T.DWG_TYPE_STYLE:o.tables.STYLE.entries.push(this.convertStyle(d,_));break;case T.DWG_TYPE_VPORT:o.tables.VPORT.entries.push(this.convertViewport(d,_));break;case T.DWG_TYPE_DICTIONARY:o.objects.DICTIONARY.push(this.convertDictionary(d,_));break;case T.DWG_TYPE_IMAGEDEF:o.objects.IMAGEDEF.push(this.convertImageDef(d,_));break;case T.DWG_TYPE_LAYERFILTER:o.objects.LAYER_FILTER.push(this.convertLayerFilter(d,_));break;case T.DWG_TYPE_LAYER_INDEX:o.objects.LAYER_INDEX.push(this.convertLayerIndex(d,_));break;case T.DWG_TYPE_LAYOUT:o.objects.LAYOUT.push(this.convertLayout(d,_));break;case T.DWG_TYPE_MLEADERSTYLE:o.objects.MLEADERSTYLE.push(this.convertMLeaderStyle(d,_));break;case T.DWG_TYPE_SPATIAL_FILTER:o.objects.SPATIAL_FILTER.push(this.convertSpatialFilter(d,_));break;case T.DWG_TYPE_XRECORD:o.objects.XRECORD.push(this.convertXRecord(d,_));break}}}const i=o.entities.filter(s=>s.type==="VIEWPORT");i.sort((s,_)=>{const d=parseInt(s.handle,16),c=parseInt(_.handle,16);return d-c}),i.forEach((s,_)=>{s.viewportId=_+1});const r=e.dwg_bmp(n);return r?.data?.length&&(o.thumbnailImage=r.data),o}getConversionStats(){return{unknownEntityCount:this.entityConverter.unknownEntityCount}}safeObjectTio(n){const o=this.libredwg;try{if(o.dwg_object_get_supertype(n)!=on.DWG_SUPERTYPE_OBJECT)return null;const e=o.dwg_object_to_object_tio(n);return e||null}catch{return null}}convertHeader(n,o){const e=this.libredwg;We.forEach(a=>{let i=a;(a=="DIMBLK"||a=="DIMBLK1"||a=="DIMBLK2")&&(i=i+"_T");let r=e.dwg_dynapi_header_data(n,i);a=="CELTYPE"||a=="CLAYER"||a=="CLAYER"||a=="DIMSTYLE"||a=="DIMTXSTY"||a=="TEXTSTYLE"?r=r?e.dwg_ref_get_object_name(r):"":a=="DRAGVS"&&(r=r?e.dwg_ref_get_absref(r)??2:2),o[a]=r})}convertClasses(n,o){const e=this.libredwg,a=e.dwg_get_num_classes(n);for(let i=0;i0&&(f=Lt(A));let h=this.convertEntities(o,a.handle);if(!h||h.length==0){h=[];const g=e.dwg_dynapi_entity_data(n,"entities");if(g){const p=e.dwg_ptr_to_object_ref_ptr_array(g,r),C=this.entityConverter;for(let L=0;L>>24&255)===195?A=u.rgb&255:f==194||(u.rgb>>>24&255)===194?h=u.rgb&16777215:u.index>=1&&u.index<=255&&(A=u.index),{...a,standardFlag:i,colorIndex:A,color:h,colorName:u.name,transparency:u.alpha,lineType:I,frozen:r!=0,off:s!=0,frozenInNew:_!=0,locked:d!=0,plotFlag:c,lineweight:E,plotStyleNameObjectId:"",materialObjectId:""}}convertLineType(n,o){const e=this.libredwg,a=this.getCommonTableEntryAttrs(n,o),i=e.dwg_dynapi_entity_data(n,"flag"),r=e.dwg_dynapi_entity_data(n,"description"),s=e.dwg_dynapi_entity_data(n,"numdashes"),_=e.dwg_dynapi_entity_data(n,"pattern_len"),d=e.dwg_dynapi_entity_data(n,"dashes"),c=d?e.dwg_ptr_to_ltype_dash_array(d,s):[];return{...a,description:r,standardFlag:i,numberOfLineTypes:s,totalPatternLength:_,pattern:this.convertLineTypePattern(c)}}convertLineTypePattern(n){const o=[];return n.forEach(e=>{o.push({elementLength:e.length||0,elementTypeFlag:e.complex_shapecode,shapeNumber:e.shape_flag,styleObjectId:e.style?w(this.libredwg.dwg_ref_get_absref(e.style)):void 0,scale:e.scale,rotation:e.rotation,offsetX:e.x_offset,offsetY:e.y_offset,text:e.text})}),o}convertStyle(n,o){const e=this.libredwg,a=this.getCommonTableEntryAttrs(n,o),i=e.dwg_dynapi_entity_data(n,"flag"),r=e.dwg_dynapi_entity_data(n,"width_factor"),s=e.dwg_dynapi_entity_data(n,"oblique_angle"),_=e.dwg_dynapi_entity_data(n,"generation"),d=e.dwg_dynapi_entity_data(n,"last_height"),c=e.dwg_dynapi_entity_data(n,"font_file"),E=e.dwg_dynapi_entity_data(n,"bigfont_file");return{...a,standardFlag:i,fixedTextHeight:0,widthFactor:r,obliqueAngle:s,textGenerationFlag:_,lastHeight:d,font:c,bigFont:E}}convertViewport(n,o){const e=this.libredwg,a=this.getCommonTableEntryAttrs(n,o),i=e.dwg_dynapi_entity_data(n,"flag"),r=e.dwg_dynapi_entity_data(n,"VIEWSIZE"),s=e.dwg_dynapi_entity_data(n,"aspect_ratio"),_=e.dwg_dynapi_entity_data(n,"VIEWCTR"),d=e.dwg_dynapi_entity_data(n,"view_target"),c=e.dwg_dynapi_entity_data(n,"VIEWDIR"),E=e.dwg_dynapi_entity_data(n,"view_twist"),u=e.dwg_dynapi_entity_data(n,"lens_length"),l=e.dwg_dynapi_entity_data(n,"front_clip_z"),I=e.dwg_dynapi_entity_data(n,"back_clip_z"),f=e.dwg_dynapi_entity_data(n,"VIEWMODE"),A=e.dwg_dynapi_entity_data(n,"render_mode"),h=e.dwg_dynapi_entity_data(n,"use_default_lights")!=0,g=e.dwg_dynapi_entity_data(n,"default_lightning_type"),p=e.dwg_dynapi_entity_data(n,"brightness"),C=e.dwg_dynapi_entity_data(n,"contrast"),L=e.dwg_dynapi_entity_data(n,"ambient_color"),R=e.dwg_dynapi_entity_data(n,"lower_left"),D=e.dwg_dynapi_entity_data(n,"upper_right"),Y=e.dwg_dynapi_entity_data(n,"circle_zoom"),U=e.dwg_dynapi_entity_data(n,"UCSICON"),H=e.dwg_dynapi_entity_data(n,"GRIDUNIT"),v=e.dwg_dynapi_entity_data(n,"SNAPANG"),B=e.dwg_dynapi_entity_data(n,"SNAPBASE"),V=e.dwg_dynapi_entity_data(n,"SNAPUNIT"),K=e.dwg_dynapi_entity_data(n,"ucsorg"),tt=e.dwg_dynapi_entity_data(n,"ucsxdir"),nt=e.dwg_dynapi_entity_data(n,"ucsydir"),et=e.dwg_dynapi_entity_data(n,"ucs_elevation"),ot=e.dwg_dynapi_entity_data(n,"grid_major"),q=e.dwg_dynapi_entity_data(n,"background"),at=q?e.dwg_ref_get_id(q)??"":void 0,z=e.dwg_dynapi_entity_data(n,"visualstyle"),it=z?e.dwg_ref_get_id(z)??"":void 0;return{...a,standardFlag:i,lowerLeftCorner:R,upperRightCorner:D,center:_,snapBasePoint:B,snapSpacing:V,gridSpacing:H,viewDirectionFromTarget:c,viewTarget:d,lensLength:u,frontClippingPlane:l,backClippingPlane:I,viewHeight:r,aspectRatio:s,snapRotationAngle:v,viewTwistAngle:E,circleSides:Y,frozenLayers:[],styleSheet:"",renderMode:A,viewMode:f,ucsIconSetting:U,ucsOrigin:K,ucsXAxis:tt,ucsYAxis:nt,orthographicType:0,elevation:et,shadePlotSetting:0,majorGridLines:ot,backgroundObjectId:at,visualStyleObjectId:it,isDefaultLightingOn:h,defaultLightingType:g,brightness:p,contrast:C,ambientColor:L.index}}getCommonTableEntryAttrs(n,o){const e=this.libredwg,a=e.dwg_object_get_tio(o),i=e.dwg_object_object_get_ownerhandle_object(a),r=e.dwg_object_get_handle_object(o);return{handle:w(r.value),ownerHandle:w(i.absolute_ref),name:e.dwg_dynapi_entity_data(n,"name")}}convertDictionary(n,o){const e=this.libredwg,a=this.getCommonObjectAttrs(o),i=e.dwg_dynapi_entity_data(n,"is_hardowner"),r=e.dwg_dynapi_entity_data(n,"cloning"),s=e.dwg_dynapi_entity_data(n,"numitems"),_=e.dwg_dynapi_entity_data(n,"itemhandles"),d=e.dwg_ptr_to_object_ref_array(_,s),c=e.dwg_object_dictionary_get_texts(o),E={};return d.forEach((u,l)=>E[c[l]]=w(u.absolute_ref)),{...a,isHardOwner:!!i,cloningFlag:r,entries:E}}convertImageDef(n,o){const e=this.libredwg,a=this.getCommonObjectAttrs(o),i=e.dwg_dynapi_entity_data(n,"image_size"),r=e.dwg_dynapi_entity_data(n,"file_path"),s=e.dwg_dynapi_entity_data(n,"is_loaded"),_=e.dwg_dynapi_entity_data(n,"pixel_size"),d=e.dwg_dynapi_entity_data(n,"resunits");return{...a,fileName:r,size:i,sizeOfOnePixel:_,isLoaded:s,resolutionUnits:d}}convertLayerFilter(n,o){const e=this.libredwg,a=this.getCommonObjectAttrs(o),i=e.dwg_dynapi_entity_data(n,"num_names")??0,r=e.dwg_dynapi_entity_data(n,"names"),s=r&&i>0?e.dwg_ptr_to_wchar_string_array(r,i).filter(_=>_!=null&&_!==""):[];return{...a,...s.length?{layerNames:s}:{}}}convertLayerIndex(n,o){const e=this.libredwg,a=this.getCommonObjectAttrs(o),i=e.dwg_dynapi_entity_data(n,"last_updated"),r=e.dwg_dynapi_entity_data(n,"num_entries")??0,s=e.dwg_dynapi_entity_data(n,"entries"),_=e.dwg_dynapi_subclass_size("LAYER_entry"),d=[],c=[],E=[];if(s&&_>0)for(let u=0;ue.dwg_dynapi_entity_data(n,d),r=d=>e.dwg_ref_get_id(d),s=d=>d>0,_=d=>d!=null?we(d):void 0;return{...a,subclassMarker:"AcDbMLeaderStyle",unknown1:i("class_version"),contentType:i("content_type"),drawMLeaderOrderType:i("mleader_order"),drawLeaderOrderType:i("leader_order"),maxLeaderSegmentPoints:i("max_points"),firstSegmentAngleConstraint:i("first_seg_angle"),secondSegmentAngleConstraint:i("second_seg_angle"),leaderLineType:i("type"),leaderLineColor:_(i("line_color")),leaderLineTypeId:r(i("line_type")),leaderLineWeight:i("linewt"),landingEnabled:s(i("has_landing")),landingGap:i("landing_gap"),doglegEnabled:s(i("has_dogleg")),doglegLength:i("landing_dist"),description:i("description"),arrowheadId:r(i("arrow_head")),arrowheadSize:i("arrow_head_size"),defaultMTextContents:i("text_default"),textStyleId:r(i("text_style")),textLeftAttachmentType:i("attach_left"),textAngleType:i("text_angle_type"),textAlignmentType:i("text_align_type"),textRightAttachmentType:i("attach_right"),textColor:_(i("text_color")),textHeight:i("text_height"),textFrameEnabled:s(i("has_text_frame")),textAlignAlwaysLeft:s(i("text_always_left")),alignSpace:i("align_space"),blockContentId:r(i("block")),blockContentColor:_(i("block_color")),blockContentScale:i("block_scale"),blockContentScaleEnabled:s(i("use_block_scale")),blockContentRotation:i("block_rotation"),blockContentRotationEnabled:s(i("use_block_rotation")),blockContentConnectionType:i("block_connection"),scale:i("scale"),overwritePropertyValue:s(i("is_changed")),annotative:s(i("is_annotative")),breakGapSize:i("break_size"),textAttachmentDirection:i("attach_dir"),bottomTextAttachmentDirection:i("attach_bottom"),topTextAttachmentDirection:i("attach_top"),unknown2:s(i("text_extended"))}}convertSpatialFilter(n,o){const e=this.libredwg,a=this.getCommonObjectAttrs(o),i=e.dwg_dynapi_entity_data(n,"origin"),r=e.dwg_dynapi_entity_data(n,"num_clip_verts"),s=e.dwg_dynapi_entity_data(n,"clip_verts"),_=e.dwg_ptr_to_point2d_array(s,r),d=e.dwg_dynapi_entity_data(n,"extrusion"),c=e.dwg_dynapi_entity_data(n,"display_boundary_on"),E=e.dwg_dynapi_entity_data(n,"front_clip_on"),u=e.dwg_dynapi_entity_data(n,"front_clip_z"),l=e.dwg_dynapi_entity_data(n,"back_clip_on"),I=e.dwg_dynapi_entity_data(n,"back_clip_z"),f=e.dwg_dynapi_entity_data(n,"transform"),A=e.dwg_ptr_to_double_array(f,12),h=e.dwg_dynapi_entity_data(n,"inverse_transform"),g=e.dwg_ptr_to_double_array(h,12);return{...a,origin:i,numberOfPointsOnClipBoundary:r,vertices:_,extrusionDirection:d,clipBoundaryVisible:!!c,frontClippingPlaneFlag:!!E,frontClippingPlaneDistance:u,backClippingPlaneFlag:!!l,backClippingPlaneDistance:I,matrix:A,invertBlockMatrix:g}}convertXRecord(n,o){const e=this.libredwg,a=this.getCommonObjectAttrs(o),i=e.dwg_dynapi_entity_data(n,"cloning"),r=e.dwg_object_xrecord_get_xdata(n)??[],s=e.dwg_object_get_tio(o),_=s?e.dwg_object_object_get_xdicobjhandle_object(s):null,d=_&&_.absolute_ref?w(_.absolute_ref):void 0;return{...a,...i!=null?{cloning:i}:{},...d?{extensionDictionary:d}:{},data:r}}getCommonObjectAttrs(n){const o=this.libredwg,e=o.dwg_object_get_tio(n),a=o.dwg_object_object_get_ownerhandle_object(e),i=o.dwg_object_get_handle_object(n);return{handle:w(i.value),ownerHandle:w(a.absolute_ref)}}}class k{constructor(){M(this,"min");M(this,"max");M(this,"valid");this.min={x:1/0,y:1/0},this.max={x:-1/0,y:-1/0},this.valid=!1}expandByPoint(n){return this.min.x=Math.min(this.min.x,n.x),this.min.y=Math.min(this.min.y,n.y),this.max.x=Math.max(this.max.x,n.x),this.max.y=Math.max(this.max.y,n.y),this.valid=!0,this}transform(n,o){const e=this.getCorners().map(a=>({x:a.x*n.x+o.x,y:a.y*n.y+o.y}));this.reset();for(const a of e)this.expandByPoint(a);return this}rotate(n,o){const e=Math.cos(n),a=Math.sin(n),i=this.getCorners().map(r=>{const s=r.x-o.x,_=r.y-o.y;return{x:o.x+s*e-_*a,y:o.y+s*a+_*e}});this.reset();for(const r of i)this.expandByPoint(r);return this}clone(){const n=new k;return n.min={x:this.min.x,y:this.min.y},n.max={x:this.max.x,y:this.max.y},n.valid=this.valid,n}reset(){this.min={x:1/0,y:1/0},this.max={x:-1/0,y:-1/0},this.valid=!1}getCorners(){return[{x:this.min.x,y:this.min.y},{x:this.min.x,y:this.max.y},{x:this.max.x,y:this.min.y},{x:this.max.x,y:this.max.y}]}}function na(t,n,o,e,a){const i=o.length;if(i===0)throw new Error("points must not be empty");const r=o[0].length;if(t<0||t>1)throw new Error(`t out of bounds [0,1]: ${t}`);if(n<1)throw new Error("degree must be at least 1 (linear)");if(n>i-1)throw new Error("degree must be less than or equal to point count - 1");const s=a??new Array(i).fill(1),_=e??(()=>{const f=[];for(let A=0;A=_[u]&&t<=_[u+1]);u++);const l=new Array(i);for(let f=0;fu-n-1+f;A--){const h=_[A+n+1-f]-_[A],g=h===0?0:(t-_[A])/h;for(let p=0;p0&&this._color<=16777215){let n=this._color.toString(16).toUpperCase();for(;n.length<6;)n="0"+n;return`0x${n}`}return""}get cssColor(){return`rgb(${this.red},${this.green},${this.blue})`}get red(){return this.color!=null?this.color>>16&255:null}get green(){return this.color!=null?this.color>>8&255:null}get blue(){return this.color!=null?this.color&255:null}get colorIndex(){return this._colorIndex}set colorIndex(n){n==null?this._colorIndex=null:(this._colorIndex=yt(n,0,256),this._color=Mn[n],this._colorName=this.getColorNameByValue(this._color))}get colorName(){return this._colorName}set colorName(n){if(n){const o=tn[n.toLowerCase()];o!==void 0?(this._colorName=n,this._color=o,this._colorIndex=this.getColorIndexByValue(this._color)):console.warn("Unknown color: "+n)}else this._colorName=null}get hasColorName(){return this._colorName==null}get hasColorIndex(){return this._colorIndex==null}get isByLayer(){return this.colorIndex==256}setByLayer(){return this.colorIndex=256,this}get isByBlock(){return this.colorIndex==0}setByBlock(){return this.colorIndex=0,this}setScalar(n){return this.setRGB(n,n,n),this}setRGB(n,o,e){const a=Math.round(yt(n,0,255)),i=Math.round(yt(o,0,255)),r=Math.round(yt(e,0,255));return this.color=(a<<16)+(i<<8)+r,this}setColorName(n){const o=tn[n.toLowerCase()];return o!==void 0?this.color=o:console.warn("Unknown color "+n),this}clone(){const n=new sn;return n.colorIndex=this.colorIndex,n.color=this.color,n._colorName=this._colorName,this}copy(n){return this.colorIndex=n.colorIndex,this.color=n.color,this._colorName=n._colorName,this}equals(n){return n.color==this.color&&n.colorIndex==this.colorIndex&&n._colorName==this._colorName}toString(){return this.isByLayer?"ByLayer":this.isByBlock?"ByBlock":this.colorName?this.colorName:this.hexColor}getColorNameByValue(n){for(const[o,e]of Object.entries(tn))if(e===n)return o;return null}getColorIndexByValue(n){const o=Mn.length-1;for(let e=1;e({x:a.x,y:a.y,bulge:a.bulge}));n&&e.push(e[0]);for(let a=0,i=e.length;a[c.x,c.y]),_=[e[o]],d=[e[o],e[e.length-1-o]];for(let c=o+1;c${e}`}:{bbox:o,element:e}}line(n){const o=new k().expandByPoint({x:n.startPoint.x,y:n.startPoint.y}).expandByPoint({x:n.endPoint.x,y:n.endPoint.y}),e=``;return{bbox:o,element:e}}ray(n){const e=n.firstPoint,a={x:e.x+n.unitDirection.x*1e4,y:e.y+n.unitDirection.y*1e4},i=new k().expandByPoint(e).expandByPoint(a),r=``;return{bbox:i,element:r}}xline(n){const e={x:n.firstPoint.x-n.unitDirection.x*1e4,y:n.firstPoint.y-n.unitDirection.y*1e4},a={x:n.firstPoint.x+n.unitDirection.x*1e4,y:n.firstPoint.y+n.unitDirection.y*1e4},i=new k().expandByPoint(e).expandByPoint(a),r=``;return{bbox:i,element:r}}extractMTextLines(n){return n.replace(/\\U\+([0-9A-Fa-f]{4})/g,(o,e)=>String.fromCharCode(parseInt(e,16))).replace(/\\P/g,` +`).replace(/\\[LOlo]/g,"").replace(/\\[Ff][^;\\]*?(?:\|[^;\\]*)*;/g,"").replace(/\\[KkCcHhWwTtAa][^;\\]*;?/g,"").replace(/\\[a-zA-Z]+;?/g,"").replace(/%%(d|p|c|%)/gi,"").replace(/\\\\/g,"\\").replace(/\\~/g," ").replace(/[{}]/g,"").split(` +`).map(o=>o.trim()).filter(o=>o.length>0)}lines(n,o,e,a,i="start"){const r=new k().expandByPoint({x:e.x,y:e.y}).expandByPoint({x:e.x+a,y:e.y-n.length*o*1.5}),s=n.map((_,d)=>{const c=e.x,E=e.y-d*o*1.5,u=`translate(${c},${E}) scale(1,-1) translate(${-c},${-E})`;return`${_}`});return{bbox:r,element:s.join(` +`)}}mtext(n){const o=n.textHeight,e=n.insertionPoint,a=this.extractMTextLines(n.text),i=n.attachmentPoint;let r="start";return i==J.BottomCenter||i==J.MiddleCenter||i==J.TopCenter?r="middle":(i==J.BottomRight||i==J.MiddleRight||i==J.TopRight)&&(r="end"),this.lines(a,o,e,n.extentsWidth,r)}table(n){const{rowCount:o,columnCount:e,rowHeightArr:a,columnWidthArr:i,startPoint:r,cells:s}=n,_=r.x,d=r.y,c=[];for(let A=0,h=d;A{const L=[];C.topBorderVisibility&&L.push(``),C.bottomBorderVisibility&&L.push(``),C.leftBorderVisibility&&L.push(``),C.rightBorderVisibility&&L.push(``);const R=A+g/2,D=h+p/2+C.textHeight/3,Y=`${C.text}`;return[...L,Y].join(` +`)}).join(` +`),u=i.reduce((A,h)=>A+h,0),l=a.reduce((A,h)=>A+h,0),I=new k().expandByPoint({x:_,y:d}).expandByPoint({x:_+u,y:d+l}),f=` + + ${E} + + `.trim();return{bbox:I,element:f}}text(n){const o=n.textHeight,e=n.startPoint,a=[n.text];let i=n.endPoint.x-n.endPoint.x;n.halign==0&&(i=n.text.length*o+n.startPoint.x);let r="start";return n.halign==Dt.CENTER?r="middle":n.halign==Dt.RIGHT&&(r="end"),this.lines(a,o,e,i,r)}vertices(n,o=!1){const e=n.reduce((i,r)=>i.expandByPoint(r),new k);let a=n.reduce((i,r,s)=>(i+=s===0?"M":"L",i+=r.x+","+r.y,i),"");return o&&(a+="Z"),{bbox:e,element:``}}circle(n){const o=new k().expandByPoint({x:n.center.x+n.radius,y:n.center.y+n.radius}).expandByPoint({x:n.center.x-n.radius,y:n.center.y-n.radius}),e=``;return{bbox:o,element:e}}ellipseOrArc(n,o,e,a,i,r,s){const _=Math.sqrt(e*e+a*a),d=i*_,c=-Math.atan2(-a,e),E=this.bboxEllipseOrArc(n,o,e,a,i,r,s);if(Math.abs(r-s)<1e-9||Math.abs(r-s+Math.PI*2)<1e-9){const u=``;return{bbox:E,element:u}}else{const u=this.rotate({x:Math.cos(r)*_,y:Math.sin(r)*d},c),l={x:n+u.x,y:o+u.y},I=this.rotate({x:Math.cos(s)*_,y:Math.sin(s)*d},c),f={x:n+I.x,y:o+I.y},h=(s`;return{bbox:E,element:p}}}bboxEllipseOrArc(n,o,e,a,i,r,s){for(;r<0;)r+=Math.PI*2;for(;s<=r;)s+=Math.PI*2;const _=[];if(Math.abs(e)<1e-12||Math.abs(a)<1e-12)for(let l=0;l<4;l++)_.push(l/2*Math.PI);else _[0]=Math.atan(-a*i/e)-Math.PI,_[1]=Math.atan(e*i/a)-Math.PI,_[2]=_[0]-Math.PI,_[3]=_[1]-Math.PI;for(let l=4;l>=0;l--){for(;_[l]s&&_.splice(l,1)}_.push(r),_.push(s);const d=_.map(l=>({x:Math.cos(l),y:Math.sin(l)})),c=[[e,-a*i],[a,e*i]];return d.map(l=>({x:l.x*c[0][0]+l.y*c[0][1]+n,y:l.x*c[1][0]+l.y*c[1][1]+o})).reduce((l,I)=>(l.expandByPoint(I),l),new k)}ellipse(n){const{bbox:o,element:e}=this.ellipseOrArc(n.center.x,n.center.y,n.majorAxisEndPoint.x,n.majorAxisEndPoint.y,n.axisRatio,n.startAngle,n.endAngle);return{bbox:o,element:e}}arc(n){const{bbox:o,element:e}=this.ellipseOrArc(n.center.x,n.center.y,n.radius,0,1,n.startAngle,n.endAngle);return{bbox:o,element:e}}dimension(n){const o=this.blockMap.get(n.name);return o?{bbox:o.bbox,element:``}:null}insert(n){const o=this.blockMap.get(n.name);if(o){const e=n.insertionPoint,a=n.rotation*(180/Math.PI),i=`translate(${e.x},${e.y}) rotate(${a}) scale(${n.xScale},${n.yScale})`;return{bbox:o.bbox.clone().transform({x:n.xScale,y:n.yScale},{x:e.x,y:e.y}).rotate(n.rotation,e),element:``}}return null}block(n,o){const e=n.entities,{bbox:a,elements:i}=e.reduce((r,s)=>{const _=this.entityToBoundsAndElement(s);if(_){const{bbox:d,element:c}=_;d.valid&&(r.bbox.expandByPoint(d.min),r.bbox.expandByPoint(d.max));const E=this.getEntityColor(o.tables.LAYER.entries,s),u=s.type=="TEXT"||s.type=="MTEXT"?E.cssColor:"none";E.isByBlock?r.elements.push(`${c}`):r.elements.push(`${c}`)}return r},{bbox:new k,elements:[]});return a.valid?{bbox:a,element:`${i.join(` +`)}`}:null}entityToBoundsAndElement(n){let o=null;switch(n.type){case"ARC":o=this.arc(n);break;case"CIRCLE":o=this.circle(n);break;case"DIMENSION":o=this.dimension(n);break;case"ELLIPSE":o=this.ellipse(n);break;case"INSERT":o=this.insert(n);break;case"LINE":o=this.line(n);break;case"LWPOLYLINE":{const e=n,a=!!(e.flag&512),i=aa(e,a);o=this.vertices(i,a);break}case"MTEXT":o=this.mtext(n);break;case"SPLINE":{const e=n;o=this.vertices(this.interpolateBSpline(e.controlPoints,e.degree,e.knots,25,e.weights));break}case"POLYLINE":break;case"RAY":o=this.ray(n);break;case"TABLE":case"ACAD_TABLE":o=this.table(n);break;case"TEXT":o=this.text(n);break;case"XLINE":o=this.xline(n);break;default:o=null;break}return o?this.addFlipXIfApplicable(n,o):null}getEntityColor(n,o){const e=new te;if(o.colorIndex!=null?e.colorIndex=o.colorIndex:o.colorName?e.colorName=o.colorName:o.color!=null&&(e.color=o.color),e.colorIndex==7&&(e.colorIndex=256),e.isByLayer){const a=n.find(i=>i.name===o.layer);a!=null&&(e.colorIndex=a.colorIndex)}return e.color==null&&(e.color=16777215),e}convert(n){let o=null;this.blockMap.clear();let e="";n.tables.BLOCK_RECORD.entries.forEach(r=>{if(Oe(r.name))o=r;else{const s=this.block(r,n);s&&(e+=s.element,this.blockMap.set(r.name,s))}});const a=this.block(o,n),i=a&&a.bbox.valid?{x:a.bbox.min.x,y:-a.bbox.max.y,width:a.bbox.max.x-a.bbox.min.x,height:a.bbox.max.y-a.bbox.min.y}:{x:0,y:0,width:0,height:0};return` + + ${e} + + ${a?a.element:""} + +`}}var ne;(function(t){t[t.BMP=2]="BMP",t[t.WMF=3]="WMF",t[t.PNG=6]="PNG"})(ne||(ne={}));const _n=class _n{constructor(n){M(this,"wasmInstance");M(this,"decoder");return this.wasmInstance=n,new Proxy(this,{get:(o,e,a)=>e in o?Reflect.get(o,e,a):Reflect.get(o.wasmInstance,e,a)})}dwg_read_data(n,o){if(o==rn.DWG){const e="tmp.dwg";try{this.wasmInstance.FS.writeFile(e,new Uint8Array(n));const a=this.wasmInstance.dwg_read_file(e);if(a.error&an.OUTOFMEM)throw this.wasmInstance.dwg_abandon(a.data),new Error("Failed to decode DWG: out of WASM memory. Rebuild with pnpm build:wasm (INITIAL_MEMORY=1GB) or use a smaller file.");return a.error!=0&&console.warn("Open dwg file with error code:",a.error),a.data}finally{this.wasmInstance.FS.analyzePath(e,!1).exists&&this.wasmInstance.FS.unlink(e)}}}dwg_write_dxf(n){const o="tmp.dwg",e="tmp.dxf";try{this.wasmInstance.FS.writeFile(o,new Uint8Array(n));const a=this.wasmInstance.dwg_write_dxf(o,e);return a!=0?(console.log("Convert dwg to dxf with error code: ",a),null):this.wasmInstance.FS.readFile(e)}finally{this.wasmInstance.FS.analyzePath(o,!1).exists&&this.wasmInstance.FS.unlink(o),this.wasmInstance.FS.analyzePath(e,!1).exists&&this.wasmInstance.FS.unlink(e)}}dwg_get_version_type(n){const o=this.wasmInstance.dwg_get_version_type(n);return be[o]}dwg_get_codepage(n){return this.wasmInstance.dwg_get_codepage(n)}dwg_bmp(n){return this.wasmInstance.dwg_bmp(n)}dwg_get_num_classes(n){return this.wasmInstance.dwg_get_num_classes(n)}dwg_get_class(n,o){return this.wasmInstance.dwg_get_class(n,o)}convert(n){const o=this.dwg_get_codepage(n),e=bn(o);return this.decoder=new TextDecoder(e),new ye(this).convert(n)}convertEx(n){const o=this.dwg_get_codepage(n),e=bn(o);this.decoder=new TextDecoder(e);const a=new ye(this);return{database:a.convert(n),stats:a.getConversionStats()}}dwg_to_svg(n){return new ia().convert(n)}dwg_free(n){if(n)try{this.wasmInstance.dwg_free(n)}catch{try{this.wasmInstance.dwg_abandon(n)}catch{}}}dwg_free_object(n){this.wasmInstance.dwg_free_object(n)}dwg_ref_object(n,o){return this.wasmInstance.dwg_ref_object(n,o)}dwg_ref_object_silent(n,o){return this.wasmInstance.dwg_ref_object_silent(n,o)}dwg_ref_object_relative(n,o,e){return this.wasmInstance.dwg_ref_object_relative(n,o,e)}dwg_resolve_handle(n,o){return this.wasmInstance.dwg_resolve_handle(n,o)}dwg_resolve_handle_silent(n,o){return this.wasmInstance.dwg_resolve_handle_silent(n,o)}dwg_resolve_handleref(n,o){return this.wasmInstance.dwg_resolve_handleref(n,o)}dwg_ref_get_absref(n){if(!n)return null;const o=this.wasmInstance.dwg_ref_get_absref(n);return o===0?null:o}dwg_ref_get_handle_value(n){if(!n)return null;const o=this.wasmInstance.dwg_ref_get_handle_value(n);return o===0n?null:o}dwg_ref_get_handle_absolute_ref(n){if(!n)return null;const o=this.wasmInstance.dwg_ref_get_handle_absolute_ref(n);return o===0n?null:o}dwg_obj_get_handle_value(n){if(!n)return null;const o=this.wasmInstance.dwg_obj_get_handle_value(n);return o===0n?null:o}dwg_ref_get_id(n){const o=this.dwg_ref_get_absref(n);return o?.toString(16).toUpperCase()}dwg_ref_get_object_name(n){const o=this.wasmInstance,e=o.dwg_ref_get_object(n),a=o.dwg_object_to_object_tio(e);return this.dwg_dynapi_entity_data(a,"name")}dwg_obj_obj_to_object(n){return this.wasmInstance.dwg_obj_obj_to_object(n)}dwg_obj_generic_to_object(n){return this.wasmInstance.dwg_obj_generic_to_object(n)}dwg_object_to_object(n){return this.wasmInstance.dwg_object_to_object(n)}dwg_object_to_object_tio(n){return this.wasmInstance.dwg_object_to_object_tio(n)}dwg_object_to_entity(n){return this.wasmInstance.dwg_object_to_entity(n)}dwg_object_to_entity_tio(n){return this.wasmInstance.dwg_object_to_entity_tio(n)}dwg_getall_entities_in_model_space(n){const o=this.wasmInstance,e=o.dwg_model_space_object(n),a=[];let i=o.get_first_owned_entity(e);for(;i;)a.push(i),i=o.get_next_owned_entity(e,i);return a}dwg_getall_object_by_type(n,o){const e=this.wasmInstance,a=e.dwg_get_num_objects(n),i=[];for(let r=0;r`${n}/${e}`});return this.createByWasmInstance(o)}};M(_n,"instance");let ee=_n;const Le=Object.freeze(Object.defineProperty({__proto__:null,get DwgAttachmentPoint(){return J},get DwgBoundaryPathEdgeType(){return dt},get DwgBoundaryPathTypeFlag(){return Kn},get DwgCodePage(){return Wn},get DwgDictionaryCloningFlags(){return Qn},get DwgDimensionTextHorizontal(){return Fn},get DwgDimensionTextLineSpacing(){return On},get DwgDimensionTextVertical(){return vn},get DwgDimensionToleranceTextVertical(){return Un},get DwgDimensionType(){return wn},get DwgDimensionZeroSuppression(){return Bn},get DwgDimensionZeroSuppressionAngular(){return kn},get DwgHatchAssociativity(){return St},get DwgHatchBoundaryAnnotation(){return Xn},get DwgHatchGradientColorFlag(){return Nt},get DwgHatchGradientFlag(){return en},get DwgHatchPatternType(){return Vn},get DwgHatchSolidFill(){return mt},get DwgHatchStyle(){return Hn},get DwgPolylineFlag(){return zn},get DwgProxyOriginalDataFormat(){return Jn},get DwgSmoothType(){return $n},get DwgTextGenerationFlag(){return Zn},get DwgTextHorizontalAlign(){return Dt},get DwgTextVerticalAlign(){return qn},get DwgThumbnailImageType(){return ne},get Dwg_Color_Method(){return Z},get Dwg_Error(){return an},get Dwg_File_Type(){return rn},get Dwg_Hatch_Edge_Type(){return ct},get Dwg_Object_Supertype(){return on},get Dwg_Object_Type(){return T},Dwg_Object_Type_Inverted:$o,HEADER_VARIABLES:We,LibreDwg:ee,createModule:jn,dwgCodePageToEncoding:bn,dwgVersions:be},Symbol.toStringTag,{value:"Module"})); +//# sourceMappingURL=dwg-worker.js.map diff --git a/public/wasm/libredwg-web.js b/public/wasm/libredwg-web.js new file mode 100644 index 0000000..988fa7e --- /dev/null +++ b/public/wasm/libredwg-web.js @@ -0,0 +1,2 @@ +async function createModule(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var programArgs=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}programArgs=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var readyPromiseResolve,readyPromiseReject;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["__wasm_call_ctors"]();FS.ignorePermissions=false}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("libredwg-web.wasm")}return new URL("libredwg-web.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={env:wasmImports,wasi_snapshot_preview1:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmExports=applySignatureConversions(wasmExports);assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr>>>0]=value;break;case"i8":HEAP8[ptr>>>0]=value;break;case"i16":HEAP16[ptr>>>1>>>0]=value;break;case"i32":HEAP32[ptr>>>2>>>0]=value;break;case"i64":HEAP64[ptr>>>3>>>0]=BigInt(value);break;case"float":HEAPF32[ptr>>>2>>>0]=value;break;case"double":HEAPF64[ptr>>>3>>>0]=value;break;case"*":HEAPU32[ptr>>>2>>>0]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>>2>>>0]=type}get_type(){return HEAPU32[this.ptr+4>>>2>>>0]}set_destructor(destructor){HEAPU32[this.ptr+8>>>2>>>0]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>>2>>>0]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12>>>0]=caught}get_caught(){return HEAP8[this.ptr+12>>>0]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13>>>0]=rethrown}get_rethrown(){return HEAP8[this.ptr+13>>>0]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>>2>>>0]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>>2>>>0]}}var uncaughtExceptionCount=0;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function ___cxa_throw(ptr,type,destructor){ptr>>>=0;type>>>=0;destructor>>>=0;var info=new ExceptionInfo(ptr);info.init(type,destructor);uncaughtExceptionCount++;abort()}var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>>2>>>0];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>nodeCrypto.randomFillSync(view)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{idx>>>=0;var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{outIdx>>>=0;if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++>>>0]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++>>>0]=192|u>>6;heap[outIdx++>>>0]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++>>>0]=224|u>>12;heap[outIdx++>>>0]=128|u>>6&63;heap[outIdx++>>>0]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++>>>0]=240|u>>18;heap[outIdx++>>>0]=128|u>>12&63;heap[outIdx++>>>0]=128|u>>6&63;heap[outIdx++>>>0]=128|u&63;i++}}heap[outIdx>>>0]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack=""}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}if(contents){if(position>0||position+length>>0)}}return{ptr,allocated}},msync(stream,buffer,offset,length,mmapFlags){MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS_modeStringToFlags=str=>{if(typeof str!="string")return str;var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_fileDataToTypedArray=data=>{if(typeof data=="string"){data=intArrayFromString(data,true)}if(!data.subarray){data=new Uint8Array(data)}return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}if(perms.includes("w")&&!(node.mode&146)){return 2}if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else if(FS.isDir(node.mode)){return 31}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode)){if(mode!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError){throw new FS.ErrnoError(22)}throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=FS_modeStringToFlags(flags);if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags??0;opts.encoding=opts.encoding??"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags??577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort(`invalid range (${from}, ${to}) or no bytes requested!`);if(to>datalength-1)abort(`only ${datalength} bytes available! programmer error!`);var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText??"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>{ptr>>>=0;return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):""};var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>>2>>>0]=stat.dev;HEAPU32[buf+4>>>2>>>0]=stat.mode;HEAPU32[buf+8>>>2>>>0]=stat.nlink;HEAPU32[buf+12>>>2>>>0]=stat.uid;HEAPU32[buf+16>>>2>>>0]=stat.gid;HEAPU32[buf+20>>>2>>>0]=stat.rdev;HEAP64[buf+24>>>3>>>0]=BigInt(stat.size);HEAP32[buf+32>>>2>>>0]=4096;HEAP32[buf+36>>>2>>>0]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>>3>>>0]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>>2>>>0]=atime%1e3*1e3*1e3;HEAP64[buf+56>>>3>>>0]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>>2>>>0]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>>3>>>0]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>>2>>>0]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>>3>>>0]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>>2>>>0]=stats.bsize;HEAPU32[buf+60>>>2>>>0]=stats.bsize;HEAP64[buf+8>>>3>>>0]=BigInt(stats.blocks);HEAP64[buf+16>>>3>>>0]=BigInt(stats.bfree);HEAP64[buf+24>>>3>>>0]=BigInt(stats.bavail);HEAP64[buf+32>>>3>>>0]=BigInt(stats.files);HEAP64[buf+40>>>3>>>0]=BigInt(stats.ffree);HEAPU32[buf+48>>>2>>>0]=stats.fsid;HEAPU32[buf+64>>>2>>>0]=stats.flags;HEAPU32[buf+56>>>2>>>0]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){varargs>>>=0;SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();var mask=289792;stream.flags=stream.flags&~mask|arg&mask;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>>1>>>0]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){buf>>>=0;try{return SYSCALLS.writeStat(buf,FS.fstat(fd))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){varargs>>>=0;SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>>2>>>0]=termios.c_iflag||0;HEAP32[argp+4>>>2>>>0]=termios.c_oflag||0;HEAP32[argp+8>>>2>>>0]=termios.c_cflag||0;HEAP32[argp+12>>>2>>>0]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17>>>0]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>>2>>>0];var c_oflag=HEAP32[argp+4>>>2>>>0];var c_cflag=HEAP32[argp+8>>>2>>>0];var c_lflag=HEAP32[argp+12>>>2>>>0];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17>>>0])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>>2>>>0]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>>1>>>0]=winsize[0];HEAP16[argp+2>>>1>>>0]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){path>>>=0;buf>>>=0;try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.lstat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){path>>>=0;buf>>>=0;try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.writeStat(buf,nofollow?FS.lstat(path):FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){path>>>=0;varargs>>>=0;SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64){mode&=~SYSCALLS.currentUmask}return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){path>>>=0;buf>>>=0;try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var AsciiToString=ptr=>{ptr>>>=0;var str="";while(1){var ch=HEAPU8[ptr++>>>0];if(!ch)return str;str+=String.fromCharCode(ch)}};var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer>>>0]:pointer=>HEAPU8[pointer>>>0];case 2:return signed?pointer=>HEAP16[pointer>>>1>>>0]:pointer=>HEAPU16[pointer>>>1>>>0];case 4:return signed?pointer=>HEAP32[pointer>>>2>>>0]:pointer=>HEAPU32[pointer>>>2>>>0];case 8:return signed?pointer=>HEAP64[pointer>>>3>>>0]:pointer=>HEAPU64[pointer>>>3>>>0];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=function(primitiveType,name,size,minRange,maxRange){primitiveType>>>=0;name>>>=0;size>>>=0;name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};function __embind_register_bool(rawType,name,trueValue,falseValue){rawType>>>=0;name>>>=0;name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer>>>0])},destructorFunction:null})}var shallowCopyInternalPointer=o=>({count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType});var throwInstanceAlreadyDeleted=obj=>{function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")};var finalizationRegistry=false;var detachFinalizer=handle=>{};var runDestructor=$$=>{if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}};var releaseClassHandle=$$=>{$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}};var attachFinalizer=handle=>{if(!globalThis.FinalizationRegistry){attachFinalizer=handle=>handle;return handle}finalizationRegistry=new FinalizationRegistry(info=>{releaseClassHandle(info.$$)});attachFinalizer=handle=>{var $$=handle.$$;var hasSmartPtr=!!$$.smartPtr;if(hasSmartPtr){var info={$$};finalizationRegistry.register(handle,info,handle)}return handle};detachFinalizer=handle=>finalizationRegistry.unregister(handle);return attachFinalizer(handle)};var deletionQueue=[];var flushPendingDeletes=()=>{while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}};var delayFunction;var init_ClassHandle=()=>{let proto=ClassHandle.prototype;Object.assign(proto,{isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;other.$$=other.$$;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right},clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}},delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}},isDeleted(){return!this.$$.ptr},deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}});const symbolDispose=Symbol.dispose;if(symbolDispose){proto[symbolDispose]=proto["delete"]}};function ClassHandle(){}var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var registeredPointers={};var ensureOverloadTable=(proto,methodName,humanName)=>{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var char_0=48;var char_9=57;var makeLegalFunctionName=name=>{name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return`_${name}`}return name};function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}var upcastPointer=(ptr,ptrClass,desiredClass)=>{while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError(`Expected null or instance of ${desiredClass.name}, got an instance of ${ptrClass.name}`)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr};var embindRepr=v=>{if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}};function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle||!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError(`Cannot convert argument of type ${handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name} to parameter type ${this.name}`)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,Emval.toHandle(()=>clonedHandle["delete"]()));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupported sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError(`null is not a valid ${this.name}`)}return 0}if(!handle.$$){throwBindingError(`Cannot pass "${embindRepr(handle)}" as a ${this.name}`)}if(!handle.$$.ptr){throwBindingError(`Cannot pass deleted object as a pointer of type ${this.name}`)}if(handle.$$.ptrType.isConst){throwBindingError(`Cannot convert argument of type ${handle.$$.ptrType.name} to parameter type ${this.name}`)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>>2>>>0])}var downcastPointer=(ptr,ptrClass,desiredClass)=>{if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)};var registeredInstances={};var getBasestPointer=(class_,ptr)=>{if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr};var getInheritedInstance=(class_,ptr)=>{ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var makeClassHandle=(prototype,record)=>{if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record,writable:true}}))};function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}var init_RegisteredPointer=()=>{Object.assign(RegisteredPointer.prototype,{getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr},destructor(ptr){this.rawDestructor?.(ptr)},readValueFromPointer:readPointer,fromWireType:RegisteredPointer_fromWireType})};function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this.toWireType=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this.toWireType=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this.toWireType=genericPointerToWireType}}var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var dynCall=(sig,ptr,args=[],promising=false)=>{var func=getWasmTableEntry(ptr);var rtn=func(...args);function convert(rtn){return sig[0]=="p"?rtn>>>0:rtn}return convert(rtn)};var getDynCaller=(sig,ptr,promising=false)=>(...args)=>dynCall(sig,ptr,args,promising);var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){if(signature.includes("p")){return getDynCaller(signature,rawFunction,isAsync)}var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}}if(0===unregisteredTypes.length){onComplete(typeConverters)}};function __embind_register_class(rawType,rawPointerType,rawConstPointerType,baseClassRawType,getActualTypeSignature,getActualType,upcastSignature,upcast,downcastSignature,downcast,name,destructorSignature,rawDestructor){rawType>>>=0;rawPointerType>>>=0;rawConstPointerType>>>=0;baseClassRawType>>>=0;getActualTypeSignature>>>=0;getActualType>>>=0;upcastSignature>>>=0;upcast>>>=0;downcastSignature>>>=0;downcast>>>=0;name>>>=0;destructorSignature>>>=0;rawDestructor>>>=0;name=AsciiToString(name);getActualType=embind__requireFunction(getActualTypeSignature,getActualType);upcast&&=embind__requireFunction(upcastSignature,upcast);downcast&&=embind__requireFunction(downcastSignature,downcast);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);var legalFunctionName=makeLegalFunctionName(name);exposePublicSymbol(legalFunctionName,function(){throwUnboundTypeError(`Cannot construct ${name} due to unbound types`,[baseClassRawType])});whenDependentTypesAreResolved([rawType,rawPointerType,rawConstPointerType],baseClassRawType?[baseClassRawType]:[],base=>{base=base[0];var baseClass;var basePrototype;if(baseClassRawType){baseClass=base.registeredClass;basePrototype=baseClass.instancePrototype}else{basePrototype=ClassHandle.prototype}var constructor=createNamedFunction(name,function(...args){if(Object.getPrototypeOf(this)!==instancePrototype){throw new BindingError(`Use 'new' to construct ${name}`)}if(undefined===registeredClass.constructor_body){throw new BindingError(`${name} has no accessible constructor`)}var body=registeredClass.constructor_body[args.length];if(undefined===body){throw new BindingError(`Tried to invoke ctor of ${name} with invalid number of parameters (${args.length}) - expected (${Object.keys(registeredClass.constructor_body).toString()}) parameters instead!`)}return body.apply(this,args)});var instancePrototype=Object.create(basePrototype,{constructor:{value:constructor}});constructor.prototype=instancePrototype;var registeredClass=new RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast);if(registeredClass.baseClass){registeredClass.baseClass.__derivedClasses??=[];registeredClass.baseClass.__derivedClasses.push(registeredClass)}var referenceConverter=new RegisteredPointer(name,registeredClass,true,false,false);var pointerConverter=new RegisteredPointer(name+"*",registeredClass,false,false,false);var constPointerConverter=new RegisteredPointer(name+" const*",registeredClass,false,true,false);registeredPointers[rawType]={pointerType:pointerConverter,constPointerType:constPointerConverter};replacePublicSymbol(legalFunctionName,constructor);return[referenceConverter,pointerConverter,constPointerConverter]})}var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};var validateThis=(this_,classType,humanName)=>{if(!(this_ instanceof Object)){throwBindingError(`${humanName} with invalid "this": ${this_}`)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(`${humanName} incompatible with "this" of type ${this_.constructor.name}`)}if(!this_.$$.ptr){throwBindingError(`cannot call emscripten binding method ${humanName} on deleted object`)}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)};var __embind_register_class_property=function(classType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){classType>>>=0;fieldName>>>=0;getterReturnType>>>=0;getterSignature>>>=0;getter>>>=0;getterContext>>>=0;setterArgumentType>>>=0;setterSignature>>>=0;setter>>>=0;setterContext>>>=0;fieldName=AsciiToString(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[classType],classType=>{classType=classType[0];var humanName=`${classType.name}.${fieldName}`;var desc={get(){throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])},enumerable:true,configurable:true};if(setter){desc.set=()=>throwUnboundTypeError(`Cannot access ${humanName} due to unbound types`,[getterReturnType,setterArgumentType])}else{desc.set=v=>throwBindingError(humanName+" is a read-only property")}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);whenDependentTypesAreResolved([],setter?[getterReturnType,setterArgumentType]:[getterReturnType],types=>{var getterReturnType=types[0];var desc={get(){var ptr=validateThis(this,classType,humanName+" getter");return getterReturnType.fromWireType(getter(getterContext,ptr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);var setterArgumentType=types[1];desc.set=function(v){var ptr=validateThis(this,classType,humanName+" setter");var destructors=[];setter(setterContext,ptr,setterArgumentType.toWireType(destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.instancePrototype,fieldName,desc);return[]});return[]})};var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];function __emval_decref(handle){handle>>>=0;if(handle>9&&0===--emval_handles[handle+1]){var value=emval_handles[handle];emval_handles[handle]=undefined;emval_freelist.push(handle)}}var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};function __embind_register_emval(rawType){rawType>>>=0;return registerType(rawType,EmValType)}var enumReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?function(pointer){return this.fromWireType(HEAP8[pointer>>>0])}:function(pointer){return this.fromWireType(HEAPU8[pointer>>>0])};case 2:return signed?function(pointer){return this.fromWireType(HEAP16[pointer>>>1>>>0])}:function(pointer){return this.fromWireType(HEAPU16[pointer>>>1>>>0])};case 4:return signed?function(pointer){return this.fromWireType(HEAP32[pointer>>>2>>>0])}:function(pointer){return this.fromWireType(HEAPU32[pointer>>>2>>>0])};default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};function getEnumValueType(rawValueType){return rawValueType===0?"object":rawValueType===1?"number":"string"}function __embind_register_enum(rawType,name,size,isSigned,rawValueType){rawType>>>=0;name>>>=0;size>>>=0;name=AsciiToString(name);const valueType=getEnumValueType(rawValueType);switch(valueType){case"object":{function ctor(){}ctor.values={};registerType(rawType,{name,constructor:ctor,valueType,fromWireType:function(c){return this.constructor.values[c]},toWireType:(destructors,c)=>c.value,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor);break}case"number":{var keysMap={};registerType(rawType,{name,keysMap,valueType,fromWireType:c=>c,toWireType:(destructors,c)=>c,readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}case"string":{var valuesMap={};var reverseMap={};var keysMap={};registerType(rawType,{name,valuesMap,reverseMap,keysMap,valueType,fromWireType:function(c){return this.reverseMap[c]},toWireType:function(destructors,c){return this.valuesMap[c]},readValueFromPointer:enumReadValueFromPointer(name,size,isSigned),destructorFunction:null});exposePublicSymbol(name,keysMap);delete Module[name].argCount;break}}}var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};function __embind_register_enum_value(rawEnumType,name,enumValue){rawEnumType>>>=0;name>>>=0;var enumType=requireRegisteredType(rawEnumType,"enum");name=AsciiToString(name);switch(enumType.valueType){case"object":{var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(`${enumType.name}_${name}`,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value;break}case"number":{enumType.keysMap[name]=enumValue;break}case"string":{enumType.valuesMap[name]=enumValue;enumType.reverseMap[enumValue]=name;enumType.keysMap[name]=name;break}}}var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>>2>>>0])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>>3>>>0])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=function(rawType,name,size){rawType>>>=0;name>>>=0;size>>>=0;name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};function usesDestructorStack(argTypes){for(var i=1;i{var array=[];for(var i=0;i>>2>>>0])}return array};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn){name>>>=0;rawArgTypesAddr>>>=0;signature>>>=0;rawInvoker>>>=0;fn>>>=0;var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})}var __embind_register_integer=function(primitiveType,name,size,minRange,maxRange){primitiveType>>>=0;name>>>=0;size>>>=0;name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};function __embind_register_memory_view(rawType,dataTypeIndex,name){rawType>>>=0;name>>>=0;var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>>2>>>0];var data=HEAPU32[handle+4>>>2>>>0];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function __embind_register_std_string(rawType,name){rawType>>>=0;name>>>=0;name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>>2>>>0];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>>0])}}_free(value);return str},toWireType(destructors,value){if(value instanceof ArrayBuffer){value=new Uint8Array(value)}var length;var valueIsOfTypeString=typeof value=="string";if(!(valueIsOfTypeString||ArrayBuffer.isView(value)&&value.BYTES_PER_ELEMENT==1)){throwBindingError("Cannot pass non-string to std::string")}if(stdStringIsUTF8&&valueIsOfTypeString){length=lengthBytesUTF8(value)}else{length=value.length}var base=_malloc(4+length+1);var ptr=base+4;HEAPU32[base>>>2>>>0]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i>>>0]=charCode}}}else{HEAPU8.set(value,ptr>>>0)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})}var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx>>>0,endIdx>>>0));var str="";for(var i=idx;i>>0];str+=String.fromCharCode(codeUnit)}return str};var stringToUTF16=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>>1>>>0]=codeUnit;outPtr+=2}HEAP16[outPtr>>>1>>>0]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i>>>0];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{outPtr>>>=0;maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>>2>>>0]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>>2>>>0]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};function __embind_register_std_wstring(rawType,charSize,name){rawType>>>=0;charSize>>>=0;name>>>=0;name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>>2>>>0];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>>2>>>0]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})}var __embind_register_void=function(rawType,name){rawType>>>=0;name>>>=0;name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>>2>>>0],`parameter ${i}`)}return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>>2>>>0]=Emval.toHandle(destructors)}return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return AsciiToString(address)}return symbol};var __emval_create_invoker=function(argCount,argTypesPtr,kind){argTypesPtr>>>=0;var GenericWireTypeSize=8;var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var captures={toValue:Emval.toValue};var args=argFromPtr.map((argFromPtr,i)=>{var captureName=`argFromPtr${i}`;captures[captureName]=argFromPtr;return`${captureName}(args${i?"+"+i*GenericWireTypeSize:""})`});var functionBody;switch(kind){case 0:functionBody="toValue(handle)";break;case 2:functionBody="new (toValue(handle))";break;case 3:functionBody="";break;case 1:captures["getStringOrSymbol"]=getStringOrSymbol;functionBody="toValue(handle)[getStringOrSymbol(methodName)]";break}functionBody+=`(${args})`;if(!retType.isVoid){captures["toReturnWire"]=toReturnWire;captures["emval_returnValue"]=emval_returnValue;functionBody=`return emval_returnValue(toReturnWire, destructorsRef, ${functionBody})`}functionBody=`return function (handle, methodName, destructorsRef, args) {\n${functionBody}\n}`;var invokerFunction=new Function(Object.keys(captures),functionBody)(...Object.values(captures));var functionName=`methodCaller<(${argTypes.map(t=>t.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};function __emval_incref(handle){handle>>>=0;if(handle>9){emval_handles[handle+1]+=1}}function __emval_invoke(caller,handle,methodName,destructorsRef,args){caller>>>=0;handle>>>=0;methodName>>>=0;destructorsRef>>>=0;args>>>=0;return emval_methodCallers[caller](handle,methodName,destructorsRef,args)}function __emval_new_array(){return Emval.toHandle([])}function __emval_new_cstring(v){v>>>=0;return Emval.toHandle(getStringOrSymbol(v))}function __emval_new_object(){return Emval.toHandle({})}function __emval_run_destructors(handle){handle>>>=0;var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)}function __emval_set_property(handle,key,value){handle>>>=0;key>>>=0;value>>>=0;handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value}function __gmtime_js(time,tmPtr){time=bigintToI53Checked(time);tmPtr>>>=0;var date=new Date(time*1e3);HEAP32[tmPtr>>>2>>>0]=date.getUTCSeconds();HEAP32[tmPtr+4>>>2>>>0]=date.getUTCMinutes();HEAP32[tmPtr+8>>>2>>>0]=date.getUTCHours();HEAP32[tmPtr+12>>>2>>>0]=date.getUTCDate();HEAP32[tmPtr+16>>>2>>>0]=date.getUTCMonth();HEAP32[tmPtr+20>>>2>>>0]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>>2>>>0]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>>2>>>0]=yday}var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};var __mktime_js=function(tmPtr){tmPtr>>>=0;var ret=(()=>{var date=new Date(HEAP32[tmPtr+20>>>2>>>0]+1900,HEAP32[tmPtr+16>>>2>>>0],HEAP32[tmPtr+12>>>2>>>0],HEAP32[tmPtr+8>>>2>>>0],HEAP32[tmPtr+4>>>2>>>0],HEAP32[tmPtr>>>2>>>0],0);if(isNaN(date.getTime())){return-1}var dst=HEAP32[tmPtr+32>>>2>>>0];var guessedOffset=date.getTimezoneOffset();var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dstOffset=Math.min(winterOffset,summerOffset);if(dst<0){HEAP32[tmPtr+32>>>2>>>0]=Number(summerOffset!=winterOffset&&dstOffset==guessedOffset)}else if(dst>0!=(dstOffset==guessedOffset)){var nonDstOffset=Math.max(winterOffset,summerOffset);var trueOffset=dst>0?dstOffset:nonDstOffset;date.setTime(date.getTime()+(trueOffset-guessedOffset)*6e4)}HEAP32[tmPtr+24>>>2>>>0]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>>2>>>0]=yday;HEAP32[tmPtr>>>2>>>0]=date.getSeconds();HEAP32[tmPtr+4>>>2>>>0]=date.getMinutes();HEAP32[tmPtr+8>>>2>>>0]=date.getHours();HEAP32[tmPtr+12>>>2>>>0]=date.getDate();HEAP32[tmPtr+16>>>2>>>0]=date.getMonth();HEAP32[tmPtr+20>>>2>>>0]=date.getYear();return date.getTime()/1e3})();return BigInt(ret)};var __tzset_js=function(timezone,daylight,std_name,dst_name){timezone>>>=0;daylight>>>=0;std_name>>>=0;dst_name>>>=0;var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>>2>>>0]=stdTimezoneOffset*60;HEAP32[daylight>>>2>>>0]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetDate.now();function _emscripten_err(str){str>>>=0;return err(UTF8ToString(str))}var _emscripten_get_now=()=>performance.now();var getHeapMax=()=>4294901760;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};function _emscripten_resize_heap(requestedSize){requestedSize>>>=0;var oldSize=HEAPU8.length;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false}var ENV={};var getExecutableName=()=>thisProgram;var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};function _environ_get(__environ,environ_buf){__environ>>>=0;environ_buf>>>=0;var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>>2>>>0]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){penviron_count>>>=0;penviron_buf_size>>>=0;var strings=getEnvStrings();HEAPU32[penviron_count>>>2>>>0]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>>2>>>0]=bufSize;return 0}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>>2>>>0];var len=HEAPU32[iov+4>>>2>>>0];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>>=0;iovcnt>>>=0;pnum>>>=0;try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>>2>>>0]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);newOffset>>>=0;try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>>3>>>0]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>>2>>>0];var len=HEAPU32[iov+4>>>2>>>0];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>>=0;iovcnt>>>=0;pnum>>>=0;try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>>2>>>0]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _random_get(buffer,size){buffer>>>=0;size>>>=0;return randomFill(HEAPU8.subarray(buffer>>>0,buffer+size>>>0))}var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer>>>0)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="pointer")return ret>>>0;if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var stringToNewUTF8=str=>{var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret};FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();init_ClassHandle();init_RegisteredPointer();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["ENV"]=ENV;Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["setValue"]=setValue;Module["UTF8ToString"]=UTF8ToString;Module["stringToNewUTF8"]=stringToNewUTF8;Module["FS"]=FS;var __Znwm,__ZdlPvm,_free,_malloc,_calloc,_realloc,___getTypeName,_strndup,__ZdaPv,__ZdaPvm,__ZdlPv,_emscripten_builtin_malloc,__Znam,__ZnamSt11align_val_t,__ZnwmSt11align_val_t,___libc_calloc,___libc_free,___libc_malloc,___libc_realloc,_emscripten_builtin_calloc,_emscripten_builtin_free,_emscripten_builtin_realloc,_malloc_size,_malloc_usable_size,_reallocf,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){__Znwm=Module["__Znwm"]=wasmExports["_Znwm"];__ZdlPvm=Module["__ZdlPvm"]=wasmExports["_ZdlPvm"];_free=wasmExports["free"];_malloc=wasmExports["malloc"];_calloc=wasmExports["calloc"];_realloc=wasmExports["realloc"];___getTypeName=wasmExports["__getTypeName"];_strndup=Module["_strndup"]=wasmExports["strndup"];__ZdaPv=Module["__ZdaPv"]=wasmExports["_ZdaPv"];__ZdaPvm=Module["__ZdaPvm"]=wasmExports["_ZdaPvm"];__ZdlPv=Module["__ZdlPv"]=wasmExports["_ZdlPv"];_emscripten_builtin_malloc=Module["_emscripten_builtin_malloc"]=wasmExports["emscripten_builtin_malloc"];__Znam=Module["__Znam"]=wasmExports["_Znam"];__ZnamSt11align_val_t=Module["__ZnamSt11align_val_t"]=wasmExports["_ZnamSt11align_val_t"];__ZnwmSt11align_val_t=Module["__ZnwmSt11align_val_t"]=wasmExports["_ZnwmSt11align_val_t"];___libc_calloc=Module["___libc_calloc"]=wasmExports["__libc_calloc"];___libc_free=Module["___libc_free"]=wasmExports["__libc_free"];___libc_malloc=Module["___libc_malloc"]=wasmExports["__libc_malloc"];___libc_realloc=Module["___libc_realloc"]=wasmExports["__libc_realloc"];_emscripten_builtin_calloc=Module["_emscripten_builtin_calloc"]=wasmExports["emscripten_builtin_calloc"];_emscripten_builtin_free=Module["_emscripten_builtin_free"]=wasmExports["emscripten_builtin_free"];_emscripten_builtin_realloc=Module["_emscripten_builtin_realloc"]=wasmExports["emscripten_builtin_realloc"];_malloc_size=Module["_malloc_size"]=wasmExports["malloc_size"];_malloc_usable_size=Module["_malloc_usable_size"]=wasmExports["malloc_usable_size"];_reallocf=Module["_reallocf"]=wasmExports["reallocf"];__emscripten_stack_restore=wasmExports["_emscripten_stack_restore"];__emscripten_stack_alloc=wasmExports["_emscripten_stack_alloc"];_emscripten_stack_get_current=wasmExports["emscripten_stack_get_current"];memory=wasmMemory=wasmExports["memory"];__indirect_function_table=wasmTable=wasmExports["__indirect_function_table"]}var wasmImports={__cxa_throw:___cxa_throw,__syscall_fcntl64:___syscall_fcntl64,__syscall_fstat64:___syscall_fstat64,__syscall_ioctl:___syscall_ioctl,__syscall_lstat64:___syscall_lstat64,__syscall_newfstatat:___syscall_newfstatat,__syscall_openat:___syscall_openat,__syscall_stat64:___syscall_stat64,_abort_js:__abort_js,_embind_register_bigint:__embind_register_bigint,_embind_register_bool:__embind_register_bool,_embind_register_class:__embind_register_class,_embind_register_class_property:__embind_register_class_property,_embind_register_emval:__embind_register_emval,_embind_register_enum:__embind_register_enum,_embind_register_enum_value:__embind_register_enum_value,_embind_register_float:__embind_register_float,_embind_register_function:__embind_register_function,_embind_register_integer:__embind_register_integer,_embind_register_memory_view:__embind_register_memory_view,_embind_register_std_string:__embind_register_std_string,_embind_register_std_wstring:__embind_register_std_wstring,_embind_register_void:__embind_register_void,_emval_create_invoker:__emval_create_invoker,_emval_decref:__emval_decref,_emval_incref:__emval_incref,_emval_invoke:__emval_invoke,_emval_new_array:__emval_new_array,_emval_new_cstring:__emval_new_cstring,_emval_new_object:__emval_new_object,_emval_run_destructors:__emval_run_destructors,_emval_set_property:__emval_set_property,_gmtime_js:__gmtime_js,_mktime_js:__mktime_js,_tzset_js:__tzset_js,emscripten_date_now:_emscripten_date_now,emscripten_err:_emscripten_err,emscripten_get_now:_emscripten_get_now,emscripten_resize_heap:_emscripten_resize_heap,environ_get:_environ_get,environ_sizes_get:_environ_sizes_get,fd_close:_fd_close,fd_read:_fd_read,fd_seek:_fd_seek,fd_write:_fd_write,random_get:_random_get};function applySignatureConversions(wasmExports){wasmExports=Object.assign({},wasmExports);var makeWrapper_pp=f=>a0=>f(a0)>>>0;var makeWrapper_ppp=f=>(a0,a1)=>f(a0,a1)>>>0;var makeWrapper_p=f=>()=>f()>>>0;wasmExports["malloc"]=makeWrapper_pp(wasmExports["malloc"]);wasmExports["calloc"]=makeWrapper_ppp(wasmExports["calloc"]);wasmExports["realloc"]=makeWrapper_ppp(wasmExports["realloc"]);wasmExports["__getTypeName"]=makeWrapper_pp(wasmExports["__getTypeName"]);wasmExports["emscripten_builtin_malloc"]=makeWrapper_pp(wasmExports["emscripten_builtin_malloc"]);wasmExports["emscripten_builtin_calloc"]=makeWrapper_ppp(wasmExports["emscripten_builtin_calloc"]);wasmExports["_emscripten_stack_alloc"]=makeWrapper_pp(wasmExports["_emscripten_stack_alloc"]);wasmExports["emscripten_stack_get_current"]=makeWrapper_p(wasmExports["emscripten_stack_get_current"]);return wasmExports}function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +;return moduleRtn}export default createModule; diff --git a/public/wasm/libredwg-web.wasm b/public/wasm/libredwg-web.wasm new file mode 100644 index 0000000..18ee41c Binary files /dev/null and b/public/wasm/libredwg-web.wasm differ diff --git a/scripts/build-apk.mjs b/scripts/build-apk.mjs index 56540d6..a8e6fcf 100644 --- a/scripts/build-apk.mjs +++ b/scripts/build-apk.mjs @@ -47,7 +47,7 @@ const sourceDir = path.join(appDir, 'java') const outDir = path.join(root, 'android', 'build') const classesDir = path.join(outDir, 'classes') const dexDir = path.join(outDir, 'dex') -const apkName = 'PhoneViewer-0.4.1.apk' +const apkName = 'PhoneViewer-0.5.0.apk' function run(command, args, options = {}) { console.log('>', path.basename(command), args.join(' ')) @@ -139,9 +139,9 @@ run(path.join(buildTools, 'aapt2.exe'), [ '--target-sdk-version', '34', '--version-code', - '5', + '6', '--version-name', - '0.4.1', + '0.5.0', '--auto-add-overlay' ]) diff --git a/scripts/copy-cad-wasm.mjs b/scripts/copy-cad-wasm.mjs new file mode 100644 index 0000000..00e9c13 --- /dev/null +++ b/scripts/copy-cad-wasm.mjs @@ -0,0 +1,27 @@ +import fs from 'node:fs' +import path from 'node:path' + +const source = path.join('node_modules', '@flyfish-dev', 'cad-viewer', 'dist', 'wasm') +const target = path.join('public', 'wasm') +const files = [ + 'dwg-worker.js', + 'dwfv-render.wasm', + 'libredwg-web.js', + 'libredwg-web.wasm' +] + +if (!fs.existsSync(source)) { + console.warn(`[cad-wasm] source not found: ${source}`) + process.exit(0) +} + +fs.mkdirSync(target, { recursive: true }) +for (const file of files) { + const from = path.join(source, file) + if (!fs.existsSync(from)) { + console.warn(`[cad-wasm] missing: ${from}`) + continue + } + fs.copyFileSync(from, path.join(target, file)) +} +console.log(`[cad-wasm] copied ${files.length} runtime files to ${target}`) diff --git a/scripts/patch-wasm-memory.mjs b/scripts/patch-wasm-memory.mjs deleted file mode 100644 index 5464889..0000000 --- a/scripts/patch-wasm-memory.mjs +++ /dev/null @@ -1,86 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' - -const source = process.argv[2] ?? 'node_modules/@mlightcad/libredwg-web/wasm/libredwg-web.wasm' -const target = process.argv[3] ?? 'public/vendor/libredwg/wasm/libredwg-web.wasm' -const INITIAL_PAGES = 4096 // 256 MiB; the module can still grow up to its 4 GiB maximum. -const MAX_PAGES = 65536 - -const buf = fs.readFileSync(source) -if (buf.readUInt32LE(0) !== 0x6d736100 || buf.readUInt32LE(4) !== 1) { - throw new Error('Not a WebAssembly module') -} - -function readU32(data, offset) { - let value = 0 - let shift = 0 - let pos = offset - while (true) { - const byte = data[pos++] - value += (byte & 0x7f) * 2 ** shift - if (!(byte & 0x80)) break - shift += 7 - if (shift > 35) throw new Error('Invalid LEB128 value') - } - return [value >>> 0, pos] -} - -function encodeU32(value) { - const bytes = [] - let v = value >>> 0 - do { - let byte = v & 0x7f - v >>>= 7 - if (v) byte |= 0x80 - bytes.push(byte) - } while (v) - return Buffer.from(bytes) -} - -function encodeSection(id, payload) { - return Buffer.concat([Buffer.from([id]), encodeU32(payload.length), payload]) -} - -const sections = [] -let pos = 8 -while (pos < buf.length) { - const id = buf[pos++] - const [size, afterSize] = readU32(buf, pos) - const payload = buf.subarray(afterSize, afterSize + size) - sections.push({ id, payload }) - pos = afterSize + size -} - -let patched = false -const output = [] -output.push(buf.subarray(0, 8)) -for (const section of sections) { - if (section.id === 5 && !patched) { - const [count, afterCount] = readU32(section.payload, 0) - if (count < 1) throw new Error('Memory section has no memory') - const flags = section.payload[afterCount] - if ((flags & 0x01) === 0) throw new Error('Memory has no maximum; refusing to patch') - const [oldMin, afterMin] = readU32(section.payload, afterCount + 1) - const [oldMax] = readU32(section.payload, afterMin) - if (oldMin !== 16384) throw new Error(`Unexpected initial memory: ${oldMin}`) - const payload = Buffer.concat([ - encodeU32(count), - Buffer.from([flags]), - encodeU32(INITIAL_PAGES), - encodeU32(MAX_PAGES), - section.payload.subarray(afterMin + encodeU32(oldMax).length) - ]) - output.push(encodeSection(5, payload)) - patched = true - console.log( - `patched memory: ${oldMin} -> ${INITIAL_PAGES} pages (${Math.round((INITIAL_PAGES * 64) / 1024)} MiB)` - ) - } else { - output.push(encodeSection(section.id, section.payload)) - } -} -if (!patched) throw new Error('Memory section not found') - -fs.mkdirSync(path.dirname(target), { recursive: true }) -fs.writeFileSync(target, Buffer.concat(output)) -console.log('wrote', target, fs.statSync(target).size, 'bytes') diff --git a/src/cad/acadAdapter.ts b/src/cad/acadAdapter.ts deleted file mode 100644 index 2fb51b7..0000000 --- a/src/cad/acadAdapter.ts +++ /dev/null @@ -1,845 +0,0 @@ -import './polyfills' -import { DwgReader } from '@node-projects/acad-ts' -import type { DwgDatabase } from '@mlightcad/libredwg-web' -import { IDENTITY, applyMat, matMul, matScaleAvg } from './geom' -import type { Mat } from './geom' -import { buildCadDocument } from './build' -import type { CadDocument } from './types' - -type AcadDoc = ReturnType -type AcadEntity = Record - -interface NormalizeState { - unknown: number -} - -interface BuildOptions { - fileName: string - fileSize: number - unknownCount: number - warnings: string[] -} - -const GRAPHICS = { - Extents: 1, - Circle: 2, - CirclePt3: 3, - CircularArc: 4, - CircularArc3Pt: 5, - Polyline: 6, - Polygon: 7, - Mesh: 8, - Shell: 9, - Text: 10, - Text2: 11, - XLine: 12, - Ray: 13, - SubentColor: 14, - SubentLayer: 16, - SubentLineType: 18, - SubentMarker: 19, - SubentFillon: 20, - SubentTrueColor: 22, - SubentLineWeight: 23, - SubentLineTypeScale: 24, - SubentThickness: 25, - SubentPlotStyleName: 26, - PushClip: 27, - PopClip: 28, - PushModelTransform: 29, - PushModelTransform2: 30, - PopModelTransform: 31, - PolylineWithNormal: 32, - LwPolyine: 33, - UnicodeText: 36, - UnicodeText2: 38 -} as const - -const DYNAMIC_BLOCK_TYPES = new Set([ - 'ACAD_PROXY_ENTITY', - 'ACAD_PROXY_OBJECT', - 'AEC_WALL', - 'AEC_WINDOW', - 'AEC_DOOR', - 'AEC_OPENING', - 'AEC_SLAB', - 'AEC_ROOF', - 'AEC_STAIR', - 'AEC_RAILING', - 'AEC_GRID', - 'AEC_COLUMN', - 'AEC_BEAM', - 'MULTILEADER' -]) - -export function buildAcadDocument( - buffer: ArrayBuffer, - fileName: string, - fileSize: number -): CadDocument { - const acad = DwgReader.readFromStream(buffer) - if (!acad.modelSpace) throw new Error('DWG 中没有可显示的模型空间。') - const state: NormalizeState = { unknown: 0 } - const blocks = normalizeBlocks(acad, state) - const layouts = normalizeLayouts(acad, blocks.map) - const layers = normalizeLayers(acad) - const lineTypes = normalizeLineTypes(acad) - const vports = normalizeVPorts(acad) - const imageDefs = normalizeImageDefs(acad) - const header = normalizeHeader(acad) - const unknownCount = state.unknown - const warnings: string[] = [] - if (unknownCount > 0) warnings.push(`${unknownCount} 个实体类型暂不支持显示。`) - const db = { - tables: { - LAYER: { entries: layers }, - BLOCK_RECORD: { entries: blocks.entries }, - LTYPE: { entries: lineTypes }, - VPORT: { entries: vports } - }, - objects: { - LAYOUT: layouts, - IMAGEDEF: imageDefs - }, - header, - entities: [] - } as unknown as DwgDatabase - return buildCadDocument(db, { - fileName, - fileSize, - unknownCount, - warnings - } satisfies BuildOptions) -} - -function normalizeBlocks(acad: AcadDoc, state: NormalizeState) { - const entries: Record[] = [] - const map = new Map>() - let index = 0 - for (const block of acad.blockRecords ?? []) { - const handle = String(block.handle ?? `block-${index}`) - const base = block.blockEntity?.basePoint ?? { x: 0, y: 0, z: 0 } - const entities: Record[] = [] - for (const entity of block.entities ?? []) { - entities.push(...normalizeEntity(entity, state)) - } - const entry = { - name: block.name ?? `*BLOCK${index}`, - handle, - baseX: Number(base.x) || 0, - baseY: Number(base.y) || 0, - baseZ: Number(base.z) || 0, - layoutHandle: block.layout?.handle == null ? '' : String(block.layout.handle), - entities - } - entries.push(entry) - if (block.handle != null) map.set(block.handle, entry) - index++ - } - return { entries, map } -} - -function normalizeLayouts(acad: AcadDoc, blocks: Map>) { - const layouts: Record[] = [] - let index = 0 - for (const layout of acad.layouts ?? []) { - const block = layout.associatedBlock - if (!block) continue - const entry = blocks.get(block.handle) - if (!entry) continue - const known = new Set(entry.entities.map((entity: any) => String(entity.handle ?? ''))) - for (const viewport of layout.viewports ?? []) { - const handle = String(viewport.handle ?? '') - if (handle && known.has(handle)) continue - entry.entities.push(...normalizeEntity(viewport, { unknown: 0 })) - if (handle) known.add(handle) - } - layouts.push({ - layoutName: layout.name ?? `Layout${index + 1}`, - tabOrder: Number(layout.tabOrder) || index, - handle: String(layout.handle ?? `layout-${index}`), - paperSpaceTableId: String(block.handle), - minExtent: layout.minExtents, - maxExtent: layout.maxExtents - }) - index++ - } - return layouts -} - -function normalizeLayers(acad: AcadDoc) { - const entries: Record[] = [] - for (const layer of acad.layers ?? []) { - const color = layer.color - const aci = color?.isTrueColor ? 7 : Number(color?.index) || 7 - const rgb = color?.isTrueColor ? Number(color.trueColor) >>> 0 : undefined - const flags = Number(layer.layerFlags) || 0 - entries.push({ - name: layer.name ?? '0', - handle: String(layer.handle ?? ''), - colorIndex: aci, - color: rgb, - off: layer.isOn === false, - frozen: (flags & 1) === 1, - lineType: layer.lineType?.name ?? 'Continuous', - lineweight: Number(layer.lineWeight) - }) - } - return entries -} - -function normalizeLineTypes(acad: AcadDoc) { - const entries: Record[] = [] - for (const lineType of acad.lineTypes ?? []) { - const segments = lineType.segments ?? [] - if (!segments.length) continue - entries.push({ - name: lineType.name, - pattern: segments.map((segment: any) => ({ - elementLength: Math.abs(Number(segment.length) || 0) - })) - }) - } - return entries -} - -function normalizeVPorts(acad: AcadDoc) { - const entries: Record[] = [] - for (const vport of acad.vPorts ?? []) { - entries.push({ - name: vport.name ?? '*Active', - center: { x: Number(vport.center?.x) || 0, y: Number(vport.center?.y) || 0 }, - viewTarget: { - x: Number(vport.target?.x) || 0, - y: Number(vport.target?.y) || 0 - }, - viewHeight: Number(vport.viewHeight) || 0, - aspectRatio: Number(vport.aspectRatio) || 1 - }) - } - return entries -} - -function normalizeImageDefs(acad: AcadDoc) { - const entries: Record[] = [] - for (const image of acad.imageDefinitions ?? []) { - entries.push({ - handle: String(image.handle ?? ''), - fileName: image.fileName ?? '栅格图像' - }) - } - return entries -} - -function normalizeHeader(acad: AcadDoc) { - const header = acad.header - return { - EXTMIN: header?.modelSpaceExtMin ?? { x: 0, y: 0, z: 0 }, - EXTMAX: header?.modelSpaceExtMax ?? { x: 0, y: 0, z: 0 }, - INSUNITS: Number(header?.insUnits) || 0, - LTSCALE: Number(header?.lineTypeScale) || 1, - ACADVER: header?.versionString ?? '' - } -} - -function normalizeEntity(entity: AcadEntity, state: NormalizeState): Record[] { - if (!entity) return [] - const type = String(entity.objectName ?? '').toUpperCase() - const base = commonFields(entity) - switch (type) { - case 'LINE': { - const start = entity.startPoint - const end = entity.endPoint - if (!start || !end) return [] - return [{ ...base, type, startPoint: start, endPoint: end, extrusionDirection: entity.normal }] - } - case 'CIRCLE': { - if (!entity.center) return [] - return [ - { - ...base, - type, - center: entity.center, - radius: Number(entity.radius) || 0, - extrusionDirection: entity.normal - } - ] - } - case 'ARC': { - if (!entity.center) return [] - const start = Number(entity.startAngle) || 0 - const end = Number(entity.endAngle) || 0 - return [ - { - ...base, - type, - center: entity.center, - radius: Number(entity.radius) || 0, - startAngle: start, - endAngle: end, - extrusionDirection: entity.normal - } - ] - } - case 'ELLIPSE': { - if (!entity.center || !entity.majorAxisEndPoint) return [] - const start = entity.isFullEllipse ? 0 : Number(entity.startParameter) || 0 - const end = entity.isFullEllipse ? Math.PI * 2 : Number(entity.endParameter) || 0 - return [ - { - ...base, - type, - center: entity.center, - majorAxisEndPoint: entity.majorAxisEndPoint, - axisRatio: Number(entity.radiusRatio) || 1, - startAngle: start, - endAngle: end, - extrusionDirection: entity.normal - } - ] - } - case 'LWPOLYLINE': { - const vertices = Array.isArray(entity.vertices) ? entity.vertices : [] - if (vertices.length < 2) return [] - return [ - { - ...base, - type, - vertices: vertices.map((vertex: any) => ({ - x: Number(vertex.location?.x) || 0, - y: Number(vertex.location?.y) || 0, - bulge: Number(vertex.bulge) || 0 - })), - flag: entity.isClosed ? 1 : 0, - constantWidth: Number(entity.constantWidth) || 0, - extrusionDirection: entity.normal - } - ] - } - case 'POLYLINE': { - const vertices = [...(entity.vertices ?? [])] - if (vertices.length < 2) return [] - return [ - { - ...base, - type, - vertices: vertices.map((vertex: any) => ({ - x: Number(vertex.location?.x) || 0, - y: Number(vertex.location?.y) || 0, - bulge: Number(vertex.bulge) || 0 - })), - flag: entity.isClosed ? 1 : 0, - extrusionDirection: entity.normal - } - ] - } - case 'SPLINE': { - const control = Array.isArray(entity.controlPoints) ? entity.controlPoints : [] - const fit = Array.isArray(entity.fitPoints) ? entity.fitPoints : [] - if (control.length < 2 && fit.length < 2) return [] - return [ - { - ...base, - type, - controlPoints: control, - fitPoints: fit, - degree: Number(entity.degree) || 3, - knots: Array.isArray(entity.knots) ? entity.knots : [], - weights: Array.isArray(entity.weights) && entity.weights.length ? entity.weights : undefined, - flag: entity.isClosed ? 1 : 0, - extrusionDirection: entity.normal - } - ] - } - case 'POINT': { - if (!entity.location) return [] - return [{ ...base, type, position: entity.location, extrusionDirection: entity.normal }] - } - case 'TEXT': { - return [normalizeText(entity, base)] - } - case 'MTEXT': { - if (!entity.insertPoint) return [] - return [ - { - ...base, - type, - text: entity.value ?? '', - insertionPoint: entity.insertPoint, - textHeight: Number(entity.height) || 0, - rotation: Number(entity.rotation) || 0, - rectWidth: Number(entity.rectangleWidth) || 0, - attachmentPoint: Number(entity.attachmentPoint) || 1, - lineSpacing: Number(entity.lineSpacing) || 1, - styleName: entity.style?.name - } - ] - } - case 'INSERT': { - const block = entity.block - if (!block) return [] - const attribs: Record[] = [] - for (const attribute of entity.attributes ?? []) { - if (!attribute || attribute.isInvisible) continue - if ((Number(attribute.flags) & 1) === 1) continue - attribs.push(normalizeText(attribute, commonFields(attribute), 'ATTRIB')) - } - return [ - { - ...base, - type, - name: block.name, - insertionPoint: entity.insertPoint, - rotation: Number(entity.rotation) || 0, - xScale: Number(entity.xScale) || 1, - yScale: Number(entity.yScale) || 1, - zScale: Number(entity.zScale) || 1, - rowCount: Number(entity.rowCount) || 1, - columnCount: Number(entity.columnCount) || 1, - rowSpacing: Number(entity.rowSpacing) || 0, - columnSpacing: Number(entity.columnSpacing) || 0, - extrusionDirection: entity.normal, - attribs - } - ] - } - case 'DIMENSION': { - const block = entity.block - if (!block) return [] - return [ - { - ...base, - type, - name: block.name, - blockName: block.name, - extrusionDirection: entity.normal - } - ] - } - case 'HATCH': { - return [normalizeHatch(entity, base)] - } - case 'SOLID': { - if (!entity.firstCorner || !entity.secondCorner || !entity.thirdCorner) return [] - return [ - { - ...base, - type, - corner1: entity.firstCorner, - corner2: entity.secondCorner, - corner3: entity.thirdCorner, - corner4: entity.fourthCorner ?? entity.thirdCorner, - extrusionDirection: entity.normal - } - ] - } - case '3DFACE': { - if (!entity.firstCorner || !entity.secondCorner || !entity.thirdCorner) return [] - return [ - { - ...base, - type, - corner1: entity.firstCorner, - corner2: entity.secondCorner, - corner3: entity.thirdCorner, - corner4: entity.fourthCorner ?? entity.thirdCorner, - extrusionDirection: entity.normal - } - ] - } - case 'WIPEOUT': { - const points = wipeoutPoints(entity) - if (points.length < 6) return [] - return [{ ...base, type, clippingBoundaryPath: points }] - } - case 'IMAGE': { - if (!entity.insertPoint) return [] - return [ - { - ...base, - type, - position: entity.insertPoint, - uPixel: entity.uVector, - vPixel: entity.vVector, - imageSize: entity.size, - imageDefHandle: entity.definition?.handle == null ? '' : String(entity.definition.handle) - } - ] - } - case 'OLE2FRAME': { - if (!entity.upperLeftCorner || !entity.lowerRightCorner) return [] - return [ - { - ...base, - type, - corner1: entity.upperLeftCorner, - corner2: entity.lowerRightCorner, - label: entity.sourceApplication || 'OLE 对象' - } - ] - } - case 'VIEWPORT': { - return [ - { - ...base, - type, - width: Number(entity.width) || 0, - height: Number(entity.height) || 0, - viewHeight: Number(entity.viewHeight) || 0, - viewportCenter: entity.center, - displayCenter: entity.viewCenter, - viewTwistAngle: Number(entity.twistAngle) || 0, - statusBitFlags: Number(entity.status) || 0, - isVisible: true - } - ] - } - case 'ACAD_PROXY_ENTITY': { - return normalizeProxyGeometries(entity, base, state) - } - case 'ATTDEF': - return [] - default: { - if (entity.proxyGeometries?.length) { - return normalizeProxyGeometries(entity, base, state) - } - if (DYNAMIC_BLOCK_TYPES.has(type)) { - state.unknown++ - } else if (type) { - state.unknown++ - } - return [] - } - } -} - -function commonFields(entity: AcadEntity) { - const color = entity.color - let colorIndex = 256 - let trueColor: number | undefined - if (color?.isByBlock) { - colorIndex = 0 - } else if (color?.isByLayer) { - colorIndex = 256 - } else if (color?.isTrueColor) { - colorIndex = 256 - trueColor = Number(color.trueColor) >>> 0 - } else if (Number.isFinite(color?.index)) { - colorIndex = Number(color.index) - } - return { - layer: entity.layer?.name ?? '0', - colorIndex, - color: trueColor, - lineweight: Number(entity.lineWeight), - lineType: entity.lineType?.name, - lineTypeScale: Number(entity.lineTypeScale) || 1, - isVisible: !entity.isInvisible - } -} - -function normalizeText(entity: AcadEntity, base: Record, type = 'TEXT') { - const horizontal = Number(entity.horizontalAlignment) || 0 - const vertical = Number(entity.verticalAlignment) || 0 - const hasAlignment = horizontal !== 0 || vertical !== 0 - const halign = horizontal === 2 ? 2 : horizontal === 1 || horizontal === 4 || horizontal === 3 || horizontal === 5 ? 1 : 0 - const start = entity.insertPoint ?? { x: 0, y: 0, z: 0 } - const end = hasAlignment ? entity.alignmentPoint ?? start : start - return { - ...base, - type, - text: entity.value ?? '', - startPoint: start, - endPoint: end, - textHeight: Number(entity.height) || 0, - rotation: Number(entity.rotation) || 0, - xScale: Number(entity.widthFactor) || 1, - halign, - valign: vertical, - generationFlag: Number(entity.mirror) || 0, - flags: Number(entity.flags) || 0, - extrusionDirection: entity.normal - } -} - -function normalizeHatch(entity: AcadEntity, base: Record) { - const paths: Record[] = [] - for (const path of entity.paths ?? []) { - let points: any[] = [] - try { - points = path.getPoints(48) ?? [] - } catch { - points = [] - } - if (points.length < 2) continue - paths.push({ - vertices: points.map((point: any) => ({ - x: Number(point.x) || 0, - y: Number(point.y) || 0, - bulge: 0 - })) - }) - } - const patternAngle = Number(entity.patternAngle) || 0 - const patternScale = Number(entity.patternScale) || 1 - const definitionLines = (entity.pattern?.lines ?? []).map((line: any) => ({ - offset: { x: Number(line.offset?.x) || 0, y: Number(line.offset?.y) || 0 } - })) - return { - ...base, - type: 'HATCH', - boundaryPaths: paths, - solidFill: entity.isSolid ? 1 : 0, - patternName: entity.pattern?.name ?? '', - patternAngle, - patternScale, - definitionLines - } -} - -function wipeoutPoints(entity: AcadEntity) { - const vertices = entity.clipBoundaryVertices ?? [] - if (!vertices.length) return [] - const origin = entity.insertPoint ?? { x: 0, y: 0, z: 0 } - const u = entity.uVector ?? { x: 1, y: 0, z: 0 } - const v = entity.vVector ?? { x: 0, y: 1, z: 0 } - const points: number[] = [] - for (const vertex of vertices) { - points.push( - (Number(origin.x) || 0) + (Number(u.x) || 0) * (Number(vertex.x) || 0) + (Number(v.x) || 0) * (Number(vertex.y) || 0), - (Number(origin.y) || 0) + (Number(u.y) || 0) * (Number(vertex.x) || 0) + (Number(v.y) || 0) * (Number(vertex.y) || 0) - ) - } - return points -} - -function normalizeProxyGeometries( - entity: AcadEntity, - base: Record, - state: NormalizeState -): Record[] { - const geometries = entity.proxyGeometries ?? [] - if (!geometries.length) return [] - const out: Record[] = [] - const stack: Mat[] = [] - let current: Mat = IDENTITY - const add = (shape: Record) => { - out.push({ ...base, ...shape, proxy: true }) - } - for (const geometry of geometries) { - const type = Number(geometry?.graphicsType) || 0 - switch (type) { - case GRAPHICS.Circle: { - const center = applyMat(current, Number(geometry.center?.x) || 0, Number(geometry.center?.y) || 0) - add({ - type: 'CIRCLE', - center: { x: center[0], y: center[1] }, - radius: Math.abs(Number(geometry.radius) || 0) * matScaleAvg(current) - }) - break - } - case GRAPHICS.CirclePt3: { - const circle = circleFrom3(geometry.point1, geometry.point2, geometry.point3) - if (!circle) break - const center = applyMat(current, circle.cx, circle.cy) - add({ - type: 'CIRCLE', - center: { x: center[0], y: center[1] }, - radius: circle.r * matScaleAvg(current) - }) - break - } - case GRAPHICS.CircularArc: { - const center = applyMat(current, Number(geometry.center?.x) || 0, Number(geometry.center?.y) || 0) - const start = Math.atan2(Number(geometry.startVectorDirection?.y) || 0, Number(geometry.startVectorDirection?.x) || 1) - const rotation = Math.atan2(current[1], current[0]) - const sweep = Math.abs(Number(geometry.sweepAngle) || 0) * (Number(geometry.arcType) === 2 ? -1 : 1) - add({ - type: 'ARC', - center: { x: center[0], y: center[1] }, - radius: Math.abs(Number(geometry.radius) || 0) * matScaleAvg(current), - startAngle: start + rotation, - endAngle: start + rotation + sweep - }) - break - } - case GRAPHICS.CircularArc3Pt: { - const circle = circleFrom3(geometry.point1, geometry.point2, geometry.point3) - if (!circle) break - const center = applyMat(current, circle.cx, circle.cy) - const start = Math.atan2(Number(geometry.point1?.y) - circle.cy, Number(geometry.point1?.x) - circle.cx) - const mid = Math.atan2(Number(geometry.point2?.y) - circle.cy, Number(geometry.point2?.x) - circle.cx) - const end = Math.atan2(Number(geometry.point3?.y) - circle.cy, Number(geometry.point3?.x) - circle.cx) - const rotation = Math.atan2(current[1], current[0]) - add({ - type: 'ARC', - center: { x: center[0], y: center[1] }, - radius: circle.r * matScaleAvg(current), - startAngle: start + rotation, - endAngle: start + rotation + sweepThrough(start, end, mid, Number(geometry.arcType) === 2) - }) - break - } - case GRAPHICS.Polyline: - case GRAPHICS.Polygon: - case GRAPHICS.PolylineWithNormal: { - const points = transformPoints(current, geometry.points) - if (points.length >= 4) { - add({ - type: 'LWPOLYLINE', - vertices: pointsToVertices(points), - flag: type === GRAPHICS.Polygon ? 1 : 0 - }) - } - break - } - case GRAPHICS.Mesh: { - const rows = Math.max(0, Number(geometry.rowCount) || 0) - const columns = Math.max(0, Number(geometry.columnCount) || 0) - const vertices = geometry.vertices ?? [] - for (let row = 0; row < rows; row++) { - const points: number[] = [] - for (let col = 0; col < columns; col++) { - const vertex = vertices[row * columns + col] - if (!vertex) continue - const point = applyMat(current, Number(vertex.x) || 0, Number(vertex.y) || 0) - points.push(point[0], point[1]) - } - if (points.length >= 4) { - add({ type: 'LWPOLYLINE', vertices: pointsToVertices(points), flag: 0 }) - } - } - for (let col = 0; col < columns; col++) { - const points: number[] = [] - for (let row = 0; row < rows; row++) { - const vertex = vertices[row * columns + col] - if (!vertex) continue - const point = applyMat(current, Number(vertex.x) || 0, Number(vertex.y) || 0) - points.push(point[0], point[1]) - } - if (points.length >= 4) { - add({ type: 'LWPOLYLINE', vertices: pointsToVertices(points), flag: 0 }) - } - } - break - } - case GRAPHICS.Shell: { - const vertices = geometry.vertices ?? [] - for (const face of geometry.faces ?? []) { - const points: number[] = [] - for (const index of face) { - const vertex = vertices[Math.abs(Number(index)) - 1] ?? vertices[Number(index)] - if (!vertex) continue - const point = applyMat(current, Number(vertex.x) || 0, Number(vertex.y) || 0) - points.push(point[0], point[1]) - } - if (points.length >= 6) { - add({ type: 'LWPOLYLINE', vertices: pointsToVertices(points), flag: 1 }) - } - } - break - } - case GRAPHICS.XLine: - case GRAPHICS.Ray: { - const p1 = geometry.point1 ?? geometry.constructionLinePoint - const p2 = geometry.point2 - if (!p1 || !p2) break - const a = applyMat(current, Number(p1.x) || 0, Number(p1.y) || 0) - const b = applyMat(current, Number(p2.x) || 0, Number(p2.y) || 0) - const dx = b[0] - a[0] - const dy = b[1] - a[1] - const len = Math.hypot(dx, dy) || 1 - const ux = dx / len - const uy = dy / len - const length = 1e6 - const points = - type === GRAPHICS.XLine - ? [a[0] - ux * length, a[1] - uy * length, a[0] + ux * length, a[1] + uy * length] - : [a[0], a[1], a[0] + ux * length, a[1] + uy * length] - add({ - type: type === GRAPHICS.XLine ? 'XLINE' : 'RAY', - firstPoint: { x: points[0], y: points[1] }, - unitDirection: { x: ux, y: uy } - }) - break - } - case GRAPHICS.PushModelTransform: - case GRAPHICS.PushModelTransform2: { - stack.push(current) - current = matMul(current, matrixToMat(geometry.matrix)) - break - } - case GRAPHICS.PopModelTransform: { - current = stack.pop() ?? IDENTITY - break - } - default: - break - } - } - if (!out.length && !entity.proxyGeometries?.length) state.unknown++ - return out -} - -function matrixToMat(matrix: any): Mat { - return [ - Number(matrix?.m00) || 0, - Number(matrix?.m10) || 0, - Number(matrix?.m01) || 0, - Number(matrix?.m11) || 0, - Number(matrix?.m03) || 0, - Number(matrix?.m13) || 0 - ] -} - -function transformPoints(mat: Mat, points: any[] = []) { - const out: number[] = [] - for (const point of points) { - const mapped = applyMat(mat, Number(point?.x) || 0, Number(point?.y) || 0) - out.push(mapped[0], mapped[1]) - } - return out -} - -function pointsToVertices(points: number[]) { - const vertices: Record[] = [] - for (let index = 0; index + 1 < points.length; index += 2) { - vertices.push({ x: points[index], y: points[index + 1], bulge: 0 }) - } - return vertices -} - -function circleFrom3(p1: any, p2: any, p3: any) { - if (!p1 || !p2 || !p3) return null - const ax = Number(p1.x) || 0 - const ay = Number(p1.y) || 0 - const bx = Number(p2.x) || 0 - const by = Number(p2.y) || 0 - const cx = Number(p3.x) || 0 - const cy = Number(p3.y) || 0 - const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) - if (Math.abs(d) < 1e-12) return null - const a2 = ax * ax + ay * ay - const b2 = bx * bx + by * by - const c2 = cx * cx + cy * cy - const ux = (a2 * (by - cy) + b2 * (cy - ay) + c2 * (ay - by)) / d - const uy = (a2 * (cx - bx) + b2 * (ax - cx) + c2 * (bx - ax)) / d - return { cx: ux, cy: uy, r: Math.hypot(ux - ax, uy - ay) } -} - -function normalizeAngle(value: number) { - const tau = Math.PI * 2 - return ((value % tau) + tau) % tau -} - -function sweepThrough(start: number, end: number, mid: number, clockwise: boolean) { - if (clockwise) { - const full = Math.PI * 2 - const s = normalizeAngle(start - end) - const m = normalizeAngle(start - mid) - return m <= s ? -s : -(s + full) - } - const s = normalizeAngle(end - start) - const m = normalizeAngle(mid - start) - return m <= s ? s : s - Math.PI * 2 -} diff --git a/src/cad/build.ts b/src/cad/build.ts deleted file mode 100644 index fd8120a..0000000 --- a/src/cad/build.ts +++ /dev/null @@ -1,1593 +0,0 @@ -import type { - DwgDatabase, - DwgEntity, - DwgLayoutObject, - DwgViewportEntity -} from '@mlightcad/libredwg-web' -import { - IDENTITY, - applyMat, - bboxAddPoint, - bboxCenter, - bboxExpand, - bboxFromPoints, - bboxHeight, - bboxTransform, - bboxUnion, - bboxValid, - bboxWidth, - bulgeToArc, - clamp, - emptyBBox, - matMul, - matRotate, - matScale, - matTranslate -} from './geom' -import type { Mat } from './geom' -import type { ColorSpec } from './colors' -import { decodeCadEscapes, parseMText, textWidth } from './text' -import type { - ArcPrim, - BulgePrim, - CadBlockInfo, - CadDocument, - CadSpace, - CirclePrim, - EllipsePrim, - HatchPrim, - ImagePrim, - LayerInfo, - PointPrim, - PolyPrim, - PrimBase, - Primitive, - SpaceGrid, - SplinePrim, - TextPrim -} from './types' - -type AnyEntity = DwgEntity & Record - -interface BuildState { - doc: CadDocument - paper: boolean - prims: Primitive[] - depth: number - stack: Set -} - -interface BuildOptions { - fileName: string - fileSize: number - unknownCount: number - warnings: string[] -} - -const MODEL_PREFIX = 'MODEL_SPACE' -const PAPER_PREFIX = 'PAPER_SPACE' - -export function buildCadDocument(db: DwgDatabase, options: BuildOptions): CadDocument { - const layerMap = new Map() - const layers: LayerInfo[] = [] - for (const entry of db.tables.LAYER.entries) { - const aci = typeof entry.colorIndex === 'number' ? entry.colorIndex : 7 - const rgb = - typeof entry.color === 'number' && entry.color > 0 && entry.color !== 0xffffff - ? entry.color >>> 0 - : undefined - const info: LayerInfo = { - name: entry.name || '0', - handle: entry.handle, - aci: aci > 0 && aci < 256 ? aci : 7, - rgb, - visible: !entry.off && !entry.frozen, - off: !!entry.off, - frozen: !!entry.frozen, - count: 0, - lineType: entry.lineType || 'Continuous', - lineWeight: Number(entry.lineweight) || -1 - } - layers.push(info) - layerMap.set(info.name, info) - layerMap.set(info.name.toLowerCase(), info) - } - if (!layerMap.has('0')) { - const fallback: LayerInfo = { - name: '0', - handle: '0', - aci: 7, - visible: true, - off: false, - frozen: false, - count: 0, - lineType: 'Continuous', - lineWeight: -1 - } - layers.unshift(fallback) - layerMap.set('0', fallback) - } - - const blocks = new Map() - const blockByHandle = new Map() - for (const entry of db.tables.BLOCK_RECORD.entries) { - const info: CadBlockInfo = { - name: entry.name, - handle: entry.handle, - layoutHandle: entry.layout || '', - baseX: entry.basePoint?.x ?? 0, - baseY: entry.basePoint?.y ?? 0, - entities: entry.entities ?? [] - } - blocks.set(entry.name.toUpperCase(), info) - blockByHandle.set(entry.handle, info) - } - - const dashPatterns = new Map() - for (const ltype of db.tables.LTYPE.entries) { - if (!ltype.pattern || ltype.pattern.length === 0) continue - const pattern = ltype.pattern.map((el) => { - const len = Number(el.elementLength) - if (!Number.isFinite(len) || len === 0) return 0.12 - return Math.abs(len) - }) - dashPatterns.set(ltype.name, pattern) - dashPatterns.set(ltype.name.toLowerCase(), pattern) - } - - const imageNames = new Map() - for (const image of db.objects.IMAGEDEF) { - imageNames.set(image.handle, image.fileName || '栅格图像') - } - - const modelBlock = - findBlockByName(blocks, '*Model_Space') ?? - [...blocks.values()].find((b) => b.name.toUpperCase().includes(MODEL_PREFIX)) - if (!modelBlock) { - options.warnings.push('未找到模型空间,图纸可能已损坏。') - } - - const doc: CadDocument = { - db, - fileName: options.fileName, - fileSize: options.fileSize, - layers, - layerMap, - blocks, - blockByHandle, - dashPatterns, - imageNames, - spaces: [], - model: null as unknown as CadSpace, - units: String(db.header.INSUNITS ?? 0), - unitLabel: unitLabelFor(db.header.INSUNITS), - version: db.header.ACADVER ?? '', - unknownCount: options.unknownCount, - warnings: options.warnings - } - - const ltscale = Number(db.header.LTSCALE) > 0 ? Number(db.header.LTSCALE) : 1 - const modelEntities = modelBlock - ? modelBlock.entities.length - ? modelBlock.entities - : db.entities.filter((e) => e.ownerBlockRecordSoftId === modelBlock.handle) - : db.entities - - const model: CadSpace = { - id: 'model', - name: '模型', - kind: 'model', - blockHandle: modelBlock?.handle ?? '', - entities: modelEntities, - entityCount: modelEntities.length, - prims: [], - bbox: emptyBBox(), - viewports: [] - } - const modelState: BuildState = { doc, paper: false, prims: [], depth: 0, stack: new Set() } - buildEntities(modelEntities, modelState, IDENTITY, undefined, ltscale) - model.prims = modelState.prims - model.bbox = boundsOfPrims(model.prims) - if (!bboxValid(model.bbox) || bboxWidth(model.bbox) <= 0 || bboxHeight(model.bbox) <= 0) { - model.bbox = headerBounds(db) - } - if (!bboxValid(model.bbox)) model.bbox = { minX: 0, minY: 0, maxX: 100, maxY: 100 } - const robust = robustFitBounds(model.prims, model.bbox) - const header = headerBoundsOrNull(db) - let fit = robust - if (header) { - const headerArea = bboxWidth(header) * bboxHeight(header) - const robustArea = bboxWidth(robust) * bboxHeight(robust) - if (headerArea > 0 && headerArea < robustArea) fit = header - } - model.fitBBox = fit - model.savedView = findSavedView(db, header, robust) - model.grid = buildSpaceGrid(model.prims, fit) - doc.model = model - doc.spaces.push(model) - - const layoutObjects = [...db.objects.LAYOUT].sort((a, b) => { - const ta = Number(a.tabOrder) || 0 - const tb = Number(b.tabOrder) || 0 - if (ta !== tb) return ta - tb - return (a.layoutName || '').localeCompare(b.layoutName || '') - }) - - const usedBlocks = new Set() - const layoutSpaces: CadSpace[] = [] - for (const layout of layoutObjects) { - if (isModelLayout(layout)) continue - let block = blockByHandle.get(layout.paperSpaceTableId) - if (!block) { - block = [...blocks.values()].find((b) => b.layoutHandle === layout.handle) - } - if (!block) { - block = [...blocks.values()].find((b) => b.handle === layout.paperSpaceTableId) - } - if (!block) continue - if (block.handle === modelBlock?.handle) continue - if (usedBlocks.has(block.handle)) continue - usedBlocks.add(block.handle) - const entities = block.entities.length - ? block.entities - : db.entities.filter((e) => e.ownerBlockRecordSoftId === block!.handle) - const state: BuildState = { doc, paper: true, prims: [], depth: 0, stack: new Set() } - buildEntities(entities, state, IDENTITY, undefined, ltscale) - const viewports = entities.filter((e) => e.type === 'VIEWPORT') as DwgViewportEntity[] - let bbox = boundsOfPrims(state.prims) - for (const vp of viewports) { - const vb = viewportBBox(vp) - if (vb) bbox = bboxUnion(bbox, vb) - } - const extent = layoutExtent(layout) - if (extent && bboxValid(extent)) { - if (!bboxValid(bbox)) bbox = extent - else if ( - bboxWidth(extent) < bboxWidth(bbox) * 8 + 2000 && - bboxHeight(extent) < bboxHeight(bbox) * 8 + 2000 - ) { - bbox = bboxUnion(bbox, extent) - } - } - if (!bboxValid(bbox) || bboxWidth(bbox) <= 0 || bboxHeight(bbox) <= 0) { - bbox = { minX: 0, minY: 0, maxX: 420, maxY: 297 } - } - layoutSpaces.push({ - id: `layout:${block.handle}`, - name: friendlyLayoutName(layout.layoutName, layoutSpaces.length + 1), - kind: 'layout', - blockHandle: block.handle, - entities, - entityCount: entities.length, - prims: state.prims, - grid: buildSpaceGrid(state.prims, bbox), - bbox, - fitBBox: bbox, - viewports, - layout - }) - } - - if (layoutSpaces.length === 0) { - const paperBlocks = [...blocks.values()] - .filter((b) => b.name.toUpperCase().includes(PAPER_PREFIX) && b.handle !== modelBlock?.handle) - .sort((a, b) => a.name.localeCompare(b.name)) - for (const block of paperBlocks) { - if (usedBlocks.has(block.handle)) continue - usedBlocks.add(block.handle) - const entities = block.entities.length - ? block.entities - : db.entities.filter((e) => e.ownerBlockRecordSoftId === block.handle) - const state: BuildState = { doc, paper: true, prims: [], depth: 0, stack: new Set() } - buildEntities(entities, state, IDENTITY, undefined, ltscale) - const viewports = entities.filter((e) => e.type === 'VIEWPORT') as DwgViewportEntity[] - let bbox = boundsOfPrims(state.prims) - for (const vp of viewports) { - const vb = viewportBBox(vp) - if (vb) bbox = bboxUnion(bbox, vb) - } - if (!bboxValid(bbox)) bbox = { minX: 0, minY: 0, maxX: 420, maxY: 297 } - layoutSpaces.push({ - id: `layout:${block.handle}`, - name: `布局 ${layoutSpaces.length + 1}`, - kind: 'layout', - blockHandle: block.handle, - entities, - entityCount: entities.length, - prims: state.prims, - grid: buildSpaceGrid(state.prims, bbox), - bbox, - fitBBox: bbox, - viewports - }) - } - } - - layoutSpaces.sort((a, b) => { - const ta = Number(a.layout?.tabOrder) || 0 - const tb = Number(b.layout?.tabOrder) || 0 - if (ta !== tb) return ta - tb - return a.name.localeCompare(b.name) - }) - doc.spaces.push(...layoutSpaces) - const counts = new Map() - for (const space of doc.spaces) { - for (const entity of space.entities) { - const name = entity.layer || '0' - counts.set(name, (counts.get(name) ?? 0) + 1) - } - } - for (const layer of layers) layer.count = counts.get(layer.name) ?? 0 - if (layoutSpaces.length === 0) { - options.warnings.push('此图纸没有可用的布局空间(仅显示模型空间)。') - } - return doc -} - -function findBlockByName(blocks: Map, name: string): CadBlockInfo | undefined { - return blocks.get(name.toUpperCase()) -} - -function isModelLayout(layout: DwgLayoutObject): boolean { - const name = (layout.layoutName || '').trim().toLowerCase() - return name === 'model' || name === '模型' -} - -function friendlyLayoutName(name: string | undefined, fallbackIndex: number): string { - const trimmed = (name || '').trim() - if (!trimmed) return `布局 ${fallbackIndex}` - const match = /^layout\s*(\d+)$/i.exec(trimmed) - if (match) return `布局 ${match[1]}` - return trimmed -} - -function unitLabelFor(insunits: unknown): string { - const value = Number(insunits) - switch (value) { - case 1: - return 'in' - case 2: - return 'ft' - case 3: - return 'mi' - case 4: - return 'mm' - case 5: - return 'cm' - case 6: - return 'm' - case 7: - return 'km' - case 8: - return 'µin' - case 9: - return 'mil' - case 10: - return 'yd' - case 13: - return 'µm' - case 14: - return 'dm' - default: - return '' - } -} - -function headerBounds(db: DwgDatabase): BBoxLike { - return headerBoundsOrNull(db) ?? { minX: 0, minY: 0, maxX: 100, maxY: 100 } -} - -function headerBoundsOrNull(db: DwgDatabase): BBoxLike | null { - const min = db.header.EXTMIN - const max = db.header.EXTMAX - if ( - min && - max && - [min.x, min.y, max.x, max.y].every((v) => Number.isFinite(v)) && - Math.abs(min.x) < 1e12 && - Math.abs(min.y) < 1e12 && - Math.abs(max.x) < 1e12 && - Math.abs(max.y) < 1e12 && - max.x > min.x && - max.y > min.y - ) { - return { minX: min.x, minY: min.y, maxX: max.x, maxY: max.y } - } - return null -} - -function insideBox(x: number, y: number, box: BBoxLike): boolean { - return x >= box.minX && x <= box.maxX && y >= box.minY && y <= box.maxY -} - -function findSavedView( - db: DwgDatabase, - header: BBoxLike | null, - robust: BBoxLike -): { cx: number; cy: number; width: number; height: number } | undefined { - const vports = db.tables.VPORT.entries - if (!vports.length) return undefined - const vp = - vports.find((entry) => entry.name.toUpperCase() === '*ACTIVE') ?? - vports.find((entry) => Number(entry.viewHeight) > 0) ?? - vports[0] - const height = Number(vp.viewHeight) - if (!Number.isFinite(height) || height <= 0) return undefined - const center = vp.center - const target = vp.viewTarget - const cx = Number(center?.x) - const cy = Number(center?.y) - const tx = Number(target?.x) - const ty = Number(target?.y) - const useCenter = Number.isFinite(cx) && Number.isFinite(cy) - const px = useCenter ? cx : tx - const py = useCenter ? cy : ty - if (!Number.isFinite(px) || !Number.isFinite(py)) return undefined - if (!insideBox(px, py, robust) && !(header && insideBox(px, py, header))) { - return undefined - } - const aspect = Number(vp.aspectRatio) - const width = height * (Number.isFinite(aspect) && aspect > 0 ? aspect : 1.6) - return { cx: px, cy: py, width, height } -} - -type BBoxLike = { minX: number; minY: number; maxX: number; maxY: number } - -function layoutExtent(layout: DwgLayoutObject): BBoxLike | null { - const min = layout.minExtent - const max = layout.maxExtent - if ( - min && - max && - [min.x, min.y, max.x, max.y].every((v) => Number.isFinite(v)) && - max.x > min.x && - max.y > min.y && - max.x - min.x < 1e7 && - max.y - min.y < 1e7 - ) { - return { minX: min.x, minY: min.y, maxX: max.x, maxY: max.y } - } - return null -} - -function viewportBBox(vp: DwgViewportEntity): BBoxLike | null { - const width = Math.abs(Number(vp.width) || 0) - const height = Math.abs(Number(vp.height) || 0) - if (width <= 0 || height <= 0) return null - const cx = vp.viewportCenter?.x ?? 0 - const cy = vp.viewportCenter?.y ?? 0 - return { minX: cx - width / 2, minY: cy - height / 2, maxX: cx + width / 2, maxY: cy + height / 2 } -} - -function boundsOfPrims(prims: Primitive[]): BBoxLike { - let bbox = emptyBBox() - for (const prim of prims) { - if (prim.ignoreBounds) continue - bbox = bboxUnion(bbox, prim.bbox) - } - return bbox -} - -function buildSpaceGrid(prims: Primitive[], bbox: BBoxLike): SpaceGrid | undefined { - if (!prims.length || !bboxValid(bbox)) return undefined - const width = Math.max(bboxWidth(bbox), 1e-9) - const height = Math.max(bboxHeight(bbox), 1e-9) - const size = clamp(Math.ceil(Math.sqrt(prims.length)), 8, 96) - const cols = size - const rows = size - const cellW = width / cols - const cellH = height / rows - const cells: number[][] = Array.from({ length: cols * rows }, () => []) - const big: number[] = [] - - for (let index = 0; index < prims.length; index++) { - const prim = prims[index] - if (prim.ignoreBounds || !bboxValid(prim.bbox)) { - big.push(index) - continue - } - if ( - prim.bbox.maxX < bbox.minX || - prim.bbox.minX > bbox.maxX || - prim.bbox.maxY < bbox.minY || - prim.bbox.minY > bbox.maxY - ) { - big.push(index) - continue - } - const c0 = clamp(Math.floor((prim.bbox.minX - bbox.minX) / cellW), 0, cols - 1) - const c1 = clamp(Math.floor((prim.bbox.maxX - bbox.minX) / cellW), 0, cols - 1) - const r0 = clamp(Math.floor((prim.bbox.minY - bbox.minY) / cellH), 0, rows - 1) - const r1 = clamp(Math.floor((prim.bbox.maxY - bbox.minY) / cellH), 0, rows - 1) - if ((c1 - c0 + 1) * (r1 - r0 + 1) > 256) { - big.push(index) - continue - } - for (let row = r0; row <= r1; row++) { - for (let col = c0; col <= c1; col++) { - cells[row * cols + col].push(index) - } - } - } - - return { minX: bbox.minX, minY: bbox.minY, cellW, cellH, cols, rows, cells, big } -} - -function robustFitBounds(prims: Primitive[], raw: BBoxLike): BBoxLike { - if (!bboxValid(raw) || prims.length < 100) return raw - const xs: number[] = [] - const ys: number[] = [] - for (const prim of prims) { - if (prim.ignoreBounds || !bboxValid(prim.bbox)) continue - xs.push((prim.bbox.minX + prim.bbox.maxX) / 2) - ys.push((prim.bbox.minY + prim.bbox.maxY) / 2) - } - if (xs.length < 100) return raw - xs.sort((a, b) => a - b) - ys.sort((a, b) => a - b) - const n = xs.length - const trim = n > 20000 ? 0.01 : 0.02 - const lo = Math.floor(n * trim) - const hi = Math.max(lo, Math.ceil(n * (1 - trim)) - 1) - let candidate: BBoxLike = { - minX: xs[lo], - minY: ys[lo], - maxX: xs[hi], - maxY: ys[hi] - } - if (!bboxValid(candidate) || bboxWidth(candidate) <= 0 || bboxHeight(candidate) <= 0) { - return raw - } - candidate = bboxExpand( - candidate, - Math.max(bboxWidth(candidate) * 0.04, 1e-9), - Math.max(bboxHeight(candidate) * 0.04, 1e-9) - ) - return candidate -} - -function buildEntities( - entities: DwgEntity[], - state: BuildState, - mat: Mat, - inherit: ColorSpec | undefined, - ltscale: number -): void { - for (const entity of entities) { - buildEntity(entity as AnyEntity, state, mat, inherit, ltscale) - } -} - -function buildEntity( - entity: AnyEntity, - state: BuildState, - mat: Mat, - inherit: ColorSpec | undefined, - ltscale: number -): void { - if (!entity || entity.isVisible === false) return - const type = String(entity.type || '').toUpperCase() - if (type === 'VIEWPORT' && !state.paper) return - const extrusion = entity.extrusionDirection - const mirror = extrusion && Number(extrusion.z) === -1 ? matScale(-1, 1) : null - const em = mirror ? matMul(mat, mirror) : mat - const color = resolveColor(entity, state.doc, inherit) - const width = lineWidthOf(entity, state.paper, state.doc) - const dashInfo = resolveDash(entity, state.doc, ltscale) - - switch (type) { - case 'LINE': { - const a = entity.startPoint - const b = entity.endPoint - if (!a || !b) return - const points = [a.x, a.y, b.x, b.y] - addPoly(state, entity, em, points, false, false, true, color, width, dashInfo) - return - } - case 'CIRCLE': { - const c = entity.center - const r = Math.abs(Number(entity.radius) || 0) - if (!c || r <= 0) return - const prim: CirclePrim = { - kind: 'circle', -...base(entity, em, color, width, dashInfo), - cx: c.x, - cy: c.y, - r, - fill: false - } - prim.bbox = circleBBox(c.x, c.y, r, em) - state.prims.push(prim) - return - } - case 'ARC': { - const c = entity.center - const r = Math.abs(Number(entity.radius) || 0) - if (!c || r <= 0) return - const a0 = Number(entity.startAngle) || 0 - const a1 = Number(entity.endAngle) || 0 - let sweep = a1 - a0 - while (sweep <= 0) sweep += Math.PI * 2 - const prim: ArcPrim = { - kind: 'arc', -...base(entity, em, color, width, dashInfo), - cx: c.x, - cy: c.y, - r, - a0, - sweep - } - prim.bbox = circleBBox(c.x, c.y, r, em) - state.prims.push(prim) - return - } - case 'ELLIPSE': { - const c = entity.center - const major = entity.majorAxisEndPoint - if (!c || !major) return - const mx = Number(major.x) || 0 - const my = Number(major.y) || 0 - if (Math.abs(mx) + Math.abs(my) < 1e-12) return - const ratio = clamp(Number(entity.axisRatio) || 1, 0.0001, 1) - const a0 = Number(entity.startAngle) || 0 - const a1 = Number(entity.endAngle) || Math.PI * 2 - let sweep = a1 - a0 - if (Math.abs(sweep) < 1e-9) sweep = Math.PI * 2 - if (sweep < 0) sweep += Math.PI * 2 - const prim: EllipsePrim = { - kind: 'ellipse', -...base(entity, em, color, width, dashInfo), - cx: c.x, - cy: c.y, - mx, - my, - ratio, - a0, - sweep, - fill: false - } - const r = Math.hypot(mx, my) * Math.max(ratio, 1) - prim.bbox = circleBBox(c.x, c.y, r, em) - state.prims.push(prim) - return - } - case 'LWPOLYLINE': { - const vertices = Array.isArray(entity.vertices) ? entity.vertices : [] - if (vertices.length < 2) return - const closed = (Number(entity.flag) & 1) === 1 - const hasBulge = vertices.some((v: any) => Math.abs(Number(v.bulge) || 0) > 1e-9) - const worldWidth = Math.abs(Number(entity.constantWidth) || 0) - if (hasBulge) { - const prim: BulgePrim = { - kind: 'bulge', -...base(entity, em, color, width, dashInfo, worldWidth > 0 ? worldWidth : undefined), - vertices: vertices.map((v: any) => ({ - x: Number(v.x) || 0, - y: Number(v.y) || 0, - bulge: Number(v.bulge) || 0 - })), - closed - } - prim.bbox = bulgeBounds(prim.vertices, closed, em) - state.prims.push(prim) - } else { - const points: number[] = [] - for (const v of vertices) points.push(Number(v.x) || 0, Number(v.y) || 0) - addPoly(state, entity, em, points, closed, false, true, color, width, dashInfo, worldWidth) - } - return - } - case 'POLYLINE': - case 'POLYLINE2D': - case 'POLYLINE3D': - case 'POLYLINE_PFACE': - case 'POLYLINE_MESH': { - const vertices = Array.isArray(entity.vertices) ? entity.vertices : [] - if (vertices.length < 2) return - const closed = (Number(entity.flag) & 1) === 1 - const hasBulge = vertices.some((v: any) => Math.abs(Number(v.bulge) || 0) > 1e-9) - if (hasBulge) { - const prim: BulgePrim = { - kind: 'bulge', -...base(entity, em, color, width, dashInfo), - vertices: vertices.map((v: any) => ({ - x: Number(v.x) || 0, - y: Number(v.y) || 0, - bulge: Number(v.bulge) || 0 - })), - closed - } - prim.bbox = bulgeBounds(prim.vertices, closed, em) - state.prims.push(prim) - } else { - const points: number[] = [] - for (const v of vertices) points.push(Number(v.x) || 0, Number(v.y) || 0) - addPoly(state, entity, em, points, closed, false, true, color, width, dashInfo) - } - return - } - case 'SPLINE': { - const control = Array.isArray(entity.controlPoints) ? entity.controlPoints : [] - const fit = Array.isArray(entity.fitPoints) ? entity.fitPoints : [] - const source = control.length >= 2 ? control : fit - if (source.length < 2) return - const flat: number[] = [] - for (const p of source) flat.push(Number(p.x) || 0, Number(p.y) || 0) - const prim: SplinePrim = { - kind: 'spline', - ...base(entity, em, color, width, dashInfo), - control: flat, - degree: Math.max(1, Number(entity.degree) || 3), - knots: Array.isArray(entity.knots) ? entity.knots.map((n: any) => Number(n) || 0) : [], - weights: Array.isArray(entity.weights) && entity.weights.length ? entity.weights : undefined, - closed: (Number(entity.flag) & 1) === 1 - } - prim.bbox = bboxTransform(em, bboxFromPoints(flat)) - if (!bboxValid(prim.bbox)) prim.bbox = circleBBox(flat[0] ?? 0, flat[1] ?? 0, 1, em) - state.prims.push(prim) - return - } - case 'TEXT': { - addTextEntity(entity, state, em, color, width, dashInfo) - return - } - case 'MTEXT': { - addMTextEntity(entity, state, em, color, width, dashInfo) - return - } - case 'ATTRIB': { - const data = entity.text - if (!data) return - addTextData(data, state, em, color, width, dashInfo, inherit, entity) - return - } - case 'ATTDEF': - case 'SEQEND': - case 'ENDBLK': - case 'BLOCK': - return - case 'INSERT': - case 'MINSERT': { - addInsert(entity, state, em, color, width, dashInfo, ltscale) - return - } - case 'DIMENSION': { - const name = String(entity.name || entity.blockName || '') - const block = state.doc.blocks.get(name.toUpperCase()) - if (!block || state.stack.has(block.name) || state.depth > 12) return - state.stack.add(block.name) - state.depth++ - buildEntities(block.entities, state, em, color, ltscale) - state.depth-- - state.stack.delete(block.name) - return - } - case 'LEADER': { - addLeader(entity, state, em, color, width, dashInfo) - return - } - case 'RAY': - case 'XLINE': { - const p = entity.firstPoint - const d = entity.unitDirection - if (!p || !d) return - const length = 1e6 - const dx = Number(d.x) || 0 - const dy = Number(d.y) || 0 - const len = Math.hypot(dx, dy) || 1 - const ux = dx / len - const uy = dy / len - const points = - type === 'XLINE' - ? [p.x - ux * length, p.y - uy * length, p.x + ux * length, p.y + uy * length] - : [p.x, p.y, p.x + ux * length, p.y + uy * length] - addPoly(state, entity, em, points, false, false, true, color, width, dashInfo, undefined, true) - return - } - case 'SOLID': - case 'TRACE': - case '3DFACE': { - const c1 = entity.corner1 - const c2 = entity.corner2 - const c3 = entity.corner3 - const c4 = entity.corner4 ?? entity.corner3 - if (!c1 || !c2 || !c3) return - const points = - type === '3DFACE' - ? [c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4.x, c4.y] - : [c1.x, c1.y, c2.x, c2.y, c4.x, c4.y, c3.x, c3.y] - addPoly(state, entity, em, points, true, type !== '3DFACE', type === '3DFACE', color, width, dashInfo) - return - } - case 'POINT': { - const p = entity.position - if (!p) return - const prim: PointPrim = { - kind: 'point', -...base(entity, em, color, width, dashInfo), - x: p.x, - y: p.y - } - prim.bbox = bboxTransform(em, { minX: p.x, minY: p.y, maxX: p.x, maxY: p.y }) - state.prims.push(prim) - return - } - case 'HATCH': { - addHatch(entity, state, em, color, width, dashInfo) - return - } - case 'WIPEOUT': { - const pts = wipeoutPoints(entity) - if (pts.length < 6) return - const prim: PolyPrim = { - kind: 'poly', -...base(entity, em, color, width, dashInfo), - points: pts, - closed: true, - fill: true, - fillBackground: true, - stroke: false - } - prim.bbox = bboxTransform(em, bboxFromPoints(pts)) - state.prims.push(prim) - return - } - case 'IMAGE': { - const pos = entity.position - const u = entity.uPixel - const v = entity.vPixel - const size = entity.imageSize - if (!pos) return - const w = Math.abs(Number(size?.x) || 0) - const h = Math.abs(Number(size?.y) || 0) - const ux = Number(u?.x) || 0 - const uy = Number(u?.y) || 0 - const vx = Number(v?.x) || 0 - const vy = Number(v?.y) || 0 - if (w <= 0 || h <= 0 || (!ux && !uy && !vx && !vy)) return - const pts = [ - pos.x, - pos.y, - pos.x + ux * w, - pos.y + uy * w, - pos.x + ux * w + vx * h, - pos.y + uy * w + vy * h, - pos.x + vx * h, - pos.y + vy * h - ] - const label = state.doc.imageNames.get(String(entity.imageDefHandle || '')) || '栅格图像' - const prim: ImagePrim = { - kind: 'image', -...base(entity, em, color, width, dashInfo), - x1: 0, - y1: 0, - x2: 0, - y2: 0, - points: pts, - label - } - const p = bboxTransform(em, bboxFromPoints(pts)) - prim.bbox = p - prim.x1 = p.minX - prim.y1 = p.minY - prim.x2 = p.maxX - prim.y2 = p.maxY - state.prims.push(prim) - return - } - case 'ACAD_TABLE': - case 'TABLE': { - addTable(entity, state, em, color, width, dashInfo) - return - } - case 'TOLERANCE': { - if (!entity.text) return - const fake = { - ...entity, - type: 'MTEXT', - insertionPoint: entity.insertionPoint ?? { x: 0, y: 0, z: 0 }, - textHeight: Number(entity.textHeight) || 2.5, - rotation: 0, - text: entity.text, - rectWidth: 0, - extentsWidth: 0, - attachmentPoint: 1, - lineSpacing: 1, - styleName: entity.styleName - } - addMTextEntity(fake, state, em, color, width, dashInfo) - return - } - case 'OLE2FRAME': { - const a = entity.corner1 - const b = entity.corner2 - if (!a || !b) return - const x1 = Math.min(Number(a.x) || 0, Number(b.x) || 0) - const x2 = Math.max(Number(a.x) || 0, Number(b.x) || 0) - const y1 = Math.min(Number(a.y) || 0, Number(b.y) || 0) - const y2 = Math.max(Number(a.y) || 0, Number(b.y) || 0) - const points = [x1, y1, x2, y1, x2, y2, x1, y2] - const prim: ImagePrim = { - kind: 'image', - ...base(entity, em, color, width, dashInfo), - x1, - y1, - x2, - y2, - points, - label: String(entity.label || 'OLE 对象') - } - prim.bbox = bboxTransform(em, bboxFromPoints(points)) - state.prims.push(prim) - return - } - default: - return - } -} - -function base( - entity: AnyEntity, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined, - worldWidth?: number, - ignoreBounds?: boolean -): PrimBase { - return { - layer: entity.layer || '0', - color, - width, - worldWidth, - dash, - bbox: emptyBBox(), - mat, - ignoreBounds, - proxy: entity.proxy === true ? true : undefined - } -} - -function addPoly( - state: BuildState, - entity: AnyEntity, - mat: Mat, - points: number[], - closed: boolean, - fill: boolean, - stroke: boolean, - color: ColorSpec, - width: number, - dash: number[] | undefined, - worldWidth?: number, - ignoreBounds?: boolean -): void { - if (points.length < 4) return - const prim: PolyPrim = { - kind: 'poly', -...base(entity, mat, color, width, dash, worldWidth, ignoreBounds), - points, - closed, - fill, - stroke - } - prim.bbox = bboxTransform(mat, bboxFromPoints(points)) - state.prims.push(prim) -} - -function addTextEntity( - entity: AnyEntity, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined -): void { - const data = { - text: entity.text, - startPoint: entity.startPoint, - endPoint: entity.endPoint, - textHeight: entity.textHeight, - rotation: entity.rotation, - xScale: entity.xScale, - halign: entity.halign, - valign: entity.valign, - generationFlag: entity.generationFlag - } - addTextData(data, state, mat, color, width, dash, undefined, entity) -} - -function addTextData( - data: Record, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined, - _inherit: ColorSpec | undefined, - entity: AnyEntity -): void { - const raw = String(data.text ?? '') - if (!raw) return - const lines = [decodeCadEscapes(raw)].filter((l) => l.length > 0) - if (!lines.length) return - const height = Math.abs(Number(data.textHeight) || 0) - if (height <= 0) return - const halignRaw = Number(data.halign) || 0 - const valignRaw = Number(data.valign) || 0 - const halign: TextPrim['halign'] = - halignRaw === 1 || halignRaw === 4 ? 'center' : halignRaw === 2 ? 'right' : 'left' - const valign: TextPrim['valign'] = - valignRaw === 3 ? 'top' : valignRaw === 2 ? 'middle' : valignRaw === 1 ? 'bottom' : 'baseline' - const hasAlign = halignRaw !== 0 || valignRaw !== 0 - const end = data.endPoint - const hasEnd = - end && (Math.abs(Number(end.x) || 0) > 1e-12 || Math.abs(Number(end.y) || 0) > 1e-12) - const anchor = hasAlign && hasEnd ? end : data.startPoint - if (!anchor) return - const x = Number(anchor.x) || 0 - const y = Number(anchor.y) || 0 - const rotation = Number(data.rotation) || 0 - const widthFactor = Math.abs(Number(data.xScale) || 1) || 1 - const lineHeight = height * 1.28 - const prim: TextPrim = { - kind: 'text', -...base(entity, mat, color, width, dash), - x, - y, - height, - rotation, - lines, - halign, - valign, - lineHeight, - widthFactor, - mirrored: (Number(data.generationFlag) & 2) === 2 - } - prim.bbox = textBBox(prim, mat) - state.prims.push(prim) -} - -function addMTextEntity( - entity: AnyEntity, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined -): void { - const raw = String(entity.text ?? '') - if (!raw) return - const height = Math.abs(Number(entity.textHeight) || 0) - if (height <= 0) return - const rectWidth = Math.abs(Number(entity.rectWidth) || 0) - const wrapWidth = rectWidth - let lines = parseMText(raw, wrapWidth, height) - if (!lines.length) lines = [decodeCadEscapes(raw)] - lines = lines.filter((line) => line.length > 0) - if (!lines.length) return - const attach = Number(entity.attachmentPoint) || 1 - const halign: TextPrim['halign'] = - attach === 2 || attach === 5 || attach === 8 - ? 'center' - : attach === 3 || attach === 6 || attach === 9 - ? 'right' - : 'left' - const valign: TextPrim['valign'] = - attach === 1 || attach === 2 || attach === 3 - ? 'top' - : attach === 4 || attach === 5 || attach === 6 - ? 'middle' - : 'bottom' - const p = entity.insertionPoint - if (!p) return - let rotation = Number(entity.rotation) || 0 - if (Math.abs(rotation) < 1e-9 && entity.direction) { - const dx = Number(entity.direction.x) || 0 - const dy = Number(entity.direction.y) || 0 - if (dx || dy) rotation = Math.atan2(dy, dx) - } - const lineSpacing = Number(entity.lineSpacing) || 1 - const lineHeight = height * 1.35 * clamp(lineSpacing, 0.3, 3) - const prim: TextPrim = { - kind: 'text', -...base(entity, mat, color, width, dash), - x: Number(p.x) || 0, - y: Number(p.y) || 0, - height, - rotation, - lines, - halign, - valign, - lineHeight, - widthFactor: 1, - mirrored: false - } - prim.bbox = textBBox(prim, mat) - state.prims.push(prim) -} - -function addInsert( - entity: AnyEntity, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined, - ltscale: number -): void { - const name = String(entity.name || '') - const block = state.doc.blocks.get(name.toUpperCase()) - if (!block) return - if (state.stack.has(block.name) || state.depth > 12) return - const pos = entity.insertionPoint ?? { x: 0, y: 0, z: 0 } - const rotation = Number(entity.rotation) || 0 - const sx = Number(entity.xScale) || 1 - const sy = Number(entity.yScale) || 1 - const rows = clamp(Math.floor(Number(entity.rowCount) || 1), 1, 100) - const cols = clamp(Math.floor(Number(entity.columnCount) || 1), 1, 100) - const rowSpacing = Number(entity.rowSpacing) || 0 - const colSpacing = Number(entity.columnSpacing) || 0 - const baseX = block.baseX || 0 - const baseY = block.baseY || 0 - state.stack.add(block.name) - state.depth++ - for (let row = 0; row < rows; row++) { - for (let col = 0; col < cols; col++) { - let m = matMul(mat, matTranslate(Number(pos.x) || 0, Number(pos.y) || 0)) - m = matMul(m, matRotate(rotation)) - m = matMul(m, matScale(sx, sy)) - if (col || row) m = matMul(m, matTranslate(col * colSpacing, row * rowSpacing)) - m = matMul(m, matTranslate(-baseX, -baseY)) - buildEntities(block.entities, state, m, color, ltscale) - } - } - state.depth-- - state.stack.delete(block.name) - - if (Array.isArray(entity.attribs)) { - for (const attrib of entity.attribs) { - if (!attrib || attrib.isVisible === false) continue - if ((Number(attrib.flags) & 1) === 1) continue - const data = attrib.text ?? attrib - const attrColor = resolveColor(attrib as AnyEntity, state.doc, color) - addTextData(data, state, mat, attrColor, width, dash, color, attrib as AnyEntity) - } - } -} - -function addLeader( - entity: AnyEntity, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined -): void { - const vertices = Array.isArray(entity.vertices) ? entity.vertices : [] - if (vertices.length < 2) return - const points: number[] = [] - for (const v of vertices) points.push(Number(v.x) || 0, Number(v.y) || 0) - addPoly(state, entity, mat, points, false, false, true, color, width, dash) - if (entity.isArrowheadEnabled !== false) { - const x0 = Number(vertices[0].x) || 0 - const y0 = Number(vertices[0].y) || 0 - const x1 = Number(vertices[1].x) || 0 - const y1 = Number(vertices[1].y) || 0 - const dx = x1 - x0 - const dy = y1 - y0 - const len = Math.hypot(dx, dy) - if (len > 1e-9) { - const size = Math.min(len * 0.18, Math.max(Number(entity.textHeight) || 0, len * 0.08, 1)) - const ux = dx / len - const uy = dy / len - const px = -uy - const py = ux - const pts = [ - x0, - y0, - x0 + ux * size + px * size * 0.42, - y0 + uy * size + py * size * 0.42, - x0 + ux * size - px * size * 0.42, - y0 + uy * size - py * size * 0.42 - ] - addPoly(state, entity, mat, pts, true, true, false, color, width, undefined) - } - } -} - -function addHatch( - entity: AnyEntity, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined -): void { - const paths: number[][] = [] - const rawPaths = Array.isArray(entity.boundaryPaths) ? entity.boundaryPaths : [] - for (const path of rawPaths) { - const pts = hatchPathPoints(path) - if (pts.length >= 6) paths.push(pts) - } - if (!paths.length) return - const solid = Number(entity.solidFill) === 1 - const angle = normalizeAngle(Number(entity.patternAngle) || 0) - let spacing = 0 - if (Array.isArray(entity.definitionLines) && entity.definitionLines.length) { - const offset = entity.definitionLines[0]?.offset - if (offset) spacing = Math.hypot(Number(offset.x) || 0, Number(offset.y) || 0) - } - if (!spacing || !Number.isFinite(spacing)) { - spacing = Math.max(1, Math.abs(Number(entity.patternScale) || 1) * 3) - } - const prim: HatchPrim = { - kind: 'hatch', -...base(entity, mat, color, solid ? 0 : Math.max(0.55, width * 0.7), dash), - paths, - solid, - patternName: String(entity.patternName || ''), - patternAngle: angle, - patternSpacing: Math.max(0.3, spacing) - } - let bbox = emptyBBox() - for (const p of paths) bbox = bboxUnion(bbox, bboxTransform(mat, bboxFromPoints(p))) - prim.bbox = bbox - state.prims.push(prim) -} - -function hatchPathPoints(path: AnyEntity): number[] { - const out: number[] = [] - if (Array.isArray(path.vertices) && path.vertices.length >= 2) { - const verts = path.vertices - for (let i = 0; i < verts.length; i++) { - const v = verts[i] - const x = Number(v.x) || 0 - const y = Number(v.y) || 0 - if (i > 0) { - const prev = verts[i - 1] - const bulge = Number(prev.bulge) || 0 - if (Math.abs(bulge) > 1e-9) { - const arc = bulgeToArc(Number(prev.x) || 0, Number(prev.y) || 0, x, y, bulge) - if (arc) { - const pts = sampleArcPoints(arc.cx, arc.cy, arc.r, arc.a0, arc.sweep, 80) - for (let j = 2; j < pts.length; j += 2) out.push(pts[j], pts[j + 1]) - continue - } - } - } - out.push(x, y) - } - const last = verts[verts.length - 1] - const first = verts[0] - const bulge = Number(last.bulge) || 0 - if (Math.abs(bulge) > 1e-9) { - const arc = bulgeToArc( - Number(last.x) || 0, - Number(last.y) || 0, - Number(first.x) || 0, - Number(first.y) || 0, - bulge - ) - if (arc) { - const pts = sampleArcPoints(arc.cx, arc.cy, arc.r, arc.a0, arc.sweep, 80) - for (let j = 2; j < pts.length; j += 2) out.push(pts[j], pts[j + 1]) - } - } - return out - } - if (Array.isArray(path.edges)) { - for (const edge of path.edges) { - const type = Number(edge.type) - if (type === 1) { - const s = edge.start - const e = edge.end - if (s && e) out.push(Number(s.x) || 0, Number(s.y) || 0, Number(e.x) || 0, Number(e.y) || 0) - } else if (type === 2) { - const c = edge.center - if (!c) continue - const r = Math.abs(Number(edge.radius) || 0) - const a0 = Number(edge.startAngle) || 0 - const a1 = Number(edge.endAngle) || 0 - let sweep = a1 - a0 - while (sweep <= 0) sweep += Math.PI * 2 - const pts = sampleArcPoints(Number(c.x) || 0, Number(c.y) || 0, r, a0, sweep, 64) - for (let j = 0; j < pts.length; j += 2) out.push(pts[j], pts[j + 1]) - } else if (type === 3) { - const c = edge.center - const ep = edge.end - if (!c || !ep) continue - const mx = (Number(ep.x) || 0) - (Number(c.x) || 0) - const my = (Number(ep.y) || 0) - (Number(c.y) || 0) - const ratio = Math.hypot(mx, my) > 0 ? Math.abs(Number(edge.lengthOfMinorAxis) || 1) / Math.hypot(mx, my) : 1 - const a0 = Number(edge.startAngle) || 0 - const a1 = Number(edge.endAngle) || 0 - let sweep = a1 - a0 - while (sweep <= 0) sweep += Math.PI * 2 - const pts = sampleEllipsePoints(Number(c.x) || 0, Number(c.y) || 0, mx, my, clamp(ratio, 0.001, 1), a0, sweep, 64) - for (let j = 0; j < pts.length; j += 2) out.push(pts[j], pts[j + 1]) - } else if (type === 4) { - const cps = Array.isArray(edge.controlPoints) ? edge.controlPoints : [] - for (const p of cps) out.push(Number(p.x) || 0, Number(p.y) || 0) - } - } - } - return out -} - -function wipeoutPoints(entity: AnyEntity): number[] { - if (Array.isArray(entity.clippingBoundaryPath) && entity.clippingBoundaryPath.length >= 2) { - const pts: number[] = [] - for (const p of entity.clippingBoundaryPath) pts.push(Number(p.x) || 0, Number(p.y) || 0) - return pts - } - const pos = entity.position - const u = entity.uPixel - const v = entity.vPixel - const size = entity.imageSize - if (!pos || !u || !v || !size) return [] - const w = Math.abs(Number(size.x) || 0) - const h = Math.abs(Number(size.y) || 0) - const ux = Number(u.x) || 0 - const uy = Number(u.y) || 0 - const vx = Number(v.x) || 0 - const vy = Number(v.y) || 0 - return [ - pos.x, - pos.y, - pos.x + ux * w, - pos.y + uy * w, - pos.x + ux * w + vx * h, - pos.y + uy * w + vy * h, - pos.x + vx * h, - pos.y + vy * h - ] -} - -function addTable( - entity: AnyEntity, - state: BuildState, - mat: Mat, - color: ColorSpec, - width: number, - dash: number[] | undefined -): void { - const rows = clamp(Math.floor(Number(entity.rowCount) || 0), 0, 500) - const cols = clamp(Math.floor(Number(entity.columnCount) || 0), 0, 200) - const rowHeights: number[] = Array.isArray(entity.rowHeightArr) ? entity.rowHeightArr : [] - const colWidths: number[] = Array.isArray(entity.columnWidthArr) ? entity.columnWidthArr : [] - const cells: AnyEntity[] = Array.isArray(entity.cells) ? entity.cells : [] - const start = entity.startPoint - if (!start || rows <= 0 || cols <= 0) return - let y = Number(start.y) || 0 - for (let r = 0; r < rows; r++) { - const rh = Math.abs(Number(rowHeights[r]) || 0) - let x = Number(start.x) || 0 - for (let c = 0; c < cols; c++) { - const cw = Math.abs(Number(colWidths[c]) || 0) - const cell = cells[r * cols + c] - if (cell) { - if (cell.topBorderVisibility) addPoly(state, entity, mat, [x, y, x + cw, y], false, false, true, color, width, dash) - if (cell.bottomBorderVisibility) addPoly(state, entity, mat, [x, y + rh, x + cw, y + rh], false, false, true, color, width, dash) - if (cell.leftBorderVisibility) addPoly(state, entity, mat, [x, y, x, y + rh], false, false, true, color, width, dash) - if (cell.rightBorderVisibility) addPoly(state, entity, mat, [x + cw, y, x + cw, y + rh], false, false, true, color, width, dash) - const text = String(cell.text ?? '') - if (text) { - const h = Math.abs(Number(cell.textHeight) || 0) - const fake = { - ...entity, - type: 'MTEXT', - insertionPoint: { x: x + cw / 2, y: y + rh / 2, z: 0 }, - textHeight: h, - rotation: 0, - text, - rectWidth: 0, - extentsWidth: 0, - attachmentPoint: 5, - lineSpacing: 1 - } - addMTextEntity(fake, state, mat, color, width, dash) - } - } - x += cw - } - y += rh - } -} - -function resolveColor(entity: AnyEntity, doc: CadDocument, inherit: ColorSpec | undefined): ColorSpec { - const ci = typeof entity.colorIndex === 'number' ? entity.colorIndex : 256 - const trueColor = typeof entity.color === 'number' ? entity.color >>> 0 : undefined - if (trueColor != null && trueColor !== 0xffffff && trueColor !== 0 && (ci === 0 || ci === 256)) { - return { kind: 'rgb', value: trueColor } - } - if (ci === 0) return inherit ?? { kind: 'aci', value: 7 } - if (ci === 256 || !Number.isFinite(ci)) return layerColor(entity.layer, doc) - const index = Math.abs(Math.round(ci)) - if (index >= 1 && index <= 255) return { kind: 'aci', value: index } - return layerColor(entity.layer, doc) -} - -function layerColor(name: string | undefined, doc: CadDocument): ColorSpec { - const key = name || '0' - const layer = doc.layerMap.get(key) ?? doc.layerMap.get(key.toLowerCase()) - if (!layer) return { kind: 'aci', value: 7 } - if (layer.rgb != null) return { kind: 'rgb', value: layer.rgb } - return { kind: 'aci', value: layer.aci || 7 } -} - -function lineWidthOf(entity: AnyEntity, paper: boolean, doc: CadDocument): number { - let lw = Number(entity.lineweight) - if (!Number.isFinite(lw) || lw < 0) { - const layer = findLayer(doc, entity.layer || '0') - if (layer && Number.isFinite(layer.lineWeight) && layer.lineWeight > 0) lw = layer.lineWeight - } - let width: number - if (!Number.isFinite(lw) || lw < 0) width = 1 - else if (lw === 0) width = 0.55 - else width = clamp(0.6 + (lw / 100) * 1.8, 0.6, 4.2) - return paper ? Math.max(0.55, width * 0.88) : width -} - -function resolveDash(entity: AnyEntity, doc: CadDocument, ltscale: number): number[] | undefined { - let name = String(entity.lineType || '').trim() - if (!name || /^bylayer$/i.test(name)) { - const layer = findLayer(doc, entity.layer || '0') - name = layer?.lineType || '' - } - if (!name || /^(byblock|bylayer|continuous)$/i.test(name)) return undefined - const entityScale = Number(entity.lineTypeScale) > 0 ? Number(entity.lineTypeScale) : 1 - const pattern = doc.dashPatterns.get(name) ?? doc.dashPatterns.get(name.toLowerCase()) - let base: number[] | undefined - if (pattern && pattern.length) { - base = pattern - } else { - const upper = name.toUpperCase() - if (upper.includes('DASHDOT') || upper.includes('DASH_DOT')) base = [4, 0.8, 0.15, 0.8] - else if (upper.includes('DASHED') || upper.includes('DASH')) base = [3, 1.2] - else if (upper.includes('HIDDEN')) base = [1.2, 0.7] - else if (upper.includes('CENTER')) base = [5, 0.9, 0.9, 0.9] - else if (upper.includes('DOT')) base = [0.15, 1] - } - if (!base) return undefined - const scale = ltscale * entityScale - const scaled = base.map((v) => Math.max(0.04, v * scale)) - return scaled.some((v) => v > 0.05) ? scaled : undefined -} - -function circleBBox(cx: number, cy: number, r: number, mat: Mat): BBoxLike { - return bboxTransform(mat, { minX: cx - r, minY: cy - r, maxX: cx + r, maxY: cy + r }) -} - -function bulgeBounds(vertices: Array<{ x: number; y: number; bulge: number }>, closed: boolean, mat: Mat): BBoxLike { - let local = emptyBBox() - for (const v of vertices) local = bboxUnion(local, { minX: v.x, minY: v.y, maxX: v.x, maxY: v.y }) - for (let i = 0; i < vertices.length; i++) { - const next = i + 1 < vertices.length ? i + 1 : closed ? 0 : -1 - if (next < 0) break - const a = vertices[i] - const b = vertices[next] - if (Math.abs(a.bulge) < 1e-9) continue - const arc = bulgeToArc(a.x, a.y, b.x, b.y, a.bulge) - if (!arc) continue - local = bboxUnion(local, arcBounds(arc.cx, arc.cy, arc.r, arc.a0, arc.sweep)) - } - return bboxTransform(mat, local) -} - -function arcBounds(cx: number, cy: number, r: number, a0: number, sweep: number): BBoxLike { - let bbox: BBoxLike = { - minX: Number.POSITIVE_INFINITY, - minY: Number.POSITIVE_INFINITY, - maxX: Number.NEGATIVE_INFINITY, - maxY: Number.NEGATIVE_INFINITY - } - bboxAddPoint(bbox, cx + r * Math.cos(a0), cy + r * Math.sin(a0)) - bboxAddPoint(bbox, cx + r * Math.cos(a0 + sweep), cy + r * Math.sin(a0 + sweep)) - const step = Math.PI / 2 - const start = Math.min(a0, a0 + sweep) - const end = Math.max(a0, a0 + sweep) - for (let a = Math.ceil(start / step) * step; a <= end; a += step) { - bboxAddPoint(bbox, cx + r * Math.cos(a), cy + r * Math.sin(a)) - } - return bbox -} - -function textBBox(prim: TextPrim, mat: Mat): BBoxLike { - const lines = prim.lines.length ? prim.lines : [''] - const count = lines.length - const lineHeight = prim.lineHeight - let maxWidth = 0 - for (const line of lines) maxWidth = Math.max(maxWidth, textWidth(line, prim.height)) - maxWidth = Math.max(maxWidth * Math.abs(prim.widthFactor), prim.height * 0.5) - const totalHeight = Math.max(lineHeight * count, prim.height) - let minX = 0 - let maxX = maxWidth - if (prim.halign === 'center') { - minX = -maxWidth / 2 - maxX = maxWidth / 2 - } else if (prim.halign === 'right') { - minX = -maxWidth - maxX = 0 - } - let minY = 0 - let maxY = totalHeight - if (prim.valign === 'top') { - minY = -totalHeight - maxY = 0 - } else if (prim.valign === 'middle') { - minY = -totalHeight / 2 - maxY = totalHeight / 2 - } - const cos = Math.cos(prim.rotation) - const sin = Math.sin(prim.rotation) - const bbox = emptyBBox() - const corners = [ - [minX, minY], - [maxX, minY], - [maxX, maxY], - [minX, maxY] - ] - for (const [lx, ly] of corners) { - const wx = prim.x + lx * cos - ly * sin - const wy = prim.y + lx * sin + ly * cos - const [sx, sy] = applyMat(mat, wx, wy) - bboxAddPoint(bbox, sx, sy) - } - return bbox -} - -function normalizeAngle(value: number): number { - if (Math.abs(value) > Math.PI * 2 + 0.001) return (value * Math.PI) / 180 - return value -} - -function sampleArcPoints( - cx: number, - cy: number, - r: number, - a0: number, - sweep: number, - segments: number -): number[] { - const out: number[] = [] - const n = clamp(segments, 4, 256) - for (let i = 0; i <= n; i++) { - const a = a0 + (sweep * i) / n - out.push(cx + r * Math.cos(a), cy + r * Math.sin(a)) - } - return out -} - -function sampleEllipsePoints( - cx: number, - cy: number, - mx: number, - my: number, - ratio: number, - a0: number, - sweep: number, - segments: number -): number[] { - const nx = -my * ratio - const ny = mx * ratio - const out: number[] = [] - const n = clamp(segments, 4, 256) - for (let i = 0; i <= n; i++) { - const a = a0 + (sweep * i) / n - out.push(cx + mx * Math.cos(a) + nx * Math.sin(a), cy + my * Math.cos(a) + ny * Math.sin(a)) - } - return out -} - -export function findLayer(doc: CadDocument, name: string): LayerInfo | undefined { - return doc.layerMap.get(name) ?? doc.layerMap.get(name.toLowerCase()) -} - -export function spaceCenter(space: CadSpace): [number, number] { - return bboxCenter(space.bbox) -} - -export function padBounds(b: BBoxLike, ratio = 0.02): BBoxLike { - return bboxExpand(b, Math.max(bboxWidth(b) * ratio, 0.001), Math.max(bboxHeight(b) * ratio, 0.001)) -} diff --git a/src/cad/cadViewer.ts b/src/cad/cadViewer.ts deleted file mode 100644 index c66ac73..0000000 --- a/src/cad/cadViewer.ts +++ /dev/null @@ -1,1067 +0,0 @@ -import type { CadDocument, CadSpace } from './types' -import { CadRenderer } from './renderer' -import { - bboxCenter, - bboxHeight, - bboxWidth, - clamp, - formatNumber, - screenToWorld, - worldToScreen -} from './geom' -import type { Camera } from './geom' -import { cssColor, cssForAci } from './colors' -import type { ViewerHost } from '../core/viewerHost' -import { clear, h, icon, iconButton } from '../core/dom' -import { getTheme, onThemeChange, toggleTheme } from '../core/theme' -import { toast } from '../core/toast' -import { showAbout } from '../ui/about' - -interface Measurement { - a: [number, number] - b: [number, number] -} - -interface Callbacks { - onBack: () => void -} - -export class CadViewer { - private host: ViewerHost - private doc: CadDocument - private renderer: CadRenderer - private callbacks: Callbacks - private canvas: HTMLCanvasElement - private ctx: CanvasRenderingContext2D - private overlayCanvas: HTMLCanvasElement - private overlayCtx: CanvasRenderingContext2D - private canvasWrap: HTMLElement - private stage: HTMLElement - private tools: HTMLElement - private hint: HTMLElement - private sheetBackdrop: HTMLElement - private sheet: HTMLElement - private sheetList: HTMLElement - private sheetSearch: HTMLInputElement - private tabs: HTMLElement - private themeBtn: HTMLButtonElement - private measureBtn: HTMLButtonElement - private clearBtn: HTMLButtonElement - private proxyBtn!: HTMLButtonElement - private fullscreenBtn: HTMLButtonElement - private proxyVisible = true - private layerVis = new Map() - private cameras = new Map() - private fitScales = new Map() - private measurements = new Map() - private pendingStart = new Map() - private current = 0 - private dpr = 1 - private renderQueued = false - private raf = 0 - private renderCamera: Camera | null = null - private fullRenderTimer = 0 - private previewing = false - private resizeObserver: ResizeObserver | null = null - private themeUnsub: (() => void) | null = null - private measureMode = false - private pointerScreen: [number, number] | null = null - private pointers = new Map() - private gesture: - | { type: 'pan'; lastX: number; lastY: number } - | { - type: 'pinch' - startScale: number - startDist: number - startMid: [number, number] - worldMid: [number, number] - } - | null = null - private tapStart: { x: number; y: number; time: number; moved: boolean } | null = null - private lastTap = { time: 0, x: 0, y: 0 } - private coordTimer = 0 - private hintTimer = 0 - private destroyed = false - - constructor(host: ViewerHost, doc: CadDocument, callbacks: Callbacks) { - this.host = host - this.doc = doc - this.callbacks = callbacks - this.renderer = new CadRenderer(doc) - this.stage = host.stage - this.canvas = h('canvas', { class: 'cad-canvas' }) - this.overlayCanvas = h('canvas', { class: 'cad-overlay-canvas' }) - const ctx = this.canvas.getContext('2d', { alpha: false }) - if (!ctx) throw new Error('无法创建 Canvas 2D 上下文') - this.ctx = ctx - const overlayCtx = this.overlayCanvas.getContext('2d') - if (!overlayCtx) throw new Error('无法创建 Canvas 2D 上下文') - this.overlayCtx = overlayCtx - this.canvasWrap = h('div', { class: 'cad-canvas-wrap' }, this.canvas, this.overlayCanvas) - - for (const layer of doc.layers) this.layerVis.set(layer.name.toLowerCase(), layer.visible) - - host.setFileInfo(doc.fileName, 'DWG 图纸', 'DWG', 'cad', `${doc.spaces.length} 个空间`) - host.setStatus('') - this.tabs = this.buildTabs() - host.setSubbar(this.tabs) - - this.tools = this.buildTools() - this.hint = h('div', { class: 'cad-hint', text: this.hintText() }) - this.sheetSearch = h('input', { - class: 'sheet-search-input', - type: 'search', - placeholder: '搜索图层…', - 'aria-label': '搜索图层' - }) - this.sheetList = h('div', { class: 'sheet-list' }) - this.sheetBackdrop = h('div', { class: 'sheet-backdrop' }) - this.sheet = this.buildSheet() - this.sheetBackdrop.appendChild(this.sheet) - - this.stage.append(this.canvasWrap, this.tools, this.hint, this.sheetBackdrop) - this.buildHeaderActions() - - this.measureBtn = this.tools.querySelector('[data-action="measure"]') as HTMLButtonElement - this.clearBtn = this.tools.querySelector('[data-action="clear-measure"]') as HTMLButtonElement - this.themeBtn = this.tools.querySelector('[data-action="theme"]') as HTMLButtonElement - this.fullscreenBtn = host.actions.querySelector('[data-action="fullscreen"]') as HTMLButtonElement - this.updateThemeButton() - this.updateClearButton() - this.updateProxyButton() - - this.bindEvents() - this.observeResize() - this.themeUnsub = onThemeChange(() => { - this.updateThemeButton() - this.schedule() - }) - this.hintTimer = window.setTimeout(() => this.hideHint(), 5200) - this.updateStatus() - this.schedule() - } - - private get space(): CadSpace { - return this.doc.spaces[this.current] ?? this.doc.model - } - - private get camera(): Camera { - const space = this.space - let camera = this.cameras.get(space.id) - if (!camera) { - camera = this.computeFit(space) - this.cameras.set(space.id, camera) - } - return camera - } - - private setCamera(camera: Camera): void { - this.cameras.set(this.space.id, camera) - } - - private computeFit(space: CadSpace): Camera { - const width = Math.max(1, this.stage.clientWidth) - const height = Math.max(1, this.stage.clientHeight) - if (space.savedView && space.savedView.width > 0 && space.savedView.height > 0) { - const view = space.savedView - const scale = Math.max( - 1e-12, - Math.min((width * 0.92) / view.width, (height * 0.9) / view.height) - ) - this.fitScales.set(space.id, scale) - return { scale, cx: view.cx, cy: view.cy } - } - const box = space.fitBBox ?? space.bbox - const bw = Math.max(bboxWidth(box), 1e-9) - const bh = Math.max(bboxHeight(box), 1e-9) - const scale = Math.max(1e-12, Math.min((width * 0.9) / bw, (height * 0.88) / bh)) - const [cx, cy] = bboxCenter(box) - this.fitScales.set(space.id, scale) - return { scale, cx, cy } - } - - private buildHeaderActions(): void { - const downloadBtn = iconButton('download', '保存当前视图为 PNG', () => this.exportPng()) - const fullscreenBtn = iconButton('maximize', '全屏', () => void this.toggleFullscreen()) - fullscreenBtn.dataset.action = 'fullscreen' - const aboutBtn = iconButton('info', '关于', () => showAbout()) - this.host.actions.append(downloadBtn, fullscreenBtn, aboutBtn) - this.fullscreenBtn = fullscreenBtn - } - - private buildTabs(): HTMLElement { - const tabs = h('div', { class: 'tabs', role: 'tablist' }) - this.doc.spaces.forEach((space, index) => { - const btn = h( - 'button', - { - class: `tab ${index === this.current ? 'active' : ''}`, - type: 'button', - role: 'tab', - 'aria-selected': index === this.current ? 'true' : 'false', - dataset: { index: String(index) } - }, - h('span', { class: 'tab-icon', html: icon(space.kind === 'model' ? 'grid' : 'file-text', 15) }), - h('span', { class: 'tab-text', text: space.name }) - ) - btn.addEventListener('click', () => this.setSpace(index)) - tabs.appendChild(btn) - }) - return tabs - } - - private buildTools(): HTMLElement { - const tools = h('div', { class: 'cad-tools' }) - const add = (name: string, label: string, action: string, onClick: () => void) => { - const btn = iconButton(name, label, (ev) => { - ev.stopPropagation() - onClick() - }) - btn.dataset.action = action - tools.appendChild(btn) - return btn - } - add('plus', '放大', 'zoom-in', () => this.zoomBy(1.32)) - add('minus', '缩小', 'zoom-out', () => this.zoomBy(1 / 1.32)) - add('fit', '适配全图', 'fit', () => this.fitCurrent()) - tools.appendChild(h('div', { class: 'tool-divider' })) - add('layers', '图层', 'layers', () => this.openSheet()) - this.proxyBtn = add('proxy', '显示代理对象', 'proxy', () => this.toggleProxyObjects()) - this.proxyBtn.classList.add('active') - this.measureBtn = add('ruler', '测量距离', 'measure', () => this.toggleMeasure()) - this.clearBtn = add('trash', '清除测量', 'clear-measure', () => this.clearMeasurements()) - this.themeBtn = add('sun', '切换背景', 'theme', () => { - toggleTheme() - this.schedule() - }) - this.clearBtn.classList.add('tool-hidden') - return tools - } - - private buildSheet(): HTMLElement { - const title = h('div', { class: 'sheet-title' }, h('span', { text: '图层' })) - const close = iconButton('x', '关闭', () => this.closeSheet()) - const head = h('div', { class: 'sheet-head' }, title, close) - const actions = h('div', { class: 'sheet-actions' }) - const showAll = h('button', { class: 'chip-btn', type: 'button', text: '全部显示' }) - const hideAll = h('button', { class: 'chip-btn', type: 'button', text: '全部隐藏' }) - const reset = h('button', { class: 'chip-btn', type: 'button', text: '恢复默认' }) - showAll.addEventListener('click', () => this.setAllLayers(true)) - hideAll.addEventListener('click', () => this.setAllLayers(false)) - reset.addEventListener('click', () => { - for (const layer of this.doc.layers) this.setLayerVisible(layer.name, layer.visible) - this.renderSheetRows() - this.schedule() - }) - actions.append(showAll, hideAll, reset) - - this.sheetSearch.addEventListener('input', () => this.filterSheet()) - this.renderSheetRows() - - return h('aside', { class: 'sheet', role: 'dialog', 'aria-label': '图层' }, head, actions, this.sheetSearch, this.sheetList) - } - - private renderSheetRows(): void { - clear(this.sheetList) - const sorted = [...this.doc.layers].sort((a, b) => { - if (a.name === '0') return -1 - if (b.name === '0') return 1 - return a.name.localeCompare(b.name, 'zh-Hans-CN') - }) - for (const layer of sorted) { - const visible = this.isLayerVisible(layer.name) - const row = h('button', { - class: `layer-row ${visible ? 'visible' : 'hidden'}`, - type: 'button', - dataset: { name: layer.name.toLowerCase(), search: layer.name.toLowerCase() } - }) - const swatch = h('span', { class: 'layer-swatch' }) - const theme = getTheme() - swatch.style.background = - layer.rgb != null ? cssColor({ kind: 'rgb', value: layer.rgb }, false, theme) : cssForAci(layer.aci, false, theme) - const info = h( - 'span', - { class: 'layer-info' }, - h('span', { class: 'layer-name', text: layer.name }), - h('span', { - class: 'layer-meta', - text: `${layer.count} 个实体${layer.off ? ' · 已关闭' : ''}${layer.frozen ? ' · 已冻结' : ''}` - }) - ) - const eye = h('span', { class: 'layer-eye', html: icon(visible ? 'eye' : 'eye-off', 18) }) - row.append(swatch, info, eye) - row.addEventListener('click', () => { - this.setLayerVisible(layer.name, !this.isLayerVisible(layer.name)) - this.renderSheetRows() - this.schedule() - }) - this.sheetList.appendChild(row) - } - this.filterSheet() - } - - private bindEvents(): void { - this.canvas.addEventListener('pointerdown', this.onPointerDown) - this.canvas.addEventListener('pointermove', this.onPointerMove) - this.canvas.addEventListener('pointerup', this.onPointerUp) - this.canvas.addEventListener('pointercancel', this.onPointerCancel) - this.canvas.addEventListener('wheel', this.onWheel, { passive: false }) - this.canvas.addEventListener('dblclick', this.onDoubleClick) - this.canvas.addEventListener('contextmenu', (ev) => ev.preventDefault()) - document.addEventListener('keydown', this.onKeyDown) - document.addEventListener('fullscreenchange', this.onFullscreenChange) - this.sheetBackdrop.addEventListener('click', (ev) => { - if (ev.target === this.sheetBackdrop) this.closeSheet() - }) - } - - private observeResize(): void { - this.resizeObserver = new ResizeObserver(() => this.resize()) - this.resizeObserver.observe(this.stage) - window.addEventListener('orientationchange', this.onOrientation) - this.resize() - } - - private onOrientation = () => { - window.setTimeout(() => { - this.resize() - const space = this.space - const camera = this.cameras.get(space.id) - const fit = this.fitScales.get(space.id) ?? 0 - if (!camera || Math.abs(camera.scale - fit) / Math.max(fit, 1e-9) < 0.08) { - this.cameras.set(space.id, this.computeFit(space)) - this.schedule() - } - }, 220) - } - - private resize(): void { - const width = this.stage.clientWidth - const height = this.stage.clientHeight - if (width <= 0 || height <= 0) return - this.dpr = Math.min(window.devicePixelRatio || 1, 2.5) - for (const canvas of [this.canvas, this.overlayCanvas]) { - canvas.width = Math.max(1, Math.round(width * this.dpr)) - canvas.height = Math.max(1, Math.round(height * this.dpr)) - canvas.style.width = `${width}px` - canvas.style.height = `${height}px` - } - const space = this.space - const camera = this.cameras.get(space.id) - const fit = this.fitScales.get(space.id) ?? 0 - if (!camera || fit <= 1e-11 || Math.abs(camera.scale - fit) / Math.max(fit, 1e-9) < 0.08) { - this.cameras.set(space.id, this.computeFit(space)) - } - this.schedule() - } - - private schedule(): void { - if (this.fullRenderTimer) { - window.clearTimeout(this.fullRenderTimer) - this.fullRenderTimer = 0 - } - if (this.destroyed || this.renderQueued) return - this.renderQueued = true - this.raf = requestAnimationFrame(() => { - this.renderQueued = false - this.render() - }) - } - - private scheduleFullSoon(delay = 140): void { - if (this.destroyed) return - if (this.fullRenderTimer) window.clearTimeout(this.fullRenderTimer) - this.fullRenderTimer = window.setTimeout(() => { - this.fullRenderTimer = 0 - this.schedule() - }, delay) - } - - private cancelScheduledRender(): void { - if (this.fullRenderTimer) { - window.clearTimeout(this.fullRenderTimer) - this.fullRenderTimer = 0 - } - if (this.raf) { - cancelAnimationFrame(this.raf) - this.raf = 0 - this.renderQueued = false - } - } - - private applyPreview(): void { - const rendered = this.renderCamera - if (!rendered) { - this.schedule() - return - } - const camera = this.camera - const scale = camera.scale / rendered.scale - const tx = (rendered.cx - camera.cx) * camera.scale - const ty = (camera.cy - rendered.cy) * camera.scale - if ( - Math.abs(scale - 1) < 1e-4 && - Math.abs(tx) < 0.05 && - Math.abs(ty) < 0.05 - ) { - this.canvasWrap.style.transform = '' - this.previewing = false - return - } - this.canvasWrap.style.transformOrigin = '50% 50%' - this.canvasWrap.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})` - this.previewing = true - } - - private render(): void { - if (this.destroyed) return - const width = this.stage.clientWidth - const height = this.stage.clientHeight - if (width <= 0 || height <= 0) return - this.canvasWrap.style.transform = '' - this.previewing = false - const started = performance.now() - const ctx = this.ctx - ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0) - ctx.clearRect(0, 0, width, height) - const camera = this.camera - this.renderer.draw(ctx, this.space, camera, width, height, { - theme: getTheme(), - layerVisible: (name) => this.isLayerVisible(name), - proxyVisible: this.proxyVisible - }) - this.renderCamera = { ...camera } - this.renderOverlay(width, height) - const elapsed = performance.now() - started - if (elapsed > 30) console.info(`[render] ${elapsed.toFixed(1)}ms`) - } - - private renderOverlay(width = this.stage.clientWidth, height = this.stage.clientHeight): void { - if (this.destroyed || this.previewing || width <= 0 || height <= 0) return - const ctx = this.overlayCtx - ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0) - ctx.clearRect(0, 0, width, height) - this.drawOverlay(ctx, width, height) - } - - private drawOverlay(ctx: CanvasRenderingContext2D, width: number, height: number): void { - const camera = this.camera - const space = this.space - const list = this.measurements.get(space.id) - const accent = getTheme() === 'dark' ? '#5b9bff' : '#2563eb' - const textColor = '#ffffff' - const labelFont = '600 12px "Noto Sans SC","PingFang SC","Microsoft YaHei",system-ui,sans-serif' - - const drawLabel = (x: number, y: number, text: string) => { - ctx.save() - ctx.font = labelFont - const w = ctx.measureText(text).width + 16 - const hgt = 24 - const rx = x - w / 2 - const ry = y - hgt - 10 - ctx.beginPath() - if (typeof ctx.roundRect === 'function') ctx.roundRect(rx, ry, w, hgt, 8) - else ctx.rect(rx, ry, w, hgt) - ctx.fillStyle = accent - ctx.shadowColor = 'rgba(0,0,0,0.28)' - ctx.shadowBlur = 6 - ctx.fill() - ctx.shadowBlur = 0 - ctx.fillStyle = textColor - ctx.textAlign = 'center' - ctx.textBaseline = 'middle' - ctx.fillText(text, x, ry + hgt / 2 + 0.5) - ctx.restore() - } - - const drawPoint = (x: number, y: number, color: string) => { - ctx.save() - ctx.strokeStyle = color - ctx.fillStyle = color - ctx.lineWidth = 1.5 - ctx.beginPath() - ctx.arc(x, y, 4.2, 0, Math.PI * 2) - ctx.stroke() - ctx.beginPath() - ctx.arc(x, y, 1.7, 0, Math.PI * 2) - ctx.fill() - ctx.restore() - } - - if (list) { - for (const m of list) { - const a = worldToScreen(camera, width, height, m.a[0], m.a[1]) - const b = worldToScreen(camera, width, height, m.b[0], m.b[1]) - ctx.save() - ctx.strokeStyle = accent - ctx.lineWidth = 1.6 - ctx.setLineDash([7, 5]) - ctx.beginPath() - ctx.moveTo(a[0], a[1]) - ctx.lineTo(b[0], b[1]) - ctx.stroke() - ctx.restore() - drawPoint(a[0], a[1], accent) - drawPoint(b[0], b[1], accent) - const dist = Math.hypot(m.b[0] - m.a[0], m.b[1] - m.a[1]) - drawLabel((a[0] + b[0]) / 2, (a[1] + b[1]) / 2, this.formatMeasurement(dist)) - } - } - - const pending = this.pendingStart.get(space.id) - if (pending) { - const a = worldToScreen(camera, width, height, pending[0], pending[1]) - drawPoint(a[0], a[1], accent) - if (this.pointerScreen) { - const [px, py] = this.pointerScreen - const world = screenToWorld(camera, width, height, px, py) - ctx.save() - ctx.strokeStyle = accent - ctx.lineWidth = 1.3 - ctx.setLineDash([5, 5]) - ctx.beginPath() - ctx.moveTo(a[0], a[1]) - ctx.lineTo(px, py) - ctx.stroke() - ctx.restore() - const dist = Math.hypot(world[0] - pending[0], world[1] - pending[1]) - drawLabel(px, py, this.formatMeasurement(dist)) - } - } - } - - private formatMeasurement(value: number): string { - const unit = this.doc.unitLabel - if (unit === 'mm' && Math.abs(value) >= 1000) return `${(value / 1000).toFixed(3)} m` - if (unit === 'mm') return `${value.toFixed(1)} mm` - if (unit === 'm') return `${value.toFixed(3)} m` - if (unit === 'cm') return `${value.toFixed(2)} cm` - if (unit) return `${value.toFixed(2)} ${unit}` - return formatNumber(value, 2) - } - - private updateStatus(extra?: string): void { - const space = this.space - const camera = this.camera - const fit = this.fitScales.get(space.id) ?? camera.scale - const pct = fit > 0 ? (camera.scale / fit) * 100 : 100 - const pctText = pct >= 1000 ? `${Math.round(pct / 100) * 100}%` : `${Math.round(pct)}%` - const unknown = this.doc.unknownCount > 0 ? ` · 不支持 ${this.doc.unknownCount}` : '' - const measureText = this.measureMode ? ' · 测量模式' : '' - const count = space.entityCount ?? space.entities.length - this.host.setStatus( - extra ?? `${space.name} · ${count} 个实体 · ${pctText}${unknown}${measureText}` - ) - } - - private updateCoordinateStatus(x: number, y: number): void { - const unit = this.doc.unitLabel ? ` ${this.doc.unitLabel}` : '' - this.updateStatus( - `${this.space.name} · X ${formatNumber(x, 2)}${unit} Y ${formatNumber(y, 2)}${unit}` - ) - if (this.coordTimer) window.clearTimeout(this.coordTimer) - this.coordTimer = window.setTimeout(() => this.updateStatus(), 1400) - } - - private isLayerVisible(name: string): boolean { - return this.layerVis.get((name || '0').toLowerCase()) ?? true - } - - private setLayerVisible(name: string, visible: boolean): void { - this.layerVis.set(name.toLowerCase(), visible) - } - - private setAllLayers(visible: boolean): void { - for (const layer of this.doc.layers) this.setLayerVisible(layer.name, visible) - this.renderSheetRows() - this.schedule() - } - - private filterSheet(): void { - const query = this.sheetSearch.value.trim().toLowerCase() - const rows = this.sheetList.querySelectorAll('.layer-row') - rows.forEach((row) => { - const name = row.dataset.search ?? '' - row.hidden = query.length > 0 && !name.includes(query) - }) - } - - private openSheet(): void { - this.sheetBackdrop.classList.add('open') - window.setTimeout(() => this.sheetSearch.focus({ preventScroll: true }), 120) - } - - private closeSheet(): void { - this.sheetBackdrop.classList.remove('open') - } - - private setSpace(index: number): void { - if (index < 0 || index >= this.doc.spaces.length || index === this.current) return - this.current = index - const tabs = this.tabs.querySelectorAll('.tab') - tabs.forEach((tab, i) => { - tab.classList.toggle('active', i === index) - tab.setAttribute('aria-selected', i === index ? 'true' : 'false') - }) - tabs[index]?.scrollIntoView({ behavior: 'smooth', inline: 'nearest', block: 'nearest' }) - const space = this.space - if (!this.cameras.has(space.id)) this.cameras.set(space.id, this.computeFit(space)) - this.pendingStart.delete(space.id) - this.updateClearButton() - this.updateStatus() - this.schedule() - } - - private fitCurrent(): void { - const space = this.space - const camera = this.computeFit(space) - this.cameras.set(space.id, camera) - this.updateStatus() - this.applyPreview() - this.scheduleFullSoon(80) - } - - private zoomBy(factor: number): void { - const width = this.stage.clientWidth - const height = this.stage.clientHeight - this.zoomAt(width / 2, height / 2, factor) - } - - private zoomAt(sx: number, sy: number, factor: number): void { - const width = this.stage.clientWidth - const height = this.stage.clientHeight - const camera = this.camera - const space = this.space - const fit = this.fitScales.get(space.id) ?? camera.scale - const next = clamp(camera.scale * factor, fit * 0.02, fit * 6000) - if (Math.abs(next - camera.scale) / camera.scale < 1e-6) return - const world = screenToWorld(camera, width, height, sx, sy) - this.setCamera({ - scale: next, - cx: world[0] - (sx - width / 2) / next, - cy: world[1] + (sy - height / 2) / next - }) - this.updateStatus() - this.applyPreview() - this.scheduleFullSoon(140) - } - - private onPointerDown = (ev: PointerEvent) => { - if (ev.button != null && ev.button !== 0 && ev.pointerType === 'mouse') return - this.cancelScheduledRender() - this.hideHint() - this.canvas.setPointerCapture(ev.pointerId) - this.pointers.set(ev.pointerId, { x: ev.clientX, y: ev.clientY }) - const rect = this.stage.getBoundingClientRect() - this.tapStart = { - x: ev.clientX - rect.left, - y: ev.clientY - rect.top, - time: performance.now(), - moved: false - } - if (this.pointers.size === 1) { - this.gesture = { - type: 'pan', - lastX: ev.clientX, - lastY: ev.clientY - } - } else if (this.pointers.size === 2) { - const [a, b] = [...this.pointers.values()] - const mid: [number, number] = [((a.x + b.x) / 2 - rect.left), ((a.y + b.y) / 2 - rect.top)] - const dist = Math.hypot(a.x - b.x, a.y - b.y) - const camera = { ...this.camera } - this.gesture = { - type: 'pinch', - startScale: camera.scale, - startDist: Math.max(dist, 1), - startMid: mid, - worldMid: screenToWorld(camera, this.stage.clientWidth, this.stage.clientHeight, mid[0], mid[1]) - } - if (this.tapStart) this.tapStart.moved = true - } - this.updateCursor() - } - - private onPointerMove = (ev: PointerEvent) => { - const rect = this.stage.getBoundingClientRect() - const local: [number, number] = [ev.clientX - rect.left, ev.clientY - rect.top] - this.pointerScreen = local - if (this.pointers.has(ev.pointerId)) { - this.pointers.set(ev.pointerId, { x: ev.clientX, y: ev.clientY }) - } - - if (this.tapStart) { - const dx = local[0] - this.tapStart.x - const dy = local[1] - this.tapStart.y - if (Math.hypot(dx, dy) > 6) this.tapStart.moved = true - } - - if (this.gesture?.type === 'pan' && this.pointers.size === 1) { - const dx = ev.clientX - this.gesture.lastX - const dy = ev.clientY - this.gesture.lastY - this.gesture.lastX = ev.clientX - this.gesture.lastY = ev.clientY - if (dx || dy) { - const camera = this.camera - this.setCamera({ - scale: camera.scale, - cx: camera.cx - dx / camera.scale, - cy: camera.cy + dy / camera.scale - }) - this.applyPreview() - } - } else if (this.gesture?.type === 'pinch' && this.pointers.size >= 2) { - const [a, b] = [...this.pointers.values()] - const mid: [number, number] = [ - (a.x + b.x) / 2 - rect.left, - (a.y + b.y) / 2 - rect.top - ] - const dist = Math.hypot(a.x - b.x, a.y - b.y) - const space = this.space - const fit = this.fitScales.get(space.id) ?? this.gesture.startScale - const next = clamp( - this.gesture.startScale * (dist / this.gesture.startDist), - fit * 0.02, - fit * 6000 - ) - this.setCamera({ - scale: next, - cx: this.gesture.worldMid[0] - (mid[0] - this.stage.clientWidth / 2) / next, - cy: this.gesture.worldMid[1] + (mid[1] - this.stage.clientHeight / 2) / next - }) - this.applyPreview() - this.updateStatus() - } - - if (ev.pointerType === 'mouse' && !this.gesture) { - const world = screenToWorld(this.camera, this.stage.clientWidth, this.stage.clientHeight, local[0], local[1]) - this.updateCoordinateStatus(world[0], world[1]) - } - if (this.pendingStart.has(this.space.id) && this.measureMode) this.renderOverlay() - } - - private onPointerUp = (ev: PointerEvent) => { - const wasPan = this.gesture?.type - this.pointers.delete(ev.pointerId) - const rect = this.stage.getBoundingClientRect() - const x = ev.clientX - rect.left - const y = ev.clientY - rect.top - const singleTap = - this.pointers.size === 0 && - this.tapStart != null && - !this.tapStart.moved && - performance.now() - this.tapStart.time < 600 && - wasPan !== 'pinch' - - if (singleTap && this.tapStart) { - const now = performance.now() - const isDouble = - !this.measureMode && - ev.pointerType !== 'mouse' && - now - this.lastTap.time < 320 && - Math.hypot(x - this.lastTap.x, y - this.lastTap.y) < 26 - if (isDouble) { - this.zoomAt(x, y, 1.85) - this.lastTap = { time: 0, x: 0, y: 0 } - } else { - this.lastTap = { time: now, x, y } - if (this.measureMode) this.handleMeasureTap(x, y) - } - } - - if (this.pointers.size === 0) { - this.gesture = null - this.tapStart = null - } else if (this.pointers.size === 1) { - const remaining = [...this.pointers.values()][0] - this.gesture = { type: 'pan', lastX: remaining.x, lastY: remaining.y } - } - if (ev.pointerType !== 'mouse') this.pointerScreen = null - this.updateStatus() - this.updateCursor() - if (this.pointers.size === 0) { - if (this.previewing) this.scheduleFullSoon(60) - } - } - - private onPointerCancel = (ev: PointerEvent) => { - this.pointers.delete(ev.pointerId) - if (this.pointers.size === 0) { - this.gesture = null - this.tapStart = null - if (this.previewing) this.scheduleFullSoon(60) - } - } - - private onWheel = (ev: WheelEvent) => { - ev.preventDefault() - const rect = this.stage.getBoundingClientRect() - const factor = Math.exp(-ev.deltaY * 0.0016) - this.zoomAt(ev.clientX - rect.left, ev.clientY - rect.top, factor) - this.hideHint() - } - - private onDoubleClick = (ev: MouseEvent) => { - const rect = this.stage.getBoundingClientRect() - this.zoomAt(ev.clientX - rect.left, ev.clientY - rect.top, 1.85) - } - - private handleMeasureTap(x: number, y: number): void { - const width = this.stage.clientWidth - const height = this.stage.clientHeight - const world = screenToWorld(this.camera, width, height, x, y) - const space = this.space - const pending = this.pendingStart.get(space.id) - if (!pending) { - this.pendingStart.set(space.id, world) - this.updateStatus('测量:请点击第二点') - } else { - const dist = Math.hypot(world[0] - pending[0], world[1] - pending[1]) - if (dist > 1e-9) { - const list = this.measurements.get(space.id) ?? [] - list.push({ a: pending, b: world }) - this.measurements.set(space.id, list) - } - this.pendingStart.delete(space.id) - this.updateClearButton() - this.updateStatus() - } - this.renderOverlay() - } - - private toggleMeasure(): void { - this.measureMode = !this.measureMode - this.measureBtn.classList.toggle('active', this.measureMode) - if (!this.measureMode) { - this.pendingStart.delete(this.space.id) - this.pointerScreen = null - } else { - this.pendingStart.delete(this.space.id) - toast('测量模式:点击两点测量距离', 'info', 2200) - } - this.updateClearButton() - this.updateStatus() - this.updateCursor() - this.renderOverlay() - } - - private clearMeasurements(): void { - this.measurements.delete(this.space.id) - this.pendingStart.delete(this.space.id) - this.updateClearButton() - this.updateStatus() - this.renderOverlay() - } - - private updateClearButton(): void { - const has = (this.measurements.get(this.space.id)?.length ?? 0) > 0 || this.pendingStart.has(this.space.id) - this.clearBtn.classList.toggle('tool-hidden', !has && !this.measureMode) - } - - private toggleProxyObjects(): void { - this.proxyVisible = !this.proxyVisible - this.updateProxyButton() - this.schedule() - toast(this.proxyVisible ? '已显示代理对象' : '已隐藏代理对象') - } - - private updateProxyButton(): void { - if (!this.proxyBtn) return - this.proxyBtn.classList.toggle('active', this.proxyVisible) - this.proxyBtn.setAttribute('aria-pressed', this.proxyVisible ? 'true' : 'false') - this.proxyBtn.setAttribute('title', this.proxyVisible ? '隐藏代理对象' : '显示代理对象') - } - - private updateCursor(): void { - this.canvas.classList.toggle('crosshair', this.measureMode) - } - - private updateThemeButton(): void { - if (!this.themeBtn) return - this.themeBtn.innerHTML = icon(getTheme() === 'dark' ? 'sun' : 'moon', 20) - this.themeBtn.setAttribute('title', getTheme() === 'dark' ? '切换为浅色背景' : '切换为深色背景') - } - - private hintText(): string { - const coarse = window.matchMedia?.('(pointer: coarse)').matches - return coarse ? '双指缩放 · 单指拖动 · 点“适配”查看全图' : '滚轮缩放 · 拖动平移 · 点“适配”查看全图' - } - - private hideHint(): void { - if (this.hintTimer) { - window.clearTimeout(this.hintTimer) - this.hintTimer = 0 - } - if (this.hint) { - this.hint.classList.add('hide') - window.setTimeout(() => this.hint?.remove(), 400) - } - } - - private async toggleFullscreen(): Promise { - try { - if (!document.fullscreenElement) { - await this.host.root.requestFullscreen?.() - } else { - await document.exitFullscreen?.() - } - } catch { - toast('当前浏览器不支持全屏', 'error') - } - } - - private onFullscreenChange = () => { - if (!this.fullscreenBtn) return - const active = !!document.fullscreenElement - this.fullscreenBtn.innerHTML = icon(active ? 'minimize' : 'maximize', 20) - this.fullscreenBtn.setAttribute('title', active ? '退出全屏' : '全屏') - window.setTimeout(() => this.resize(), 80) - } - - private exportPng(): void { - this.render() - const output = document.createElement('canvas') - output.width = this.canvas.width - output.height = this.canvas.height - const outputCtx = output.getContext('2d') - if (!outputCtx) { - toast('导出失败', 'error') - return - } - outputCtx.drawImage(this.canvas, 0, 0) - outputCtx.drawImage(this.overlayCanvas, 0, 0) - output.toBlob((blob) => { - if (!blob) { - toast('导出失败', 'error') - return - } - const base = this.doc.fileName.replace(/\.[^.]+$/, '') || 'drawing' - const safeSpace = this.space.name.replace(/[\\/:*?"<>|]+/g, '_') - const fileName = `${base}-${safeSpace}.png` - const bridge = ( - window as unknown as { - AndroidViewer?: { saveBase64?: (name: string, mime: string, data: string) => void } - } - ).AndroidViewer - if (bridge?.saveBase64) { - const reader = new FileReader() - reader.onload = () => { - try { - const result = String(reader.result ?? '') - const data = result.includes(',') ? result.slice(result.indexOf(',') + 1) : result - bridge.saveBase64?.(fileName, 'image/png', data) - toast('已保存到 Android 下载目录', 'success') - } catch { - toast('保存失败', 'error') - } - } - reader.onerror = () => toast('保存失败', 'error') - reader.readAsDataURL(blob) - return - } - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = fileName - document.body.appendChild(a) - a.click() - a.remove() - window.setTimeout(() => URL.revokeObjectURL(url), 4000) - toast('已保存当前视图', 'success') - }, 'image/png') - } - - private onKeyDown = (ev: KeyboardEvent) => { - if (document.activeElement && /^(input|textarea|select)$/i.test(document.activeElement.tagName)) return - const target = ev.target as HTMLElement | null - if (target && target.closest('.sheet')) return - switch (ev.key) { - case '+': - case '=': - ev.preventDefault() - this.zoomBy(1.3) - break - case '-': - case '_': - ev.preventDefault() - this.zoomBy(1 / 1.3) - break - case '0': - ev.preventDefault() - this.fitCurrent() - break - case 'm': - case 'M': - ev.preventDefault() - this.toggleMeasure() - break - case 'l': - case 'L': - ev.preventDefault() - this.openSheet() - break - case 'Escape': - if (this.sheetBackdrop.classList.contains('open')) { - this.closeSheet() - } else if (this.measureMode) { - this.toggleMeasure() - } - break - case 'ArrowLeft': - this.panByKey(80, 0) - ev.preventDefault() - break - case 'ArrowRight': - this.panByKey(-80, 0) - ev.preventDefault() - break - case 'ArrowUp': - this.panByKey(0, 80) - ev.preventDefault() - break - case 'ArrowDown': - this.panByKey(0, -80) - ev.preventDefault() - break - case 'f': - case 'F': - void this.toggleFullscreen() - break - default: - break - } - } - - private panByKey(dx: number, dy: number): void { - const camera = this.camera - this.setCamera({ - scale: camera.scale, - cx: camera.cx - dx / camera.scale, - cy: camera.cy + dy / camera.scale - }) - this.updateStatus() - this.applyPreview() - this.scheduleFullSoon(140) - } - - destroy(): void { - if (this.destroyed) return - this.destroyed = true - cancelAnimationFrame(this.raf) - this.resizeObserver?.disconnect() - window.removeEventListener('orientationchange', this.onOrientation) - document.removeEventListener('keydown', this.onKeyDown) - document.removeEventListener('fullscreenchange', this.onFullscreenChange) - this.themeUnsub?.() - if (this.coordTimer) window.clearTimeout(this.coordTimer) - if (this.hintTimer) window.clearTimeout(this.hintTimer) - this.canvas.removeEventListener('pointerdown', this.onPointerDown) - this.canvas.removeEventListener('pointermove', this.onPointerMove) - this.canvas.removeEventListener('pointerup', this.onPointerUp) - this.canvas.removeEventListener('pointercancel', this.onPointerCancel) - this.canvas.removeEventListener('wheel', this.onWheel) - this.canvas.removeEventListener('dblclick', this.onDoubleClick) - this.host.destroy() - } -} - diff --git a/src/cad/colors.ts b/src/cad/colors.ts deleted file mode 100644 index 2e3e8a1..0000000 --- a/src/cad/colors.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { Theme } from '../core/theme' - -export type ColorSpec = { kind: 'aci'; value: number } | { kind: 'rgb'; value: number } - -export const ACI_COLORS: readonly number[] = [ - 0, 16711680, 16776960, 65280, 65535, 255, 16711935, 16777215, 8421504, 12632256, 16711680, 16744319, - 13369344, 13395558, 10027008, 10046540, 8323072, 8339263, 4980736, 4990502, 16727808, 16752511, - 13382400, 13401958, 10036736, 10051404, 8331008, 8343359, 4985600, 4992806, 16744192, 16760703, - 13395456, 13408614, 10046464, 10056268, 8339200, 8347455, 4990464, 4995366, 16760576, 16768895, - 13408512, 13415014, 10056192, 10061132, 8347392, 8351551, 4995328, 4997670, 16776960, 16777087, - 13421568, 13421670, 10000384, 10000460, 8355584, 8355647, 5000192, 5000230, 12582656, 14679935, - 10079232, 11717734, 7510016, 8755276, 6258432, 7307071, 3755008, 4344870, 8388352, 12582783, - 6736896, 10079334, 5019648, 7510092, 4161280, 6258495, 2509824, 3755046, 4194048, 10485631, - 3394560, 8375398, 2529280, 6264908, 2064128, 5209919, 1264640, 3099686, 65280, 8388479, 52224, - 6736998, 38912, 5019724, 32512, 4161343, 19456, 2509862, 65343, 8388511, 52275, 6737023, 38950, - 5019743, 32543, 4161359, 19475, 2509871, 65407, 8388543, 52326, 6737049, 38988, 5019762, 32575, - 4161375, 19494, 2509881, 65471, 8388575, 52377, 6737074, 39026, 5019781, 32607, 4161391, 19513, - 2509890, 65535, 8388607, 52428, 6737100, 39064, 5019800, 32639, 4161407, 19532, 2509900, 49151, - 8380415, 39372, 6730444, 29336, 5014936, 24447, 4157311, 14668, 2507340, 32767, 8372223, 26316, - 6724044, 19608, 5010072, 16255, 4153215, 9804, 2505036, 16383, 8364031, 13260, 6717388, 9880, - 5005208, 8063, 4149119, 4940, 2502476, 255, 8355839, 204, 6710988, 152, 5000344, 127, 4145023, 76, - 2500172, 4129023, 10452991, 3342540, 8349388, 2490520, 6245528, 2031743, 5193599, 1245260, 3089996, - 8323327, 12550143, 6684876, 10053324, 4980888, 7490712, 4128895, 6242175, 2490444, 3745356, - 12517631, 14647295, 10027212, 11691724, 7471256, 8735896, 6226047, 7290751, 3735628, 4335180, - 16711935, 16744447, 13369548, 13395660, 9961624, 9981080, 8323199, 8339327, 4980812, 4990540, - 16711871, 16744415, 13369497, 13395634, 9961586, 9981061, 8323167, 8339311, 4980793, 4990530, - 16711807, 16744383, 13369446, 13395609, 9961548, 9981042, 8323135, 8339295, 4980774, 4990521, - 16711743, 16744351, 13369395, 13395583, 9961510, 9981023, 8323103, 8339279, 4980755, 4990511, - 3355443, 5987163, 8684676, 11382189, 14079702, 16777215, 0 -] - -export function rgbFromInt(value: number): [number, number, number] { - return [(value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff] -} - -export function rgbToHex(r: number, g: number, b: number): string { - return `#${((1 << 24) | (Math.round(r) << 16) | (Math.round(g) << 8) | Math.round(b)) - .toString(16) - .slice(1)}` -} - -export function cssColor(spec: ColorSpec, paper: boolean, theme: Theme): string { - if (spec.kind === 'aci') { - const index = Math.max(0, Math.min(255, Math.round(spec.value))) - if (index === 7) return paper ? '#111318' : theme === 'dark' ? '#f4f7fb' : '#111318' - if (index === 0 || index === 256) return paper ? '#111318' : theme === 'dark' ? '#f4f7fb' : '#111318' - const rgb = ACI_COLORS[index] ?? 0xffffff - const [r, g, b] = rgbFromInt(rgb) - return rgbToCss(adjust(r, g, b, paper ? false : theme === 'dark')) - } - const [r, g, b] = rgbFromInt(spec.value) - return rgbToCss(adjustTrueColor(r, g, b, paper ? false : theme === 'dark')) -} - -function rgbToCss([r, g, b]: [number, number, number]): string { - return `rgb(${r},${g},${b})` -} - -function adjust(r: number, g: number, b: number, dark: boolean): [number, number, number] { - const lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 - if (dark) { - if (lum < 0.16) return mix(r, g, b, 255, 0.58) - if (lum < 0.28) return mix(r, g, b, 255, 0.26) - } else { - if (lum > 0.9) return mix(r, g, b, 0, 0.62) - if (lum > 0.8) return mix(r, g, b, 0, 0.22) - } - return [r, g, b] -} - -function adjustTrueColor(r: number, g: number, b: number, dark: boolean): [number, number, number] { - const lum = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 - if (dark && lum < 0.1) return mix(r, g, b, 255, 0.72) - if (!dark && lum > 0.94) return mix(r, g, b, 0, 0.55) - return [r, g, b] -} - -function mix(r: number, g: number, b: number, target: number, amount: number): [number, number, number] { - return [ - Math.round(r + (target - r) * amount), - Math.round(g + (target - g) * amount), - Math.round(b + (target - b) * amount) - ] -} - -export function cssForAci(index: number, paper: boolean, theme: Theme): string { - return cssColor({ kind: 'aci', value: index }, paper, theme) -} - -export function luminanceOfRgb(value: number): number { - const [r, g, b] = rgbFromInt(value) - return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 -} diff --git a/src/cad/dwgWorker.ts b/src/cad/dwgWorker.ts deleted file mode 100644 index 655dc0a..0000000 --- a/src/cad/dwgWorker.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { buildAcadDocument } from './acadAdapter' -import { slimDocument } from './slim' -import type { CadDocument } from './types' - -interface WorkerContext { - onmessage: ((event: MessageEvent) => void) | null - postMessage: (message: unknown, transfer?: Transferable[]) => void -} - -interface LoadMessage { - type: 'load' - file: File - name: string - size: number -} - -type InMessage = { type: 'init' } | LoadMessage - -const ctx = self as unknown as WorkerContext - -let busy = false - -function post(message: unknown): void { - ctx.postMessage(message) -} - -async function handleLoad(message: LoadMessage): Promise { - if (busy) { - post({ type: 'error', code: 'busy', message: '正在解析其他图纸,请稍候。' }) - return - } - busy = true - const started = performance.now() - try { - post({ type: 'progress', message: '正在读取文件…' }) - const readStart = performance.now() - const buffer = await message.file.arrayBuffer() - const readMs = performance.now() - readStart - post({ type: 'progress', message: '正在解析 DWG 图纸…' }) - const buildStart = performance.now() - const doc = buildAcadDocument(buffer, message.name, message.size) - const built = slimDocument(doc) - const buildMs = performance.now() - buildStart - post({ - type: 'done', - doc: built, - timings: { - readMs, - parseMs: buildMs, - buildMs, - totalMs: performance.now() - started - } - }) - } catch (error) { - const detail = error instanceof Error ? error.message : String(error) - post({ type: 'error', code: 'parse', message: detail }) - } finally { - busy = false - } -} - -ctx.onmessage = (event: MessageEvent) => { - const message = event.data as InMessage - if (!message) return - if (message.type === 'init') { - post({ type: 'ready' }) - return - } - if (message.type === 'load') { - void handleLoad(message) - } -} diff --git a/src/cad/flyfishViewer.ts b/src/cad/flyfishViewer.ts new file mode 100644 index 0000000..32d56cb --- /dev/null +++ b/src/cad/flyfishViewer.ts @@ -0,0 +1,381 @@ +import './polyfills' +import { + CadViewer, + type CadViewerLoadResult, + type CadDocument as FlyCadDocument +} from '@flyfish-dev/cad-viewer' +import '@flyfish-dev/cad-viewer/styles.css' +import { clear, h, icon, iconButton } from '../core/dom' +import { formatBytes } from '../core/files' +import { getTheme, onThemeChange, toggleTheme } from '../core/theme' +import { toast } from '../core/toast' +import type { ViewerHost } from '../core/viewerHost' +import { showAbout } from '../ui/about' + +interface Callbacks { + onBack: () => void +} + +interface LayerRow { + name: string + refs: Array> +} + +export class FlyfishCadViewer { + private host: ViewerHost + private file: File + private callbacks: Callbacks + private viewer: CadViewer + private container: HTMLElement + private tools: HTMLElement + private sheetBackdrop: HTMLElement + private sheetList: HTMLElement + private themeBtn!: HTMLButtonElement + private fullscreenBtn!: HTMLButtonElement + private result: CadViewerLoadResult | null = null + private themeUnsub: (() => void) | null = null + private resizeObserver: ResizeObserver | null = null + private destroyed = false + + constructor(host: ViewerHost, file: File, callbacks: Callbacks) { + this.host = host + this.file = file + this.callbacks = callbacks + this.container = h('div', { class: 'flyfish-cad' }) + host.stage.appendChild(this.container) + this.host.setFileInfo(file.name, 'DWG 图纸', 'DWG', 'cad', formatBytes(file.size)) + this.host.setSubbar(null) + this.host.setStatus('正在加载 DWG 引擎…') + + this.viewer = new CadViewer({ + container: this.container, + renderer: 'auto', + wasmPath: new URL('wasm/', document.baseURI).href, + workerUrl: new URL('wasm/dwg-worker.js', document.baseURI).href, + dwfWasmUrl: new URL('wasm/dwfv-render.wasm', document.baseURI).href, + useWorker: true, + workerTimeoutMs: 120000, + autoFit: true, + canvasOptions: { + fitMode: 'auto', + background: '#0d1117', + foreground: '#f4f7fb', + contrastMode: 'adaptive', + enableSpatialIndex: true, + spatialIndexCellCount: 96, + maxCurveSegments: 72, + maxVisibleTextLabels: 2400 + }, + onLoadProgress: (progress) => { + this.host.setLoaderMessage(progress.message || '正在解析 DWG 图纸…', this.file.name) + }, + onViewChange: () => this.updateStatus(), + onError: (error) => { + this.host.setStatus(`DWG 错误:${error.message}`) + } + }) + + this.tools = this.buildTools() + this.sheetBackdrop = h('div', { class: 'sheet-backdrop' }) + this.sheetList = h('div', { class: 'sheet-list' }) + this.sheetBackdrop.appendChild(this.buildSheet()) + host.stage.append(this.tools, this.sheetBackdrop) + this.buildHeaderActions() + this.applyTheme() + this.updateThemeButton() + this.themeUnsub = onThemeChange(() => { + this.applyTheme() + this.updateThemeButton() + }) + this.resizeObserver = new ResizeObserver(() => this.viewer.resize()) + this.resizeObserver.observe(this.host.stage) + document.addEventListener('fullscreenchange', this.onFullscreenChange) + } + + async load(): Promise { + this.host.showLoader('正在加载 DWG 图纸…', this.file.name) + try { + const result = await this.viewer.loadFile(this.file) + this.result = result + this.host.hideLoader() + this.host.setFileInfo( + this.file.name, + 'DWG 图纸', + 'DWG', + 'cad', + `${result.document.entities.length.toLocaleString()} 个实体 · ${formatBytes(this.file.size)}` + ) + this.viewer.fit('auto') + this.applyTheme() + this.updateStatus() + if (result.warnings.length) toast(result.warnings[0], 'info', 6000) + this.refreshSheetRows() + } catch (error) { + this.host.hideLoader() + throw error + } + } + + private buildHeaderActions(): void { + const downloadBtn = iconButton('download', '保存当前视图为 PNG', () => void this.exportPng()) + const fullscreenBtn = iconButton('maximize', '全屏', () => void this.toggleFullscreen()) + fullscreenBtn.dataset.action = 'fullscreen' + const aboutBtn = iconButton('info', '关于', () => showAbout()) + this.host.actions.append(downloadBtn, fullscreenBtn, aboutBtn) + this.fullscreenBtn = fullscreenBtn + } + + private buildTools(): HTMLElement { + const tools = h('div', { class: 'cad-tools' }) + const add = (name: string, label: string, action: string, onClick: () => void) => { + const btn = iconButton(name, label, (ev) => { + ev.stopPropagation() + onClick() + }) + btn.dataset.action = action + tools.appendChild(btn) + return btn + } + add('plus', '放大', 'zoom-in', () => { + this.viewer.zoomIn() + this.updateStatus() + }) + add('minus', '缩小', 'zoom-out', () => { + this.viewer.zoomOut() + this.updateStatus() + }) + add('fit', '适配全图', 'fit', () => { + this.viewer.fit('auto') + this.updateStatus() + }) + tools.appendChild(h('div', { class: 'tool-divider' })) + add('layers', '图层', 'layers', () => this.openSheet()) + this.themeBtn = add('sun', '切换背景', 'theme', () => { + toggleTheme() + this.applyTheme() + this.updateThemeButton() + }) + return tools + } + + private buildSheet(): HTMLElement { + const title = h('div', { class: 'sheet-title' }, h('span', { text: '图层' })) + const close = iconButton('x', '关闭', () => this.closeSheet()) + const head = h('div', { class: 'sheet-head' }, title, close) + const actions = h('div', { class: 'sheet-actions' }) + const showAll = h('button', { class: 'chip-btn', type: 'button', text: '全部显示' }) + const hideAll = h('button', { class: 'chip-btn', type: 'button', text: '全部隐藏' }) + const reset = h('button', { class: 'chip-btn', type: 'button', text: '恢复默认' }) + showAll.addEventListener('click', () => this.setAllLayers(true)) + hideAll.addEventListener('click', () => this.setAllLayers(false)) + reset.addEventListener('click', () => { + for (const group of this.layerGroups()) { + for (const layer of group.refs) layer.isVisible = true + } + this.applyLayerChange() + this.refreshSheetRows() + }) + actions.append(showAll, hideAll, reset) + this.sheetBackdrop.addEventListener('click', (event) => { + if (event.target === this.sheetBackdrop) this.closeSheet() + }) + return h( + 'aside', + { class: 'sheet', role: 'dialog', 'aria-label': '图层' }, + head, + actions, + this.sheetList + ) + } + + private layerGroups(): LayerRow[] { + const doc = this.viewer.getDocument() + if (!doc) return [] + const groups = new Map() + for (const layer of Object.values(doc.layers)) { + const name = String(layer.name ?? '').trim() + if (!name) continue + const key = name.toLowerCase() + const existing = groups.get(key) + if (existing) existing.refs.push(layer as unknown as Record) + else groups.set(key, { name, refs: [layer as unknown as Record] }) + } + return [...groups.values()].sort((a, b) => { + if (a.name === '0') return -1 + if (b.name === '0') return 1 + return a.name.localeCompare(b.name, 'zh-Hans-CN') + }) + } + + private refreshSheetRows(): void { + if (!this.sheetList) return + clear(this.sheetList) + for (const group of this.layerGroups()) { + const visible = group.refs.some((layer) => layer.isVisible !== false) + const row = h('button', { + class: `layer-row ${visible ? 'visible' : 'hidden'}`, + type: 'button', + dataset: { name: group.name.toLowerCase() } + }) + const swatch = h('span', { class: 'layer-swatch' }) + const first = group.refs[0] as { color?: unknown; colorIndex?: number } + swatch.style.background = layerSwatch(first.color, first.colorIndex) + const info = h( + 'span', + { class: 'layer-info' }, + h('span', { class: 'layer-name', text: group.name }), + h('span', { class: 'layer-meta', text: visible ? '显示' : '隐藏' }) + ) + const eye = h('span', { class: 'layer-eye', html: icon(visible ? 'eye' : 'eye-off', 18) }) + row.append(swatch, info, eye) + row.addEventListener('click', () => { + for (const layer of group.refs) layer.isVisible = !visible + this.applyLayerChange() + this.refreshSheetRows() + }) + this.sheetList.appendChild(row) + } + } + + private setAllLayers(visible: boolean): void { + for (const group of this.layerGroups()) { + for (const layer of group.refs) layer.isVisible = visible + } + this.applyLayerChange() + this.refreshSheetRows() + } + + private applyLayerChange(): void { + const doc = this.viewer.getDocument() as FlyCadDocument | undefined + if (!doc) return + const renderer = this.viewer.renderer + const state = renderer.getViewState() + renderer.setDocument(doc) + renderer.setViewState(state) + renderer.render() + this.updateStatus() + } + + private openSheet(): void { + this.refreshSheetRows() + this.sheetBackdrop.classList.add('open') + } + + private closeSheet(): void { + this.sheetBackdrop.classList.remove('open') + } + + private applyTheme(): void { + const dark = getTheme() === 'dark' + this.container.style.background = dark ? '#0d1117' : '#f6f8fc' + this.viewer.setCanvasOptions({ + background: dark ? '#0d1117' : '#f6f8fc', + foreground: dark ? '#f4f7fb' : '#111318', + contrastMode: 'adaptive' + }) + } + + private updateThemeButton(): void { + if (!this.themeBtn) return + this.themeBtn.innerHTML = icon(getTheme() === 'dark' ? 'sun' : 'moon', 20) + this.themeBtn.setAttribute('title', getTheme() === 'dark' ? '切换为浅色背景' : '切换为深色背景') + } + + private updateStatus(): void { + if (this.destroyed || !this.result) return + const zoom = Math.round(this.viewer.getZoomPercent()) + const backend = (this.viewer.renderer as { backend?: string }).backend ?? 'canvas2d' + const entities = this.result.document.entities.length.toLocaleString() + this.host.setStatus(`${this.file.name} · ${zoom}% · ${entities} 实体 · ${backend}`) + } + + private onFullscreenChange = () => { + if (!this.fullscreenBtn) return + const active = !!document.fullscreenElement + this.fullscreenBtn.innerHTML = icon(active ? 'minimize' : 'maximize', 20) + this.fullscreenBtn.setAttribute('title', active ? '退出全屏' : '全屏') + window.setTimeout(() => this.viewer.resize(), 80) + } + + private async toggleFullscreen(): Promise { + try { + if (document.fullscreenElement) await document.exitFullscreen?.() + else await this.host.root.requestFullscreen?.() + } catch { + toast('当前浏览器不支持全屏', 'error') + } + } + + private async exportPng(): Promise { + try { + const source = await this.viewer.captureCanvas() + const output = document.createElement('canvas') + output.width = source.width + output.height = source.height + const ctx = output.getContext('2d') + if (!ctx) throw new Error('无法创建导出画布') + ctx.drawImage(source, 0, 0) + output.toBlob((blob) => { + if (!blob) { + toast('导出失败', 'error') + return + } + const baseName = this.file.name.replace(/\.[^.]+$/, '') || 'drawing' + const candidate = `${baseName}-当前视图.png` + const bridge = (window as unknown as { AndroidViewer?: { saveBase64?: (name: string, mime: string, data: string) => void } }).AndroidViewer + if (bridge?.saveBase64) { + const reader = new FileReader() + reader.onload = () => { + const data = String(reader.result ?? '') + bridge.saveBase64?.(candidate, 'image/png', data.includes(',') ? data.slice(data.indexOf(',') + 1) : data) + toast('已保存到 Android 下载目录', 'success') + } + reader.onerror = () => toast('保存失败', 'error') + reader.readAsDataURL(blob) + return + } + const url = URL.createObjectURL(blob) + const anchor = document.createElement('a') + anchor.href = url + anchor.download = candidate + document.body.appendChild(anchor) + anchor.click() + anchor.remove() + window.setTimeout(() => URL.revokeObjectURL(url), 4000) + toast('已保存当前视图', 'success') + }, 'image/png') + } catch (error) { + toast(error instanceof Error ? error.message : '导出失败', 'error') + } + } + + destroy(): void { + if (this.destroyed) return + this.destroyed = true + this.resizeObserver?.disconnect() + this.themeUnsub?.() + document.removeEventListener('fullscreenchange', this.onFullscreenChange) + try { + this.viewer.destroy() + } catch { + /* ignore */ + } + this.container.remove() + this.tools.remove() + this.sheetBackdrop.remove() + this.host.destroy() + this.callbacks.onBack + } +} + +function layerSwatch(color: unknown, colorIndex?: number): string { + if (typeof color === 'string') return color + if (typeof color === 'number' && Number.isFinite(color)) { + const value = color & 0xffffff + return `#${value.toString(16).padStart(6, '0')}` + } + if (typeof colorIndex === 'number' && colorIndex >= 1 && colorIndex <= 255) { + return `hsl(${(colorIndex * 47) % 360} 60% 55%)` + } + return '#8a94a6' +} diff --git a/src/cad/geom.ts b/src/cad/geom.ts deleted file mode 100644 index 0c2caea..0000000 --- a/src/cad/geom.ts +++ /dev/null @@ -1,399 +0,0 @@ -export type Mat = readonly [number, number, number, number, number, number] - -export const IDENTITY: Mat = [1, 0, 0, 1, 0, 0] - -export function matMul(a: Mat, b: Mat): Mat { - return [ - a[0] * b[0] + a[2] * b[1], - a[1] * b[0] + a[3] * b[1], - a[0] * b[2] + a[2] * b[3], - a[1] * b[2] + a[3] * b[3], - a[0] * b[4] + a[2] * b[5] + a[4], - a[1] * b[4] + a[3] * b[5] + a[5] - ] -} - -export function matTranslate(tx: number, ty: number): Mat { - return [1, 0, 0, 1, tx, ty] -} - -export function matScale(sx: number, sy: number): Mat { - return [sx, 0, 0, sy, 0, 0] -} - -export function matRotate(rad: number): Mat { - const c = Math.cos(rad) - const s = Math.sin(rad) - return [c, s, -s, c, 0, 0] -} - -export function applyMat(m: Mat, x: number, y: number): [number, number] { - return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]] -} - -export function applyMatVec(m: Mat, x: number, y: number): [number, number] { - return [m[0] * x + m[2] * y, m[1] * x + m[3] * y] -} - -export function matDet(m: Mat): number { - return m[0] * m[3] - m[1] * m[2] -} - -export function matInvert(m: Mat): Mat | null { - const det = matDet(m) - if (!Number.isFinite(det) || Math.abs(det) < 1e-14) return null - const a = m[3] / det - const b = -m[1] / det - const c = -m[2] / det - const d = m[0] / det - return [a, b, c, d, -(a * m[4] + c * m[5]), -(b * m[4] + d * m[5])] -} - -export function matScaleX(m: Mat): number { - return Math.hypot(m[0], m[1]) -} - -export function matScaleY(m: Mat): number { - return Math.hypot(m[2], m[3]) -} - -export function matScaleAvg(m: Mat): number { - return Math.sqrt(Math.abs(matDet(m))) || matScaleX(m) -} - -export function isSimilarity(m: Mat, eps = 1e-5): boolean { - const sx = matScaleX(m) - const sy = matScaleY(m) - if (Math.abs(sx - sy) > eps * Math.max(1, sx, sy)) return false - const dot = m[0] * m[2] + m[1] * m[3] - return Math.abs(dot) <= eps * Math.max(1, sx * sy) -} - -export interface BBox { - minX: number - minY: number - maxX: number - maxY: number -} - -export function emptyBBox(): BBox { - return { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity } -} - -export function bboxValid(b: BBox): boolean { - return ( - Number.isFinite(b.minX) && - Number.isFinite(b.minY) && - Number.isFinite(b.maxX) && - Number.isFinite(b.maxY) && - b.maxX >= b.minX && - b.maxY >= b.minY - ) -} - -export function bboxAddPoint(b: BBox, x: number, y: number): BBox { - if (!Number.isFinite(x) || !Number.isFinite(y)) return b - if (x < b.minX) b.minX = x - if (y < b.minY) b.minY = y - if (x > b.maxX) b.maxX = x - if (y > b.maxY) b.maxY = y - return b -} - -export function bboxFromPoints(points: ArrayLike, b: BBox = emptyBBox()): BBox { - for (let i = 0; i + 1 < points.length; i += 2) bboxAddPoint(b, points[i], points[i + 1]) - return b -} - -export function bboxUnion(a: BBox, b: BBox): BBox { - if (!bboxValid(a)) return { ...b } - if (!bboxValid(b)) return { ...a } - return { - minX: Math.min(a.minX, b.minX), - minY: Math.min(a.minY, b.minY), - maxX: Math.max(a.maxX, b.maxX), - maxY: Math.max(a.maxY, b.maxY) - } -} - -export function bboxTransform(m: Mat, b: BBox): BBox { - const out = emptyBBox() - bboxAddPoint(out, ...applyMat(m, b.minX, b.minY)) - bboxAddPoint(out, ...applyMat(m, b.maxX, b.minY)) - bboxAddPoint(out, ...applyMat(m, b.minX, b.maxY)) - bboxAddPoint(out, ...applyMat(m, b.maxX, b.maxY)) - return out -} - -export function bboxExpand(b: BBox, dx: number, dy = dx): BBox { - return { minX: b.minX - dx, minY: b.minY - dy, maxX: b.maxX + dx, maxY: b.maxY + dy } -} - -export function bboxWidth(b: BBox): number { - return b.maxX - b.minX -} - -export function bboxHeight(b: BBox): number { - return b.maxY - b.minY -} - -export function bboxCenter(b: BBox): [number, number] { - return [(b.minX + b.maxX) / 2, (b.minY + b.maxY) / 2] -} - -export function bboxIntersects(a: BBox, b: BBox): boolean { - return a.minX <= b.maxX && a.maxX >= b.minX && a.minY <= b.maxY && a.maxY >= b.minY -} - -export interface Camera { - scale: number - cx: number - cy: number -} - -export function cameraMatrix(cam: Camera, width: number, height: number): Mat { - const s = cam.scale - return [s, 0, 0, -s, width / 2 - s * cam.cx, height / 2 + s * cam.cy] -} - -export function worldToScreen( - cam: Camera, - width: number, - height: number, - x: number, - y: number -): [number, number] { - return [width / 2 + (x - cam.cx) * cam.scale, height / 2 - (y - cam.cy) * cam.scale] -} - -export function screenToWorld( - cam: Camera, - width: number, - height: number, - sx: number, - sy: number -): [number, number] { - return [cam.cx + (sx - width / 2) / cam.scale, cam.cy + (height / 2 - sy) / cam.scale] -} - -export function clamp(value: number, min: number, max: number): number { - return value < min ? min : value > max ? max : value -} - -export function arcSegmentCount(radiusScreen: number, sweep: number): number { - const r = Math.max(radiusScreen, 0.35) - const tolerance = 0.22 - const ratio = clamp(1 - tolerance / r, -1, 1) - const maxAngle = 2 * Math.acos(ratio) - const count = Math.ceil(Math.abs(sweep) / Math.max(maxAngle, 0.02)) - return clamp(count, 4, 1600) -} - -export function sampleArc( - cx: number, - cy: number, - r: number, - a0: number, - sweep: number, - radiusScreen: number, - out: number[] = [] -): number[] { - const n = arcSegmentCount(radiusScreen, sweep) - out.push(cx + r * Math.cos(a0), cy + r * Math.sin(a0)) - for (let i = 1; i <= n; i++) { - const a = a0 + (sweep * i) / n - out.push(cx + r * Math.cos(a), cy + r * Math.sin(a)) - } - return out -} - -export function sampleArcToPath( - cx: number, - cy: number, - r: number, - a0: number, - sweep: number, - radiusScreen: number, - moveTo: (x: number, y: number) => void, - lineTo: (x: number, y: number) => void -): void { - const n = arcSegmentCount(radiusScreen, sweep) - moveTo(cx + r * Math.cos(a0), cy + r * Math.sin(a0)) - for (let i = 1; i <= n; i++) { - const a = a0 + (sweep * i) / n - lineTo(cx + r * Math.cos(a), cy + r * Math.sin(a)) - } -} - -export function sampleEllipse( - cx: number, - cy: number, - mx: number, - my: number, - ratio: number, - a0: number, - sweep: number, - radiusScreen: number, - out: number[] = [] -): number[] { - const nx = -my * ratio - const ny = mx * ratio - const n = arcSegmentCount(radiusScreen, sweep) - const c0 = Math.cos(a0) - const s0 = Math.sin(a0) - out.push(cx + mx * c0 + nx * s0, cy + my * c0 + ny * s0) - for (let i = 1; i <= n; i++) { - const a = a0 + (sweep * i) / n - const c = Math.cos(a) - const s = Math.sin(a) - out.push(cx + mx * c + nx * s, cy + my * c + ny * s) - } - return out -} - -export function sampleEllipseToPath( - cx: number, - cy: number, - mx: number, - my: number, - ratio: number, - a0: number, - sweep: number, - radiusScreen: number, - moveTo: (x: number, y: number) => void, - lineTo: (x: number, y: number) => void -): void { - const nx = -my * ratio - const ny = mx * ratio - const n = arcSegmentCount(radiusScreen, sweep) - const c0 = Math.cos(a0) - const s0 = Math.sin(a0) - moveTo(cx + mx * c0 + nx * s0, cy + my * c0 + ny * s0) - for (let i = 1; i <= n; i++) { - const a = a0 + (sweep * i) / n - const c = Math.cos(a) - const s = Math.sin(a) - lineTo(cx + mx * c + nx * s, cy + my * c + ny * s) - } -} - -export interface BulgeArc { - cx: number - cy: number - r: number - a0: number - sweep: number -} - -export function bulgeToArc(x1: number, y1: number, x2: number, y2: number, bulge: number): BulgeArc | null { - if (!Number.isFinite(bulge) || Math.abs(bulge) < 1e-12) return null - const dx = x2 - x1 - const dy = y2 - y1 - const chord = Math.hypot(dx, dy) - if (chord < 1e-12) return null - const theta = 4 * Math.atan(bulge) - const half = Math.abs(theta) / 2 - const r = chord / (2 * Math.sin(half)) - if (!Number.isFinite(r) || r <= 0) return null - const mx = (x1 + x2) / 2 - const my = (y1 + y2) / 2 - const nx = -dy / chord - const ny = dx / chord - const d = chord / 2 / Math.tan(theta / 2) - const cx = mx + nx * d - const cy = my + ny * d - const a0 = Math.atan2(y1 - cy, x1 - cx) - return { cx, cy, r, a0, sweep: theta } -} - -export function splineDomain(knots: number[], degree: number, count: number): [number, number] { - if (knots.length < count + degree + 1 || count <= degree) { - return [0, 1] - } - return [knots[degree], knots[count]] -} - -export function evaluateSpline( - control: number[], - degree: number, - knots: number[], - weights: number[] | undefined, - t: number -): [number, number] { - const count = control.length / 2 - if (count === 0) return [0, 0] - if (count <= degree || knots.length < count + degree + 1) { - const firstX = control[0] - const firstY = control[1] - return [firstX, firstY] - } - let k = degree - for (let i = degree; i < count; i++) { - if (t >= knots[i] && t <= knots[i + 1]) { - k = i - if (t < knots[i + 1]) break - } - } - const pts: Array<[number, number, number]> = [] - for (let j = 0; j <= degree; j++) { - const idx = k - degree + j - const w = weights && weights.length > idx ? weights[idx] : 1 - pts.push([control[idx * 2] * w, control[idx * 2 + 1] * w, w]) - } - for (let r = 1; r <= degree; r++) { - for (let j = degree; j >= r; j--) { - const i = k - degree + j - const denom = knots[i + degree + 1 - r] - knots[i] - const alpha = denom === 0 ? 0 : (t - knots[i]) / denom - const prev = pts[j - 1] - const cur = pts[j] - pts[j] = [ - prev[0] * (1 - alpha) + cur[0] * alpha, - prev[1] * (1 - alpha) + cur[1] * alpha, - prev[2] * (1 - alpha) + cur[2] * alpha - ] - } - } - const result = pts[degree] - return [result[2] === 0 ? 0 : result[0] / result[2], result[2] === 0 ? 0 : result[1] / result[2]] -} - -export function sampleSpline( - control: number[], - degree: number, - knots: number[], - weights: number[] | undefined, - segments: number, - out: number[] = [] -): number[] { - const count = control.length / 2 - if (count === 0) return out - if (count === 1) { - out.push(control[0], control[1]) - return out - } - if (degree < 1 || count <= degree || knots.length < count + degree + 1) { - for (let i = 0; i < control.length; i++) out.push(control[i]) - return out - } - const [start, end] = splineDomain(knots, degree, count) - if (!(end > start)) { - for (let i = 0; i < control.length; i++) out.push(control[i]) - return out - } - const n = clamp(Math.round(segments), 8, 2048) - for (let i = 0; i <= n; i++) { - const t = start + ((end - start) * i) / n - const p = evaluateSpline(control, degree, knots, weights, t) - out.push(p[0], p[1]) - } - return out -} - -export function formatNumber(value: number, maxDecimals = 3): string { - if (!Number.isFinite(value)) return '—' - const abs = Math.abs(value) - if (abs >= 1e7 || (abs > 0 && abs < 1e-4)) return value.toExponential(2) - const fixed = value.toFixed(maxDecimals) - return fixed.replace(/\.?0+$/, '') -} diff --git a/src/cad/loader.ts b/src/cad/loader.ts deleted file mode 100644 index a3bf013..0000000 --- a/src/cad/loader.ts +++ /dev/null @@ -1,186 +0,0 @@ -import type { CadDocument } from './types' -import { slimDocument } from './slim' - -interface WorkerDoneMessage { - type: 'done' - doc: CadDocument - timings?: { readMs: number; parseMs: number; buildMs: number; totalMs: number } -} - -interface WorkerMessage { - type: 'progress' | 'ready' | 'done' | 'error' - message?: string - code?: string - doc?: CadDocument - timings?: WorkerDoneMessage['timings'] -} - -interface ActiveLoad { - resolve: (doc: CadDocument) => void - reject: (error: Error) => void - onProgress?: (message: string) => void -} - -let worker: Worker | null = null -let workerDisabled = false -let enginePromise: Promise | null = null -let engineResolve: (() => void) | null = null -let engineReject: ((error: Error) => void) | null = null -let engineTimer = 0 -let activeLoad: ActiveLoad | null = null - -function disableWorker(error: Error): void { - workerDisabled = true - if (engineTimer) { - window.clearTimeout(engineTimer) - engineTimer = 0 - } - if (engineReject) { - engineReject(error) - engineReject = null - engineResolve = null - enginePromise = null - } - if (activeLoad) { - activeLoad.reject(error) - activeLoad = null - } - try { - worker?.terminate() - } catch { - /* ignore */ - } - worker = null -} - -function handleWorkerMessage(event: MessageEvent): void { - const message = event.data as WorkerMessage - if (!message) return - if (message.type === 'progress') { - activeLoad?.onProgress?.(message.message ?? '') - return - } - if (message.type === 'ready') { - if (engineTimer) { - window.clearTimeout(engineTimer) - engineTimer = 0 - } - const resolve = engineResolve - engineResolve = null - engineReject = null - resolve?.() - return - } - if (message.type === 'done' && message.doc) { - if (message.timings) { - console.info( - `[dwg] read ${Math.round(message.timings.readMs)}ms · parse ${Math.round( - message.timings.parseMs - )}ms · build ${Math.round(message.timings.buildMs)}ms · total ${Math.round( - message.timings.totalMs - )}ms` - ) - } - const active = activeLoad - activeLoad = null - active?.resolve(message.doc) - return - } - if (message.type === 'error') { - const error = new Error(message.message || 'DWG 解析失败') - if (message.code === 'engine') { - disableWorker(error) - return - } - const active = activeLoad - activeLoad = null - active?.reject(error) - } -} - -function ensureWorker(): Worker | null { - if (workerDisabled) return null - if (!worker) { - try { - worker = new Worker(new URL('./dwgWorker.ts', import.meta.url), { type: 'module' }) - } catch { - workerDisabled = true - return null - } - worker.onmessage = handleWorkerMessage - worker.onerror = () => { - disableWorker(new Error('DWG 解析 Worker 启动失败')) - } - } - if (!enginePromise) { - enginePromise = new Promise((resolve, reject) => { - engineResolve = resolve - engineReject = reject - engineTimer = window.setTimeout(() => { - disableWorker(new Error('DWG 引擎加载超时')) - }, 60000) - try { - worker!.postMessage({ type: 'init' }) - } catch (error) { - disableWorker(error instanceof Error ? error : new Error(String(error))) - } - }) - } - return worker -} - -export async function preloadDwgEngine(): Promise { - const instance = ensureWorker() - if (!instance || !enginePromise) return - try { - await enginePromise - } catch { - /* main-thread fallback will be used on open */ - } -} - -export async function loadDwg( - file: File, - onProgress?: (message: string) => void -): Promise { - const instance = ensureWorker() - if (!instance || !enginePromise) { - return loadDwgOnMainThread(file, onProgress) - } - try { - await enginePromise - } catch { - return loadDwgOnMainThread(file, onProgress) - } - if (workerDisabled || !worker) { - return loadDwgOnMainThread(file, onProgress) - } - try { - return await new Promise((resolve, reject) => { - activeLoad = { resolve, reject, onProgress } - worker!.postMessage({ - type: 'load', - file, - name: file.name, - size: file.size - }) - }) - } catch (error) { - const failure = error instanceof Error ? error : new Error(String(error)) - disableWorker(failure) - return loadDwgOnMainThread(file, onProgress) - } -} - -async function loadDwgOnMainThread( - file: File, - onProgress?: (message: string) => void -): Promise { - onProgress?.('正在读取文件…') - const buffer = await file.arrayBuffer() - onProgress?.('正在解析 DWG 图纸…') - await new Promise((resolve) => setTimeout(resolve, 20)) - const { buildAcadDocument } = await import('./acadAdapter') - const doc = buildAcadDocument(buffer, file.name, file.size) - return slimDocument(doc) -} diff --git a/src/cad/renderer.ts b/src/cad/renderer.ts deleted file mode 100644 index 1a2459e..0000000 --- a/src/cad/renderer.ts +++ /dev/null @@ -1,643 +0,0 @@ -import type { DwgViewportEntity } from '@mlightcad/libredwg-web' -import type { Camera, Mat } from './geom' -import { - applyMat, - applyMatVec, - bboxFromPoints, - bboxHeight, - bboxIntersects, - bboxTransform, - bboxUnion, - bboxValid, - bboxWidth, - bulgeToArc, - cameraMatrix, - clamp, - emptyBBox, - matInvert, - matMul, - matRotate, - matScale, - matScaleAvg, - matScaleY, - matTranslate, - sampleArcToPath, - sampleEllipseToPath, - sampleSpline -} from './geom' -import { cssColor } from './colors' -import type { CadDocument, CadSpace, Primitive } from './types' -import type { Theme } from '../core/theme' - -export interface RenderOptions { - layerVisible: (name: string) => boolean - proxyVisible?: boolean - theme: Theme -} - -const FONT_FAMILY = - '"Noto Sans SC", "PingFang SC", "Microsoft YaHei", "Segoe UI", system-ui, sans-serif' - -const MODEL_BG = { dark: '#0d1117', light: '#f6f8fc' } -const LAYOUT_BG = { dark: '#121722', light: '#e8edf5' } -const PAGE_COLOR = '#ffffff' - -export class CadRenderer { - private doc: CadDocument - private layerHandleToName = new Map() - - constructor(doc: CadDocument) { - this.doc = doc - for (const layer of doc.layers) { - if (layer.handle) this.layerHandleToName.set(layer.handle, layer.name) - } - } - - draw( - ctx: CanvasRenderingContext2D, - space: CadSpace, - camera: Camera, - width: number, - height: number, - options: RenderOptions - ): void { - const theme = options.theme - const isLayout = space.kind === 'layout' - ctx.save() - ctx.fillStyle = isLayout ? LAYOUT_BG[theme] : MODEL_BG[theme] - ctx.fillRect(0, 0, width, height) - - const cameraMat = cameraMatrix(camera, width, height) - if (isLayout) { - this.drawLayout(ctx, space, camera, cameraMat, width, height, options) - } else { - this.drawPrimitives(ctx, space, cameraMat, camera, false, options) - } - ctx.restore() - } - - private drawLayout( - ctx: CanvasRenderingContext2D, - space: CadSpace, - camera: Camera, - cameraMat: Mat, - _width: number, - _height: number, - options: RenderOptions - ): void { - const page = space.bbox - if (!bboxValid(page)) return - const p0 = applyMat(cameraMat, page.minX, page.minY) - const p1 = applyMat(cameraMat, page.maxX, page.maxY) - const left = Math.min(p0[0], p1[0]) - const top = Math.min(p0[1], p1[1]) - const right = Math.max(p0[0], p1[0]) - const bottom = Math.max(p0[1], p1[1]) - const pageRect = { x: left, y: top, w: right - left, h: bottom - top } - - ctx.save() - ctx.shadowColor = 'rgba(0,0,0,0.38)' - ctx.shadowBlur = 22 - ctx.shadowOffsetY = 8 - ctx.fillStyle = PAGE_COLOR - ctx.fillRect(pageRect.x, pageRect.y, pageRect.w, pageRect.h) - ctx.restore() - - ctx.save() - ctx.beginPath() - ctx.rect(pageRect.x, pageRect.y, pageRect.w, pageRect.h) - ctx.clip() - - for (const vp of space.viewports) { - if (!this.isRenderableViewport(vp)) continue - this.drawViewport(ctx, space, vp, camera, cameraMat, options) - } - - this.drawPrimitives(ctx, space, cameraMat, camera, true, options) - - ctx.restore() - - ctx.strokeStyle = 'rgba(0,0,0,0.16)' - ctx.lineWidth = 1 - ctx.strokeRect(pageRect.x + 0.5, pageRect.y + 0.5, Math.max(0, pageRect.w - 1), Math.max(0, pageRect.h - 1)) - } - - private isRenderableViewport(vp: DwgViewportEntity): boolean { - const width = Math.abs(Number(vp.width) || 0) - const height = Math.abs(Number(vp.height) || 0) - const viewHeight = Math.abs(Number(vp.viewHeight) || 0) - if (width <= 0 || height <= 0 || viewHeight <= 0) return false - const flags = Number(vp.statusBitFlags) || 0 - if (flags & 131072) return false - return true - } - - private drawViewport( - ctx: CanvasRenderingContext2D, - space: CadSpace, - vp: DwgViewportEntity, - camera: Camera, - cameraMat: Mat, - options: RenderOptions - ): void { - const model = this.doc.model - if (!model) return - const width = Math.abs(Number(vp.width) || 0) - const height = Math.abs(Number(vp.height) || 0) - const viewHeight = Math.abs(Number(vp.viewHeight) || 0) - if (width <= 0 || height <= 0 || viewHeight <= 0) return - const center = vp.viewportCenter ?? { x: 0, y: 0 } - const viewCenter = vp.displayCenter ?? { x: 0, y: 0 } - const twist = Number(vp.viewTwistAngle) || 0 - const scale = height / viewHeight - let modelToPaper = matTranslate(Number(center.x) || 0, Number(center.y) || 0) - modelToPaper = matMul(modelToPaper, matRotate(-twist)) - modelToPaper = matMul(modelToPaper, matScale(scale, scale)) - modelToPaper = matMul( - modelToPaper, - matTranslate(-(Number(viewCenter.x) || 0), -(Number(viewCenter.y) || 0)) - ) - const totalMat = matMul(cameraMat, modelToPaper) - - const corners = [ - [Number(center.x) - width / 2, Number(center.y) - height / 2], - [Number(center.x) + width / 2, Number(center.y) - height / 2], - [Number(center.x) + width / 2, Number(center.y) + height / 2], - [Number(center.x) - width / 2, Number(center.y) + height / 2] - ].map(([x, y]) => applyMat(cameraMat, x, y)) - - const frozen = new Set() - if (Array.isArray(vp.frozenLayerIds)) { - for (const handle of vp.frozenLayerIds) { - const name = this.layerHandleToName.get(String(handle)) - if (name) frozen.add(name) - } - } - - ctx.save() - ctx.beginPath() - ctx.moveTo(corners[0][0], corners[0][1]) - for (let i = 1; i < corners.length; i++) ctx.lineTo(corners[i][0], corners[i][1]) - ctx.closePath() - ctx.clip() - this.drawPrimitives( - ctx, - model, - totalMat, - camera, - true, - options, - (prim) => !frozen.has(prim.layer) - ) - ctx.restore() - - if (this.isViewportFrameVisible(vp)) { - const c = corners - const minX = Math.min(...c.map((p) => p[0])) - const maxX = Math.max(...c.map((p) => p[0])) - const minY = Math.min(...c.map((p) => p[1])) - const maxY = Math.max(...c.map((p) => p[1])) - if (maxX - minX > 8 && maxY - minY > 8) { - ctx.save() - ctx.strokeStyle = options.theme === 'dark' ? 'rgba(0,0,0,0.22)' : 'rgba(30,50,90,0.18)' - ctx.lineWidth = 1 - ctx.setLineDash([4, 4]) - ctx.strokeRect(minX + 0.5, minY + 0.5, maxX - minX - 1, maxY - minY - 1) - ctx.restore() - } - } - void camera - } - - private isViewportFrameVisible(vp: DwgViewportEntity): boolean { - const flags = Number(vp.statusBitFlags) || 0 - return (flags & 131072) === 0 - } - - private drawPrimitives( - ctx: CanvasRenderingContext2D, - source: Pick, - totalMat: Mat, - _camera: Camera, - paper: boolean, - options: RenderOptions, - extraFilter?: (prim: Primitive) => boolean - ): void { - const cw = ctx.canvas.clientWidth || ctx.canvas.width - const ch = ctx.canvas.clientHeight || ctx.canvas.height - for (const prim of this.visiblePrimitives(source, totalMat, cw, ch)) { - if (!options.layerVisible(prim.layer)) continue - if (options.proxyVisible === false && prim.proxy) continue - if (extraFilter && !extraFilter(prim)) continue - let sb = null - if (!prim.ignoreBounds) { - sb = bboxTransform(totalMat, prim.bbox) - if (sb.maxX < -32 || sb.minX > cw + 32 || sb.maxY < -32 || sb.minY > ch + 32) { - continue - } - } - if (prim.kind === 'text') { - const size = prim.height * matScaleY(totalMat) - if (size < 2.8) continue - this.drawText(ctx, prim, totalMat, paper, options) - } else { - if (sb) { - const sw = sb.maxX - sb.minX - const sh = sb.maxY - sb.minY - if (Math.max(sw, sh) < 0.55) continue - } - this.drawPrimitive(ctx, prim, totalMat, paper, options) - } - } - } - - private visiblePrimitives( - source: Pick, - mat: Mat, - width: number, - height: number - ): Primitive[] { - const prims = source.prims - const grid = source.grid - const inverse = matInvert(mat) - if (!inverse) return [] - - const corners = [ - applyMat(inverse, -32, -32), - applyMat(inverse, width + 32, -32), - applyMat(inverse, -32, height + 32), - applyMat(inverse, width + 32, height + 32) - ] - let minX = Infinity - let minY = Infinity - let maxX = -Infinity - let maxY = -Infinity - for (const [x, y] of corners) { - if (x < minX) minX = x - if (y < minY) minY = y - if (x > maxX) maxX = x - if (y > maxY) maxY = y - } - const world = { minX, minY, maxX, maxY } - - if (!grid) { - return prims.filter((prim) => prim.ignoreBounds || bboxIntersects(prim.bbox, world)) - } - - const seen = new Uint8Array(prims.length) - const indices: number[] = [] - const add = (index: number) => { - if (seen[index]) return - seen[index] = 1 - const prim = prims[index] - if (prim && (prim.ignoreBounds || bboxIntersects(prim.bbox, world))) indices.push(index) - } - - for (const index of grid.big) add(index) - - const c0 = clamp(Math.floor((minX - grid.minX) / grid.cellW), 0, grid.cols - 1) - const c1 = clamp(Math.floor((maxX - grid.minX) / grid.cellW), 0, grid.cols - 1) - const r0 = clamp(Math.floor((minY - grid.minY) / grid.cellH), 0, grid.rows - 1) - const r1 = clamp(Math.floor((maxY - grid.minY) / grid.cellH), 0, grid.rows - 1) - for (let row = r0; row <= r1; row++) { - const base = row * grid.cols - for (let col = c0; col <= c1; col++) { - const cell = grid.cells[base + col] - if (!cell) continue - for (const index of cell) add(index) - } - } - - return indices.map((index) => prims[index]) - } - - private drawPrimitive( - ctx: CanvasRenderingContext2D, - prim: Exclude, - mat: Mat, - paper: boolean, - options: RenderOptions - ): void { - const scale = matScaleAvg(mat) - const color = cssColor(prim.color, paper, options.theme) - const lineWidth = Math.max(0.35, prim.width) - ctx.save() - ctx.lineWidth = lineWidth - ctx.strokeStyle = color - ctx.fillStyle = color - ctx.lineCap = 'butt' - ctx.lineJoin = 'round' - if (prim.alpha != null && prim.alpha < 1) ctx.globalAlpha = clamp(prim.alpha, 0.05, 1) - const dash = dashInPixels(prim.dash, scale) - if (dash) ctx.setLineDash(dash) - else ctx.setLineDash([]) - - switch (prim.kind) { - case 'poly': { - if (prim.points.length < 4) break - ctx.beginPath() - const p = applyMat(mat, prim.points[0], prim.points[1]) - ctx.moveTo(p[0], p[1]) - for (let i = 2; i < prim.points.length; i += 2) { - const q = applyMat(mat, prim.points[i], prim.points[i + 1]) - ctx.lineTo(q[0], q[1]) - } - if (prim.closed) ctx.closePath() - if (prim.fill) { - ctx.setLineDash([]) - ctx.fillStyle = prim.fillBackground - ? paper - ? PAGE_COLOR - : options.theme === 'dark' - ? MODEL_BG.dark - : MODEL_BG.light - : color - ctx.fill() - } - if (prim.stroke) { - if (prim.worldWidth && prim.worldWidth > 0) { - ctx.lineWidth = clamp(prim.worldWidth * scale, lineWidth, 48) - } - ctx.stroke() - } - break - } - case 'circle': { - const r = Math.max(0.1, prim.r * scale) - if (r < 0.25) break - ctx.beginPath() - sampleArcToPath( - prim.cx, - prim.cy, - prim.r, - 0, - Math.PI * 2, - r, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.moveTo(p[0], p[1]) - }, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.lineTo(p[0], p[1]) - } - ) - ctx.closePath() - if (prim.fill) ctx.fill() - ctx.stroke() - break - } - case 'arc': { - const r = Math.max(0.1, prim.r * scale) - if (r < 0.25) break - ctx.beginPath() - sampleArcToPath( - prim.cx, - prim.cy, - prim.r, - prim.a0, - prim.sweep, - r, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.moveTo(p[0], p[1]) - }, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.lineTo(p[0], p[1]) - } - ) - ctx.stroke() - break - } - case 'ellipse': { - const r = Math.max(0.1, Math.hypot(prim.mx, prim.my) * scale) - if (r < 0.25) break - ctx.beginPath() - sampleEllipseToPath( - prim.cx, - prim.cy, - prim.mx, - prim.my, - prim.ratio, - prim.a0, - prim.sweep, - r, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.moveTo(p[0], p[1]) - }, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.lineTo(p[0], p[1]) - } - ) - if (prim.fill) { - ctx.closePath() - ctx.fill() - } - ctx.stroke() - break - } - case 'bulge': { - if (prim.vertices.length < 2) break - ctx.beginPath() - const first = applyMat(mat, prim.vertices[0].x, prim.vertices[0].y) - ctx.moveTo(first[0], first[1]) - for (let i = 0; i < prim.vertices.length; i++) { - const next = i + 1 < prim.vertices.length ? i + 1 : prim.closed ? 0 : -1 - if (next < 0) break - const a = prim.vertices[i] - const b = prim.vertices[next] - const arc = bulgeToArc(a.x, a.y, b.x, b.y, a.bulge) - if (arc) { - const r = Math.max(0.1, arc.r * scale) - sampleArcToPath( - arc.cx, - arc.cy, - arc.r, - arc.a0, - arc.sweep, - r, - () => {}, - (x, y) => { - const p = applyMat(mat, x, y) - ctx.lineTo(p[0], p[1]) - } - ) - } else { - const p = applyMat(mat, b.x, b.y) - ctx.lineTo(p[0], p[1]) - } - } - if (prim.closed) ctx.closePath() - if (prim.worldWidth && prim.worldWidth > 0) ctx.lineWidth = clamp(prim.worldWidth * scale, lineWidth, 48) - ctx.stroke() - break - } - case 'spline': { - const local = bboxFromPoints(prim.control) - const extent = Math.max(bboxWidth(local), bboxHeight(local)) * scale - const spans = Math.max(1, prim.control.length / 2 - prim.degree) - const segments = clamp(Math.ceil(extent / 2.5) + spans * 6, 32, 1400) - const pts = sampleSpline(prim.control, prim.degree, prim.knots, prim.weights, segments) - if (pts.length < 4) break - ctx.beginPath() - const p0 = applyMat(mat, pts[0], pts[1]) - ctx.moveTo(p0[0], p0[1]) - for (let i = 2; i < pts.length; i += 2) { - const p = applyMat(mat, pts[i], pts[i + 1]) - ctx.lineTo(p[0], p[1]) - } - if (prim.closed) ctx.closePath() - ctx.stroke() - break - } - case 'hatch': { - ctx.beginPath() - for (const path of prim.paths) { - if (path.length < 6) continue - const p0 = applyMat(mat, path[0], path[1]) - ctx.moveTo(p0[0], p0[1]) - for (let i = 2; i < path.length; i += 2) { - const p = applyMat(mat, path[i], path[i + 1]) - ctx.lineTo(p[0], p[1]) - } - ctx.closePath() - } - if (prim.solid) { - ctx.setLineDash([]) - ctx.fill('evenodd') - break - } - const sb = bboxTransform(mat, prim.bbox) - if (!bboxValid(sb)) break - if (prim.patternSpacing * scale < 1.4) break - ctx.save() - ctx.clip('evenodd') - let spacing = Math.max(2, prim.patternSpacing * scale) - const angle = -prim.patternAngle - const dx = Math.cos(angle) - const dy = Math.sin(angle) - const nx = -dy - const ny = dx - const cx = (sb.minX + sb.maxX) / 2 - const cy = (sb.minY + sb.maxY) / 2 - const diagonal = Math.hypot(bboxWidth(sb), bboxHeight(sb)) - if ((diagonal * 2) / spacing > 12000) spacing = (diagonal * 2) / 12000 - ctx.beginPath() - const limit = diagonal + spacing - for (let offset = -limit; offset <= limit; offset += spacing) { - const ox = cx + nx * offset - const oy = cy + ny * offset - ctx.moveTo(ox - dx * limit, oy - dy * limit) - ctx.lineTo(ox + dx * limit, oy + dy * limit) - } - ctx.lineWidth = 0.7 - ctx.strokeStyle = color - ctx.setLineDash([]) - ctx.stroke() - ctx.restore() - break - } - case 'point': { - const p = applyMat(mat, prim.x, prim.y) - ctx.beginPath() - ctx.arc(p[0], p[1], 1.8, 0, Math.PI * 2) - ctx.fill() - ctx.strokeStyle = color - ctx.lineWidth = 1 - ctx.beginPath() - ctx.moveTo(p[0] - 3, p[1]) - ctx.lineTo(p[0] + 3, p[1]) - ctx.moveTo(p[0], p[1] - 3) - ctx.lineTo(p[0], p[1] + 3) - ctx.stroke() - break - } - case 'image': { - ctx.beginPath() - if (prim.points && prim.points.length >= 6) { - const p0 = applyMat(mat, prim.points[0], prim.points[1]) - ctx.moveTo(p0[0], p0[1]) - for (let i = 2; i < prim.points.length; i += 2) { - const p = applyMat(mat, prim.points[i], prim.points[i + 1]) - ctx.lineTo(p[0], p[1]) - } - ctx.closePath() - } else { - const a = applyMat(mat, prim.x1, prim.y1) - const b = applyMat(mat, prim.x2, prim.y2) - ctx.rect(a[0], a[1], b[0] - a[0], b[1] - a[1]) - } - ctx.globalAlpha = 0.16 - ctx.fillStyle = color - ctx.fill() - ctx.globalAlpha = 1 - ctx.setLineDash([5, 4]) - ctx.strokeStyle = color - ctx.lineWidth = 1 - ctx.stroke() - break - } - } - ctx.restore() - } - - private drawText( - ctx: CanvasRenderingContext2D, - prim: Extract, - mat: Mat, - paper: boolean, - options: RenderOptions - ): void { - if (!prim.lines.length) return - const anchor = applyMat(mat, prim.x, prim.y) - const yScale = Math.hypot(mat[2], mat[3]) - const size = prim.height * yScale - if (!Number.isFinite(size) || size < 2.4) return - if (size > 2600) return - const cos = Math.cos(prim.rotation) - const sin = Math.sin(prim.rotation) - const axis = applyMatVec(mat, cos, sin) - const angle = Math.atan2(axis[1], axis[0]) - const color = cssColor(prim.color, paper, options.theme) - const lineHeightPx = Math.max(size * 1.1, prim.lineHeight * yScale) - const totalHeight = lineHeightPx * prim.lines.length - - ctx.save() - ctx.translate(anchor[0], anchor[1]) - ctx.rotate(angle) - ctx.scale(prim.widthFactor * (prim.mirrored ? -1 : 1), 1) - ctx.font = `${Math.min(size, 2600)}px ${FONT_FAMILY}` - ctx.fillStyle = color - ctx.textAlign = prim.halign - ctx.setLineDash([]) - - if (prim.lines.length === 1 && (prim.valign === 'baseline' || prim.valign === 'bottom')) { - ctx.textBaseline = prim.valign === 'baseline' ? 'alphabetic' : 'bottom' - ctx.fillText(prim.lines[0], 0, 0) - } else { - ctx.textBaseline = 'top' - let y0 = 0 - if (prim.valign === 'middle') y0 = -totalHeight / 2 - else if (prim.valign === 'bottom') y0 = -totalHeight - for (let i = 0; i < prim.lines.length; i++) { - ctx.fillText(prim.lines[i], 0, y0 + i * lineHeightPx) - } - } - ctx.restore() - } -} - -function dashInPixels(dash: number[] | undefined, scale: number): number[] | null { - if (!dash || !dash.length) return null - let meaningful = false - const out: number[] = [] - for (const value of dash) { - const px = Math.max(0.3, value * scale) - if (px > 0.6) meaningful = true - out.push(Math.min(px, 400)) - } - return meaningful ? out : null -} diff --git a/src/cad/slim.ts b/src/cad/slim.ts deleted file mode 100644 index 706674d..0000000 --- a/src/cad/slim.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { DwgDatabase } from '@mlightcad/libredwg-web' -import type { CadDocument, CadSpace } from './types' - -export function slimDocument(doc: CadDocument): CadDocument { - const spaces: CadSpace[] = doc.spaces.map((space) => ({ - ...space, - entities: [], - entityCount: space.entityCount ?? space.entities.length - })) - const model = spaces.find((space) => space.kind === 'model') ?? spaces[0] - return { - ...doc, - db: undefined as unknown as DwgDatabase, - blocks: new Map(), - blockByHandle: new Map(), - dashPatterns: new Map(), - imageNames: new Map(), - spaces, - model - } -} diff --git a/src/cad/text.ts b/src/cad/text.ts deleted file mode 100644 index 52d2a9e..0000000 --- a/src/cad/text.ts +++ /dev/null @@ -1,118 +0,0 @@ -export function decodeCadEscapes(text: string): string { - if (!text) return '' - let out = text - out = out.replace(/\\U\+([0-9a-fA-F]{4})/g, (_m, hex: string) => String.fromCharCode(parseInt(hex, 16))) - out = out.replace(/%%([dDpPcC%])/g, (_m, code: string) => { - switch (code.toLowerCase()) { - case 'd': - return '°' - case 'p': - return '±' - case 'c': - return 'Ø' - case '%': - return '%' - default: - return '' - } - }) - out = out.replace(/\\P/gi, '\n') - out = out.replace(/\\~/g, ' ') - out = out.replace(/\\\\/g, '\\') - out = out.replace(/\\\{/g, '{').replace(/\\\}/g, '}') - out = out.replace(/[{}]/g, '') - out = out.replace(/\r/g, '') - return out -} - -export function parseMText(raw: string, width: number, height: number): string[] { - if (!raw) return [] - let text = raw - text = text.replace(/\\U\+([0-9a-fA-F]{4})/g, (_m, hex: string) => String.fromCharCode(parseInt(hex, 16))) - text = text.replace(/\\P/gi, '\n') - text = text.replace(/\\~/g, ' ') - text = text.replace(/\\S([^;]*);/g, (_m, stacked: string) => stacked.replace(/[\^#]/g, '/')) - text = text.replace(/\\[AaCcFfHhQqTtWwXx][^;\\]*;/g, '') - text = text.replace(/\\[LlOoKkNn]/g, '') - text = text.replace(/\\[a-zA-Z][^;\\]*;?/g, '') - text = text.replace(/%%([dDpPcC%])/g, (_m, code: string) => { - switch (code.toLowerCase()) { - case 'd': - return '°' - case 'p': - return '±' - case 'c': - return 'Ø' - case '%': - return '%' - default: - return '' - } - }) - text = text.replace(/\\\\/g, '\\') - text = text.replace(/\\\{/g, '{').replace(/\\\}/g, '}') - text = text.replace(/[{}]/g, '') - text = text.replace(/\r/g, '') - const explicit = text.split('\n') - const result: string[] = [] - for (const line of explicit) { - if (width > 0) { - result.push(...wrapLine(line, width, height)) - } else { - result.push(line.replace(/\s+$/, '')) - } - } - return result.filter((line, index, arr) => line.length > 0 || (index > 0 && index < arr.length - 1)) -} - -function charWidth(ch: string, height: number): number { - const code = ch.codePointAt(0) ?? 32 - if (code >= 0x2e80) return height - if (ch === ' ') return height * 0.32 - if (/[ilI1.,'`:;|!]/.test(ch)) return height * 0.28 - if (/[mwMW@%&]/.test(ch)) return height * 0.85 - if (/[A-Z]/.test(ch)) return height * 0.66 - if (/[0-9]/.test(ch)) return height * 0.58 - return height * 0.54 -} - -export function textWidth(text: string, height: number): number { - let width = 0 - for (const ch of text) width += charWidth(ch, height) - return width -} - -export function wrapLine(line: string, width: number, height: number): string[] { - const trimmed = line.trim() - if (!trimmed) return [''] - if (width <= 0 || textWidth(trimmed, height) <= width) return [trimmed] - const lines: string[] = [] - let current = '' - let currentWidth = 0 - let lastBreak = -1 - const chars = Array.from(trimmed) - for (let i = 0; i < chars.length; i++) { - const ch = chars[i] - const w = charWidth(ch, height) - if (currentWidth + w > width && current.length > 0) { - if (lastBreak > 0 && /[A-Za-z0-9]/.test(chars[i - 1] ?? '')) { - const head = current.slice(0, lastBreak) - const tail = current.slice(lastBreak).trimStart() - lines.push(head.trimEnd()) - current = tail - currentWidth = textWidth(current, height) - lastBreak = -1 - } else { - lines.push(current.trimEnd()) - current = '' - currentWidth = 0 - lastBreak = -1 - } - } - current += ch - currentWidth += w - if (ch === ' ' || ch === '-' || ch === '/') lastBreak = current.length - } - if (current.trim()) lines.push(current.trimEnd()) - return lines.length ? lines : [''] -} diff --git a/src/cad/types.ts b/src/cad/types.ts deleted file mode 100644 index 29d7588..0000000 --- a/src/cad/types.ts +++ /dev/null @@ -1,196 +0,0 @@ -import type { - DwgBlockRecordTableEntry, - DwgDatabase, - DwgEntity, - DwgLayoutObject, - DwgViewportEntity -} from '@mlightcad/libredwg-web' -import type { BBox, Mat } from './geom' -import type { ColorSpec } from './colors' - -export interface LayerInfo { - name: string - handle: string - aci: number - rgb?: number - visible: boolean - off: boolean - frozen: boolean - count: number - lineType: string - lineWeight: number -} - -export interface CadBlockInfo { - name: string - handle: string - layoutHandle: string - baseX: number - baseY: number - entities: DwgEntity[] -} - -export interface PrimBase { - layer: string - color: ColorSpec - width: number - worldWidth?: number - dash?: number[] - alpha?: number - bbox: BBox - mat: Mat - ignoreBounds?: boolean - proxy?: boolean -} - -export interface PolyPrim extends PrimBase { - kind: 'poly' - points: number[] - closed: boolean - fill: boolean - fillBackground?: boolean - stroke: boolean -} - -export interface BulgePrim extends PrimBase { - kind: 'bulge' - vertices: Array<{ x: number; y: number; bulge: number }> - closed: boolean -} - -export interface CirclePrim extends PrimBase { - kind: 'circle' - cx: number - cy: number - r: number - fill: boolean -} - -export interface ArcPrim extends PrimBase { - kind: 'arc' - cx: number - cy: number - r: number - a0: number - sweep: number -} - -export interface EllipsePrim extends PrimBase { - kind: 'ellipse' - cx: number - cy: number - mx: number - my: number - ratio: number - a0: number - sweep: number - fill: boolean -} - -export interface SplinePrim extends PrimBase { - kind: 'spline' - control: number[] - degree: number - knots: number[] - weights?: number[] - closed: boolean -} - -export interface TextPrim extends PrimBase { - kind: 'text' - x: number - y: number - height: number - rotation: number - lines: string[] - halign: 'left' | 'center' | 'right' - valign: 'baseline' | 'bottom' | 'middle' | 'top' - lineHeight: number - widthFactor: number - mirrored: boolean -} - -export interface HatchPrim extends PrimBase { - kind: 'hatch' - paths: number[][] - solid: boolean - patternName?: string - patternAngle: number - patternSpacing: number -} - -export interface PointPrim extends PrimBase { - kind: 'point' - x: number - y: number -} - -export interface ImagePrim extends PrimBase { - kind: 'image' - x1: number - y1: number - x2: number - y2: number - points?: number[] - label: string -} - -export type Primitive = - | PolyPrim - | BulgePrim - | CirclePrim - | ArcPrim - | EllipsePrim - | SplinePrim - | TextPrim - | HatchPrim - | PointPrim - | ImagePrim - -export interface SpaceGrid { - minX: number - minY: number - cellW: number - cellH: number - cols: number - rows: number - cells: number[][] - big: number[] -} - -export interface CadSpace { - id: string - name: string - kind: 'model' | 'layout' - blockHandle: string - entities: DwgEntity[] - entityCount: number - prims: Primitive[] - grid?: SpaceGrid - bbox: BBox - fitBBox?: BBox - savedView?: { cx: number; cy: number; width: number; height: number } - viewports: DwgViewportEntity[] - layout?: DwgLayoutObject -} - -export interface CadDocument { - db: DwgDatabase - fileName: string - fileSize: number - layers: LayerInfo[] - layerMap: Map - blocks: Map - blockByHandle: Map - dashPatterns: Map - imageNames: Map - spaces: CadSpace[] - model: CadSpace - units: string - unitLabel: string - version: string - unknownCount: number - warnings: string[] -} - -export type BlockRecord = DwgBlockRecordTableEntry diff --git a/src/core/app.ts b/src/core/app.ts index 820c98f..1b4e649 100644 --- a/src/core/app.ts +++ b/src/core/app.ts @@ -68,11 +68,6 @@ export function initApp(): void { bindGlobalEvents() void refreshRecent() handleLaunchQueue() - window.setTimeout(() => { - void import('../cad/loader') - .then((module) => module.preloadDwgEngine()) - .catch(() => {}) - }, 300) ;(window as unknown as { __phoneViewerBack?: () => boolean }).__phoneViewerBack = () => { const modalClose = document.querySelector('.modal-backdrop.show .modal-close') if (modalClose) { @@ -286,14 +281,16 @@ async function openFile(file: File): Promise { let createdViewer: ActiveViewer | null = null try { if (target === 'cad') { - const { loadDwg } = await import('../cad/loader') - const doc = await loadDwg(file, (message) => host?.setLoaderMessage(message, file.name)) - if (token !== openToken) return - const { CadViewer } = await import('../cad/cadViewer') - createdViewer = new CadViewer(host!, doc, { onBack: () => closeActiveViewer() }) - activeViewer = createdViewer + const { FlyfishCadViewer } = await import('../cad/flyfishViewer') + const viewer = new FlyfishCadViewer(host!, file, { onBack: () => closeActiveViewer() }) + createdViewer = viewer + await viewer.load() + if (token !== openToken) { + viewer.destroy() + return + } + activeViewer = viewer host?.hideLoader() - if (doc.warnings.length) toast(doc.warnings[0], 'info', 5000) } else { const [{ loadMuPDF }, { DocViewer }] = await Promise.all([ import('../doc/mupdfLoader'), diff --git a/src/styles.css b/src/styles.css index fbd4f64..a52c78e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -2359,3 +2359,15 @@ a { display: block; pointer-events: none; } + +.flyfish-cad { + position: absolute; + inset: 0; + overflow: hidden; +} + +.flyfish-cad > div { + width: 100%; + height: 100%; +} + diff --git a/src/ui/about.ts b/src/ui/about.ts index 5b5d1ca..6aa78e1 100644 --- a/src/ui/about.ts +++ b/src/ui/about.ts @@ -1,7 +1,7 @@ import { h } from '../core/dom' import { openModal } from './modal' -const VERSION = '0.4.1' +const VERSION = '0.5.0' export function showAbout(): void { const body = h( @@ -32,7 +32,7 @@ export function showAbout(): void { h( 'ul', { class: 'about-list' }, - h('li', {}, 'DWG 解析:@node-projects/acad-ts — MIT'), + h('li', {}, 'DWG 渲染:@flyfish-dev/cad-viewer — AGPL-3.0(含 LibreDWG WebAssembly,GPL-3.0)'), h('li', {}, '文档渲染:MuPDF.js(SumatraPDF 同源渲染内核家族)— AGPL-3.0'), h('li', {}, '本应用以 AGPL-3.0 发布;完整许可文本见项目 LICENSE 文件。') ), diff --git a/src/ui/guide.ts b/src/ui/guide.ts index 3d9e77f..67c725d 100644 --- a/src/ui/guide.ts +++ b/src/ui/guide.ts @@ -35,13 +35,11 @@ export function showGuide(): void { section( 'DWG 图纸', list( - '顶部 Tab 切换模型空间和各个布局空间。', + '采用 WebGL 常驻渲染,支持块展开、图层、文字、填充、线型、样条和标注。', '右侧图层按钮:按图层显示/隐藏,支持全部显示、全部隐藏、恢复默认。', - '尺子按钮:点两点测量距离;清除按钮清空当前空间的测量线。', - '立方体按钮:显示/隐藏第三方代理对象,默认显示。', - '适配按钮:一键显示全图;背景按钮切换深色/浅色图纸背景。', - '保存按钮:把当前视图导出为 PNG。', - '布局空间会按纸张和视口显示,模型内容自动裁剪到视口内。' + '适配按钮:自动聚焦有意义的图形内容;放大/缩小按钮调整视图。', + '背景按钮切换深色/浅色图纸背景;保存按钮把当前视图导出为 PNG。', + '支持 DWG 2006–2026 常用版本;代理对象没有图形数据时不会显示。' ) ), section( diff --git a/vite.config.ts b/vite.config.ts index 1388abb..df2d833 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ includeAssets: ['icons/favicon.svg', 'icons/apple-touch-icon.png'], manifest: { name: '万能看图 · DWG/PDF 浏览', - short_name: '涓囪兘鐪嬪浘', + short_name: '万能看图', description: '手机与平板上的 DWG 图纸浏览器,支持模型空间与布局空间、图层控制、测量,以及 PDF / EPUB / XPS / CBZ / 图片阅读。', lang: 'zh-CN', @@ -55,20 +55,28 @@ export default defineConfig({ ] }, workbox: { - globPatterns: ['**/*.{js,css,html,svg,png,ico,woff2}'], + globPatterns: ['**/*.{js,css,html,svg,png,ico,woff2,wasm}'], globIgnores: ['**/vendor/**', '**/samples/**'], - maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, + maximumFileSizeToCacheInBytes: 12 * 1024 * 1024, navigateFallback: 'index.html', cleanupOutdatedCaches: true, runtimeCaching: [ { - urlPattern: /\/vendor\/(?:libredwg|mupdf)\//, + urlPattern: /\/vendor\/mupdf\//, handler: 'CacheFirst', options: { cacheName: 'viewer-engines-v1', expiration: { maxEntries: 12, maxAgeSeconds: 60 * 60 * 24 * 365 } } }, + { + urlPattern: /\/wasm\//, + handler: 'CacheFirst', + options: { + cacheName: 'viewer-cad-wasm-v1', + expiration: { maxEntries: 8, maxAgeSeconds: 60 * 60 * 24 * 365 } + } + }, { urlPattern: /\/samples\//, handler: 'CacheFirst',