Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7564d4abf | ||
|
|
14d1f06927 |
+9
-3
@@ -30,12 +30,18 @@ async function runE2EDataTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('\n4. Verifying Complex Linetype Math in Viewer2D:');
|
console.log('\n4. Verifying Complex Linetype Math in Viewer2D with DWG Embedded Data:');
|
||||||
const { getLinPattern } = require('./src/viewer2d/linParser.ts');
|
const { getLinPattern, mergeDwgLinetypePattern } = await import('file:///d:/MYCLAUDE_PROJECT/dwg-dxf-viewer-sample/src/viewer2d/linParser.ts');
|
||||||
const targetTypes = ['H-DICHL06', 'H-DICHL06R', 'H-DICHL02R', 'H-DICHL01R', 'H-DICHL05'];
|
const targetTypes = ['H-DICHL06', 'H-DICHL06R', 'H-DICHL02R', 'H-DICHL01R', 'H-DICHL05'];
|
||||||
|
|
||||||
|
const dwgLtMap = new Map();
|
||||||
|
for (const lt of lineTypes) {
|
||||||
|
if (lt.name && lt.pattern) dwgLtMap.set(lt.name.toUpperCase(), lt);
|
||||||
|
}
|
||||||
|
|
||||||
for (const name of targetTypes) {
|
for (const name of targetTypes) {
|
||||||
const pattern = getLinPattern(name);
|
const dwgLt = dwgLtMap.get(name);
|
||||||
|
const pattern = mergeDwgLinetypePattern(name, dwgLt?.pattern || [], dwgLt?.description);
|
||||||
if (!pattern) {
|
if (!pattern) {
|
||||||
console.error(` [FAIL] Pattern ${name} not found in registry!`);
|
console.error(` [FAIL] Pattern ${name} not found in registry!`);
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
+16
-2
@@ -203,14 +203,24 @@ document.getElementById('file')!.addEventListener('change', (ev) => {
|
|||||||
(ev.target as HTMLInputElement).value = '';
|
(ev.target as HTMLInputElement).value = '';
|
||||||
});
|
});
|
||||||
let dark = true;
|
let dark = true;
|
||||||
|
document.getElementById('fit')?.addEventListener('click', () => {
|
||||||
|
viewer.fit();
|
||||||
|
});
|
||||||
document.getElementById('theme')!.addEventListener('click', () => {
|
document.getElementById('theme')!.addEventListener('click', () => {
|
||||||
dark = !dark;
|
dark = !dark;
|
||||||
viewer.setTheme(dark);
|
viewer.setTheme(dark);
|
||||||
|
fillLayers();
|
||||||
});
|
});
|
||||||
document.getElementById('layersBtn')!.addEventListener('click', () => {
|
document.getElementById('layersBtn')!.addEventListener('click', () => {
|
||||||
layersPanel.style.display = layersPanel.style.display === 'block' ? 'none' : 'block';
|
layersPanel.style.display = layersPanel.style.display === 'block' ? 'none' : 'block';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if ((e.key === 'f' || e.key === 'F') && (e.target as HTMLElement)?.tagName !== 'INPUT') {
|
||||||
|
viewer.fit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener('dragover', (e) => e.preventDefault());
|
window.addEventListener('dragover', (e) => e.preventDefault());
|
||||||
window.addEventListener('drop', (e) => {
|
window.addEventListener('drop', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -219,7 +229,11 @@ window.addEventListener('drop', (e) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const model = new URLSearchParams(location.search).get('model');
|
const model = new URLSearchParams(location.search).get('model');
|
||||||
if (model) void run(model, () => loadUrl(model));
|
if (model) {
|
||||||
else void run('C0060202-001-평면및종단면도.dwg', () => loadUrl(civilFile, 'C0060202-001-평면및종단면도.dwg'));
|
void run(model, () => loadUrl(model));
|
||||||
|
} else {
|
||||||
|
setStatus('ready');
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', () => viewer.resize());
|
window.addEventListener('resize', () => viewer.resize());
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -5,7 +5,7 @@ import type { CadSpace } from './cadSpaces';
|
|||||||
export class Viewer2D {
|
export class Viewer2D {
|
||||||
constructor(container: HTMLElement);
|
constructor(container: HTMLElement);
|
||||||
/** Render a DWG/DXF parseResult. `keepView` preserves pan/zoom on re-render. */
|
/** Render a DWG/DXF parseResult. `keepView` preserves pan/zoom on re-render. */
|
||||||
load(result: unknown, opts?: { keepView?: boolean; spaceHandle?: string | number | null }): void;
|
load(result: unknown, opts?: { keepView?: boolean; keepTheme?: boolean; spaceHandle?: string | number | null }): void;
|
||||||
/** Zoom-extents to the loaded drawing. */
|
/** Zoom-extents to the loaded drawing. */
|
||||||
fit(): void;
|
fit(): void;
|
||||||
/** Resync renderer/camera to the container size (call after un-hiding). */
|
/** Resync renderer/camera to the container size (call after un-hiding). */
|
||||||
|
|||||||
+69
-36
@@ -11,7 +11,7 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|||||||
import { aciToHex } from './aciColors.js';
|
import { aciToHex } from './aciColors.js';
|
||||||
import { buildAllowedOwnerHexes, listCadSpaces } from './cadSpaces';
|
import { buildAllowedOwnerHexes, listCadSpaces } from './cadSpaces';
|
||||||
import { SlugTextEngine, SlugTextBatch } from './slugText';
|
import { SlugTextEngine, SlugTextBatch } from './slugText';
|
||||||
import { getLinPattern } from './linParser';
|
import { getLinPattern, mergeDwgLinetypePattern } from './linParser';
|
||||||
|
|
||||||
const DEFAULT_COLOR = 0xc9d1d9;
|
const DEFAULT_COLOR = 0xc9d1d9;
|
||||||
const ARC_SEGS = 64;
|
const ARC_SEGS = 64;
|
||||||
@@ -50,6 +50,8 @@ export class Viewer2D {
|
|||||||
|
|
||||||
this._scene = new THREE.Scene();
|
this._scene = new THREE.Scene();
|
||||||
this._scene.background = new THREE.Color(0x0d1117);
|
this._scene.background = new THREE.Color(0x0d1117);
|
||||||
|
this._isDark = true;
|
||||||
|
this._isLoading = false;
|
||||||
|
|
||||||
const w = container.clientWidth || 800;
|
const w = container.clientWidth || 800;
|
||||||
const h = container.clientHeight || 600;
|
const h = container.clientHeight || 600;
|
||||||
@@ -243,10 +245,29 @@ export class Viewer2D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
load(result, opts = {}) {
|
load(result, opts = {}) {
|
||||||
|
this._isLoading = true;
|
||||||
|
try {
|
||||||
this._lastResult = result;
|
this._lastResult = result;
|
||||||
this._selMeta = null;
|
this._selMeta = null;
|
||||||
this._clear();
|
this._clear();
|
||||||
this._entityMeta = [];
|
this._entityMeta = [];
|
||||||
|
|
||||||
|
// Model vs Paper (Layout): never draw both spaces at once.
|
||||||
|
// - opts.spaceHandle provided (including null) → set active space
|
||||||
|
// - omitted → keep previous _activeSpaceHex if still valid for this file
|
||||||
|
if (Object.prototype.hasOwnProperty.call(opts, 'spaceHandle')) {
|
||||||
|
const sh = opts.spaceHandle;
|
||||||
|
if (sh == null || sh === '') this._activeSpaceHex = null;
|
||||||
|
else this._activeSpaceHex = typeof sh === 'number' ? sh.toString(16) : String(sh).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts.keepTheme) {
|
||||||
|
const spacesList = listCadSpaces(result);
|
||||||
|
const activeSp = spacesList.find((s) => s.handleHex === this._activeSpaceHex);
|
||||||
|
const isPaper = activeSp?.kind === 'paper';
|
||||||
|
this.setTheme(!isPaper);
|
||||||
|
}
|
||||||
|
|
||||||
this._buildLayerMap(result?.tables?.layers);
|
this._buildLayerMap(result?.tables?.layers);
|
||||||
this._buildStyleMap(result?.tables?.styles || result?.tables?.textStyles);
|
this._buildStyleMap(result?.tables?.styles || result?.tables?.textStyles);
|
||||||
this._buildLinetypeMap(result);
|
this._buildLinetypeMap(result);
|
||||||
@@ -265,22 +286,6 @@ export class Viewer2D {
|
|||||||
this._pickFills = [];
|
this._pickFills = [];
|
||||||
this._pickCurIdx = -1;
|
this._pickCurIdx = -1;
|
||||||
|
|
||||||
// Model vs Paper (Layout): never draw both spaces at once.
|
|
||||||
// - opts.spaceHandle provided (including null) → set active space
|
|
||||||
// - omitted → keep previous _activeSpaceHex if still valid for this file
|
|
||||||
if (Object.prototype.hasOwnProperty.call(opts, 'spaceHandle')) {
|
|
||||||
const sh = opts.spaceHandle;
|
|
||||||
if (sh == null || sh === '') this._activeSpaceHex = null;
|
|
||||||
else this._activeSpaceHex = typeof sh === 'number' ? sh.toString(16) : String(sh).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!opts.keepTheme) {
|
|
||||||
const spacesList = listCadSpaces(result);
|
|
||||||
const activeSp = spacesList.find((s) => s.handleHex === this._activeSpaceHex);
|
|
||||||
const isPaper = activeSp?.kind === 'paper';
|
|
||||||
this.setTheme(!isPaper);
|
|
||||||
}
|
|
||||||
|
|
||||||
const entities = result?.entities || [];
|
const entities = result?.entities || [];
|
||||||
const lineVerts = [];
|
const lineVerts = [];
|
||||||
const lineColors = [];
|
const lineColors = [];
|
||||||
@@ -1049,14 +1054,24 @@ export class Viewer2D {
|
|||||||
const fitBox = box.isEmpty() ? null : (this._trimmedContentBox(fitSamples, box) || box);
|
const fitBox = box.isEmpty() ? null : (this._trimmedContentBox(fitSamples, box) || box);
|
||||||
this._contentBox = fitBox ? fitBox.clone() : null;
|
this._contentBox = fitBox ? fitBox.clone() : null;
|
||||||
if (!opts.keepView && fitBox && !fitBox.isEmpty()) this._fit(fitBox);
|
if (!opts.keepView && fitBox && !fitBox.isEmpty()) this._fit(fitBox);
|
||||||
|
} finally {
|
||||||
|
this._isLoading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── UI integration ─────────────────────────────────────────────────────────
|
// ── UI integration ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Swap canvas background for theme. dark=true → near-black (Model), false → white (Layout). */
|
/** Swap canvas background for theme. dark=true → near-black (Model), false → white (Layout). */
|
||||||
setTheme(dark) {
|
setTheme(dark) {
|
||||||
this._scene.background = new THREE.Color(dark ? 0x0a0b0d : 0xffffff);
|
const isDarkBool = !!dark;
|
||||||
|
const changed = (this._isDark !== isDarkBool);
|
||||||
|
this._isDark = isDarkBool;
|
||||||
|
this._scene.background = new THREE.Color(this._isDark ? 0x0a0b0d : 0xffffff);
|
||||||
if (this._gridVisible) this._rebuildGrid();
|
if (this._gridVisible) this._rebuildGrid();
|
||||||
|
|
||||||
|
if (changed && this._lastResult && !this._isLoading) {
|
||||||
|
this.load(this._lastResult, { keepView: true, keepTheme: true, spaceHandle: this._activeSpaceHex });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setGrid(visible) {
|
setGrid(visible) {
|
||||||
@@ -1547,7 +1562,7 @@ export class Viewer2D {
|
|||||||
const aci = Math.abs(l.colorIndex ?? l.color ?? l.colorNumber ?? 7);
|
const aci = Math.abs(l.colorIndex ?? l.color ?? l.colorNumber ?? 7);
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
colorHex: '#' + ((aciToHex(aci) ?? DEFAULT_COLOR).toString(16).padStart(6, '0')),
|
colorHex: '#' + ((aciToHex(aci, this._isDark) ?? DEFAULT_COLOR).toString(16).padStart(6, '0')),
|
||||||
count: counts.get(name) || 0,
|
count: counts.get(name) || 0,
|
||||||
visible: !hidden.has(name),
|
visible: !hidden.has(name),
|
||||||
};
|
};
|
||||||
@@ -1566,7 +1581,8 @@ export class Viewer2D {
|
|||||||
}
|
}
|
||||||
if (!ltName || ltName === 'Continuous' || ltName === 'ByLayer' || ltName === 'ByBlock') return null;
|
if (!ltName || ltName === 'Continuous' || ltName === 'ByLayer' || ltName === 'ByBlock') return null;
|
||||||
|
|
||||||
const pattern = getLinPattern(ltName);
|
const upperKey = ltName.toUpperCase().trim();
|
||||||
|
const pattern = this._dwgLinPatterns?.get(upperKey) || getLinPattern(ltName);
|
||||||
if (!pattern) return null;
|
if (!pattern) return null;
|
||||||
|
|
||||||
const entScale = (e.entityHeader?.linetypeScale > 0 ? e.entityHeader.linetypeScale : 1);
|
const entScale = (e.entityHeader?.linetypeScale > 0 ? e.entityHeader.linetypeScale : 1);
|
||||||
@@ -1750,10 +1766,11 @@ export class Viewer2D {
|
|||||||
} else if (elem.type === 'text') {
|
} else if (elem.type === 'text') {
|
||||||
const pState = getPathState(s);
|
const pState = getPathState(s);
|
||||||
if (elem.text && pendingTexts) {
|
if (elem.text && pendingTexts) {
|
||||||
// 1. Text Height: Exact text style DWG table height if fixed, else linetype elem.scale
|
// 1. Data-driven Text Height: (fixedH * elem.scale) or (elem.scale * 0.5) to match arrowhead scale ratio
|
||||||
const sName = (elem.style || 'STANDARD').toUpperCase();
|
const sName = (elem.style || 'STANDARD').toUpperCase();
|
||||||
const fixedH = this._textStyleHeightMap?.get(sName) || 0;
|
const fixedH = this._textStyleHeightMap?.get(sName) || 0;
|
||||||
const baseH = fixedH > 0 ? fixedH : (elem.scale || 0.2);
|
const sFactor = (elem.scale > 0 ? elem.scale * 0.5 : 0.1);
|
||||||
|
const baseH = fixedH > 0 ? (fixedH * sFactor) : sFactor;
|
||||||
const h = baseH * ltscale;
|
const h = baseH * ltscale;
|
||||||
|
|
||||||
let angle = pState.angle + ((elem.rotation || 0) * Math.PI) / 180;
|
let angle = pState.angle + ((elem.rotation || 0) * Math.PI) / 180;
|
||||||
@@ -1771,18 +1788,24 @@ export class Viewer2D {
|
|||||||
const sinT = Math.sin(angle);
|
const sinT = Math.sin(angle);
|
||||||
|
|
||||||
const xo = (elem.xOffset || 0) * ltscale;
|
const xo = (elem.xOffset || 0) * ltscale;
|
||||||
// alignV: 2 (middle) centers text quad on origin; set yo=0 so middle lies directly on grey arrow line axis
|
// Vertical mid alignment: set yo = 0 so text middle lies directly on line center axis
|
||||||
const yo = 0;
|
const yo = 0;
|
||||||
|
|
||||||
// 2. Exact Visual Midpoint: position text at exact midpoint of empty text gap (between arrowhead tip and next dash)
|
// 2. Exact Visual Midpoint: determine primary text gap (previous vs next gap)
|
||||||
let gapMidOffset = 0;
|
const prevElem = elements[(elemIdx - 2 + elements.length) % elements.length];
|
||||||
const nextElem = elements[elemIdx % elements.length];
|
const nextElem = elements[elemIdx % elements.length];
|
||||||
if (nextElem && nextElem.type === 'gap') {
|
|
||||||
const gapLen = Math.abs(nextElem.val || 0) * ltscale;
|
|
||||||
gapMidOffset = gapLen / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const midState = gapMidOffset > 1e-6 ? getPathState(s + gapMidOffset) : pState;
|
const prevGapLen = (prevElem && prevElem.type === 'gap') ? Math.abs(prevElem.val || 0) * ltscale : 0;
|
||||||
|
const nextGapLen = (nextElem && nextElem.type === 'gap') ? Math.abs(nextElem.val || 0) * ltscale : 0;
|
||||||
|
|
||||||
|
let midState = pState;
|
||||||
|
if (prevGapLen > nextGapLen && prevGapLen > 1e-6) {
|
||||||
|
// Text belongs inside previous gap (e.g. H-DICHL06R reverse mode S2 = -0.99)
|
||||||
|
midState = getPathState(s - prevGapLen / 2);
|
||||||
|
} else if (nextGapLen > 1e-6) {
|
||||||
|
// Text belongs inside next gap (e.g. H-DICHL06 forward mode S3 = -0.74)
|
||||||
|
midState = getPathState(s + nextGapLen / 2);
|
||||||
|
}
|
||||||
|
|
||||||
const tx = midState.x + (xo * cosT - yo * sinT);
|
const tx = midState.x + (xo * cosT - yo * sinT);
|
||||||
const ty = midState.y + (xo * sinT + yo * cosT);
|
const ty = midState.y + (xo * sinT + yo * cosT);
|
||||||
@@ -1793,8 +1816,8 @@ export class Viewer2D {
|
|||||||
height: Math.max(h, 0.01),
|
height: Math.max(h, 0.01),
|
||||||
rotation: angle,
|
rotation: angle,
|
||||||
color,
|
color,
|
||||||
alignH: 1, // center horizontally in gap between arrowheads
|
alignH: 1, // center horizontally in gap
|
||||||
alignV: 2, // center vertically on line axis
|
alignV: 2, // center vertically on line axis (vertical mid)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1906,7 +1929,7 @@ export class Viewer2D {
|
|||||||
const handle = l.handle?.value ?? l.handle;
|
const handle = l.handle?.value ?? l.handle;
|
||||||
const name = l.name ?? l.layerName;
|
const name = l.name ?? l.layerName;
|
||||||
const aci = Math.abs(l.colorIndex ?? l.color ?? l.colorNumber ?? 7);
|
const aci = Math.abs(l.colorIndex ?? l.color ?? l.colorNumber ?? 7);
|
||||||
const hex = aciToHex(aci) ?? DEFAULT_COLOR;
|
const hex = aciToHex(aci, this._isDark) ?? DEFAULT_COLOR;
|
||||||
if (handle != null) this._layerByHandle.set(String(handle), hex);
|
if (handle != null) this._layerByHandle.set(String(handle), hex);
|
||||||
if (name) this._layerByName.set(name, hex);
|
if (name) this._layerByName.set(name, hex);
|
||||||
if (handle != null && name) this._layerNameByHandle.set(String(handle), name);
|
if (handle != null && name) this._layerNameByHandle.set(String(handle), name);
|
||||||
@@ -1929,11 +1952,21 @@ export class Viewer2D {
|
|||||||
// _resolveDash to turn an entity's linetype into a dash/gap size.
|
// _resolveDash to turn an entity's linetype into a dash/gap size.
|
||||||
_buildLinetypeMap(result) {
|
_buildLinetypeMap(result) {
|
||||||
this._ltPatterns = new Map();
|
this._ltPatterns = new Map();
|
||||||
|
this._dwgLinPatterns = new Map();
|
||||||
this._layerLtByName = new Map();
|
this._layerLtByName = new Map();
|
||||||
this._layerLtByHandle = new Map();
|
this._layerLtByHandle = new Map();
|
||||||
this._globalLtscale = (result?.vars?.ltscale > 0 ? result.vars.ltscale : 1);
|
this._globalLtscale = (result?.vars?.ltscale > 0 ? result.vars.ltscale : 1);
|
||||||
for (const lt of (result?.tables?.lineTypes ?? [])) {
|
for (const lt of (result?.tables?.lineTypes ?? [])) {
|
||||||
if (lt?.name) this._ltPatterns.set(lt.name, Array.isArray(lt.pattern) ? lt.pattern : []);
|
if (lt?.name) {
|
||||||
|
const patArr = Array.isArray(lt.pattern) ? lt.pattern : [];
|
||||||
|
this._ltPatterns.set(lt.name, patArr);
|
||||||
|
if (patArr.length > 0) {
|
||||||
|
const merged = mergeDwgLinetypePattern(lt.name, patArr, lt.description);
|
||||||
|
if (merged) {
|
||||||
|
this._dwgLinPatterns.set(lt.name.toUpperCase().trim(), merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const l of (result?.tables?.layers ?? [])) {
|
for (const l of (result?.tables?.layers ?? [])) {
|
||||||
const name = l.name ?? l.layerName;
|
const name = l.name ?? l.layerName;
|
||||||
@@ -1988,7 +2021,7 @@ export class Viewer2D {
|
|||||||
const aci = entity.entityHeader?.colorIndex
|
const aci = entity.entityHeader?.colorIndex
|
||||||
?? entity.color ?? entity.colorIndex ?? entity.colorNumber ?? 256;
|
?? entity.color ?? entity.colorIndex ?? entity.colorNumber ?? 256;
|
||||||
if (aci != null) {
|
if (aci != null) {
|
||||||
const hex = aciToHex(aci);
|
const hex = aciToHex(aci, this._isDark);
|
||||||
if (hex !== null) return hex;
|
if (hex !== null) return hex;
|
||||||
}
|
}
|
||||||
const lh = entity.layerHandle?.value ?? entity.layerHandle;
|
const lh = entity.layerHandle?.value ?? entity.layerHandle;
|
||||||
@@ -2882,7 +2915,7 @@ export class Viewer2D {
|
|||||||
} else if (t.bgColorRgb && (t.bgColorRgb.r != null)) {
|
} else if (t.bgColorRgb && (t.bgColorRgb.r != null)) {
|
||||||
bgColor = ((t.bgColorRgb.r & 255) << 16) | ((t.bgColorRgb.g & 255) << 8) | (t.bgColorRgb.b & 255);
|
bgColor = ((t.bgColorRgb.r & 255) << 16) | ((t.bgColorRgb.g & 255) << 8) | (t.bgColorRgb.b & 255);
|
||||||
} else if (t.bgColorIndex != null) {
|
} else if (t.bgColorIndex != null) {
|
||||||
const hex = aciToHex(t.bgColorIndex);
|
const hex = aciToHex(t.bgColorIndex, this._isDark);
|
||||||
if (hex != null) bgColor = hex;
|
if (hex != null) bgColor = hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ const VARIANTS_SL = [
|
|||||||
[0.00, 0.30], // 9: dark gray
|
[0.00, 0.30], // 9: dark gray
|
||||||
];
|
];
|
||||||
|
|
||||||
export function aciToHex(aci) {
|
export function aciToHex(aci, isDark = true) {
|
||||||
if (aci == null || aci === 0 || aci === 256) return null;
|
if (aci == null || aci === 0 || aci === 256) return null;
|
||||||
|
if (aci === 7) return isDark ? 0xFFFFFF : 0x000000;
|
||||||
if (aci <= 9) return FIXED[aci] ?? null;
|
if (aci <= 9) return FIXED[aci] ?? null;
|
||||||
if (aci >= 250 && aci <= 255) {
|
if (aci >= 250 && aci <= 255) {
|
||||||
const v = Math.round(40 + (aci - 250) * 43);
|
const v = Math.round(40 + (aci - 250) * 43);
|
||||||
|
|||||||
@@ -299,3 +299,92 @@ export function getLinPattern(name: string): LinPattern | undefined {
|
|||||||
export function getAllRegisteredPatterns(): Map<string, LinPattern> {
|
export function getAllRegisteredPatterns(): Map<string, LinPattern> {
|
||||||
return globalRegistry;
|
return globalRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mergeDwgLinetypePattern(
|
||||||
|
ltName: string,
|
||||||
|
dwgPattern: number[],
|
||||||
|
description?: string
|
||||||
|
): LinPattern | undefined {
|
||||||
|
if (!ltName) return undefined;
|
||||||
|
const base = getLinPattern(ltName);
|
||||||
|
if (!dwgPattern || !dwgPattern.length) return base;
|
||||||
|
|
||||||
|
if (!base) {
|
||||||
|
const elements: LinElement[] = [];
|
||||||
|
let totalLength = 0;
|
||||||
|
for (const val of dwgPattern) {
|
||||||
|
if (val > 1e-6) {
|
||||||
|
elements.push({
|
||||||
|
type: 'dash',
|
||||||
|
val,
|
||||||
|
scale: 1,
|
||||||
|
rotation: 0,
|
||||||
|
isAbsoluteAngle: false,
|
||||||
|
isUprightAngle: false,
|
||||||
|
xOffset: 0,
|
||||||
|
yOffset: 0,
|
||||||
|
});
|
||||||
|
totalLength += val;
|
||||||
|
} else if (val < -1e-6) {
|
||||||
|
const absVal = Math.abs(val);
|
||||||
|
elements.push({
|
||||||
|
type: 'gap',
|
||||||
|
val: -absVal,
|
||||||
|
scale: 1,
|
||||||
|
rotation: 0,
|
||||||
|
isAbsoluteAngle: false,
|
||||||
|
isUprightAngle: false,
|
||||||
|
xOffset: 0,
|
||||||
|
yOffset: 0,
|
||||||
|
});
|
||||||
|
totalLength += absVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: ltName,
|
||||||
|
description: description || '',
|
||||||
|
elements,
|
||||||
|
patternLength: Math.max(totalLength, 0.1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge DWG numeric dash/gap values into base pattern elements
|
||||||
|
const newElements: LinElement[] = [];
|
||||||
|
let dwgValIdx = 0;
|
||||||
|
let totalLength = 0;
|
||||||
|
|
||||||
|
for (const elem of base.elements) {
|
||||||
|
if (elem.type === 'dash') {
|
||||||
|
const dwgVal =
|
||||||
|
dwgValIdx < dwgPattern.length && dwgPattern[dwgValIdx] > 0
|
||||||
|
? dwgPattern[dwgValIdx]
|
||||||
|
: elem.val || 0.05;
|
||||||
|
if (dwgValIdx < dwgPattern.length && dwgPattern[dwgValIdx] > 0) {
|
||||||
|
dwgValIdx++;
|
||||||
|
}
|
||||||
|
newElements.push({ ...elem, val: dwgVal });
|
||||||
|
totalLength += dwgVal;
|
||||||
|
} else if (elem.type === 'gap') {
|
||||||
|
const dwgVal =
|
||||||
|
dwgValIdx < dwgPattern.length && dwgPattern[dwgValIdx] < 0
|
||||||
|
? dwgPattern[dwgValIdx]
|
||||||
|
: elem.val;
|
||||||
|
if (dwgValIdx < dwgPattern.length && dwgPattern[dwgValIdx] < 0) {
|
||||||
|
dwgValIdx++;
|
||||||
|
}
|
||||||
|
const absVal = Math.abs(dwgVal || 0.1);
|
||||||
|
newElements.push({ ...elem, val: -absVal });
|
||||||
|
totalLength += absVal;
|
||||||
|
} else {
|
||||||
|
newElements.push({ ...elem });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
description: description || base.description,
|
||||||
|
elements: newElements,
|
||||||
|
patternLength: Math.max(totalLength, 0.1),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user