viewer.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { Transform } from "konva/lib/Util";
  2. import { Pos, Size } from "../utils/math.ts";
  3. import { alignPortMat } from "@/utils/align-port.ts";
  4. import mitt from "mitt";
  5. import { IRect } from "konva/lib/types";
  6. import { MathUtils } from "three";
  7. export class Viewer {
  8. size?: Size;
  9. sizeMat: Transform | null;
  10. viewSize?: Size
  11. viewMat: Transform;
  12. bus = mitt<{ transformChange: Transform, viewSizeChange: void }>();
  13. constructor() {
  14. this.viewMat = new Transform();
  15. this.sizeMat = null
  16. }
  17. setSize(size: Size) {
  18. this.size = size;
  19. this.updateSizeMat()
  20. }
  21. setViewSize(size?: Size) {
  22. this.viewSize = size
  23. this.updateSizeMat()
  24. }
  25. updateSizeMat() {
  26. if (!this.size || !this.viewSize) {
  27. this.sizeMat = null
  28. } else {
  29. const diffw = this.size.width - this.viewSize.width
  30. const diffh = this.size.height - this.viewSize.height
  31. this.sizeMat = new Transform().translate(diffw / 2, diffh / 2)
  32. }
  33. this.bus.emit("transformChange", this.transform);
  34. this.bus.emit('viewSizeChange')
  35. }
  36. setBound({
  37. targetBound,
  38. size,
  39. padding = 0,
  40. }: {
  41. targetBound: IRect;
  42. size?: Size;
  43. padding?: number;
  44. }) {
  45. size = size || this.size;
  46. if (!size) {
  47. throw "size属性未设置";
  48. }
  49. const selfBound = {
  50. x: 0,
  51. y: 0,
  52. width: size.width - padding * 2,
  53. height: size.height - padding * 2,
  54. };
  55. const mat = new Transform()
  56. .translate(padding, padding)
  57. .multiply(alignPortMat(selfBound, targetBound, true));
  58. const rotate = this.viewMat.decompose().rotation
  59. this.setViewMat(mat);
  60. this.rotatePixel({x: size.width / 2, y: size.height / 2}, MathUtils.degToRad(rotate))
  61. }
  62. move(position: Pos, initMat = this.viewMat) {
  63. this.mutMat(new Transform().translate(position.x, position.y), initMat);
  64. }
  65. movePixel(position: Pos, initMat = this.viewMat) {
  66. if (isNaN(position.x) || isNaN(position.y)) {
  67. console.error(`无效移动位置${position.x} ${position.y}`);
  68. return;
  69. }
  70. const mat = initMat.copy().invert();
  71. const p1 = mat.point({ x: 0, y: 0 });
  72. const p2 = mat.point(position);
  73. this.move({ x: p2.x - p1.x, y: p2.y - p1.y });
  74. // const info = initMat.decompose()
  75. // const tf = new Transform()
  76. // tf.rotate(info.rotation)
  77. // tf.scale(info.scaleX, info.scaleY)
  78. // this.move(tf.invert().point(position), this.viewMat)
  79. }
  80. private min = 0.005
  81. private max = 50
  82. setScaleRange(min: number, max: number) {
  83. this.min = min
  84. this.max = max
  85. }
  86. scale(center: Pos, scale: number, initMat = this.viewMat) {
  87. const base = initMat.decompose().scaleX;
  88. const min = this.min
  89. const max = this.max
  90. const isMin = base * scale < min
  91. const isMax = base * scale > max
  92. if (isMax || isMin) {
  93. console.error(`缩放范围${min}~${max} 将自动调整缩放值`);
  94. if (scale > 1 && isMin) {
  95. scale = min / base
  96. } else if (scale < 1 && isMax) {
  97. scale = max / base
  98. } else {
  99. return;
  100. }
  101. }
  102. if (isNaN(center.x) || isNaN(center.y)) {
  103. console.error(`无效中心点${center.x} ${center.y}`);
  104. return;
  105. }
  106. this.mutMat(
  107. new Transform()
  108. .translate(center.x, center.y)
  109. .multiply(
  110. new Transform()
  111. .scale(scale, scale)
  112. .multiply(new Transform().translate(-center.x, -center.y))
  113. ),
  114. initMat
  115. );
  116. }
  117. scalePixel(center: Pos, scale: number, initMat = this.viewMat) {
  118. if (this.viewSize && this.size) {
  119. const offsetX = (this.size.width - this.viewSize.width) / 2
  120. const offsetY = (this.size.height - this.viewSize.height) / 2
  121. center = {
  122. x: center.x - offsetX,
  123. y: center.y - offsetY,
  124. }
  125. }
  126. const pos = initMat.copy().invert().point(center);
  127. this.scale(pos, scale, initMat);
  128. }
  129. rotate(center: Pos, angleRad: number, initMat = this.viewMat) {
  130. this.mutMat(
  131. new Transform()
  132. .translate(center.x, center.y)
  133. .multiply(
  134. new Transform()
  135. .rotate(angleRad)
  136. .multiply(new Transform().translate(-center.x, -center.y))
  137. ),
  138. initMat
  139. );
  140. }
  141. rotatePixel(center: Pos, angleRad: number, initMat = this.viewMat) {
  142. const pos = initMat.copy().invert().point(center);
  143. this.rotate(pos, angleRad, initMat);
  144. }
  145. mutMat(mat: Transform, initMat = this.viewMat) {
  146. // this.setViewMat(mat.copy().multiply(initMat))
  147. this.setViewMat(initMat.copy().multiply(mat));
  148. }
  149. setViewMat(mat: number[] | Transform) {
  150. if (mat instanceof Transform) {
  151. this.viewMat = mat.copy();
  152. } else {
  153. this.viewMat = new Transform(mat);
  154. }
  155. this.bus.emit("transformChange", this.transform);
  156. }
  157. get transform() {
  158. return this.sizeMat ? this.sizeMat.copy().multiply(this.viewMat) : this.viewMat.copy();
  159. }
  160. get current() {
  161. return this.transform.decompose();
  162. }
  163. }