Newer
Older
####################################################################################################
#
# Patro - A Python library to make patterns for fashion design
# Copyright (C) 2017 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
"""Module to implement segment.
"""
####################################################################################################
__all__ = ['Segment2D']
####################################################################################################
from .Line import Line2D
from .Primitive import Primitive2P, Primitive2DMixin
from .Vector import Vector2D
####################################################################################################
class Segment2D(Primitive2DMixin, Primitive2P):
"""Class to implement 2D Segment"""
# Fixme: _p0 versus p0
#######################################
def __init__(self, p0, p1):
"""Construct a :class:`Segment2D` between two points."""
##############################################
@property
def vector(self):
return self._p1 - self._p0
@property
def length(self):
@property
def center(self):
# midpoint, barycenter
##############################################
@property
def cross_product(self):
return self._p0.cross(self._p1)
##############################################
def to_line(self):
return Line2D.from_two_points(self._p0, self._p1)
##############################################
##############################################
"""Checks if the line segments intersect.
return 1 if there is an intersection
0 otherwise
"""
segment1 = self
# triangle_orientation returns 0 if two points are identical, except from the situation
# when p0 and p1 are identical and different from p2
ccw11 = triangle_orientation(segment1.p0, segment1.p1, segment2.p0)
ccw12 = triangle_orientation(segment1.p0, segment1.p1, segment2.p1)
ccw21 = triangle_orientation(segment2.p0, segment2.p1, segment1.p0)
ccw22 = triangle_orientation(segment2.p0, segment2.p1, segment1.p1)
return (((ccw11 * ccw12 < 0) and (ccw21 * ccw22 < 0))
# one ccw value is zero to detect an intersection
or (ccw11 * ccw12 * ccw21 * ccw22 == 0))
##############################################
def intersection(self, segment2):
# P = (1-t) * Pa0 + t * Pa1 = Pa0 + t * (Pa1 - Pa0)
# = (1-u) * Pb0 + u * Pb1 = Pb0 + u * (Pb1 - Pb0)
line1 = self.to_line()
line2 = segment2.to_line()
s1, s2 = line1.intersection_abscissae(line2)
if s1 is None:
return None, None
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
else:
intersect = (0 <= s1 <= 1) and (0 <= s2 <= 1)
return self.interpolate(s1), intersect
##############################################
def share_vertex_with(self, segment2):
# return (
# self._p0 == segment2._p0 or
# self._p0 == segment2._p1 or
# self._p1 == segment2._p0 or
# self._p1 == segment2._p1
# )
if (self._p0 == segment2._p0 or
self._p0 == segment2._p1):
return self._p0
elif (self._p1 == segment2._p0 or
self._p1 == segment2._p1):
return self._p1
else:
return None
##############################################
def side_of(self, point):
"""Tests if a point is left/on/right of a line.
> 0 if point is left of the line
= 0 if point is on the line
< 0 if point is right of the line
"""
v1 = self.vector
v2 = point - self._p0
return v1.cross(v2)
##############################################
def left_of(self, point):
"""Tests if a point is left a line"""
return self.side_of(point) > 0
def right_of(self, point):
"""Tests if a point is right a line"""
return self.side_of(point) < 0
def is_collinear(self, point):
"""Tests if a point is on line"""
return self.side_of(point) == 0
##############################################
def distance_to_point(self, point):
line = self.to_line()
if line.v.magnitude_square == 0:
return (self._p0 - point).magnitude
d, s = line.distance_and_abscissa_to_line(point)
if 0 <= s <= self.length:
else:
if s < 0:
p = self._p0
else:
p = self._p1
return (p - point).magnitude