Skip to content

Commit

Permalink
edit type annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
mozman committed Jan 24, 2024
1 parent 4f91b05 commit bf737bf
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 45 deletions.
53 changes: 21 additions & 32 deletions src/ezdxf/math/_mapbox_earcut.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,18 @@
# A Steiner point is defined as a hole with a single point!
#
from __future__ import annotations
from typing import TypeVar, Sequence, Optional
from typing_extensions import Protocol
from typing import Sequence, Optional, Protocol, TypeVar

import math


class _Point(Protocol):
class Point(Protocol):
x: float
y: float


T = TypeVar("T", bound=_Point)


class Node:
def __init__(self, i: int, point: T):
def __init__(self, i: int, point: Point) -> None:
self.i: int = i

# store source point for output
Expand Down Expand Up @@ -91,9 +87,10 @@ def __eq__(self, other):
return self.x == other.x and self.y == other.y


def earcut(
exterior: list[T], holes: list[list[T]]
) -> list[Sequence[T]]:
T = TypeVar("T", bound=Point)


def earcut(exterior: list[T], holes: list[list[T]]) -> list[Sequence[T]]:
"""Implements a modified ear slicing algorithm, optimized by z-order
curve hashing and extended to handle holes, twisted polygons, degeneracies
and self-intersections in a way that doesn't guarantee correctness of
Expand Down Expand Up @@ -145,11 +142,11 @@ def earcut(
inv_size = max(max_x - min_x, max_y - min_y)
inv_size = 32767 / inv_size if inv_size != 0 else 0

earcut_linked(outer_node, triangles, min_x, min_y, inv_size, 0)
earcut_linked(outer_node, triangles, min_x, min_y, inv_size, 0) # type: ignore
return triangles


def linked_list(points: Sequence[T], start: int, ccw: bool) -> Node:
def linked_list(points: Sequence[Point], start: int, ccw: bool) -> Node:
"""Create a circular doubly linked list from polygon points in the specified
winding order
"""
Expand All @@ -171,7 +168,7 @@ def linked_list(points: Sequence[T], start: int, ccw: bool) -> Node:
return last


def signed_area(points: Sequence[T]) -> float:
def signed_area(points: Sequence[Point]) -> float:
s: float = 0.0
if not len(points):
return s
Expand Down Expand Up @@ -238,9 +235,9 @@ def sign(num: float) -> int:


def on_segment(p: Node, q: Node, r: Node) -> bool:
return max(p.x, r.x) >= q.x >= min(p.x, r.x) and max(
return max(p.x, r.x) >= q.x >= min(p.x, r.x) and max(p.y, r.y) >= q.y >= min(
p.y, r.y
) >= q.y >= min(p.y, r.y)
)


def intersects(p1: Node, q1: Node, p2: Node, q2: Node) -> bool:
Expand All @@ -264,7 +261,7 @@ def intersects(p1: Node, q1: Node, p2: Node, q2: Node) -> bool:
return False


def insert_node(i: int, point: T, last: Node) -> Node:
def insert_node(i: int, point: Point, last: Node) -> Node:
"""create a node and optionally link it with previous one (in a circular
doubly linked list)
"""
Expand Down Expand Up @@ -292,7 +289,7 @@ def remove_node(p: Node) -> None:


def eliminate_holes(
holes: Sequence[Sequence[T]], start: int, outer_node: Node
holes: Sequence[Sequence[Point]], start: int, outer_node: Node
) -> Node:
"""link every hole into the outer loop, producing a single-ring polygon
without holes
Expand Down Expand Up @@ -359,7 +356,7 @@ def filter_points(start: Node, end: Optional[Node] = None) -> Node:
# main ear slicing loop which triangulates a polygon (given as a linked list)
def earcut_linked(
ear: Node,
triangles: list[Sequence[T]],
triangles: list[Sequence[Point]],
min_x: float,
min_y: float,
inv_size: float,
Expand All @@ -380,9 +377,7 @@ def earcut_linked(
next = ear.next

_is_ear = (
is_ear_hashed(ear, min_x, min_y, inv_size)
if inv_size
else is_ear(ear)
is_ear_hashed(ear, min_x, min_y, inv_size) if inv_size else is_ear(ear)
)
if _is_ear:
# cut off the triangle
Expand Down Expand Up @@ -593,9 +588,7 @@ def index_curve(start: Node, min_x: float, min_y: float, inv_size: float):
sort_linked(p)


def z_order(
x0: float, y0: float, min_x: float, min_y: float, inv_size: float
) -> int:
def z_order(x0: float, y0: float, min_x: float, min_y: float, inv_size: float) -> int:
"""Z-order of a point given coords and inverse of the longer side of data
bbox.
"""
Expand All @@ -620,7 +613,7 @@ def z_order(
# http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
def sort_linked(head: Node) -> Node:
in_size = 1
tail : Node
tail: Node
while True:
p = head
head = None # type: ignore
Expand Down Expand Up @@ -688,7 +681,7 @@ def split_polygon(a: Node, b: Node) -> Node:


# go through all polygon nodes and cure small local self-intersections
def cure_local_intersections(start: Node, triangles: list[Sequence[T]]) -> Node:
def cure_local_intersections(start: Node, triangles: list[Sequence[Point]]) -> Node:
p = start
while True:
a = p.prev
Expand All @@ -714,7 +707,7 @@ def cure_local_intersections(start: Node, triangles: list[Sequence[T]]) -> Node:

def split_ear_cut(
start: Node,
triangles: list[Sequence[T]],
triangles: list[Sequence[Point]],
min_x: float,
min_y: float,
inv_size: float,
Expand Down Expand Up @@ -792,17 +785,13 @@ def find_hole_bridge(hole: Node, outer_node: Node) -> Node:
p.y,
)
):

tan = abs(hy - p.y) / (hx - p.x) # tangential

if locally_inside(p, hole) and (
tan < tan_min
or (
tan == tan_min
and (
p.x > m.x
or (p.x == m.x and sector_contains_sector(m, p))
)
and (p.x > m.x or (p.x == m.x and sector_contains_sector(m, p)))
)
):
m = p
Expand Down
25 changes: 12 additions & 13 deletions src/ezdxf/math/triangulation.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Copyright (c) 2022-2024, Manfred Moitzi
# License: MIT License
from __future__ import annotations
from typing import Iterable, Iterator, Sequence, Optional
from typing import Iterable, Iterator, Sequence
import ezdxf
from ezdxf.math import Vec2, UVec, Vec3, safe_normal_vector, OCS

from ._mapbox_earcut import earcut

if ezdxf.options.use_c_ext:
try:
from ezdxf.acc.mapbox_earcut import earcut
from ezdxf.acc.mapbox_earcut import earcut # type: ignore
except ImportError:
from ._mapbox_earcut import earcut
else:
from ._mapbox_earcut import earcut
pass

__all__ = [
"mapbox_earcut_2d",
Expand All @@ -20,7 +20,7 @@


def mapbox_earcut_2d(
exterior: Iterable[UVec], holes: Optional[Iterable[Iterable[UVec]]] = None
exterior: Iterable[UVec], holes: Iterable[Iterable[UVec]] | None = None
) -> list[Sequence[Vec2]]:
"""Mapbox triangulation algorithm with hole support for 2D polygons.
Expand All @@ -43,7 +43,7 @@ def mapbox_earcut_2d(
.. _Steiner point: https://en.wikipedia.org/wiki/Steiner_point_(computational_geometry)
"""
points: list[Vec2] = Vec2.list(exterior)
points = Vec2.list(exterior)
if len(points) == 0:
return []
holes_: list[list[Vec2]] = []
Expand All @@ -53,10 +53,9 @@ def mapbox_earcut_2d(


def mapbox_earcut_3d(
exterior: Iterable[UVec], holes: Optional[Iterable[Iterable[UVec]]] = None
) -> Iterator[tuple[Vec3, Vec3, Vec3]]:
"""Mapbox triangulation algorithm with hole support for flat
3D polygons.
exterior: Iterable[UVec], holes: Iterable[Iterable[UVec]] | None = None
) -> Iterator[Sequence[Vec3]]:
"""Mapbox triangulation algorithm with hole support for flat 3D polygons.
Implements a modified ear slicing algorithm, optimized by z-order
curve hashing and extended to handle holes, twisted polygons, degeneracies
Expand Down Expand Up @@ -98,10 +97,10 @@ def mapbox_earcut_3d(
holes_ocs: list[list[Vec3]] = []
if holes:
holes_ocs = [list(ocs.points_from_wcs(hole)) for hole in holes]

# Vec3 supports the _Point protocol in _mapbox_earcut.py
# required attributes: x, y
for triangle in earcut(exterior_ocs, holes_ocs): # type: ignore
yield tuple( # type: ignore
yield tuple(
ocs.points_to_wcs(Vec3(v.x, v.y, elevation) for v in triangle)
)

0 comments on commit bf737bf

Please sign in to comment.