Repository: excalidraw/excalidraw
File: packages/excalidraw/eraser/index.ts (TypeScript)

Code before:

const eraserTest = (
  pathSegment: LineSegment<GlobalPoint>,
  element: ExcalidrawElement,
  elementsMap: ElementsMap,
): boolean => {
  const lastPoint = pathSegment[1];
  const boundBoxDiagonal = Math.hypot(element.width, element.height);
  const threshold = 1;
  if (
    shouldTestInside(element) &&
    isPointInElement(lastPoint, element, elementsMap)
  ) {
    return true;
  }

  // for freedraw-points, lines and arrows of shared endpoints
  if (boundBoxDiagonal < threshold) {
    const tolerance =
      element.type === "freedraw"
        ? element.strokeWidth * 2.125
        : element.strokeWidth / 2;
    const [cx, cy] = [
      element.x + element.width / 2,
      element.y + element.height / 2,
    ];
    const distance = Math.hypot(lastPoint[0] - cx, lastPoint[1] - cy);
    const hitRadius = Math.max(element.width, element.height) / 2 + tolerance;

    if (distance <= hitRadius) {
      return true;
    }
  }

  const boundTextElement = getBoundTextElement(element, elementsMap);

  return (
    intersectElementWithLineSegment(element, elementsMap, pathSegment, 0, true)
      .length > 0 ||
    (!!boundTextElement &&
      intersectElementWithLineSegment(
        {
          ...boundTextElement,
          ...computeBoundTextPosition(element, boundTextElement, elementsMap),
        },
        elementsMap,
        pathSegment,
        0,
        true,
      ).length > 0)
  );

Review comment (suggestion):
This calculation does not account for bound objects (specifically text). Use the proper `elementCenterPoint()` function.

Code after:
          if (isBoundToContainer(element)) {
            this.elementsToErase.add(element.containerId);
          }

          this.elementsToErase.add(element.id);
        }
      }
    }

    return Array.from(this.elementsToErase);
  }

  endPath(): void {
    super.endPath();
    super.clearTrails();
    this.elementsToErase.clear();
    this.groupsToErase.clear();
  }
}

const eraserTest = (
  pathSegment: LineSegment<GlobalPoint>,
  element: ExcalidrawElement,
  elementsMap: ElementsMap,
  zoom: number,
): boolean => {
  const lastPoint = pathSegment[1];

  // PERF: Do a quick bounds intersection test first because it's cheap
  const threshold = isFreeDrawElement(element) ? 15 : element.strokeWidth / 2;
  const segmentBounds = [
    Math.min(pathSegment[0][0], pathSegment[1][0]) - threshold,
    Math.min(pathSegment[0][1], pathSegment[1][1]) - threshold,
    Math.max(pathSegment[0][0], pathSegment[1][0]) + threshold,
    Math.max(pathSegment[0][1], pathSegment[1][1]) + threshold,
  ] as Bounds;
  const origElementBounds = getElementBounds(element, elementsMap);
  const elementBounds: Bounds = [
    origElementBounds[0] - threshold,
    origElementBounds[1] - threshold,
    origElementBounds[2] + threshold,
    origElementBounds[3] + threshold,
  ];

  if (!doBoundsIntersect(segmentBounds, elementBounds)) {
    return false;
  }

  // There are shapes where the inner area should trigger erasing
  // even though the eraser path segment doesn't intersect with or
  // get close to the shape's stroke