| 123456789101112131415161718192021222324252627282930313233343536 |
- export function pickObjectIdAt(imageData: ImageData, x: number, y: number): number | null {
- const pixelX = Math.max(0, Math.min(imageData.width - 1, Math.floor(x)));
- const pixelY = Math.max(0, Math.min(imageData.height - 1, Math.floor(y)));
- const offset = (pixelY * imageData.width + pixelX) * 4;
- const red = imageData.data[offset];
- const green = imageData.data[offset + 1];
- const blue = imageData.data[offset + 2];
- const alpha = imageData.data[offset + 3];
- if (!alpha || (red === 0 && green === 0 && blue === 0)) return null;
- return red + (green << 8) + (blue << 16);
- }
- /**
- * Map a click on the plate canvas to a pixel in the object-ID mask.
- *
- * The canvas paints at the mask's own resolution and is displayed with
- * object-contain inside a square box, so a mask that isn't square is
- * letterboxed. Measuring off the element rect would then offset every click by
- * the size of the bars. Returns null for clicks on a bar: those are off the
- * plate, and pickObjectIdAt would otherwise clamp them onto whichever object
- * touches that border.
- */
- export function plateClickToMaskPoint(
- bounds: { left: number; top: number; width: number; height: number },
- maskWidth: number,
- maskHeight: number,
- clientX: number,
- clientY: number,
- ): { x: number; y: number } | null {
- const scale = Math.min(bounds.width / maskWidth, bounds.height / maskHeight);
- if (!(scale > 0)) return null;
- const x = (clientX - bounds.left - (bounds.width - maskWidth * scale) / 2) / scale;
- const y = (clientY - bounds.top - (bounds.height - maskHeight * scale) / 2) / scale;
- if (x < 0 || y < 0 || x >= maskWidth || y >= maskHeight) return null;
- return { x, y };
- }
|