geometry.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import type { PPTElement } from '@/lib/types/slides';
  2. import type { PercentageGeometry } from '@/lib/types/action';
  3. /**
  4. * Calculate percentage coordinates (0-100) for an element
  5. *
  6. * @param element - PPT element
  7. * @param viewportSize - Viewport width base, default 1000px
  8. * @returns Percentage geometry info, or null if the element has no position info
  9. */
  10. export function getElementPercentageGeometry(
  11. element: PPTElement,
  12. viewportSize: number = 1000,
  13. ): PercentageGeometry | null {
  14. // Only positioned elements have left/top/width/height
  15. if (
  16. !('left' in element) ||
  17. !('top' in element) ||
  18. !('width' in element) ||
  19. !('height' in element)
  20. ) {
  21. return null;
  22. }
  23. const { left, top, width, height } = element;
  24. // Calculate percentage coordinates (relative to viewportSize)
  25. const x = (left / viewportSize) * 100;
  26. const y = (top / (viewportSize * 0.5625)) * 100; // 16:9 ratio
  27. const w = (width / viewportSize) * 100;
  28. const h = (height / (viewportSize * 0.5625)) * 100;
  29. // Calculate center point
  30. const centerX = x + w / 2;
  31. const centerY = y + h / 2;
  32. return {
  33. x,
  34. y,
  35. w,
  36. h,
  37. centerX,
  38. centerY,
  39. };
  40. }
  41. /**
  42. * Find percentage geometry info by scene and element ID
  43. *
  44. * @param scene - Scene object
  45. * @param elementId - Element ID
  46. * @param viewportSize - Viewport width base, default 1000px
  47. * @returns Percentage geometry info, or null if element is not found or has no position info
  48. */
  49. export function findElementGeometry(
  50. // eslint-disable-next-line @typescript-eslint/no-explicit-any -- scene can be old or new format with different shapes
  51. scene: Record<string, any>,
  52. elementId: string,
  53. viewportSize: number = 1000,
  54. ): PercentageGeometry | null {
  55. // Support two scene structures:
  56. // 1. scene.elements (old format)
  57. // 2. scene.content.canvas.elements (new format)
  58. let elements: PPTElement[] | undefined;
  59. if (scene.type === 'slide') {
  60. if (scene.elements) {
  61. // Old format
  62. elements = scene.elements;
  63. } else if (scene.content?.canvas?.elements) {
  64. // New format
  65. elements = scene.content.canvas.elements;
  66. }
  67. }
  68. if (!elements) {
  69. return null;
  70. }
  71. const element = elements.find((el: PPTElement) => el.id === elementId);
  72. if (!element) {
  73. return null;
  74. }
  75. return getElementPercentageGeometry(element, viewportSize);
  76. }
  77. /**
  78. * Calculate which corner has the shortest distance to the element center
  79. *
  80. * @param geometry - Percentage geometry info
  81. * @returns Nearest corner coordinates { x: 0-100, y: 0-100 }
  82. */
  83. export function findNearestCorner(geometry: PercentageGeometry): {
  84. x: number;
  85. y: number;
  86. } {
  87. const { centerX, centerY } = geometry;
  88. // Coordinates of the four corners
  89. const corners = [
  90. { x: 0, y: 0 }, // Top-left
  91. { x: 100, y: 0 }, // Top-right
  92. { x: 0, y: 100 }, // Bottom-left
  93. { x: 100, y: 100 }, // Bottom-right
  94. ];
  95. // Calculate distances and find the nearest corner
  96. let minDistance = Infinity;
  97. let nearestCorner = corners[0];
  98. for (const corner of corners) {
  99. const distance = Math.sqrt(Math.pow(corner.x - centerX, 2) + Math.pow(corner.y - centerY, 2));
  100. if (distance < minDistance) {
  101. minDistance = distance;
  102. nearestCorner = corner;
  103. }
  104. }
  105. return nearestCorner;
  106. }