Newer
Older
####################################################################################################
#
# PyValentina - A Python implementation of Valentina Pattern Drafting Software
# 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/>.
#
####################################################################################################
####################################################################################################
from .Line import Line2D
from .Primitive import Primitive2D, ReversablePrimitiveMixin, bounding_box_from_points
from .Vector import Vector2D
####################################################################################################
def triangle_orientation(p0, p1, p2):
dx1 = p1.x - p0.x
dy1 = p1.y - p0.y
dx2 = p2.x - p0.x
dy2 = p2.y - p0.y
# second slope is greater than the first one --> counter-clockwise
if dx1 * dy2 > dx2 * dy1:
return 1
# first slope is greater than the second one --> clockwise
elif dx1 * dy2 < dx2 * dy1:
return -1
# both slopes are equal --> collinear line segments
else:
# p0 is between p1 and p2
if dx1 * dx2 < 0 or dy1 * dy2 < 0:
return -1
# p2 is between p0 and p1, as the length is compared
# square roots are avoided to increase performance
elif dx1 * dx1 + dy1 * dy1 >= dx2 * dx2 + dy2 * dy2:
return 0
# p1 is between p0 and p2
else:
return 1
####################################################################################################
def interpolate_two_points(p0, p1, t):
return p0 * (1 - t) + p1 * t
####################################################################################################
class Segment2D(Primitive2D, ReversablePrimitiveMixin):
""" 2D Segment """
#######################################
def __init__(self, p0, p1):
""" Construct a :class:`Segment2D` between two points. """
self._p0 = Vector2D(p0)
self._p1 = Vector2D(p1)
##############################################
def clone(self):
return self.__class__(self._p0, self._p1)
##############################################
def bounding_box(self):
return bounding_box_from_points((self._p0, self._p1))
##############################################
def reverse(self):
return self.__class__(self._p1, self._p0)
##############################################
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
@property
def p0(self):
return self._p0
@p0.setter
def p0(self, value):
self._p0 = value
@property
def p1(self):
return self._p1
@p1.setter
def p1(self, value):
self._p1 = value
##############################################
@property
def vector(self):
return self._p1 - self._p0
@property
def length(self):
return self.vector.magnitude()
@property
def center(self):
# midpoint, barycenter
return (self._p0 * self._p1) / 2
##############################################
def to_line(self):
return Line2D.from_two_points(self._p1, self._p0)
##############################################
def point_at_t(self, t):
# return interpolate_two_points(self._p0, self._p1)
return self._p0 * (1 - t) + self._p1 * t
##############################################
def intersect(self, segment2):
"""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))