import { Transform } from "konva/lib/Util"; import { Pos, Size } from "../utils/math.ts"; import { alignPortMat } from "@/utils/align-port.ts"; import mitt from "mitt"; import { IRect } from "konva/lib/types"; import { MathUtils } from "three"; export class Viewer { size?: Size; sizeMat: Transform | null; viewSize?: Size viewMat: Transform; bus = mitt<{ transformChange: Transform, viewSizeChange: void }>(); constructor() { this.viewMat = new Transform(); this.sizeMat = null } setSize(size: Size) { this.size = size; this.updateSizeMat() } setViewSize(size?: Size) { this.viewSize = size this.updateSizeMat() } updateSizeMat() { if (!this.size || !this.viewSize) { this.sizeMat = null } else { const diffw = this.size.width - this.viewSize.width const diffh = this.size.height - this.viewSize.height this.sizeMat = new Transform().translate(diffw / 2, diffh / 2) } this.bus.emit("transformChange", this.transform); this.bus.emit('viewSizeChange') } setBound({ targetBound, size, padding = 0, }: { targetBound: IRect; size?: Size; padding?: number; }) { size = size || this.size; if (!size) { throw "size属性未设置"; } const selfBound = { x: 0, y: 0, width: size.width - padding * 2, height: size.height - padding * 2, }; const mat = new Transform() .translate(padding, padding) .multiply(alignPortMat(selfBound, targetBound, true)); const rotate = this.viewMat.decompose().rotation this.setViewMat(mat); this.rotatePixel({x: size.width / 2, y: size.height / 2}, MathUtils.degToRad(rotate)) } move(position: Pos, initMat = this.viewMat) { this.mutMat(new Transform().translate(position.x, position.y), initMat); } movePixel(position: Pos, initMat = this.viewMat) { if (isNaN(position.x) || isNaN(position.y)) { console.error(`无效移动位置${position.x} ${position.y}`); return; } const mat = initMat.copy().invert(); const p1 = mat.point({ x: 0, y: 0 }); const p2 = mat.point(position); this.move({ x: p2.x - p1.x, y: p2.y - p1.y }); // const info = initMat.decompose() // const tf = new Transform() // tf.rotate(info.rotation) // tf.scale(info.scaleX, info.scaleY) // this.move(tf.invert().point(position), this.viewMat) } private min = 0.005 private max = 50 setScaleRange(min: number, max: number) { this.min = min this.max = max } scale(center: Pos, scale: number, initMat = this.viewMat) { const base = initMat.decompose().scaleX; const min = this.min const max = this.max const isMin = base * scale < min const isMax = base * scale > max if (isMax || isMin) { // console.error(`缩放范围${min}~${max} 将自动调整缩放值`); if (scale > 1 && isMin) { scale = min / base } else if (scale < 1 && isMax) { scale = max / base } else { return; } } if (isNaN(center.x) || isNaN(center.y)) { console.error(`无效中心点${center.x} ${center.y}`); return; } this.mutMat( new Transform() .translate(center.x, center.y) .multiply( new Transform() .scale(scale, scale) .multiply(new Transform().translate(-center.x, -center.y)) ), initMat ); } scalePixel(center: Pos, scale: number, initMat = this.viewMat) { if (this.viewSize && this.size) { const offsetX = (this.size.width - this.viewSize.width) / 2 const offsetY = (this.size.height - this.viewSize.height) / 2 center = { x: center.x - offsetX, y: center.y - offsetY, } } const pos = initMat.copy().invert().point(center); this.scale(pos, scale, initMat); } rotate(center: Pos, angleRad: number, initMat = this.viewMat) { this.mutMat( new Transform() .translate(center.x, center.y) .multiply( new Transform() .rotate(angleRad) .multiply(new Transform().translate(-center.x, -center.y)) ), initMat ); } rotatePixel(center: Pos, angleRad: number, initMat = this.viewMat) { const pos = initMat.copy().invert().point(center); this.rotate(pos, angleRad, initMat); } mutMat(mat: Transform, initMat = this.viewMat) { // this.setViewMat(mat.copy().multiply(initMat)) this.setViewMat(initMat.copy().multiply(mat)); } setViewMat(mat: number[] | Transform) { if (mat instanceof Transform) { this.viewMat = mat.copy(); } else { this.viewMat = new Transform(mat); } this.bus.emit("transformChange", this.transform); } get transform() { return this.sizeMat ? this.sizeMat.copy().multiply(this.viewMat) : this.viewMat.copy(); } get current() { return this.transform.decompose(); } }