feat(viewer2d): make the lineweight display scale adjustable from the toolbar
The px-per-mm mapping was a hard-coded guess twice over, and the right value depends on the monitor - AutoCAD has the same knob as the Adjust Display Scale slider. Expose it: a slider plus a number box next to the lineweight toggle, with a readout of what the drawing's own weights come out to (0.35mm -> 7px, 0.50mm -> 10px at the 20 px/mm default). The value persists in localStorage. Retuning applies to the live materials instead of rebuilding the scene. The hairline cut-off is a millimetre value, so changing the scale moves every weight by the same factor without changing which bucket a segment belongs to - each fat batch now remembers its weight in mm, and only material.linewidth is rewritten. That keeps a drag on the slider instant even on the 37 MB road drawing. Measured headless on its 0.35mm gutter line: 2-3px at scale 8, 7px at 20, 14px at 40 - matching the readout in each case. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -469,7 +469,11 @@ export class Viewer2D {
|
||||
const key = `${curDash ? curDash.key : ''}#${lwPx}`;
|
||||
let bk = segBuckets.get(key);
|
||||
if (!bk) {
|
||||
bk = { dash: curDash?.dash ?? 0, gap: curDash?.gap ?? 0, lwPx, verts: [], colors: [], segLayer: [], entIdx: -2 };
|
||||
// lwMm is kept so setLineweightScale() can retune the material width
|
||||
// without rebuilding the scene (the hairline cut-off is in mm, so
|
||||
// bucket membership does not depend on the scale).
|
||||
bk = { dash: curDash?.dash ?? 0, gap: curDash?.gap ?? 0, lwPx, lwMm: this._curLwMm || 0,
|
||||
verts: [], colors: [], segLayer: [], entIdx: -2 };
|
||||
segBuckets.set(key, bk);
|
||||
}
|
||||
// Selection highlight recolors an entity's own vertices. Its colors no
|
||||
@@ -1208,7 +1212,7 @@ export class Viewer2D {
|
||||
this._sizeLineMaterial(fmat);
|
||||
const fline = new LineSegments2(fgeom, fmat);
|
||||
if (bk.dash > 0) fline.computeLineDistances();
|
||||
this._registerFat(fline, bk.segLayer);
|
||||
this._registerFat(fline, bk.segLayer, bk.lwMm);
|
||||
bk.rt = this._fatBufs[this._fatBufs.length - 1];
|
||||
this._group.add(fline);
|
||||
} else {
|
||||
@@ -1343,7 +1347,7 @@ export class Viewer2D {
|
||||
* into the front of the instance buffers and drops `instanceCount`.
|
||||
* The untouched originals are kept so any later mask starts from clean data.
|
||||
*/
|
||||
_registerFat(mesh, segLayerArr) {
|
||||
_registerFat(mesh, segLayerArr, lwMm = 0) {
|
||||
const geo = mesh.geometry;
|
||||
const posBuf = geo.attributes.instanceStart?.data;
|
||||
const colBuf = geo.attributes.instanceColorStart?.data;
|
||||
@@ -1351,7 +1355,7 @@ export class Viewer2D {
|
||||
posBuf.setUsage(THREE.DynamicDrawUsage);
|
||||
colBuf?.setUsage(THREE.DynamicDrawUsage);
|
||||
this._fatBufs.push({
|
||||
mesh,
|
||||
mesh, lwMm,
|
||||
segLayer: Int32Array.from(segLayerArr),
|
||||
posBuf, colBuf,
|
||||
posSrc: Float32Array.from(posBuf.array),
|
||||
@@ -1383,18 +1387,31 @@ export class Viewer2D {
|
||||
/**
|
||||
* Display scale in screen pixels per mm of lineweight — AutoCAD's
|
||||
* "Adjust Display Scale" slider. Default 20 (1 px per 0.05 mm).
|
||||
*
|
||||
* Applied to the live materials, not by rebuilding: the hairline cut-off is
|
||||
* a millimetre value, so changing the scale moves every weight by the same
|
||||
* factor without changing which bucket a segment belongs to. Retuning is
|
||||
* therefore instant even on a 37 MB drawing.
|
||||
*
|
||||
* @param {number|null} pxPerMm null restores the default.
|
||||
*/
|
||||
setLineweightScale(pxPerMm) {
|
||||
this._lwScale = (pxPerMm > 0) ? pxPerMm : null;
|
||||
if (this._lastResult && !this._isLoading) {
|
||||
this.load(this._lastResult, { keepView: true, keepTheme: true, spaceHandle: this._activeSpaceHex });
|
||||
const scale = this.getLineweightScale();
|
||||
for (const buf of this._fatBufs) {
|
||||
if (!(buf.lwMm > 0)) continue;
|
||||
buf.mesh.material.linewidth = Math.min(LW_MAX_PX, Math.round(buf.lwMm * scale * 10) / 10);
|
||||
}
|
||||
}
|
||||
|
||||
/** Current lineweight display scale (px per mm). */
|
||||
getLineweightScale() { return this._lwScale || LW_PX_PER_MM; }
|
||||
|
||||
/** Lineweights present in the current drawing, in mm (ascending). */
|
||||
getLineweightClasses() {
|
||||
return [...new Set(this._fatBufs.map((b) => b.lwMm).filter((m) => m > 0))].sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
/** Track an entity-owned object so layer toggles can flip its visibility. */
|
||||
_addObj(obj, layer) {
|
||||
const key = layer ?? this._curLayer;
|
||||
@@ -2339,10 +2356,15 @@ export class Viewer2D {
|
||||
* assigned a weight render exactly as before.
|
||||
*/
|
||||
_lwPx(entity, inheritedPx = 0) {
|
||||
this._curLwMm = 0;
|
||||
if (!this._lwDisplay) return 0;
|
||||
let raw = entity?.entityHeader?.lineWeight ?? entity?.lineWeight ?? entity?.lineweight;
|
||||
if (typeof raw !== 'number') return 0;
|
||||
if (raw === -2) return inheritedPx; // ByBlock
|
||||
if (raw === -2) { // ByBlock
|
||||
const scale = this.getLineweightScale();
|
||||
this._curLwMm = inheritedPx > 0 ? inheritedPx / scale : 0;
|
||||
return inheritedPx;
|
||||
}
|
||||
if (raw === -1) { // ByLayer
|
||||
const lh = entity.layerHandle?.value ?? entity.layerHandle;
|
||||
const byHandle = lh != null ? this._layerLwByHandle.get(String(lh)) : undefined;
|
||||
@@ -2352,7 +2374,8 @@ export class Viewer2D {
|
||||
}
|
||||
const mm = raw === -3 ? LW_DEFAULT_MM : raw / 100;
|
||||
if (!(mm > LW_HAIRLINE_MM)) return 0;
|
||||
const scale = this._lwScale || LW_PX_PER_MM;
|
||||
this._curLwMm = mm;
|
||||
const scale = this.getLineweightScale();
|
||||
return Math.min(LW_MAX_PX, Math.round(mm * scale * 10) / 10);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user