mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Zone/mask editor improvements (#11236)
* add points to completed polygons in zone/mask editor * change line order so edges are more easily clickable
This commit is contained in:
		
							parent
							
								
									f0054ceba4
								
							
						
					
					
						commit
						51dcdd6f4b
					
				@ -629,6 +629,7 @@ export default function MasksAndZones({
 | 
				
			|||||||
              scaledHeight &&
 | 
					              scaledHeight &&
 | 
				
			||||||
              editingPolygons ? (
 | 
					              editingPolygons ? (
 | 
				
			||||||
                <PolygonCanvas
 | 
					                <PolygonCanvas
 | 
				
			||||||
 | 
					                  containerRef={containerRef}
 | 
				
			||||||
                  camera={cameraConfig.name}
 | 
					                  camera={cameraConfig.name}
 | 
				
			||||||
                  width={scaledWidth}
 | 
					                  width={scaledWidth}
 | 
				
			||||||
                  height={scaledHeight}
 | 
					                  height={scaledHeight}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { useMemo, useRef, useState, useEffect } from "react";
 | 
					import React, { useMemo, useRef, useState, useEffect, RefObject } from "react";
 | 
				
			||||||
import PolygonDrawer from "./PolygonDrawer";
 | 
					import PolygonDrawer from "./PolygonDrawer";
 | 
				
			||||||
import { Stage, Layer, Image } from "react-konva";
 | 
					import { Stage, Layer, Image } from "react-konva";
 | 
				
			||||||
import Konva from "konva";
 | 
					import Konva from "konva";
 | 
				
			||||||
@ -7,6 +7,7 @@ import { Polygon, PolygonType } from "@/types/canvas";
 | 
				
			|||||||
import { useApiHost } from "@/api";
 | 
					import { useApiHost } from "@/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PolygonCanvasProps = {
 | 
					type PolygonCanvasProps = {
 | 
				
			||||||
 | 
					  containerRef: RefObject<HTMLDivElement>;
 | 
				
			||||||
  camera: string;
 | 
					  camera: string;
 | 
				
			||||||
  width: number;
 | 
					  width: number;
 | 
				
			||||||
  height: number;
 | 
					  height: number;
 | 
				
			||||||
@ -18,6 +19,7 @@ type PolygonCanvasProps = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function PolygonCanvas({
 | 
					export function PolygonCanvas({
 | 
				
			||||||
 | 
					  containerRef,
 | 
				
			||||||
  camera,
 | 
					  camera,
 | 
				
			||||||
  width,
 | 
					  width,
 | 
				
			||||||
  height,
 | 
					  height,
 | 
				
			||||||
@ -55,10 +57,6 @@ export function PolygonCanvas({
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }, [videoElement]);
 | 
					  }, [videoElement]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getMousePos = (stage: Konva.Stage) => {
 | 
					 | 
				
			||||||
    return [stage.getPointerPosition()!.x, stage.getPointerPosition()!.y];
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const addPointToPolygon = (polygon: Polygon, newPoint: number[]) => {
 | 
					  const addPointToPolygon = (polygon: Polygon, newPoint: number[]) => {
 | 
				
			||||||
    const points = polygon.points;
 | 
					    const points = polygon.points;
 | 
				
			||||||
    const pointsOrder = polygon.pointsOrder;
 | 
					    const pointsOrder = polygon.pointsOrder;
 | 
				
			||||||
@ -99,37 +97,6 @@ export function PolygonCanvas({
 | 
				
			|||||||
    return { updatedPoints, updatedPointsOrder };
 | 
					    return { updatedPoints, updatedPointsOrder };
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const isMouseOverFirstPoint = (polygon: Polygon, mousePos: number[]) => {
 | 
					 | 
				
			||||||
    if (!polygon || !polygon.points || polygon.points.length < 1) {
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const [firstPoint] = polygon.points;
 | 
					 | 
				
			||||||
    const distance = Math.hypot(
 | 
					 | 
				
			||||||
      mousePos[0] - firstPoint[0],
 | 
					 | 
				
			||||||
      mousePos[1] - firstPoint[1],
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    return distance < 10;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const isMouseOverAnyPoint = (polygon: Polygon, mousePos: number[]) => {
 | 
					 | 
				
			||||||
    if (!polygon || !polygon.points || polygon.points.length === 0) {
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (let i = 1; i < polygon.points.length; i++) {
 | 
					 | 
				
			||||||
      const point = polygon.points[i];
 | 
					 | 
				
			||||||
      const distance = Math.hypot(
 | 
					 | 
				
			||||||
        mousePos[0] - point[0],
 | 
					 | 
				
			||||||
        mousePos[1] - point[1],
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      if (distance < 10) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleMouseDown = (e: KonvaEventObject<MouseEvent | TouchEvent>) => {
 | 
					  const handleMouseDown = (e: KonvaEventObject<MouseEvent | TouchEvent>) => {
 | 
				
			||||||
    if (activePolygonIndex === undefined || !polygons) {
 | 
					    if (activePolygonIndex === undefined || !polygons) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
@ -138,11 +105,13 @@ export function PolygonCanvas({
 | 
				
			|||||||
    const updatedPolygons = [...polygons];
 | 
					    const updatedPolygons = [...polygons];
 | 
				
			||||||
    const activePolygon = updatedPolygons[activePolygonIndex];
 | 
					    const activePolygon = updatedPolygons[activePolygonIndex];
 | 
				
			||||||
    const stage = e.target.getStage()!;
 | 
					    const stage = e.target.getStage()!;
 | 
				
			||||||
    const mousePos = getMousePos(stage);
 | 
					    const mousePos = stage.getPointerPosition() ?? { x: 0, y: 0 };
 | 
				
			||||||
 | 
					    const intersection = stage.getIntersection(mousePos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
      activePolygon.points.length >= 3 &&
 | 
					      activePolygon.points.length >= 3 &&
 | 
				
			||||||
      isMouseOverFirstPoint(activePolygon, mousePos)
 | 
					      intersection?.getClassName() == "Circle" &&
 | 
				
			||||||
 | 
					      intersection?.name() == "point-0"
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
      // Close the polygon
 | 
					      // Close the polygon
 | 
				
			||||||
      updatedPolygons[activePolygonIndex] = {
 | 
					      updatedPolygons[activePolygonIndex] = {
 | 
				
			||||||
@ -152,12 +121,13 @@ export function PolygonCanvas({
 | 
				
			|||||||
      setPolygons(updatedPolygons);
 | 
					      setPolygons(updatedPolygons);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
        !activePolygon.isFinished &&
 | 
					        (!activePolygon.isFinished &&
 | 
				
			||||||
        !isMouseOverAnyPoint(activePolygon, mousePos)
 | 
					          intersection?.getClassName() !== "Circle") ||
 | 
				
			||||||
 | 
					        (activePolygon.isFinished && intersection?.name() == "unfilled-line")
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
        const { updatedPoints, updatedPointsOrder } = addPointToPolygon(
 | 
					        const { updatedPoints, updatedPointsOrder } = addPointToPolygon(
 | 
				
			||||||
          activePolygon,
 | 
					          activePolygon,
 | 
				
			||||||
          mousePos,
 | 
					          [mousePos.x, mousePos.y],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        updatedPolygons[activePolygonIndex] = {
 | 
					        updatedPolygons[activePolygonIndex] = {
 | 
				
			||||||
@ -168,62 +138,6 @@ export function PolygonCanvas({
 | 
				
			|||||||
        setPolygons(updatedPolygons);
 | 
					        setPolygons(updatedPolygons);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleMouseOverStartPoint = (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => {
 | 
					 | 
				
			||||||
    if (activePolygonIndex === undefined || !polygons) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const activePolygon = polygons[activePolygonIndex];
 | 
					 | 
				
			||||||
    if (!activePolygon.isFinished && activePolygon.points.length >= 3) {
 | 
					 | 
				
			||||||
      e.target.getStage()!.container().style.cursor = "default";
 | 
					 | 
				
			||||||
      e.currentTarget.scale({ x: 2, y: 2 });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleMouseOutStartPoint = (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => {
 | 
					 | 
				
			||||||
    e.currentTarget.scale({ x: 1, y: 1 });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (activePolygonIndex === undefined || !polygons) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const activePolygon = polygons[activePolygonIndex];
 | 
					 | 
				
			||||||
    if (
 | 
					 | 
				
			||||||
      (!activePolygon.isFinished && activePolygon.points.length >= 3) ||
 | 
					 | 
				
			||||||
      activePolygon.isFinished
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
      e.currentTarget.scale({ x: 1, y: 1 });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleMouseOverAnyPoint = (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => {
 | 
					 | 
				
			||||||
    if (!polygons) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    e.target.getStage()!.container().style.cursor = "move";
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const handleMouseOutAnyPoint = (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => {
 | 
					 | 
				
			||||||
    if (activePolygonIndex === undefined || !polygons) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    const activePolygon = polygons[activePolygonIndex];
 | 
					 | 
				
			||||||
    if (activePolygon.isFinished) {
 | 
					 | 
				
			||||||
      e.target.getStage()!.container().style.cursor = "default";
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      e.target.getStage()!.container().style.cursor = "crosshair";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handlePointDragMove = (
 | 
					  const handlePointDragMove = (
 | 
				
			||||||
@ -237,7 +151,8 @@ export function PolygonCanvas({
 | 
				
			|||||||
    const activePolygon = updatedPolygons[activePolygonIndex];
 | 
					    const activePolygon = updatedPolygons[activePolygonIndex];
 | 
				
			||||||
    const stage = e.target.getStage();
 | 
					    const stage = e.target.getStage();
 | 
				
			||||||
    if (stage) {
 | 
					    if (stage) {
 | 
				
			||||||
      const index = e.target.index - 1;
 | 
					      // we add an unfilled line for adding points when finished
 | 
				
			||||||
 | 
					      const index = e.target.index - (activePolygon.isFinished ? 2 : 1);
 | 
				
			||||||
      const pos = [e.target._lastPos!.x, e.target._lastPos!.y];
 | 
					      const pos = [e.target._lastPos!.x, e.target._lastPos!.y];
 | 
				
			||||||
      if (pos[0] < 0) pos[0] = 0;
 | 
					      if (pos[0] < 0) pos[0] = 0;
 | 
				
			||||||
      if (pos[1] < 0) pos[1] = 0;
 | 
					      if (pos[1] < 0) pos[1] = 0;
 | 
				
			||||||
@ -272,26 +187,17 @@ export function PolygonCanvas({
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleStageMouseOver = (
 | 
					  const handleStageMouseOver = () => {
 | 
				
			||||||
    e: Konva.KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => {
 | 
					 | 
				
			||||||
    if (activePolygonIndex === undefined || !polygons) {
 | 
					    if (activePolygonIndex === undefined || !polygons) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const updatedPolygons = [...polygons];
 | 
					    const updatedPolygons = [...polygons];
 | 
				
			||||||
    const activePolygon = updatedPolygons[activePolygonIndex];
 | 
					    const activePolygon = updatedPolygons[activePolygonIndex];
 | 
				
			||||||
    const stage = e.target.getStage()!;
 | 
					 | 
				
			||||||
    const mousePos = getMousePos(stage);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (
 | 
					    if (containerRef.current && !activePolygon.isFinished) {
 | 
				
			||||||
      activePolygon.isFinished ||
 | 
					      containerRef.current.style.cursor = "crosshair";
 | 
				
			||||||
      isMouseOverAnyPoint(activePolygon, mousePos) ||
 | 
					    }
 | 
				
			||||||
      isMouseOverFirstPoint(activePolygon, mousePos)
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    e.target.getStage()!.container().style.cursor = "crosshair";
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
@ -336,6 +242,7 @@ export function PolygonCanvas({
 | 
				
			|||||||
              selectedZoneMask.includes(polygon.type)) &&
 | 
					              selectedZoneMask.includes(polygon.type)) &&
 | 
				
			||||||
            index !== activePolygonIndex && (
 | 
					            index !== activePolygonIndex && (
 | 
				
			||||||
              <PolygonDrawer
 | 
					              <PolygonDrawer
 | 
				
			||||||
 | 
					                stageRef={stageRef}
 | 
				
			||||||
                key={index}
 | 
					                key={index}
 | 
				
			||||||
                points={polygon.points}
 | 
					                points={polygon.points}
 | 
				
			||||||
                isActive={index === activePolygonIndex}
 | 
					                isActive={index === activePolygonIndex}
 | 
				
			||||||
@ -344,10 +251,6 @@ export function PolygonCanvas({
 | 
				
			|||||||
                color={polygon.color}
 | 
					                color={polygon.color}
 | 
				
			||||||
                handlePointDragMove={handlePointDragMove}
 | 
					                handlePointDragMove={handlePointDragMove}
 | 
				
			||||||
                handleGroupDragEnd={handleGroupDragEnd}
 | 
					                handleGroupDragEnd={handleGroupDragEnd}
 | 
				
			||||||
                handleMouseOverStartPoint={handleMouseOverStartPoint}
 | 
					 | 
				
			||||||
                handleMouseOutStartPoint={handleMouseOutStartPoint}
 | 
					 | 
				
			||||||
                handleMouseOverAnyPoint={handleMouseOverAnyPoint}
 | 
					 | 
				
			||||||
                handleMouseOutAnyPoint={handleMouseOutAnyPoint}
 | 
					 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
@ -356,6 +259,7 @@ export function PolygonCanvas({
 | 
				
			|||||||
          (selectedZoneMask === undefined ||
 | 
					          (selectedZoneMask === undefined ||
 | 
				
			||||||
            selectedZoneMask.includes(polygons[activePolygonIndex].type)) && (
 | 
					            selectedZoneMask.includes(polygons[activePolygonIndex].type)) && (
 | 
				
			||||||
            <PolygonDrawer
 | 
					            <PolygonDrawer
 | 
				
			||||||
 | 
					              stageRef={stageRef}
 | 
				
			||||||
              key={activePolygonIndex}
 | 
					              key={activePolygonIndex}
 | 
				
			||||||
              points={polygons[activePolygonIndex].points}
 | 
					              points={polygons[activePolygonIndex].points}
 | 
				
			||||||
              isActive={true}
 | 
					              isActive={true}
 | 
				
			||||||
@ -364,10 +268,6 @@ export function PolygonCanvas({
 | 
				
			|||||||
              color={polygons[activePolygonIndex].color}
 | 
					              color={polygons[activePolygonIndex].color}
 | 
				
			||||||
              handlePointDragMove={handlePointDragMove}
 | 
					              handlePointDragMove={handlePointDragMove}
 | 
				
			||||||
              handleGroupDragEnd={handleGroupDragEnd}
 | 
					              handleGroupDragEnd={handleGroupDragEnd}
 | 
				
			||||||
              handleMouseOverStartPoint={handleMouseOverStartPoint}
 | 
					 | 
				
			||||||
              handleMouseOutStartPoint={handleMouseOutStartPoint}
 | 
					 | 
				
			||||||
              handleMouseOverAnyPoint={handleMouseOverAnyPoint}
 | 
					 | 
				
			||||||
              handleMouseOutAnyPoint={handleMouseOutAnyPoint}
 | 
					 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
      </Layer>
 | 
					      </Layer>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,11 @@
 | 
				
			|||||||
import { useCallback, useMemo, useRef, useState } from "react";
 | 
					import {
 | 
				
			||||||
 | 
					  RefObject,
 | 
				
			||||||
 | 
					  useCallback,
 | 
				
			||||||
 | 
					  useEffect,
 | 
				
			||||||
 | 
					  useMemo,
 | 
				
			||||||
 | 
					  useRef,
 | 
				
			||||||
 | 
					  useState,
 | 
				
			||||||
 | 
					} from "react";
 | 
				
			||||||
import { Line, Circle, Group } from "react-konva";
 | 
					import { Line, Circle, Group } from "react-konva";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  minMax,
 | 
					  minMax,
 | 
				
			||||||
@ -9,9 +16,9 @@ import {
 | 
				
			|||||||
import type { KonvaEventObject } from "konva/lib/Node";
 | 
					import type { KonvaEventObject } from "konva/lib/Node";
 | 
				
			||||||
import Konva from "konva";
 | 
					import Konva from "konva";
 | 
				
			||||||
import { Vector2d } from "konva/lib/types";
 | 
					import { Vector2d } from "konva/lib/types";
 | 
				
			||||||
import { isMobileOnly } from "react-device-detect";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PolygonDrawerProps = {
 | 
					type PolygonDrawerProps = {
 | 
				
			||||||
 | 
					  stageRef: RefObject<Konva.Stage>;
 | 
				
			||||||
  points: number[][];
 | 
					  points: number[][];
 | 
				
			||||||
  isActive: boolean;
 | 
					  isActive: boolean;
 | 
				
			||||||
  isHovered: boolean;
 | 
					  isHovered: boolean;
 | 
				
			||||||
@ -19,21 +26,10 @@ type PolygonDrawerProps = {
 | 
				
			|||||||
  color: number[];
 | 
					  color: number[];
 | 
				
			||||||
  handlePointDragMove: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
 | 
					  handlePointDragMove: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
 | 
				
			||||||
  handleGroupDragEnd: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
 | 
					  handleGroupDragEnd: (e: KonvaEventObject<MouseEvent | TouchEvent>) => void;
 | 
				
			||||||
  handleMouseOverStartPoint: (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => void;
 | 
					 | 
				
			||||||
  handleMouseOutStartPoint: (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => void;
 | 
					 | 
				
			||||||
  handleMouseOverAnyPoint: (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => void;
 | 
					 | 
				
			||||||
  handleMouseOutAnyPoint: (
 | 
					 | 
				
			||||||
    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					 | 
				
			||||||
  ) => void;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function PolygonDrawer({
 | 
					export default function PolygonDrawer({
 | 
				
			||||||
 | 
					  stageRef,
 | 
				
			||||||
  points,
 | 
					  points,
 | 
				
			||||||
  isActive,
 | 
					  isActive,
 | 
				
			||||||
  isHovered,
 | 
					  isHovered,
 | 
				
			||||||
@ -41,31 +37,41 @@ export default function PolygonDrawer({
 | 
				
			|||||||
  color,
 | 
					  color,
 | 
				
			||||||
  handlePointDragMove,
 | 
					  handlePointDragMove,
 | 
				
			||||||
  handleGroupDragEnd,
 | 
					  handleGroupDragEnd,
 | 
				
			||||||
  handleMouseOverStartPoint,
 | 
					 | 
				
			||||||
  handleMouseOutStartPoint,
 | 
					 | 
				
			||||||
  handleMouseOverAnyPoint,
 | 
					 | 
				
			||||||
  handleMouseOutAnyPoint,
 | 
					 | 
				
			||||||
}: PolygonDrawerProps) {
 | 
					}: PolygonDrawerProps) {
 | 
				
			||||||
  const vertexRadius = isMobileOnly ? 12 : 6;
 | 
					  const vertexRadius = 6;
 | 
				
			||||||
  const flattenedPoints = useMemo(() => flattenPoints(points), [points]);
 | 
					  const flattenedPoints = useMemo(() => flattenPoints(points), [points]);
 | 
				
			||||||
  const [stage, setStage] = useState<Konva.Stage>();
 | 
					 | 
				
			||||||
  const [minMaxX, setMinMaxX] = useState([0, 0]);
 | 
					  const [minMaxX, setMinMaxX] = useState([0, 0]);
 | 
				
			||||||
  const [minMaxY, setMinMaxY] = useState([0, 0]);
 | 
					  const [minMaxY, setMinMaxY] = useState([0, 0]);
 | 
				
			||||||
  const groupRef = useRef<Konva.Group>(null);
 | 
					  const groupRef = useRef<Konva.Group>(null);
 | 
				
			||||||
 | 
					  const [cursor, setCursor] = useState("default");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleGroupMouseOver = (
 | 
					  const handleMouseOverPoint = (
 | 
				
			||||||
    e: Konva.KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
				
			||||||
  ) => {
 | 
					  ) => {
 | 
				
			||||||
    if (!isFinished) return;
 | 
					    if (!e.target) return;
 | 
				
			||||||
    e.target.getStage()!.container().style.cursor = "move";
 | 
					
 | 
				
			||||||
    setStage(e.target.getStage()!);
 | 
					    if (!isFinished && points.length >= 3 && e.target.name() === "point-0") {
 | 
				
			||||||
 | 
					      e.target.scale({ x: 2, y: 2 });
 | 
				
			||||||
 | 
					      setCursor("crosshair");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      setCursor("move");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleGroupMouseOut = (
 | 
					  const handleMouseOutPoint = (
 | 
				
			||||||
    e: Konva.KonvaEventObject<MouseEvent | TouchEvent>,
 | 
					    e: KonvaEventObject<MouseEvent | TouchEvent>,
 | 
				
			||||||
  ) => {
 | 
					  ) => {
 | 
				
			||||||
    if (!e.target || !isFinished) return;
 | 
					    if (!e.target) return;
 | 
				
			||||||
    e.target.getStage()!.container().style.cursor = "default";
 | 
					
 | 
				
			||||||
 | 
					    if (isFinished) {
 | 
				
			||||||
 | 
					      setCursor("default");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      setCursor("crosshair");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (e.target.name() === "point-0") {
 | 
				
			||||||
 | 
					      e.target.scale({ x: 1, y: 1 });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleGroupDragStart = () => {
 | 
					  const handleGroupDragStart = () => {
 | 
				
			||||||
@ -76,13 +82,13 @@ export default function PolygonDrawer({
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const groupDragBound = (pos: Vector2d) => {
 | 
					  const groupDragBound = (pos: Vector2d) => {
 | 
				
			||||||
    if (!stage) {
 | 
					    if (!stageRef.current) {
 | 
				
			||||||
      return pos;
 | 
					      return pos;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let { x, y } = pos;
 | 
					    let { x, y } = pos;
 | 
				
			||||||
    const sw = stage.width();
 | 
					    const sw = stageRef.current.width();
 | 
				
			||||||
    const sh = stage.height();
 | 
					    const sh = stageRef.current.height();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (minMaxY[0] + y < 0) y = -1 * minMaxY[0];
 | 
					    if (minMaxY[0] + y < 0) y = -1 * minMaxY[0];
 | 
				
			||||||
    if (minMaxX[0] + x < 0) x = -1 * minMaxX[0];
 | 
					    if (minMaxX[0] + x < 0) x = -1 * minMaxX[0];
 | 
				
			||||||
@ -99,6 +105,14 @@ export default function PolygonDrawer({
 | 
				
			|||||||
    [color],
 | 
					    [color],
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!stageRef.current) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stageRef.current.container().style.cursor = cursor;
 | 
				
			||||||
 | 
					  }, [stageRef, cursor]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Group
 | 
					    <Group
 | 
				
			||||||
      name="polygon"
 | 
					      name="polygon"
 | 
				
			||||||
@ -107,55 +121,62 @@ export default function PolygonDrawer({
 | 
				
			|||||||
      onDragStart={isActive ? handleGroupDragStart : undefined}
 | 
					      onDragStart={isActive ? handleGroupDragStart : undefined}
 | 
				
			||||||
      onDragEnd={isActive ? handleGroupDragEnd : undefined}
 | 
					      onDragEnd={isActive ? handleGroupDragEnd : undefined}
 | 
				
			||||||
      dragBoundFunc={isActive ? groupDragBound : undefined}
 | 
					      dragBoundFunc={isActive ? groupDragBound : undefined}
 | 
				
			||||||
      onMouseOver={isActive ? handleGroupMouseOver : undefined}
 | 
					 | 
				
			||||||
      onTouchStart={isActive ? handleGroupMouseOver : undefined}
 | 
					 | 
				
			||||||
      onMouseOut={isActive ? handleGroupMouseOut : undefined}
 | 
					 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <Line
 | 
					      <Line
 | 
				
			||||||
 | 
					        name="filled-line"
 | 
				
			||||||
        points={flattenedPoints}
 | 
					        points={flattenedPoints}
 | 
				
			||||||
        stroke={colorString(true)}
 | 
					        stroke={colorString(true)}
 | 
				
			||||||
        strokeWidth={3}
 | 
					        strokeWidth={3}
 | 
				
			||||||
 | 
					        hitStrokeWidth={12}
 | 
				
			||||||
        closed={isFinished}
 | 
					        closed={isFinished}
 | 
				
			||||||
        fill={colorString(isActive || isHovered ? true : false)}
 | 
					        fill={colorString(isActive || isHovered ? true : false)}
 | 
				
			||||||
 | 
					        onMouseOver={() =>
 | 
				
			||||||
 | 
					          isFinished ? setCursor("move") : setCursor("crosshair")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        onMouseOut={() =>
 | 
				
			||||||
 | 
					          isFinished ? setCursor("default") : setCursor("crosshair")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
 | 
					      {isFinished && isActive && (
 | 
				
			||||||
 | 
					        <Line
 | 
				
			||||||
 | 
					          name="unfilled-line"
 | 
				
			||||||
 | 
					          points={flattenedPoints}
 | 
				
			||||||
 | 
					          hitStrokeWidth={12}
 | 
				
			||||||
 | 
					          closed={isFinished}
 | 
				
			||||||
 | 
					          fillEnabled={false}
 | 
				
			||||||
 | 
					          onMouseOver={() => setCursor("crosshair")}
 | 
				
			||||||
 | 
					          onMouseOut={() =>
 | 
				
			||||||
 | 
					            isFinished ? setCursor("default") : setCursor("crosshair")
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
      {points.map((point, index) => {
 | 
					      {points.map((point, index) => {
 | 
				
			||||||
        if (!isActive) {
 | 
					        if (!isActive) {
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const x = point[0];
 | 
					        const x = point[0];
 | 
				
			||||||
        const y = point[1];
 | 
					        const y = point[1];
 | 
				
			||||||
        const startPointAttr =
 | 
					 | 
				
			||||||
          index === 0
 | 
					 | 
				
			||||||
            ? {
 | 
					 | 
				
			||||||
                hitStrokeWidth: 12,
 | 
					 | 
				
			||||||
                onMouseOver: handleMouseOverStartPoint,
 | 
					 | 
				
			||||||
                onMouseOut: handleMouseOutStartPoint,
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            : null;
 | 
					 | 
				
			||||||
        const otherPointsAttr =
 | 
					 | 
				
			||||||
          index !== 0
 | 
					 | 
				
			||||||
            ? {
 | 
					 | 
				
			||||||
                onMouseOver: handleMouseOverAnyPoint,
 | 
					 | 
				
			||||||
                onMouseOut: handleMouseOutAnyPoint,
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            : null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
          <Circle
 | 
					          <Circle
 | 
				
			||||||
            key={index}
 | 
					            key={index}
 | 
				
			||||||
 | 
					            name={`point-${index}`}
 | 
				
			||||||
            x={x}
 | 
					            x={x}
 | 
				
			||||||
            y={y}
 | 
					            y={y}
 | 
				
			||||||
            radius={vertexRadius}
 | 
					            radius={vertexRadius}
 | 
				
			||||||
            stroke={colorString(true)}
 | 
					            stroke={colorString(true)}
 | 
				
			||||||
            fill="#ffffff"
 | 
					            fill="#ffffff"
 | 
				
			||||||
            strokeWidth={3}
 | 
					            strokeWidth={3}
 | 
				
			||||||
 | 
					            hitStrokeWidth={index === 0 ? 12 : 9}
 | 
				
			||||||
 | 
					            onMouseOver={handleMouseOverPoint}
 | 
				
			||||||
 | 
					            onMouseOut={handleMouseOutPoint}
 | 
				
			||||||
            draggable={isActive}
 | 
					            draggable={isActive}
 | 
				
			||||||
            onDragMove={isActive ? handlePointDragMove : undefined}
 | 
					            onDragMove={isActive ? handlePointDragMove : undefined}
 | 
				
			||||||
            dragBoundFunc={(pos) => {
 | 
					            dragBoundFunc={(pos) => {
 | 
				
			||||||
              if (stage) {
 | 
					              if (stageRef.current) {
 | 
				
			||||||
                return dragBoundFunc(
 | 
					                return dragBoundFunc(
 | 
				
			||||||
                  stage.width(),
 | 
					                  stageRef.current.width(),
 | 
				
			||||||
                  stage.height(),
 | 
					                  stageRef.current.height(),
 | 
				
			||||||
                  vertexRadius,
 | 
					                  vertexRadius,
 | 
				
			||||||
                  pos,
 | 
					                  pos,
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
@ -163,8 +184,6 @@ export default function PolygonDrawer({
 | 
				
			|||||||
                return pos;
 | 
					                return pos;
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
            {...startPointAttr}
 | 
					 | 
				
			||||||
            {...otherPointsAttr}
 | 
					 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      })}
 | 
					      })}
 | 
				
			||||||
 | 
				
			|||||||
@ -41,7 +41,7 @@ export default function PolygonEditControls({
 | 
				
			|||||||
          ...activePolygon.pointsOrder.slice(0, lastPointOrderIndex),
 | 
					          ...activePolygon.pointsOrder.slice(0, lastPointOrderIndex),
 | 
				
			||||||
          ...activePolygon.pointsOrder.slice(lastPointOrderIndex + 1),
 | 
					          ...activePolygon.pointsOrder.slice(lastPointOrderIndex + 1),
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        isFinished: false,
 | 
					        isFinished: activePolygon.isFinished && activePolygon.points.length > 3,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      setPolygons(updatedPolygons);
 | 
					      setPolygons(updatedPolygons);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user