【问题标题】:Cannot get intersecting points of polygons in canvas无法在画布中获取多边形的交点
【发布时间】:2021-07-03 01:42:30
【问题描述】:

我目前正在画布中绘制两个随机多边形,并希望找到它们的交点,这样我就可以使用这些 x 和 y 坐标在最后将多边形合并为一个。即使多边形相互交叉,它的坐标也不匹配。如果有什么方法可以找到,请告诉我?

const Canvas: React.FC = () => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [drawingState, setDrawingState] = useState<boolean>(false);
  const [startingStateX, setStartingStateX] = useState(0);
  const [startingStateY, setStartingStateY] = useState(0);
  const [currentDrawing, setCurrentDrawing] = useState<number[][]>([]);
  const [drawnObject, setDrawnObject] = useState<number[][][]>([]);

  const { current } = canvasRef;
  let ctx: CanvasRenderingContext2D | null;
  if (current !== null) {
    ctx = current.getContext("2d");
  }

  const initiateDrawing = (x: number, y: number) => {
    setDrawingState(true);
    setStartingStateX(x);
    setStartingStateY(y);
    draw(x, y);
  };

  const draw = (x: number, y: number) => {
    if (drawingState && ctx !== null) {
      ctx.lineWidth = 1;
      ctx.lineCap = "round";
      ctx.lineTo(x, y);
      ctx.stroke();
      ctx.beginPath();
      ctx.moveTo(x, y);
      setCurrentDrawing((prev: number[][]) => [...prev, [x, y]]);
    }
  };

  const endDrawing = () => {
    setDrawingState(false);
    if (ctx !== null) {
      ctx.lineWidth = 1;
      ctx.lineCap = "round";
      ctx.lineTo(startingStateX, startingStateY);
      ctx.stroke();
      ctx.closePath();
      ctx.beginPath();
      setDrawnObject((prev: number[][][]) => [...prev, currentDrawing]);
      setCurrentDrawing([]);
    }
  };

  return (
    <>
      <div id="canvasWrapper">
        <canvas
          onMouseDown={(e) => initiateDrawing(e.clientX, e.clientY)}
          onMouseUp={endDrawing}
          onMouseMove={(e) => draw(e.clientX, e.clientY)}
          ref={canvasRef}
          width={window.innerWidth * 0.8}
          height={window.innerHeight}
        />
      </div>
      <List drawnObject={drawnObject} />
    </>
  );
};

export default Canvas;

底部是我找到交叉点的逻辑。

interface listProps {
  drawnObject: number[][][];
}

const List: React.FC<listProps> = (props: listProps) => {
  const [clickedPolygon, setClickedPolygon] = useState<number[][][]>([]);
  const [clickedIndex, setClickedIndex] = useState<number[]>([]);

  const handleClickedPolygon = (index: number, polygon: number[][]) => {
    const checkIndex = clickedIndex.indexOf(index);
    if (checkIndex === -1) {
      setClickedIndex((prev: number[]) => [...prev, index]);
      setClickedPolygon((prev: number[][][]) => [
        ...prev,
        props.drawnObject[index],
      ]);
    } else {
      const updatedIndexList = clickedIndex.filter((el) => el !== index);
      setClickedIndex(updatedIndexList);
      const updatedPolygonList = clickedPolygon.filter((el, i) => i !== index);
      setClickedPolygon(updatedPolygonList);
    }
  };

  const findCrossingPoint = () => {
    const maxIndex = clickedPolygon.reduce(
      (acc, cur, idx, arr) => (arr[acc].length >= cur.length ? acc : idx),
      0
    );
    const startPolygon = clickedPolygon[maxIndex];
    for (let i = 0; i < startPolygon.length; i++) {
      for (let j = 0; j < clickedPolygon.length; j++) {
        if (j !== maxIndex) {
          for (let k = 0; k < clickedPolygon[j].length; k++) {
            if (
              startPolygon[i][0] === clickedPolygon[j][k][0] &&
              startPolygon[i][1] === clickedPolygon[j][k][1]
            ) {
              console.log(startPolygon[i][0]);
            }
          }
        }
      }
    }
  };

  return (
    <div id="listContainer">
      {props.drawnObject.length !== 0 &&
        props.drawnObject.map((object, index) => (
          <div key={index} className="object">
            <input
              id="object"
              type="checkbox"
              onClick={() => handleClickedPolygon(index, object)}
            />
            <label htmlFor="object">Polygon {index}</label>
          </div>
        ))}
      {props.drawnObject.length !== 0 && (
        <div id="buttonContainer">
          <div onClick={findCrossingPoint}>merge selected</div>
          <div onClick={() => console.log(props.drawnObject)}>export all</div>
        </div>
      )}
    </div>
  );
};

export default List;

【问题讨论】:

  • 不确定您的问题是什么,但由于使用了不存在的事件,您提供的代码将无法正常工作。 onMouseDownonMouseUponMouseMove,应该是 onmousedownonmouseuponmousemove,由于某种原因,所有 DOM 事件都只使用小写名称(猜测某个地方的人没有得到 camelCase 备忘录制定规格时)

标签: javascript reactjs html5-canvas


【解决方案1】:

合并两个多边形是一个相当复杂的问题。由于多边形是随机的(即包括非凸和自相交的情况),它带来了更多的复杂性。但即使是简单的凸多边形情况,您也会得到多个交点,因此获取交点只是算法的一小部分。

回到寻找交点问题。一个天真的解决方案如下:

  • 遍历多边形 A 的边
  • 遍历多边形 B 的边
  • 计算多边形 A 的当前侧与多边形 B 的当前侧的交点(有很多算法可以解决这个问题,例如查看 this Wikipedia article

我相信有更复杂的算法比上面的运行得更快,你可以尝试谷歌它们。

作为下一步(合并多边形),您必须遍历每对相交的边,并确定四个生成的子片段中的哪一个应该是新多边形的一部分,哪些应该被丢弃。通常应该丢弃位于 poly A 或 poly B 内部的那些(请参阅Point in polygon 问题),但要注意当 poly A 的一侧与 poly B 的一侧以上相交的情况。

所以你可以看到这个问题与 JS 或 React 相关,而不是数学问题。祝你好运!

【讨论】:

  • 感谢您的回复!但现在的问题是我已经有逻辑循环遍历多边形 A 和 B 以查找是否有任何相同/匹配的坐标(因为它们相互交叉)但没有。我只是想知道是否必须应用其他代码才能在提取时获得画布标签的相交坐标。试过 canvasRef.current.getBoundingClientRect();但它也不起作用。
  • 嗯...我想我不明白。所以你有一个函数可以搜索两个多边形之间的匹配点,但没有任何匹配点。这很好,因为在大多数情况下,它们的相交,而不是有共同的顶点。所以是的,您需要实现计算这些交点的附加函数。从一个简单的函数开始,它计算两条线段的交点,然后将其应用于每对多边形的边。
  • @Paige 你说的是I already have logics to loop through both polygon A and B to find ... 但这不在你提出的问题的代码示例中,你介意补充一下吗
  • @HelderSepulveda 我在问题上附加了逻辑!请尽快检查!
  • @yuvin 我会试试的!
猜你喜欢
  • 1970-01-01
  • 2020-11-10
  • 1970-01-01
  • 2012-11-28
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多