fix(viewer2d): correct Slug GPU text scale to match DWG cap-height
SlugTextBatch.add() scaled em-unit glyph outlines by the raw DWG `height` value, but glyph ink only reaches capHeightEm (~0.74 em) of the em box. Rendered cap height ended up ~26% short of spec (e.g. a 3.6-height TABLE cell text rendered at ~2.68), while the canvas-sprite fallback already corrected for this via measured ascent. Divide by capHeightEm so both paths agree. Rebuilds dist2 (was stale since before the Entity selection/Property Inspector/Layer panel work landed).
This commit is contained in:
@@ -0,0 +1 @@
|
||||
function A(t){let e,n;try{const f=x(t,i.__wbindgen_malloc),c=w,o=i.parse_dwg_json(f,c);var r=o[0],s=o[1];if(o[3])throw r=0,s=0,T(o[2]);return e=r,n=s,l(r,s)}finally{i.__wbindgen_free(e,n,1)}}function h(){return{__proto__:null,"./acadrust_dwg_bg.js":{__proto__:null,__wbg_Error_92b29b0548f8b746:function(e,n){return Error(l(e,n))},__wbg_String_8564e559799eccda:function(e,n){const r=String(n),s=S(r,i.__wbindgen_malloc,i.__wbindgen_realloc),f=w;m().setInt32(e+4,f,!0),m().setInt32(e+0,s,!0)},__wbg___wbindgen_is_string_ea5e6cc2e4141dfe:function(e){return typeof e=="string"},__wbg___wbindgen_throw_344f42d3211c4765:function(e,n){throw new Error(l(e,n))},__wbg_new_32b398fb48b6d94a:function(){return new Array},__wbg_new_7796ffc7ed656783:function(){return new Map},__wbg_new_da52cf8fe3429cb2:function(){return new Object},__wbg_set_575dd786d51585f8:function(e,n,r){return e.set(n,r)},__wbg_set_6be42768c690e380:function(e,n,r){e[n]=r},__wbg_set_8a16b38e4805b298:function(e,n,r){e[n>>>0]=r},__wbindgen_cast_0000000000000001:function(e){return e},__wbindgen_cast_0000000000000002:function(e){return e},__wbindgen_cast_0000000000000003:function(e,n){return l(e,n)},__wbindgen_cast_0000000000000004:function(e){return BigInt.asUintN(64,e)},__wbindgen_init_externref_table:function(){const e=i.__wbindgen_externrefs,n=e.grow(4);e.set(0,void 0),e.set(n+0,void 0),e.set(n+1,null),e.set(n+2,!0),e.set(n+3,!1)}}}}let a=null;function m(){return(a===null||a.buffer.detached===!0||a.buffer.detached===void 0&&a.buffer!==i.memory.buffer)&&(a=new DataView(i.memory.buffer)),a}function l(t,e){return E(t>>>0,e)}let u=null;function b(){return(u===null||u.byteLength===0)&&(u=new Uint8Array(i.memory.buffer)),u}function x(t,e){const n=e(t.length*1,1)>>>0;return b().set(t,n/1),w=t.length,n}function S(t,e,n){if(n===void 0){const o=d.encode(t),_=e(o.length,1)>>>0;return b().subarray(_,_+o.length).set(o),w=o.length,_}let r=t.length,s=e(r,1)>>>0;const f=b();let c=0;for(;c<r;c++){const o=t.charCodeAt(c);if(o>127)break;f[s+c]=o}if(c!==r){c!==0&&(t=t.slice(c)),s=n(s,r,r=c+t.length*3,1)>>>0;const o=b().subarray(s+c,s+r),_=d.encodeInto(t,o);c+=_.written,s=n(s,r,c,1)>>>0}return w=c,s}function T(t){const e=i.__wbindgen_externrefs.get(t);return i.__externref_table_dealloc(t),e}let g=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});g.decode();const D=2146435072;let y=0;function E(t,e){return y+=e,y>=D&&(g=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),g.decode(),y=e),g.decode(b().subarray(t,t+e))}const d=new TextEncoder;"encodeInto"in d||(d.encodeInto=function(t,e){const n=d.encode(t);return e.set(n),{read:t.length,written:n.length}});let w=0,i;function R(t,e){return i=t.exports,a=null,u=null,i.__wbindgen_start(),i}async function W(t,e){if(typeof Response=="function"&&t instanceof Response){if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(t,e)}catch(s){if(t.ok&&n(t.type)&&t.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",s);else throw s}const r=await t.arrayBuffer();return await WebAssembly.instantiate(r,e)}else{const r=await WebAssembly.instantiate(t,e);return r instanceof WebAssembly.Instance?{instance:r,module:t}:r}function n(r){switch(r){case"basic":case"cors":case"default":return!0}return!1}}async function M(t){if(i!==void 0)return i;t!==void 0&&(Object.getPrototypeOf(t)===Object.prototype?{module_or_path:t}=t:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),t===void 0&&(t=new URL("/assets/acadrust_dwg_bg-Dt2ifJSA.wasm",import.meta.url));const e=h();(typeof t=="string"||typeof Request=="function"&&t instanceof Request||typeof URL=="function"&&t instanceof URL)&&(t=fetch(t));const{instance:n,module:r}=await W(await t,e);return R(n)}const O="/assets/acadrust_dwg_bg-Dt2ifJSA.wasm";let p=null;function v(){return p||(p=M({module_or_path:O}).then(()=>{})),p}function I(t){return JSON.parse(A(t))}export{v as initAcadrustParser,I as parseDwgAcadrust};
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
+100
-2
@@ -96,8 +96,70 @@
|
||||
}
|
||||
#coords .lbl { color: var(--accent); margin-right: 6px; font-weight: 600; }
|
||||
#coords .sep { color: var(--line); margin: 0 12px; }
|
||||
/* 공통 패널 윈도우 스타일 (Properties & Layers - Draggable & Resizable) */
|
||||
.cad-panel {
|
||||
position: fixed; top: 56px; width: 320px; height: 480px;
|
||||
min-width: 250px; min-height: 140px; max-width: 90vw; max-height: 90vh;
|
||||
z-index: 15; background: rgba(22,27,34,.96); backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.5);
|
||||
display: flex; flex-direction: column; overflow: hidden; resize: both;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.cad-panel.collapsed {
|
||||
height: 42px !important;
|
||||
min-height: 42px !important;
|
||||
max-height: 42px !important;
|
||||
overflow: hidden !important;
|
||||
resize: none !important;
|
||||
}
|
||||
.cad-panel .hdr {
|
||||
padding: 10px 14px; border-bottom: 1px solid var(--line);
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
background: rgba(13,17,23,.7); position: sticky; top: 0; z-index: 5;
|
||||
cursor: grab; user-select: none; flex-shrink: 0;
|
||||
}
|
||||
.cad-panel .hdr:active { cursor: grabbing; }
|
||||
.cad-panel .hdr h3 { margin: 0; font-size: 13px; font-weight: 700; color: var(--ink); display: flex; align-items: center; gap: 6px; }
|
||||
.cad-panel .hdr-actions { display: flex; gap: 4px; align-items: center; }
|
||||
.cad-panel .hdr-btn {
|
||||
background: none; border: none; color: var(--muted); font-size: 13px;
|
||||
cursor: pointer; padding: 2px 6px; border-radius: 4px; display: inline-flex;
|
||||
align-items: center; justify-content: center; transition: all .15s;
|
||||
}
|
||||
.cad-panel .hdr-btn:hover { color: var(--ink); background: rgba(255,255,255,.1); }
|
||||
|
||||
#properties { right: 12px; }
|
||||
#layers { right: 344px; display: none; }
|
||||
|
||||
#propBody { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
#layerBody { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
#layerList { flex: 1; overflow-y: auto; }
|
||||
|
||||
#zoomSelBtn {
|
||||
font: 11px var(--sans); padding: 3px 8px; border-radius: 6px;
|
||||
border: 1px solid var(--accent); background: rgba(63,185,80,.15); color: var(--accent);
|
||||
cursor: pointer; display: none; align-items: center; gap: 4px; transition: all .15s;
|
||||
}
|
||||
#zoomSelBtn:hover { background: var(--accent); color: #fff; }
|
||||
|
||||
.layer-search-wrap { padding: 8px 10px; border-bottom: 1px solid var(--line); background: rgba(13,17,23,.3); }
|
||||
.layer-search-input {
|
||||
width: 100%; background: var(--bg); border: 1px solid var(--line);
|
||||
border-radius: 6px; padding: 6px 10px; color: var(--ink); font-size: 12px;
|
||||
outline: none; transition: border-color .15s;
|
||||
}
|
||||
.layer-search-input:focus { border-color: var(--accent); }
|
||||
.layer-sub-actions {
|
||||
display: flex; gap: 6px; padding: 6px 10px; border-bottom: 1px solid var(--line);
|
||||
background: rgba(22,27,34,.5);
|
||||
}
|
||||
.layer-sub-btn {
|
||||
font-size: 11px; padding: 2px 8px; border-radius: 4px; border: 1px solid var(--line);
|
||||
background: transparent; color: var(--muted); cursor: pointer; transition: all .15s;
|
||||
}
|
||||
.layer-sub-btn:hover { color: var(--ink); border-color: var(--accent); background: rgba(63,185,80,.1); }
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-B4PWk4UF.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CmUgC68r.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
@@ -114,6 +176,7 @@
|
||||
<button id="fit" type="button">Fit (F)</button>
|
||||
<button id="theme" type="button">Theme</button>
|
||||
<button id="layersBtn" type="button">Layers</button>
|
||||
<button id="propsBtn" type="button">Props (속성)</button>
|
||||
<div id="status">ready</div>
|
||||
</div>
|
||||
|
||||
@@ -123,7 +186,42 @@
|
||||
<span class="lbl">Y</span><span id="coordY">—</span>
|
||||
</div>
|
||||
|
||||
<div id="layers"><h3>Layers</h3><div id="layerList"></div></div>
|
||||
<!-- 레이어 (Layers) 관리 패널 -->
|
||||
<div id="layers" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3>
|
||||
<span>🎨</span> 레이어 (Layers)
|
||||
<span id="layerCount" style="font-family:var(--mono);font-size:11px;color:var(--muted);background:rgba(255,255,255,.08);padding:1px 6px;border-radius:4px;font-weight:400"></span>
|
||||
</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="collapseLayersBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closeLayersBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="layerBody">
|
||||
<div class="layer-search-wrap">
|
||||
<input id="layerSearch" class="layer-search-input" type="text" placeholder="🔍 레이어 검색..." />
|
||||
</div>
|
||||
<div class="layer-sub-actions">
|
||||
<button id="layerShowAll" class="layer-sub-btn" type="button">전체 켜기</button>
|
||||
<button id="layerHideAll" class="layer-sub-btn" type="button">전체 끄기</button>
|
||||
</div>
|
||||
<div id="layerList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 속성 (Properties) 패널 -->
|
||||
<div id="properties" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3><span>📋</span> 속성 (Properties)</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="zoomSelBtn" type="button" title="선택 요소 위치로 확대">🔍 선택 확대</button>
|
||||
<button id="collapsePropsBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closePropsBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="propBody"></div>
|
||||
</div>
|
||||
|
||||
<aside id="recipe">
|
||||
<h3>Module recipe</h3>
|
||||
|
||||
@@ -328,7 +328,10 @@ export class SlugTextBatch {
|
||||
colorInt: number, alignH: number, alignV: number): void {
|
||||
const eng = this.engine;
|
||||
const lines = String(text).split('\n');
|
||||
const scale = height;
|
||||
// Glyph outlines are em-scaled (getPath(0,0,1)), so raw cap ink height is
|
||||
// capHeightEm em, not 1em. Divide by it so the rendered cap height equals
|
||||
// the DWG `height` field, matching the canvas-sprite fallback's ascent fit.
|
||||
const scale = height / eng.capHeightEm;
|
||||
const lineStep = height * 5 / 3; // CAD default MTEXT line spacing
|
||||
const blockH = (lines.length - 1) * lineStep + height;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user