@annotorious/annotorious 3.4.7 → 3.5.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.
- package/dist/annotation/shapes/Line.svelte.d.ts +1 -0
- package/dist/annotation/shapes/index.d.ts +1 -0
- package/dist/annotation/utils/index.d.ts +0 -1
- package/dist/annotorious.css +1 -1
- package/dist/annotorious.es.js +1546 -1333
- package/dist/annotorious.es.js.map +1 -1
- package/dist/annotorious.js +2 -2
- package/dist/annotorious.js.map +1 -1
- package/dist/model/core/Shape.d.ts +2 -1
- package/dist/model/core/index.d.ts +1 -0
- package/dist/model/core/line/Line.d.ts +8 -0
- package/dist/model/core/line/index.d.ts +2 -0
- package/dist/model/core/line/lineUtils.d.ts +1 -0
- package/dist/model/core/shapeUtils.d.ts +4 -2
- package/dist/state/ImageAnnotationStore.d.ts +1 -1
- package/dist/state/spatialTree.d.ts +1 -1
- package/package.json +4 -4
- package/src/Annotorious.css +1 -0
- package/src/annotation/SVGAnnotationLayer.svelte +6 -2
- package/src/annotation/editors/multipolygon/MultiPolygonEditor.svelte +3 -5
- package/src/annotation/editors/polygon/PolygonEditor.svelte +1 -1
- package/src/annotation/shapes/Line.svelte +33 -0
- package/src/annotation/shapes/index.ts +2 -1
- package/src/annotation/tools/polygon/RubberbandPolygon.svelte +2 -2
- package/src/annotation/tools/rectangle/RubberbandRectangle.svelte +1 -1
- package/src/annotation/utils/index.ts +0 -1
- package/src/model/core/Shape.ts +3 -1
- package/src/model/core/index.ts +1 -0
- package/src/model/core/line/Line.ts +15 -0
- package/src/model/core/line/index.ts +2 -0
- package/src/model/core/line/lineUtils.ts +23 -0
- package/src/model/core/shapeUtils.ts +11 -3
- package/src/model/w3c/svg/SVGSelector.ts +51 -10
- package/src/state/ImageAnnotationStore.ts +1 -1
- package/src/state/ImageAnnotatorState.ts +2 -2
- package/src/state/spatialTree.ts +6 -6
- package/dist/annotation/utils/math.d.ts +0 -1
- package/src/annotation/utils/math.ts +0 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Bounds, Shape, ShapeType } from './Shape';
|
|
2
2
|
export interface ShapeUtil<T extends Shape> {
|
|
3
3
|
area: (shape: T) => number;
|
|
4
|
-
intersects: (shape: T, x: number, y: number) => boolean;
|
|
4
|
+
intersects: (shape: T, x: number, y: number, buffer?: number) => boolean;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* Registers a new ShapeUtil for a given shape type.
|
|
@@ -20,9 +20,10 @@ export declare const computeArea: (shape: Shape) => number;
|
|
|
20
20
|
* @param shape the shape
|
|
21
21
|
* @param x point x coord
|
|
22
22
|
* @param y point y coord
|
|
23
|
+
* @param buffer optional buffer around the point to consider as intersection
|
|
23
24
|
* @returns true if shape and point intersect
|
|
24
25
|
*/
|
|
25
|
-
export declare const intersects: (shape: Shape, x: number, y: number) => boolean;
|
|
26
|
+
export declare const intersects: (shape: Shape, x: number, y: number, buffer?: number) => boolean;
|
|
26
27
|
/**
|
|
27
28
|
* Computes Bounds from a given list of points.
|
|
28
29
|
* @param points the points
|
|
@@ -33,3 +34,4 @@ export declare const computePolygonArea: (points: [number, number][]) => number;
|
|
|
33
34
|
export declare const isPointInPolygon: (points: [number, number][], x: number, y: number) => boolean;
|
|
34
35
|
export declare const pointsToPath: (points: [number, number][], close?: boolean) => string;
|
|
35
36
|
export declare const simplifyPoints: (points: number[][], tolerance?: number) => [number, number][];
|
|
37
|
+
export declare const distance: (a: [number, number], b: [number, number]) => number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Annotation, Filter, Store, SvelteAnnotatorState, SvelteStore } from '@annotorious/core';
|
|
2
2
|
export type ImageAnnotationStore<I extends Annotation> = Store<I> & {
|
|
3
|
-
getAt(x: number, y: number, filter?: Filter<I
|
|
3
|
+
getAt(x: number, y: number, filter?: Filter<I>, buffer?: number): I | undefined;
|
|
4
4
|
getIntersecting(x: number, y: number, width: number, height: number): I[];
|
|
5
5
|
};
|
|
6
6
|
export type SvelteImageAnnotationStore<I extends Annotation = Annotation> = SvelteStore<I> & ImageAnnotationStore<I>;
|
|
@@ -10,7 +10,7 @@ interface IndexedTarget {
|
|
|
10
10
|
export declare const createSpatialTree: () => {
|
|
11
11
|
all: () => IndexedTarget[];
|
|
12
12
|
clear: () => void;
|
|
13
|
-
getAt: (x: number, y: number, filter?: Filter<Annotation
|
|
13
|
+
getAt: (x: number, y: number, filter?: Filter<Annotation>, buffer?: number) => ImageAnnotationTarget[];
|
|
14
14
|
getIntersecting: (x: number, y: number, width: number, height: number) => ImageAnnotationTarget[];
|
|
15
15
|
insert: (target: AnnotationTarget) => void;
|
|
16
16
|
remove: (target: AnnotationTarget) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@annotorious/annotorious",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Add image annotation functionality to any web page with a few lines of JavaScript",
|
|
5
5
|
"author": "Rainer Simon",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"typescript": "5.8.3",
|
|
47
47
|
"vite": "^5.4.19",
|
|
48
48
|
"vite-plugin-dts": "^4.5.4",
|
|
49
|
-
"vitest": "^3.
|
|
49
|
+
"vitest": "^3.2.4"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@annotorious/core": "3.
|
|
52
|
+
"@annotorious/core": "3.5.0",
|
|
53
53
|
"dequal": "^2.0.3",
|
|
54
54
|
"rbush": "^4.0.1",
|
|
55
55
|
"simplify-js": "^1.2.4",
|
|
56
|
-
"svg-pathdata": "^
|
|
56
|
+
"svg-pathdata": "^8.0.0",
|
|
57
57
|
"uuid": "^11.1.0"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/src/Annotorious.css
CHANGED
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
import { isImageAnnotation, ShapeType } from '../model';
|
|
6
6
|
import type { ImageAnnotation, Shape} from '../model';
|
|
7
7
|
import { getEditor as _getEditor, EditorMount } from './editors';
|
|
8
|
-
import { Ellipse, Polygon, Rectangle} from './shapes';
|
|
8
|
+
import { Ellipse, Line, MultiPolygon, Polygon, Rectangle} from './shapes';
|
|
9
9
|
import { getTool, listDrawingTools, ToolMount } from './tools';
|
|
10
10
|
import { enableResponsive } from './utils';
|
|
11
11
|
import { createSVGTransform } from './Transform';
|
|
12
12
|
import { addEventListeners, getSVGPoint } from './SVGAnnotationLayerPointerEvent';
|
|
13
13
|
import type { SvelteImageAnnotatorState } from 'src/state';
|
|
14
14
|
import type { DrawingMode } from 'src/AnnotoriousOpts';
|
|
15
|
-
import MultiPolygon from './shapes/MultiPolygon.svelte';
|
|
16
15
|
|
|
17
16
|
/** Props **/
|
|
18
17
|
export let drawingEnabled: boolean;
|
|
@@ -181,6 +180,11 @@
|
|
|
181
180
|
annotation={annotation}
|
|
182
181
|
geom={selector.geometry}
|
|
183
182
|
style={style} />
|
|
183
|
+
{:else if (selector?.type === ShapeType.LINE)}
|
|
184
|
+
<Line
|
|
185
|
+
annotation={annotation}
|
|
186
|
+
geom={selector.geometry}
|
|
187
|
+
style={style} />
|
|
184
188
|
{/if}
|
|
185
189
|
{/key}
|
|
186
190
|
{/if}
|
|
@@ -264,13 +264,11 @@
|
|
|
264
264
|
|
|
265
265
|
if (hasSelected) {
|
|
266
266
|
const updatedRings = polygon.rings.map((ring, ringIdx) => {
|
|
267
|
-
const
|
|
267
|
+
const selectedCornersInRing = selectedCorners.filter(s => s.polygon === polygonIdx && s.ring === ringIdx);
|
|
268
268
|
|
|
269
269
|
// Rings needs 3 points min
|
|
270
|
-
if (
|
|
271
|
-
const points = ring.points.filter((_, i) =>
|
|
272
|
-
!selectedCorners.some(s => s.polygon === polygonIdx && s.ring === ringIdx && s.point === i));
|
|
273
|
-
|
|
270
|
+
if (selectedCornersInRing.length && ring.points.length - selectedCornersInRing.length >= 3) {
|
|
271
|
+
const points = ring.points.filter((_, i) => !selectedCornersInRing.some(s => s.point === i));
|
|
274
272
|
return { points };
|
|
275
273
|
} else {
|
|
276
274
|
// No points selected on this ring
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
|
|
212
212
|
const onDeleteSelected = () => {
|
|
213
213
|
// Polygon needs 3 points min
|
|
214
|
-
if (geom.points.length <
|
|
214
|
+
if (geom.points.length - selectedCorners.length < 3) return;
|
|
215
215
|
|
|
216
216
|
const points = geom.points.filter((_, i) => !selectedCorners.includes(i)) as [number, number][];
|
|
217
217
|
const bounds = boundsFromPoints(points);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DrawingStyleExpression } from '@annotorious/core';
|
|
3
|
+
import type { Geometry, LineGeometry, ImageAnnotation } from '../../model';
|
|
4
|
+
import { computeStyle } from '../utils/styling';
|
|
5
|
+
|
|
6
|
+
/** Props */
|
|
7
|
+
export let annotation: ImageAnnotation;
|
|
8
|
+
export let geom: Geometry;
|
|
9
|
+
export let style: DrawingStyleExpression<ImageAnnotation> | undefined;
|
|
10
|
+
|
|
11
|
+
$: computedStyle = computeStyle(annotation, style);
|
|
12
|
+
|
|
13
|
+
const { points } = geom as LineGeometry;
|
|
14
|
+
const [[x1, y1], [x2, y2]] = points;
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<g class="a9s-annotation" data-id={annotation.id}>
|
|
18
|
+
<line
|
|
19
|
+
class="a9s-outer"
|
|
20
|
+
style={computedStyle ? 'display:none;' : undefined}
|
|
21
|
+
x1={x1}
|
|
22
|
+
y1={y1}
|
|
23
|
+
x2={x2}
|
|
24
|
+
y2={y2} />
|
|
25
|
+
|
|
26
|
+
<line
|
|
27
|
+
class="a9s-inner"
|
|
28
|
+
style={computedStyle}
|
|
29
|
+
x1={x1}
|
|
30
|
+
y1={y1}
|
|
31
|
+
x2={x2}
|
|
32
|
+
y2={y2} />
|
|
33
|
+
</g>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as Ellipse } from './Ellipse.svelte';
|
|
2
2
|
export { default as MultiPolygon } from './MultiPolygon.svelte';
|
|
3
3
|
export { default as Polygon } from './Polygon.svelte';
|
|
4
|
-
export { default as Rectangle } from './Rectangle.svelte';
|
|
4
|
+
export { default as Rectangle } from './Rectangle.svelte';
|
|
5
|
+
export { default as Line } from './Line.svelte';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount, createEventDispatcher } from 'svelte';
|
|
3
3
|
import type { DrawingMode } from '../../../AnnotoriousOpts';
|
|
4
|
-
import { boundsFromPoints, computeArea, ShapeType, type Polygon } from '../../../model';
|
|
5
|
-
import {
|
|
4
|
+
import { boundsFromPoints, computeArea, distance, ShapeType, type Polygon } from '../../../model';
|
|
5
|
+
import { getMaskDimensions } from '../../utils';
|
|
6
6
|
import type { Transform } from '../..';
|
|
7
7
|
|
|
8
8
|
const dispatch = createEventDispatcher<{ create: Polygon }>();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createEventDispatcher, onMount } from 'svelte';
|
|
3
3
|
import type { DrawingMode } from '../../../AnnotoriousOpts';
|
|
4
4
|
import { ShapeType, type Rectangle } from '../../../model';
|
|
5
|
-
import {
|
|
5
|
+
import type { Transform } from '../..';
|
|
6
6
|
|
|
7
7
|
const dispatch = createEventDispatcher<{ create: Rectangle }>();
|
|
8
8
|
|
package/src/model/core/Shape.ts
CHANGED
package/src/model/core/index.ts
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Bounds, Geometry, Shape } from '../Shape';
|
|
2
|
+
|
|
3
|
+
export interface Line extends Shape {
|
|
4
|
+
|
|
5
|
+
geometry: LineGeometry;
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LineGeometry extends Geometry {
|
|
10
|
+
|
|
11
|
+
points: [[number, number], [number, number]]
|
|
12
|
+
|
|
13
|
+
bounds: Bounds;
|
|
14
|
+
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ShapeType } from '../Shape';
|
|
2
|
+
import { distance, registerShapeUtil, type ShapeUtil } from '../shapeUtils';
|
|
3
|
+
import type { Line } from './Line';
|
|
4
|
+
|
|
5
|
+
const LineUtil: ShapeUtil<Line> = {
|
|
6
|
+
|
|
7
|
+
area: (_: Line): number => 0,
|
|
8
|
+
|
|
9
|
+
intersects: (l: Line, x: number, y: number, buffer: number = 2): boolean => {
|
|
10
|
+
// See https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
|
|
11
|
+
const [[x1, y1], [x2, y2]] = l.geometry.points;
|
|
12
|
+
|
|
13
|
+
// Twice the area of the triangle formed by connecting the three points
|
|
14
|
+
const area = Math.abs(((y2 - y1) * x) - ((x2 - x1) * y) + (x2 * y1) - (y2 * x1));
|
|
15
|
+
|
|
16
|
+
const length = distance([x1, y1], [x2, y2]);
|
|
17
|
+
|
|
18
|
+
return area / length <= buffer;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
registerShapeUtil(ShapeType.LINE, LineUtil);
|
|
@@ -5,7 +5,7 @@ export interface ShapeUtil<T extends Shape> {
|
|
|
5
5
|
|
|
6
6
|
area: (shape: T) => number;
|
|
7
7
|
|
|
8
|
-
intersects: (shape: T, x: number, y: number) => boolean;
|
|
8
|
+
intersects: (shape: T, x: number, y: number, buffer?: number) => boolean;
|
|
9
9
|
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -31,10 +31,11 @@ export const computeArea = (shape: Shape) => Utils[shape.type].area(shape);
|
|
|
31
31
|
* @param shape the shape
|
|
32
32
|
* @param x point x coord
|
|
33
33
|
* @param y point y coord
|
|
34
|
+
* @param buffer optional buffer around the point to consider as intersection
|
|
34
35
|
* @returns true if shape and point intersect
|
|
35
36
|
*/
|
|
36
|
-
export const intersects = (shape: Shape, x: number, y: number): boolean =>
|
|
37
|
-
Utils[shape.type].intersects(shape, x, y);
|
|
37
|
+
export const intersects = (shape: Shape, x: number, y: number, buffer?: number): boolean =>
|
|
38
|
+
Utils[shape.type].intersects(shape, x, y, buffer);
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Computes Bounds from a given list of points.
|
|
@@ -108,4 +109,11 @@ export const pointsToPath = (points: [number, number][], close: boolean = true):
|
|
|
108
109
|
export const simplifyPoints = (points: number[][], tolerance = 1): [number, number][] => {
|
|
109
110
|
const mapped = points.map(([x, y]) => ({ x, y }));
|
|
110
111
|
return simplify(mapped, tolerance, true).map(pt => [pt.x, pt.y]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const distance = (a: [number, number], b: [number, number]): number => {
|
|
115
|
+
const dx = Math.abs(b[0] - a[0]);
|
|
116
|
+
const dy = Math.abs(b[1] - a[1]);
|
|
117
|
+
|
|
118
|
+
return Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
|
|
111
119
|
}
|
|
@@ -4,6 +4,8 @@ import { svgPathToMultiPolygonElement } from './pathParser';
|
|
|
4
4
|
import type {
|
|
5
5
|
Ellipse,
|
|
6
6
|
EllipseGeometry,
|
|
7
|
+
Line,
|
|
8
|
+
LineGeometry,
|
|
7
9
|
MultiPolygon,
|
|
8
10
|
MultiPolygonGeometry,
|
|
9
11
|
Polygon,
|
|
@@ -59,6 +61,30 @@ const parseSVGEllipse = (value: string): Ellipse => {
|
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
const parseSVGLine = (value: string): Line => {
|
|
65
|
+
const doc = parseSVGXML(value);
|
|
66
|
+
|
|
67
|
+
const x1 = parseFloat(doc.getAttribute("x1")!);
|
|
68
|
+
const x2 = parseFloat(doc.getAttribute("x2")!);
|
|
69
|
+
const y1 = parseFloat(doc.getAttribute("y1")!);
|
|
70
|
+
const y2 = parseFloat(doc.getAttribute("y2")!);
|
|
71
|
+
|
|
72
|
+
const bounds = {
|
|
73
|
+
minX: Math.min(x1, x2),
|
|
74
|
+
minY: Math.min(y1, y2),
|
|
75
|
+
maxX: Math.max(x1, x2),
|
|
76
|
+
maxY: Math.max(y1, y2),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
type: ShapeType.LINE,
|
|
81
|
+
geometry: {
|
|
82
|
+
points: [[x1, y1], [x2, y2]],
|
|
83
|
+
bounds,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
const parseSVGPath = (value: string): Polygon | MultiPolygon => {
|
|
63
89
|
const doc = parseSVGXML(value);
|
|
64
90
|
|
|
@@ -99,6 +125,8 @@ export const parseSVGSelector = <T extends Shape>(valueOrSelector: SVGSelector |
|
|
|
99
125
|
return parseSVGPath(value) as unknown as T;
|
|
100
126
|
else if (value.includes('<ellipse '))
|
|
101
127
|
return parseSVGEllipse(value) as unknown as T;
|
|
128
|
+
else if (value.includes("<line "))
|
|
129
|
+
return parseSVGLine(value) as unknown as T;
|
|
102
130
|
else
|
|
103
131
|
throw 'Unsupported SVG shape: ' + value;
|
|
104
132
|
}
|
|
@@ -113,16 +141,29 @@ const serializeMultiPolygon = (geom: MultiPolygonGeometry) => {
|
|
|
113
141
|
export const serializeSVGSelector = (shape: Shape): SVGSelector => {
|
|
114
142
|
let value: string | undefined;
|
|
115
143
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
144
|
+
switch (shape.type) {
|
|
145
|
+
case ShapeType.POLYGON: {
|
|
146
|
+
const geom = shape.geometry as PolygonGeometry;
|
|
147
|
+
const { points } = geom;
|
|
148
|
+
value = `<svg><polygon points="${points.map((xy) => xy.join(',')).join(' ')}" /></svg>`;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case ShapeType.ELLIPSE: {
|
|
152
|
+
const geom = shape.geometry as EllipseGeometry;
|
|
153
|
+
value = `<svg><ellipse cx="${geom.cx}" cy="${geom.cy}" rx="${geom.rx}" ry="${geom.ry}" /></svg>`;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case ShapeType.MULTIPOLYGLON: {
|
|
157
|
+
const geom = shape.geometry as MultiPolygonGeometry;
|
|
158
|
+
value = `<svg>${serializeMultiPolygon(geom)}</svg>`;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case ShapeType.LINE: {
|
|
162
|
+
const geom = shape.geometry as LineGeometry;
|
|
163
|
+
const [[x1, y1], [x2, y2]] = geom.points;
|
|
164
|
+
value = `<svg><line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" /></svg>`;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
126
167
|
}
|
|
127
168
|
|
|
128
169
|
if (value) {
|
|
@@ -2,7 +2,7 @@ import type { Annotation, Filter, Store, SvelteAnnotatorState, SvelteStore } fro
|
|
|
2
2
|
|
|
3
3
|
export type ImageAnnotationStore<I extends Annotation> = Store<I> & {
|
|
4
4
|
|
|
5
|
-
getAt(x: number, y: number, filter?: Filter<I
|
|
5
|
+
getAt(x: number, y: number, filter?: Filter<I>, buffer?: number): I | undefined;
|
|
6
6
|
|
|
7
7
|
getIntersecting(x: number, y: number, width: number, height: number): I[];
|
|
8
8
|
|
|
@@ -54,8 +54,8 @@ export const createImageAnnotatorState = <I extends Annotation, E extends unknow
|
|
|
54
54
|
tree.update(oldValue.target, newValue.target));
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
const getAt = (x: number, y: number, filter?: Filter<I
|
|
58
|
-
const targets = tree.getAt(x, y, filter as Filter<Annotation
|
|
57
|
+
const getAt = (x: number, y: number, filter?: Filter<I>, buffer?: number): I | undefined => {
|
|
58
|
+
const targets = tree.getAt(x, y, filter as Filter<Annotation>, buffer);
|
|
59
59
|
|
|
60
60
|
if (filter) {
|
|
61
61
|
// Resolve annotations first, so we can filter
|
package/src/state/spatialTree.ts
CHANGED
|
@@ -75,18 +75,18 @@ export const createSpatialTree = () => {
|
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
const getAt = (x: number, y: number, filter?: Filter<Annotation
|
|
78
|
+
const getAt = (x: number, y: number, filter?: Filter<Annotation>, buffer: number = 0): ImageAnnotationTarget[] => {
|
|
79
79
|
const idxHits = tree.search({
|
|
80
|
-
minX: x,
|
|
81
|
-
minY: y,
|
|
82
|
-
maxX: x,
|
|
83
|
-
maxY: y
|
|
80
|
+
minX: x - buffer,
|
|
81
|
+
minY: y - buffer,
|
|
82
|
+
maxX: x + buffer,
|
|
83
|
+
maxY: y + buffer
|
|
84
84
|
}).map(item => item.target);
|
|
85
85
|
|
|
86
86
|
// Exact hit test on shape (not needed for rectangles!)
|
|
87
87
|
const exactHits = idxHits.filter(target => {
|
|
88
88
|
return (target.selector.type === ShapeType.RECTANGLE) ||
|
|
89
|
-
intersects(target.selector, x, y);
|
|
89
|
+
intersects(target.selector, x, y, buffer);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
// Get smallest shape
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const distance: (a: [number, number], b: [number, number]) => number;
|