feat: enhance complex linetype rendering with DWG text style data, gap midpoint centering, and theme defaults
This commit is contained in:
+62
-12
@@ -248,6 +248,7 @@ export class Viewer2D {
|
||||
this._clear();
|
||||
this._entityMeta = [];
|
||||
this._buildLayerMap(result?.tables?.layers);
|
||||
this._buildStyleMap(result?.tables?.styles || result?.tables?.textStyles);
|
||||
this._buildLinetypeMap(result);
|
||||
// Sheet orientation: the active model-space viewport's VIEWTWIST rotates the
|
||||
// view so a drawing stored tilted in WCS (rotated survey sheets — header UCS
|
||||
@@ -273,6 +274,13 @@ export class Viewer2D {
|
||||
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 lineVerts = [];
|
||||
const lineColors = [];
|
||||
@@ -1045,9 +1053,9 @@ export class Viewer2D {
|
||||
|
||||
// ── UI integration ─────────────────────────────────────────────────────────
|
||||
|
||||
/** Swap canvas background for theme. dark=true → near-black, false → light. */
|
||||
/** Swap canvas background for theme. dark=true → near-black (Model), false → white (Layout). */
|
||||
setTheme(dark) {
|
||||
this._scene.background = new THREE.Color(dark ? 0x0a0b0d : 0xf7f6f3);
|
||||
this._scene.background = new THREE.Color(dark ? 0x0a0b0d : 0xffffff);
|
||||
if (this._gridVisible) this._rebuildGrid();
|
||||
}
|
||||
|
||||
@@ -1697,12 +1705,25 @@ export class Viewer2D {
|
||||
};
|
||||
};
|
||||
|
||||
const elements = linPattern.elements || [];
|
||||
const patternLen = linPattern.patternLength * ltscale;
|
||||
if (patternLen < 1e-6) return;
|
||||
if (patternLen < 1e-6 || !elements.length) return;
|
||||
|
||||
// Calculate distance from pattern start to shape (arrowhead) element if present
|
||||
let shapeOffset = -1;
|
||||
let accum = 0;
|
||||
for (const e of elements) {
|
||||
if (e.type === 'shape') {
|
||||
shapeOffset = accum;
|
||||
break;
|
||||
}
|
||||
if (e.type === 'dash') accum += Math.max((e.val || 0.05) * ltscale, 0.01 * ltscale);
|
||||
else if (e.type === 'gap') accum += Math.abs(e.val || 0.1) * ltscale;
|
||||
}
|
||||
|
||||
let s = 0;
|
||||
let elemIdx = 0;
|
||||
const elements = linPattern.elements;
|
||||
let lastShapePos = -1;
|
||||
|
||||
while (s < totalLen) {
|
||||
const elem = elements[elemIdx % elements.length];
|
||||
@@ -1723,12 +1744,18 @@ export class Viewer2D {
|
||||
const gapLen = Math.abs(elem.val || 0.1) * ltscale;
|
||||
s += gapLen;
|
||||
} else if (elem.type === 'shape') {
|
||||
lastShapePos = s;
|
||||
const pState = getPathState(s);
|
||||
this._renderShapeSymbol(elem, pState, ltscale, color, pushSeg);
|
||||
} else if (elem.type === 'text') {
|
||||
const pState = getPathState(s);
|
||||
if (elem.text && pendingTexts) {
|
||||
const h = (elem.scale || 0.2) * ltscale;
|
||||
// 1. Text Height: Exact text style DWG table height if fixed, else linetype elem.scale
|
||||
const sName = (elem.style || 'STANDARD').toUpperCase();
|
||||
const fixedH = this._textStyleHeightMap?.get(sName) || 0;
|
||||
const baseH = fixedH > 0 ? fixedH : (elem.scale || 0.2);
|
||||
const h = baseH * ltscale;
|
||||
|
||||
let angle = pState.angle + ((elem.rotation || 0) * Math.PI) / 180;
|
||||
if (elem.isAbsoluteAngle) {
|
||||
angle = ((elem.rotation || 0) * Math.PI) / 180;
|
||||
@@ -1744,20 +1771,30 @@ export class Viewer2D {
|
||||
const sinT = Math.sin(angle);
|
||||
|
||||
const xo = (elem.xOffset || 0) * ltscale;
|
||||
const rawYo = (elem.yOffset != null && Math.abs(elem.yOffset) > 1e-4) ? Math.abs(elem.yOffset) : 0.02;
|
||||
const yo = rawYo * ltscale;
|
||||
// alignV: 2 (middle) centers text quad on origin; set yo=0 so middle lies directly on grey arrow line axis
|
||||
const yo = 0;
|
||||
|
||||
const tx = pState.x + (xo * cosT - yo * sinT);
|
||||
const ty = pState.y + (xo * sinT + yo * cosT);
|
||||
// 2. Exact Visual Midpoint: position text at exact midpoint of empty text gap (between arrowhead tip and next dash)
|
||||
let gapMidOffset = 0;
|
||||
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 tx = midState.x + (xo * cosT - yo * sinT);
|
||||
const ty = midState.y + (xo * sinT + yo * cosT);
|
||||
|
||||
pendingTexts.push({
|
||||
text: elem.text,
|
||||
pos: { x: tx, y: ty, z: pState.z },
|
||||
pos: { x: tx, y: ty, z: midState.z },
|
||||
height: Math.max(h, 0.01),
|
||||
rotation: angle,
|
||||
color,
|
||||
alignH: 1,
|
||||
alignV: 2,
|
||||
alignH: 1, // center horizontally in gap between arrowheads
|
||||
alignV: 2, // center vertically on line axis
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1846,6 +1883,19 @@ export class Viewer2D {
|
||||
}
|
||||
}
|
||||
|
||||
_buildStyleMap(styles) {
|
||||
if (!this._textStyleHeightMap) this._textStyleHeightMap = new Map();
|
||||
this._textStyleHeightMap.clear();
|
||||
if (!styles || !Array.isArray(styles)) return;
|
||||
for (const st of styles) {
|
||||
const sName = (st.name || st.styleName || '').toUpperCase();
|
||||
const h = st.height ?? st.fixedTextHeight ?? 0;
|
||||
if (sName && h > 0) {
|
||||
this._textStyleHeightMap.set(sName, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_buildLayerMap(layers) {
|
||||
this._layerByHandle.clear();
|
||||
this._layerByName.clear();
|
||||
|
||||
Reference in New Issue
Block a user