File: closed_path.py

package info (click to toggle)
python-pypathlib 0.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 156 kB
  • sloc: python: 360; makefile: 30
file content (95 lines) | stat: -rw-r--r-- 2,920 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import numpy

from .helpers import shoelace
from .path import Path


class ClosedPath(Path):
    def __init__(self, points):
        closed_points = numpy.concatenate([points, [points[0]]])
        super(ClosedPath, self).__init__(closed_points)

        assert self.points.shape[0] > 2
        assert self.points.shape[1] == 2

        self.area = 0.5 * shoelace(self.points)
        self.positive_orientation = self.area >= 0
        if self.area < 0:
            self.area = -self.area

        self._is_convex_node = None
        return

    def signed_squared_distance(self, x):
        """Negative inside the polgon.
        """
        x = numpy.array(x)
        assert x.shape[1] == 2
        t, dist2, idx = self._all_distances(x)
        contains_points = self._contains_points(t, x, idx)
        dist2[contains_points] *= -1
        return dist2

    def signed_distance(self, x):
        """Negative inside the polgon.
        """
        x = numpy.array(x)
        assert x.shape[1] == 2
        t, dist2, idx = self._all_distances(x)
        dist = numpy.sqrt(dist2)
        contains_points = self._contains_points(t, x, idx)
        dist[contains_points] *= -1
        return dist

    def _contains_points(self, t, x, idx):
        r = numpy.arange(idx.shape[0])

        contains_points = numpy.zeros(x.shape[0], dtype=bool)

        pts0 = self.points
        pts1 = numpy.roll(self.points, -1, axis=0)

        # If the point is closest to a polygon edge, check which which side of the edge
        # it is on.
        is_closest_to_side = (0.0 < t[r, idx]) & (t[r, idx] < 1.0)
        tri = numpy.array(
            [
                x[is_closest_to_side],
                pts0[idx[is_closest_to_side]],
                pts1[idx[is_closest_to_side]],
            ]
        )

        contains_points[is_closest_to_side] = (
            shoelace(tri) > 0.0
        ) == self.positive_orientation

        # If the point is closest to a polygon node, check if the node is convex or
        # concave.
        is_closest_to_pt0 = t[r, idx] <= 0.0
        contains_points[is_closest_to_pt0] = ~self.is_convex_node[
            idx[is_closest_to_pt0]
        ]

        is_closest_to_pt1 = 1.0 <= t[r, idx]
        n = self.points.shape[0] - 1
        contains_points[is_closest_to_pt1] = ~self.is_convex_node[
            (idx[is_closest_to_pt1] + 1) % n
        ]

        return contains_points

    def contains_points(self, x, tol=1.0e-15):
        return self.signed_distance(x) < tol

    @property
    def is_convex_node(self):
        points = self.points[:-1]
        if self._is_convex_node is None:
            tri = numpy.array(
                [numpy.roll(points, +1, axis=0), points, numpy.roll(points, -1, axis=0)]
            )
            self._is_convex_node = numpy.equal(
                shoelace(tri) >= 0, self.positive_orientation
            )
        return self._is_convex_node