From fb18a29f3641a3f1dedb4ba523d5a7910bd8b559 Mon Sep 17 00:00:00 2001 From: Riho Kirss Date: Sat, 5 Sep 2026 11:11:09 +0300 Subject: [PATCH] fix(SheetBoard): stop double-applying devicePixelRatio to the scissor/viewport rect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _doRender() pre-multiplied the CSS-pixel slot rectangle by window.devicePixelRatio before handing it to setScissor()/setViewport(). Three.js already scales those by the renderer's own pixel ratio, which is set to window.devicePixelRatio, and the canvas is sized with setSize() in CSS pixels — so the ratio was applied twice. At devicePixelRatio != 1 the rendered viewport content drifts away from the DOM border overlay, by an amount that grows with the ratio and with the slot's distance from the bottom of the board. Pass the rectangle in CSS pixels and let the renderer do the scaling. --- packages/obc/src/core/SheetBoard/index.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/obc/src/core/SheetBoard/index.ts b/packages/obc/src/core/SheetBoard/index.ts index 0ba11b92..8348adfa 100644 --- a/packages/obc/src/core/SheetBoard/index.ts +++ b/packages/obc/src/core/SheetBoard/index.ts @@ -595,7 +595,6 @@ export class SheetBoard extends LitElement { const hostRect = this.getBoundingClientRect(); const hostW = hostRect.width; const hostH = hostRect.height; - const dpr = window.devicePixelRatio; // Clear the whole canvas to transparent first. renderer.setScissorTest(false); @@ -666,14 +665,18 @@ export class SheetBoard extends LitElement { if (sx + sw <= 0 || sx >= hostW || sy + sh <= 0 || sy >= hostH) continue; - // Convert to WebGL coords: origin bottom-left, device pixels. - const glX = Math.round(sx * dpr); - const glY = Math.round((hostH - sy - sh) * dpr); - const glW = Math.round(sw * dpr); - const glH = Math.round(sh * dpr); - - renderer.setScissor(glX, glY, glW, glH); - renderer.setViewport(glX, glY, glW, glH); + // Convert to WebGL coords: origin bottom-left. These stay in CSS + // pixels — setScissor() and setViewport() multiply by the renderer's + // own pixel ratio internally, and the canvas was sized with + // setSize() in CSS pixels too, so scaling here as well would apply + // devicePixelRatio twice. + const vpX = Math.round(sx); + const vpY = Math.round(hostH - sy - sh); + const vpW = Math.round(sw); + const vpH = Math.round(sh); + + renderer.setScissor(vpX, vpY, vpW, vpH); + renderer.setViewport(vpX, vpY, vpW, vpH); renderer.setClearColor(0xffffff, 1); renderer.clear(); renderer.render(scene, vp.camera);