From a2e53277abed9d846391e528e2c212df0814dbe5 Mon Sep 17 00:00:00 2001 From: vovastelmashchuk Date: Sun, 5 Jan 2025 12:38:00 +0200 Subject: [PATCH] migrate to new library --- .gitmodules | 3 - libs/autocad-dxf | 1 - package-lock.json | 995 +++++++++++++++++++++++++++++- package.json | 2 +- server/api/project.post.js | 45 +- server/core/dxf/entityToPoints.js | 398 ++++++++---- server/core/dxf/parser.js | 91 ++- server/core/svg/generator.js | 7 +- 8 files changed, 1373 insertions(+), 169 deletions(-) delete mode 100644 .gitmodules delete mode 160000 libs/autocad-dxf diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 2ff979f..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "libs/autocad-dxf"] - path = libs/autocad-dxf - url = git@github.com:VovaStelmashchuk/autocad-dxf.git diff --git a/libs/autocad-dxf b/libs/autocad-dxf deleted file mode 160000 index a038df2..0000000 --- a/libs/autocad-dxf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a038df21fd1c2bc6875c961f9dae5c786d5b004f diff --git a/package-lock.json b/package-lock.json index fba1aff..b012ef6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "nuxt-app", "hasInstallScript": true, "dependencies": { + "@deepnest/dxf2svg-processor": "file:./libs/deepnest_dxf2svg-processor", "@nuxtjs/tailwindcss": "^6.12.2", "@pulsecron/pulse": "^1.6.7", "autocad-dxf": "file:./libs/autocad-dxf", @@ -31,6 +32,24 @@ "version": "1.4.1", "license": "MIT" }, + "libs/deepnest_dxf2svg-processor": { + "name": "@deepnest/dxf2svg-processor", + "version": "0.1.0", + "funding": [ + { + "type": "patreon", + "url": "https://patreon.com/deepnest_next" + } + ], + "license": "MIT", + "devDependencies": { + "@napi-rs/cli": "^2.18.4", + "ava": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -545,6 +564,10 @@ "node": ">=10.0.0" } }, + "node_modules/@deepnest/dxf2svg-processor": { + "resolved": "libs/deepnest_dxf2svg-processor", + "link": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.24.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", @@ -1259,6 +1282,23 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@napi-rs/cli": { + "version": "2.18.4", + "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", + "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", + "dev": true, + "license": "MIT", + "bin": { + "napi": "scripts/index.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, "node_modules/@netlify/functions": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/@netlify/functions/-/functions-2.8.2.tgz", @@ -3022,6 +3062,19 @@ "acorn": "^8" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", @@ -3223,6 +3276,39 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ast-kit": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-1.3.2.tgz", @@ -3311,6 +3397,153 @@ "postcss": "^8.1.0" } }, + "node_modules/ava": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.2.0.tgz", + "integrity": "sha512-+GZk5PbyepjiO/68hzCZCUepQOQauKfNnI7sA4JukBTg97jD7E+tDKEA7OhGOGr6EorNNMM9+jqvgHVOTOzG4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vercel/nft": "^0.27.5", + "acorn": "^8.13.0", + "acorn-walk": "^8.3.4", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.2.0", + "cbor": "^9.0.2", + "chalk": "^5.3.0", + "chunkd": "^2.0.1", + "ci-info": "^4.0.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.3.7", + "emittery": "^1.0.3", + "figures": "^6.1.0", + "globby": "^14.0.2", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.0.0", + "ms": "^2.1.3", + "p-map": "^7.0.2", + "package-config": "^5.0.0", + "picomatch": "^4.0.2", + "plur": "^5.1.0", + "pretty-ms": "^9.1.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^6.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^22 || >=23" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "node_modules/ava/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ava/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ava/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ava/node_modules/ci-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz", + "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ava/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ava/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/b4a": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", @@ -3380,6 +3613,13 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true, + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -3603,6 +3843,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -3644,6 +3897,19 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3702,6 +3968,13 @@ "node": ">=10" } }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -3718,6 +3991,13 @@ "node": ">=8" } }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true, + "license": "MIT" + }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -3727,6 +4007,77 @@ "consola": "^3.2.3" } }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/clipboardy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", @@ -3833,6 +4184,19 @@ "node": ">= 0.12.0" } }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3872,6 +4236,13 @@ "node": ">= 10" } }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -3918,6 +4289,26 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, "node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", @@ -3960,6 +4351,16 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/cookie-es": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", @@ -4273,6 +4674,32 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/date.js": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz", @@ -4617,6 +5044,19 @@ "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", "license": "ISC" }, + "node_modules/emittery": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.3.tgz", + "integrity": "sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4778,6 +5218,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -4787,6 +5241,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -4855,6 +5319,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -4895,6 +5366,22 @@ "reusify": "^1.0.4" } }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -4913,6 +5400,19 @@ "node": ">=8" } }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/find-yarn-workspace-root": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", @@ -5070,6 +5570,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", @@ -5530,6 +6043,16 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, "node_modules/image-meta": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/image-meta/-/image-meta-0.2.1.tgz", @@ -5562,6 +6085,29 @@ "node": ">=14.0.0" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/index-to-position": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", @@ -5633,6 +6179,16 @@ "url": "https://github.com/sponsors/brc-dd" } }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -5781,6 +6337,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", @@ -5811,6 +6384,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-what": { "version": "4.1.16", "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", @@ -5898,6 +6484,16 @@ "node": ">=0.10.0" } }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6313,6 +6909,19 @@ "listhen": "bin/listhen.mjs" } }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/local-pkg": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", @@ -6409,6 +7018,22 @@ "source-map-js": "^1.2.0" } }, + "node_modules/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6419,6 +7044,19 @@ "node": ">= 0.4" } }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "license": "MIT", + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -6431,7 +7069,23 @@ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.6" + } + }, + "node_modules/memoize": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.0.0.tgz", + "integrity": "sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" } }, "node_modules/memory-pager": { @@ -6525,6 +7179,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6959,6 +7626,16 @@ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "license": "MIT" }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.19" + } + }, "node_modules/nopt": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.0.0.tgz", @@ -7399,6 +8076,36 @@ "node": ">=0.10.0" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -7447,6 +8154,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-path": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", @@ -7718,6 +8438,22 @@ "pathe": "^1.1.2" } }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -8435,6 +9171,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -8682,6 +9434,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -9046,6 +9811,35 @@ "node": ">=4" } }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -9189,6 +9983,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -9250,6 +10087,36 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -9496,6 +10363,75 @@ "node": ">=16" } }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/supertap/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/supertap/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/supertap/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/supertap/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/supports-color": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", @@ -9816,6 +10752,16 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, "node_modules/terser": { "version": "5.37.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", @@ -9870,6 +10816,16 @@ "node": ">=0.8" } }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -11426,6 +12382,16 @@ "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "license": "MIT" }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6" + } + }, "node_modules/whatwg-url": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", @@ -11495,6 +12461,33 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/write-file-atomic": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", diff --git a/package.json b/package.json index b037acb..f645f61 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "vite": "^6.0.6", "vue": "latest", "vue-router": "latest", - "autocad-dxf": "file:./libs/autocad-dxf" + "@deepnest/dxf2svg-processor": "file:./libs/deepnest_dxf2svg-processor" }, "devDependencies": { "patch-package": "^8.0.0", diff --git a/server/api/project.post.js b/server/api/project.post.js index c40d221..991c02f 100644 --- a/server/api/project.post.js +++ b/server/api/project.post.js @@ -3,35 +3,52 @@ import { connectDB } from "~~/server/db/mongo"; import { getDxfArray } from "~~/server/utils/multipart"; import { generateSvg } from "../core/svg/generator"; import { generateRandomString } from "../utils"; +import { dxf2Json } from "~~/libs/deepnest_dxf2svg-processor"; export default defineEventHandler(async (event) => { + const db = await connectDB(); try { const fields = await readMultipartFormData(event); const dxfFileFields = fields.filter((field) => field.name === "dxf"); + const dxfArray = getDxfArray(dxfFileFields); + + const slug = `slug-${generateRandomString(6)}`; + + const insertResult = await db.collection("projects_v2").insertOne({ + slug: slug, + dxf: dxfArray, + uploadedAt: new Date(), + }); + + const dxfRecords = dxfArray.map(function (dxf) { + const dxfAsObjectStr = dxf2Json({ + stringData: dxf.data, + }); + const dxfAsObject = JSON.parse(dxfAsObjectStr); - const dxfRecords = getDxfArray(dxfFileFields).map(function (dxf) { - const svgResult = generateSvg(dxf.data); return { slug: `${dxf.filename}-${generateRandomString(6)}`, name: dxf.filename, data: dxf.data, - svg: svgResult.svg, - generateSvgError: svgResult.error, + asObject: dxfAsObject, }; }); - const db = await connectDB(); - const insertResult = await db.collection("projects_v2").insertOne({ - dxf: dxfRecords, - uploadedAt: new Date(), - }); + db.collection("projects_v2").updateOne( + { _id: insertResult.insertedId }, + { $set: { dxf: dxfRecords } } + ); - const id = insertResult.insertedId.toString(); + dxfRecords.forEach((dxfRecord) => { + const svgResult = generateSvg(dxfRecord.asObject); + dxfRecord.svg = svgResult.svg; + dxfRecord.generateSvgError = svgResult.error; - const slug = `slug-${id}`; - await db - .collection("projects_v2") - .updateOne({ _id: insertResult.insertedId }, { $set: { slug: slug } }); + db.collection("projects_v2").updateOne( + { _id: insertResult.insertedId }, + { $set: { dxf: dxfRecords } } + ); + }); return { message: "Project saved", diff --git a/server/core/dxf/entityToPoints.js b/server/core/dxf/entityToPoints.js index 2726cf5..04eee3c 100644 --- a/server/core/dxf/entityToPoints.js +++ b/server/core/dxf/entityToPoints.js @@ -1,74 +1,90 @@ /** - * @param {object} entity - A parsed DXF entity (from autocad-dxf library). - * @param {object} dxfObj - An instance of Entities (so we can call e.g. res.nurbs(spline)). + * @param {object} entity - A parsed DXF entity (from the new library). + * @param {object} dxfObj - An instance of Entities (so we can call methods like e.g. res.nurbs(spline)). * @param {number} tolerance - The maximum chord length for arcs/circles/ellipses. * @returns {Array<{x:number, y:number}>} Approximate list of (x, y) points. */ -export function entityToPoints(entity, dxfObj, tolerance = 1.0) { - if (!entity || !entity.etype) return []; +export function entityToPoints(entity, tolerance) { + if (!entity || !entity.specific) return []; - const etype = entity.etype.toLowerCase(); + // Determine the entity type by examining the keys in 'specific' + const specificKeys = Object.keys(entity.specific); + if (specificKeys.length === 0) return []; + + const etype = specificKeys[0].toLowerCase(); // Assuming one key per entity switch (etype) { case "line": - return lineToPoints(entity); + return lineToPoints(entity.specific.Line); case "lwpolyline": case "polyline": - return polylineToPoints(entity); + return polylineToPoints( + entity.specific.LwPolyline || entity.specific.Polyline + ); case "circle": - return circleToPoints(entity, tolerance); + return circleToPoints(entity.specific.Circle, tolerance); case "arc": - return arcToPoints(entity, tolerance); + return arcToPoints(entity.specific.Arc, tolerance); case "ellipse": - return ellipseToPoints(entity, tolerance); + return ellipseToPoints(entity.specific.Ellipse, tolerance); case "spline": - return splineToPoints(entity, dxfObj, tolerance); + return splineToPoints(entity.specific.Spline, tolerance); default: + console.warn(`Unsupported entity type: ${etype}`); return []; } } -function lineToPoints(entity) { +function lineToPoints(lineEntity) { return [ - { x: entity.start_x, y: entity.start_y }, - { x: entity.end_x, y: entity.end_y }, + { x: lineEntity.p1.x, y: lineEntity.p1.y }, + { x: lineEntity.p2.x, y: lineEntity.p2.y }, ]; } -function polylineToPoints(entity) { - return entity.vertices || []; +function polylineToPoints(polylineEntity) { + if (!polylineEntity.vertices || !Array.isArray(polylineEntity.vertices)) { + console.warn("Polyline entity has no vertices."); + return []; + } + + // Convert vertices to {x, y} format + return polylineEntity.vertices.map((vertex) => ({ + x: vertex.x, + y: vertex.y, + })); } -function circleToPoints(entity, tolerance) { - const centerX = entity.x; - const centerY = entity.y; - const r = entity.radius; +function circleToPoints(circleEntity, tolerance) { + const centerX = circleEntity.center.x; + const centerY = circleEntity.center.y; + const r = circleEntity.radius; if (r <= 0) return []; - // We'll approximate it as a single arc from angle=0 to 2π + // Approximate the circle as an arc from 0 to 2π return approximateArc2D(centerX, centerY, r, 0, 2 * Math.PI, tolerance); } -function arcToPoints(entity, tolerance) { - const centerX = entity.x; - const centerY = entity.y; - const r = entity.radius; +function arcToPoints(arcEntity, tolerance) { + const centerX = arcEntity.center.x; + const centerY = arcEntity.center.y; + const r = arcEntity.radius; if (r <= 0) return []; - // DXF angles are in degrees -> convert to radians - let startAngle = degToRad(entity.start_angle); - let endAngle = degToRad(entity.end_angle); + // DXF angles are typically in degrees; convert to radians + let startAngle = degToRad(arcEntity.start_angle); + let endAngle = degToRad(arcEntity.end_angle); - // arcs can wrap "backwards" if end < start. - let totalAngle = endAngle - startAngle; - if (totalAngle < 0) { - totalAngle += 2 * Math.PI; // handle negative, "backwards" arcs + // Handle negative or backward sweeps + let sweep = endAngle - startAngle; + if (sweep <= 0) { + sweep += 2 * Math.PI; } return approximateArc2D( @@ -76,72 +92,255 @@ function arcToPoints(entity, tolerance) { centerY, r, startAngle, - startAngle + totalAngle, + startAngle + sweep, tolerance ); } -function ellipseToPoints(entity, tolerance) { - // Typically a DXF ellipse might have: - // x, y, major_axis_ratio, axis_ratio, etc. - // Some also store start_angle, end_angle in radians already, - // plus a rotation. - // In practice, ellipse entities can be more complex. - // We'll do a basic approach. - - // If your entity has a custom key naming, - // adapt it accordingly. - // For example, some contain: - // center_x, center_y, major_axis, minor_axis, rotation, - // start_angle, end_angle - const cx = entity.x || 0; - const cy = entity.y || 0; - const majorR = entity.major_axis || 0; - const minorR = entity.minor_axis || 0; - const rotation = degToRad(entity.rotation || 0); - - // start/end angles in degrees or radians? - // Check if the library returns them in degrees or radians. - // We'll assume degrees for consistency with arcs: - let startAngle = degToRad(entity.start_angle || 0); - let endAngle = degToRad(entity.end_angle || 360); - - let totalAngle = endAngle - startAngle; - if (totalAngle < 0) totalAngle += 2 * Math.PI; +function ellipseToPoints(ellipseEntity, tolerance) { + const cx = ellipseEntity.center.x; + const cy = ellipseEntity.center.y; + const majorAxis = ellipseEntity.major_axis; // Assuming major_axis is a vector {x, y, z} + const minorAxisRatio = ellipseEntity.minor_axis_ratio; // Ratio of minor to major axis + const rotation = ellipseEntity.rotation + ? degToRad(ellipseEntity.rotation) + : 0; // Assuming rotation is in degrees + + const majorLength = Math.sqrt(majorAxis.x ** 2 + majorAxis.y ** 2); + const minorLength = majorLength * minorAxisRatio; + + // DXF might provide start and end angles; default to full ellipse if not specified + let startAngle = ellipseEntity.start_angle + ? degToRad(ellipseEntity.start_angle) + : 0; + let endAngle = ellipseEntity.end_angle + ? degToRad(ellipseEntity.end_angle) + : 2 * Math.PI; + + let sweep = endAngle - startAngle; + if (sweep <= 0) { + sweep += 2 * Math.PI; + } return approximateEllipse2D( cx, cy, - majorR, - minorR, + majorLength, + minorLength, rotation, startAngle, - startAngle + totalAngle, + startAngle + sweep, tolerance ); } -function splineToPoints(entity, dxfObj, tolerance) { - // Use the library to get NURBS data - const splineData = dxfObj.nurbs(entity); +/** + * Converts a NURBS spline entity to an array of (x, y) points using adaptive sampling based on tolerance. + * + * @param {object} splineEntity - A parsed DXF spline entity from the new library. + * @param {number} tolerance - The maximum allowed chord length between consecutive points. + * @returns {Array<{x: number, y: number}>} An array of points approximating the spline. + */ +export function splineToPoints(splineEntity, tolerance = 1.0) { + // Extract NURBS parameters from the spline entity + const degree = splineEntity.degree; + const controlPoints = splineEntity.control_points; // Array of {x, y, z} + const knotVector = splineEntity.knot_vector; // Array of numbers + const weights = splineEntity.weights; // Array of numbers (same length as controlPoints) + + // Validate the presence of necessary NURBS parameters + if ( + typeof degree !== "number" || + !Array.isArray(controlPoints) || + !Array.isArray(knotVector) || + !Array.isArray(weights) || + controlPoints.length !== weights.length + ) { + console.warn("Spline entity is missing required NURBS parameters."); + return []; + } - // `splineData` typically has an array of intervals - // and the polynomial coefficients for each interval. - // You would sample each interval from param=0..1 (or from knot to knot) - // subdividing until chord length < tolerance. + const numControlPoints = controlPoints.length; + const numKnots = knotVector.length; - // For brevity, let's just return the control points. - // Real usage: implement an adaptive approach. - const ctrlPoints = splineData.control_points; - if (!ctrlPoints) return []; + // Ensure the knot vector is valid for the given degree and number of control points + if (numKnots !== numControlPoints + degree + 1) { + console.warn( + "Invalid knot vector length for the given degree and number of control points." + ); + return []; + } - // Convert them to {x, y}. (Ignoring z for 2D.) - const points = ctrlPoints.map((pt) => ({ - x: pt.x || 0, - y: pt.y || 0, - })); + /** + * Evaluates the NURBS spline at a given parameter t using de Boor's algorithm. + * + * @param {number} t - The parameter value at which to evaluate the spline. + * @returns {{x: number, y: number, z: number}} The point on the spline at parameter t. + */ + function evaluateNURBS(t) { + // Find the knot span where t resides + const span = findKnotSpan(t, degree, knotVector, numControlPoints); + + // Compute the basis functions for the current span + const N = basisFunctions(span, t, degree, knotVector); + + // Initialize numerator and denominator for rational NURBS + let numerator = { x: 0, y: 0, z: 0 }; + let denominator = 0; + + // Accumulate the weighted control points + for (let i = 0; i <= degree; i++) { + const idx = span - degree + i; + const wN = weights[idx] * N[i]; + numerator.x += wN * controlPoints[idx].x; + numerator.y += wN * controlPoints[idx].y; + numerator.z += wN * controlPoints[idx].z; + denominator += wN; + } + + // Avoid division by zero + if (denominator === 0) { + console.warn("Denominator in NURBS evaluation is zero."); + return { x: 0, y: 0, z: 0 }; + } + + // Compute the final point by dividing the numerator by the denominator + return { + x: numerator.x / denominator, + y: numerator.y / denominator, + z: numerator.z / denominator, + }; + } - return points; + /** + * Finds the knot span index for a given parameter t. + * + * @param {number} t - The parameter value. + * @param {number} degree - The degree of the spline. + * @param {Array} knots - The knot vector. + * @param {number} numControlPoints - The number of control points. + * @returns {number} The knot span index. + */ + function findKnotSpan(t, degree, knots, numControlPoints) { + const n = numControlPoints - 1; + + // Special case when t is the last knot + if (t === knots[n + 1]) { + return n; + } + + // Binary search to find the correct knot span + let low = degree; + let high = n + 1; + let mid = Math.floor((low + high) / 2); + + while (t < knots[mid] || t >= knots[mid + 1]) { + if (t < knots[mid]) { + high = mid; + } else { + low = mid; + } + mid = Math.floor((low + high) / 2); + } + + return mid; + } + + /** + * Computes the non-zero basis functions for a given knot span and parameter t. + * + * @param {number} span - The knot span index. + * @param {number} t - The parameter value. + * @param {number} degree - The degree of the spline. + * @param {Array} knots - The knot vector. + * @returns {Array} The array of basis function values. + */ + function basisFunctions(span, t, degree, knots) { + const N = new Array(degree + 1).fill(0); + const left = new Array(degree + 1).fill(0); + const right = new Array(degree + 1).fill(0); + + N[0] = 1.0; + + for (let j = 1; j <= degree; j++) { + left[j] = t - knots[span + 1 - j]; + right[j] = knots[span + j] - t; + let saved = 0.0; + + for (let r = 0; r < j; r++) { + const temp = N[r] / (right[r + 1] + left[j - r]); + N[r] = saved + right[r + 1] * temp; + saved = left[j - r] * temp; + } + + N[j] = saved; + } + + return N; + } + + /** + * Recursively samples the spline between parameters t0 and t1. + * + * @param {number} t0 - The starting parameter. + * @param {number} t1 - The ending parameter. + * @param {{x: number, y: number, z: number}} p0 - The point at t0. + * @param {{x: number, y: number, z: number}} p1 - The point at t1. + */ + function sample(t0, t1, p0, p1) { + const tm = (t0 + t1) / 2; + const pm = evaluateNURBS(tm); + + // Compute the midpoint of the chord between p0 and p1 + const chordMid = { + x: (p0.x + p1.x) / 2, + y: (p0.y + p1.y) / 2, + z: (p0.z + p1.z) / 2, + }; + + // Calculate the deviation (distance) between the actual midpoint and chord midpoint + const dx = pm.x - chordMid.x; + const dy = pm.y - chordMid.y; + const dz = pm.z - chordMid.z; + const deviation = Math.sqrt(dx * dx + dy * dy + dz * dz); + + // If deviation exceeds tolerance, subdivide further + if (deviation > tolerance) { + sample(t0, tm, p0, pm); + sample(tm, t1, pm, p1); + } else { + // Accept the endpoint p1 + points.push({ x: p1.x, y: p1.y }); + } + } + + /** + * Approximates the spline by adaptive sampling. + * + * @returns {Array<{x: number, y: number}>} The array of sampled points. + */ + function approximateSpline() { + const points = []; + + // Define the valid parameter range based on the knot vector and degree + const tMin = knotVector[degree]; + const tMax = knotVector[knotVector.length - degree - 1]; + + // Evaluate the starting and ending points + const pStart = evaluateNURBS(tMin); + const pEnd = evaluateNURBS(tMax); + + // Initialize with the starting point + points.push({ x: pStart.x, y: pStart.y }); + + // Begin recursive sampling + sample(tMin, tMax, pStart, pEnd); + + return points; + } + + // Perform the approximation and return the points + return approximateSpline(); } /**************************************************** @@ -153,16 +352,10 @@ function approximateArc2D(cx, cy, radius, angleStart, angleEnd, tolerance) { if (radius <= 0) return points; let sweep = angleEnd - angleStart; - // ensure positive sweep - if (sweep < 0) { + if (sweep <= 0) { sweep += 2 * Math.PI; } - // If tolerance is too big or radius is too small, - // we might do just 2 points. But let's keep it robust: - // chord length = 2*r*sin(θ/2) <= tolerance - // => θ <= 2 * arcsin( tolerance/(2*r) ) - // We'll clamp so we don't exceed the sweep. const angleStep = getMaxAngleStep(radius, tolerance); const steps = Math.max(1, Math.ceil(sweep / angleStep)); @@ -194,35 +387,20 @@ function approximateEllipse2D( if (majorR <= 0 || minorR <= 0) return points; let sweep = angleEnd - angleStart; - if (sweep < 0) { + if (sweep <= 0) { sweep += 2 * Math.PI; } - // For an ellipse, the chord length depends on local curvature. - // We'll do a simpler approach: approximate an "effective radius" - // as the larger of (majorR, minorR), then do the same chord-based method. - // This is *not* perfect (the minor axis side might be oversampled or undersampled). - // If you need a more precise approach, consider the local curvature formula - // for ellipses. For typical usage, this is usually "good enough." - const maxRadius = Math.max(majorR, minorR); const angleStep = getMaxAngleStep(maxRadius, tolerance); const steps = Math.max(1, Math.ceil(sweep / angleStep)); - // param angle goes from angleStart..angleEnd - // Then for each angle 'α', we get a point: - // px = majorR*cos(α) - // py = minorR*sin(α) - // Then rotate by 'rotation' about center (cx, cy). - // So final coords: - // x = cx + (px*cos(rotation) - py*sin(rotation)) - // y = cy + (px*sin(rotation) + py*cos(rotation)) - for (let i = 0; i <= steps; i++) { - const α = angleStart + (sweep * i) / steps; - const px = majorR * Math.cos(α); - const py = minorR * Math.sin(α); + const alpha = angleStart + (sweep * i) / steps; + const px = majorR * Math.cos(alpha); + const py = minorR * Math.sin(alpha); + // Apply rotation const xr = px * Math.cos(rotation) - py * Math.sin(rotation); const yr = px * Math.sin(rotation) + py * Math.cos(rotation); @@ -246,13 +424,9 @@ function getMaxAngleStep(radius, tolerance) { // fallback: maybe 1 degree in radians return Math.PI / 180; } - // clamp the ratio to avoid domain errors const ratio = Math.min(1, tolerance / (2 * radius)); const theta = 2 * Math.asin(ratio); - // If ratio is small, theta in radians might be tiny - // We'll impose an upper bound, e.g., 30° in radians - // for extremely large tolerance - return Math.min(theta, (30 * Math.PI) / 180); + return Math.min(theta, (30 * Math.PI) / 180); // Cap at 30 degrees } /**************************************************** diff --git a/server/core/dxf/parser.js b/server/core/dxf/parser.js index bd3d1fb..2958566 100644 --- a/server/core/dxf/parser.js +++ b/server/core/dxf/parser.js @@ -1,30 +1,16 @@ -import Entities from "autocad-dxf"; import { entityToPoints } from "./entityToPoints.js"; /** - * @param {string} dxfString + * @param {Array} dxfEntities * @param {number} tolerance * * @returns {{closed: {polygon: {x: number, y: number}[], originEntities: any[]}[], open: {polygon: {x: number, y: number}[], originEntities: any[]}[]}} */ -export function parseAndCombine(dxfString, tolerance) { - const dxfObj = new Entities(dxfString, tolerance); - const rawEntities = dxfObj.entities || []; - - const lines = []; - rawEntities.forEach((ent) => { - if (ent.etype === "LINE") { - lines.push({ - start_x: ent.start_x, - start_y: ent.start_y, - end_x: ent.end_x, - end_y: ent.end_y, - }); - } - }); +export function parseAndCombine(dxfObject, tolerance) { + const dxfEntities = dxfObject["__entities"]; - const allProcessed = rawEntities.map((ent) => { - const points = entityToPoints(ent, dxfObj, tolerance); + const allProcessed = dxfEntities.map((ent) => { + const points = entityToPoints(ent, tolerance); const isClosed = isInherentlyClosed(ent) || isClosedPolygon(points, tolerance); return { @@ -69,34 +55,61 @@ export function parseAndCombine(dxfString, tolerance) { } /** - * Checks if an entity (based on its 'etype' or properties) is inherently closed: - * - circle - * - lwpolyline/polyline with `type==="Closed"` + * Determines if a DXF entity is inherently closed based on its type. + * Inherently closed entities include: + * - Circle + * - LwPolyline or Polyline with 'isClosed' property set to true + * + * @param {object} entity - A parsed DXF entity from the new library. + * @returns {boolean} - Returns true if the entity is inherently closed; otherwise, false. */ function isInherentlyClosed(entity) { - if (!entity || !entity.etype) return false; - const etype = entity.etype.toLowerCase(); + if (!entity || !entity.specific) return false; + + // Determine the entity type by examining the keys in 'specific' + const specificKeys = Object.keys(entity.specific); + if (specificKeys.length === 0) return false; - // Circles => definitely closed + const etype = specificKeys[0].toLowerCase(); // Assuming one key per entity + + // Check for inherently closed entity types if (etype === "circle") { return true; } - // "Closed" polylines - if ( - (etype === "polyline" || etype === "lwpolyline") && - entity.type === "Closed" - ) { - return true; + if (etype === "lwpolyline" || etype === "polyline") { + const polyline = entity.specific.LwPolyline || entity.specific.Polyline; + // Depending on the library, the property indicating closure might vary. + // Common possibilities: 'isClosed', 'closed', 'type === "Closed"' + // Adjust the property access based on your library's specification. + + // Example 1: Using 'isClosed' boolean property + if (polyline.isClosed === true) { + return true; + } + + // Example 2: Using 'type' property + if (polyline.type && polyline.type.toLowerCase() === "closed") { + return true; + } + + // Example 3: Using 'closed' boolean property + if (polyline.closed === true) { + return true; + } } return false; } /** - * Checks if a polygon is "closed" by verifying: - * - length >= 3 - * - first point is within 'tolerance' distance of last point + * Determines if a polygon is closed by verifying: + * - It has at least three points. + * - The distance between the first and last points is within the specified tolerance. + * + * @param {Array<{x: number, y: number}>} points - An array of points defining the polygon. + * @param {number} tolerance - The maximum allowed distance between the first and last points to consider the polygon closed. + * @returns {boolean} - Returns true if the polygon is closed; otherwise, false. */ function isClosedPolygon(points, tolerance) { if (!points || points.length < 3) return false; @@ -105,6 +118,16 @@ function isClosedPolygon(points, tolerance) { return distance(first, last) <= tolerance; } +/** + * Calculates the Euclidean distance between two points. + * + * Assumes that the function `distance` is already defined elsewhere in your codebase. + * + * @param {{x: number, y: number}} pointA - The first point. + * @param {{x: number, y: number}} pointB - The second point. + * @returns {number} - The distance between pointA and pointB. + */ + /** * Merge open polygons that share endpoints (within tolerance). * If a merge forms a closed loop, it goes into `newlyClosed`. diff --git a/server/core/svg/generator.js b/server/core/svg/generator.js index 9d0ba7d..aa5eb79 100644 --- a/server/core/svg/generator.js +++ b/server/core/svg/generator.js @@ -1,10 +1,11 @@ import { parseAndCombine } from "../dxf/parser"; + /** - * @param {string} dxfString + * @param {string} any * @returns {{svg: string, error: string}} */ -export function generateSvg(dxfString) { - const polygones = parseAndCombine(dxfString, 0.1); +export function generateSvg(dxfObject) { + const polygones = parseAndCombine(dxfObject, 0.1); const svg = createSVGFromPolygons(polygones.closed); let error = null;