@annotorious/annotorious 3.7.22 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,8 @@ import type {
12
12
  PolygonGeometry,
13
13
  Polyline,
14
14
  PolylineGeometry,
15
+ Rectangle,
16
+ RectangleGeometry,
15
17
  Shape
16
18
  } from '../../core';
17
19
 
@@ -107,6 +109,68 @@ const parseSVGPathToPolyline = (value: string): Polyline => {
107
109
  }
108
110
  }
109
111
 
112
+ const parseSVGRect = (value: string): Rectangle => {
113
+ const doc = parseSVGXML(value);
114
+
115
+ const rect = doc.nodeName === 'rect' ? doc : Array.from(doc.querySelectorAll('rect'))[0];
116
+ if (!rect) throw new Error('Could not parse SVG rect');
117
+
118
+ const x = parseFloat(rect.getAttribute('x')!);
119
+ const y = parseFloat(rect.getAttribute('y')!);
120
+ const w = parseFloat(rect.getAttribute('width')!);
121
+ const h = parseFloat(rect.getAttribute('height')!);
122
+
123
+ const transform = rect.getAttribute('transform');
124
+ let rot = 0;
125
+
126
+ if (transform && transform.startsWith('rotate(')) {
127
+ const match = transform.match(/rotate\(([^)]+)\)/);
128
+ if (match) {
129
+ const params = match[1].split(/\s+/).map(parseFloat);
130
+ rot = (params[0] * Math.PI) / 180;
131
+ }
132
+ }
133
+
134
+ // Compute bounds
135
+ const cx = x + w / 2;
136
+ const cy = y + h / 2;
137
+
138
+ const corners = [
139
+ [x, y],
140
+ [x + w, y],
141
+ [x + w, y + h],
142
+ [x, y + h]
143
+ ];
144
+
145
+ // Rotate corners around center
146
+ const rotatedCorners = corners.map(([px, py]) => {
147
+ const dx = px - cx;
148
+ const dy = py - cy;
149
+
150
+ const cos = Math.cos(rot);
151
+ const sin = Math.sin(rot);
152
+
153
+ return [
154
+ cx + dx * cos - dy * sin,
155
+ cy + dx * sin + dy * cos
156
+ ];
157
+ });
158
+
159
+ const bounds = boundsFromPoints(rotatedCorners as [number, number][]);
160
+
161
+ return {
162
+ type: ShapeType.RECTANGLE,
163
+ geometry: {
164
+ x,
165
+ y,
166
+ w,
167
+ h,
168
+ rot,
169
+ bounds
170
+ }
171
+ };
172
+ }
173
+
110
174
  const parseSVGPathToPolygon = (value: string): Polygon | MultiPolygon => {
111
175
  const doc = parseSVGXML(value);
112
176
 
@@ -151,6 +215,8 @@ export const parseSVGSelector = <T extends Shape>(valueOrSelector: SVGSelector |
151
215
  return parseSVGEllipse(value) as unknown as T;
152
216
  else if (value.includes("<line "))
153
217
  return parseSVGLine(value) as unknown as T;
218
+ else if (value.includes('<rect '))
219
+ return parseSVGRect(value) as unknown as T;
154
220
  else
155
221
  throw 'Unsupported SVG shape: ' + value;
156
222
  }
@@ -166,6 +232,21 @@ export const serializeSVGSelector = (shape: Shape): SVGSelector => {
166
232
  let value: string | undefined;
167
233
 
168
234
  switch (shape.type) {
235
+ case ShapeType.RECTANGLE: {
236
+ const geom = shape.geometry as RectangleGeometry;
237
+ const { x, y, w, h, rot } = geom;
238
+
239
+ if (!rot) {
240
+ value = `<svg><rect x="${x}" y="${y}" width="${w}" height="${h}" /></svg>`;
241
+ } else {
242
+ const cx = x + w / 2;
243
+ const cy = y + h / 2;
244
+ const angle = ((rot ?? 0) * 180) / Math.PI;
245
+
246
+ value = `<svg><rect x="${x}" y="${y}" width="${w}" height="${h}" transform="rotate(${angle} ${cx} ${cy})" /></svg>`;
247
+ }
248
+ break;
249
+ }
169
250
  case ShapeType.POLYGON: {
170
251
  const geom = shape.geometry as PolygonGeometry;
171
252
  const { points } = geom;
@@ -83,11 +83,8 @@ export const createSpatialTree = () => {
83
83
  maxY: y + buffer
84
84
  }).map(item => item.target);
85
85
 
86
- // Exact hit test on shape (not needed for rectangles!)
87
- const exactHits = idxHits.filter(target => {
88
- return (target.selector.type === ShapeType.RECTANGLE) ||
89
- intersects(target.selector, x, y, buffer);
90
- });
86
+ // Exact hit test on shape
87
+ const exactHits = idxHits.filter(({ selector }) => intersects(selector, x, y, buffer));
91
88
 
92
89
  // Get smallest shape
93
90
  if (exactHits.length > 0) {