fix: v2.12 全部 2D 画布上下颠倒修复(组合/编辑/效果图/AI变体)
- 根因: 四画布 ty 按 y 向上渲染, 而模型坐标 y 向下(v2.11 实证) → 样板内部布局颠倒 - 统一去翻转: ty 直接映射 + toModel 反演同步 + 顶底序归位 - WindoorCanvas: 尺寸手柄(右下角)/中梃竖向拖动 dy 符号/扇符号与标注位置同步修正 - 验证: 组合页 2D 与 3D 方向一致, 设计图纸 704 布局=官方平面图, ui-shots 6 页无报错
This commit is contained in:
@@ -66,7 +66,7 @@ let lastTransform = { scale: 1, midX: 0, midY: 0, W: 0, H: 0 }
|
||||
|
||||
function toModel(sx: number, sy: number): Pt {
|
||||
const { scale, midX, midY, W, H } = lastTransform
|
||||
return { x: (sx - W / 2 - view.cx) / scale + midX, y: (H / 2 + view.cy - sy) / scale + midY }
|
||||
return { x: (sx - W / 2 - view.cx) / scale + midX, y: (sy - H / 2 - view.cy) / scale + midY }
|
||||
}
|
||||
|
||||
// ============ 分割树重放 ============
|
||||
@@ -125,7 +125,7 @@ function onSizeInput() {
|
||||
function hitResizeHandle(sx: number, sy: number): 'w' | 'h' | 'wh' | null {
|
||||
const { scale, midX, midY, W, H } = lastTransform
|
||||
const x1 = W / 2 + (modelBox.x1 - midX) * scale + view.cx
|
||||
const y1 = H / 2 - (modelBox.y0 - midY) * scale + view.cy
|
||||
const y1 = H / 2 + (modelBox.y1 - midY) * scale + view.cy
|
||||
const TOL = 8
|
||||
const nearRight = Math.abs(sx - x1) < TOL && sy <= y1 + TOL
|
||||
const nearBottom = Math.abs(sy - y1) < TOL && sx <= x1 + TOL
|
||||
@@ -139,7 +139,7 @@ function hitResizeHandle(sx: number, sy: number): 'w' | 'h' | 'wh' | null {
|
||||
function hitMullion(sx: number, sy: number): { shapeIdx: number; spIdx: number } | null {
|
||||
const { scale, midX, midY, W, H } = lastTransform
|
||||
const tx = (x: number) => W / 2 + (x - midX) * scale + view.cx
|
||||
const ty = (y: number) => H / 2 - (y - midY) * scale + view.cy
|
||||
const ty = (y: number) => H / 2 + (y - midY) * scale + view.cy
|
||||
let best: { shapeIdx: number; spIdx: number; d: number } | null = null
|
||||
replays.forEach((rp, shapeIdx) => {
|
||||
rp.clips.forEach(({ sp, bb }, spIdx) => {
|
||||
@@ -183,8 +183,9 @@ function render() {
|
||||
const scale = fit * view.scale
|
||||
const midX = (modelBox.x0 + modelBox.x1) / 2, midY = (modelBox.y0 + modelBox.y1) / 2
|
||||
lastTransform = { scale, midX, midY, W, H }
|
||||
// 模型坐标 y 向下(屏幕系): 直接映射, 不做上下翻转
|
||||
const tx = (x: number) => W / 2 + (x - midX) * scale + view.cx
|
||||
const ty = (y: number) => H / 2 - (y - midY) * scale + view.cy
|
||||
const ty = (y: number) => H / 2 + (y - midY) * scale + view.cy
|
||||
|
||||
const glassAlpha = 'rgba(211,221,218,0.6)'
|
||||
const profile = '#5c6166'
|
||||
@@ -193,7 +194,7 @@ function render() {
|
||||
dd.sm.forEach((shape, si) => drawFrame(ctx, shape, replays[si], si, tx, ty, { glassAlpha, profile }))
|
||||
|
||||
if (props.editable) {
|
||||
const x1 = tx(modelBox.x1), y1 = ty(modelBox.y0)
|
||||
const x1 = tx(modelBox.x1), y1 = ty(modelBox.y1)
|
||||
ctx.fillStyle = '#409eff'
|
||||
ctx.fillRect(x1 - 5, y1 - 5, 10, 10)
|
||||
ctx.strokeStyle = '#fff'
|
||||
@@ -233,7 +234,7 @@ function drawFrame(
|
||||
// 外框
|
||||
if (regions.length) {
|
||||
const bb = bboxOf(regions.flat())
|
||||
const x0 = tx(bb.x0), y0 = ty(bb.y1), x1 = tx(bb.x1), y1 = ty(bb.y0)
|
||||
const x0 = tx(bb.x0), y0 = ty(bb.y0), x1 = tx(bb.x1), y1 = ty(bb.y1)
|
||||
const ew = shape.fm?.ew ?? [60, 60, 60, 60]
|
||||
const avg = ew.reduce((a, b) => a + b, 0) / ew.length
|
||||
ctx.strokeStyle = cm.bn ?? color.profile
|
||||
@@ -248,7 +249,7 @@ function drawFrame(
|
||||
// 扇(推断: 有效bd → polyId挂接区域 → 帧bbox按扇数均分) — 共享推断模块
|
||||
const glyphs = inferSashGlyphs(shape, rp).sashes
|
||||
for (const g of glyphs) {
|
||||
const bx0 = tx(g.x0), bx1 = tx(g.x1), by0 = ty(g.y1), by1 = ty(g.y0)
|
||||
const bx0 = tx(g.x0), bx1 = tx(g.x1), by0 = ty(g.y0), by1 = ty(g.y1)
|
||||
const w = bx1 - bx0, h = by1 - by0
|
||||
if (w < 4 || h < 4) continue
|
||||
ctx.save()
|
||||
@@ -296,7 +297,7 @@ function drawFrame(
|
||||
// 尺寸标注
|
||||
if (regions.length) {
|
||||
const bb = bboxOf(regions.flat())
|
||||
const x0 = tx(bb.x0), x1 = tx(bb.x1), y0 = ty(bb.y1), y1 = ty(bb.y0)
|
||||
const x0 = tx(bb.x0), x1 = tx(bb.x1), y0 = ty(bb.y0), y1 = ty(bb.y1)
|
||||
const wmm = Math.round(bb.x1 - bb.x0), hmm = Math.round(bb.y1 - bb.y0)
|
||||
ctx.fillStyle = '#7a8087'
|
||||
ctx.font = '11px sans-serif'
|
||||
@@ -322,7 +323,7 @@ function drawBar(
|
||||
bb: { x0: number; y0: number; x1: number; y1: number },
|
||||
tx: (x: number) => number, ty: (y: number) => number, isSelected: boolean, profile: string,
|
||||
) {
|
||||
const x0 = tx(bb.x0), x1 = tx(bb.x1), y0 = ty(bb.y1), y1 = ty(bb.y0)
|
||||
const x0 = tx(bb.x0), x1 = tx(bb.x1), y0 = ty(bb.y0), y1 = ty(bb.y1)
|
||||
ctx.save()
|
||||
if (axis === 'v') {
|
||||
const px = tx(pos)
|
||||
@@ -502,7 +503,7 @@ function onMove(e: MouseEvent) {
|
||||
const bb = clip?.bb ?? modelBox
|
||||
const { scale } = lastTransform
|
||||
const axis = spAxis(sp)
|
||||
const delta = axis === 'v' ? (e.clientX - moveDrag.startX) / scale : -(e.clientY - moveDrag.startY) / scale
|
||||
const delta = axis === 'v' ? (e.clientX - moveDrag.startX) / scale : (e.clientY - moveDrag.startY) / scale
|
||||
const lo = (axis === 'v' ? bb.x0 : bb.y0) + 30
|
||||
const hi = (axis === 'v' ? bb.x1 : bb.y1) - 30
|
||||
const newPos = Math.min(hi, Math.max(lo, moveDrag.startPos + delta))
|
||||
|
||||
Reference in New Issue
Block a user