def bulge_stop_point(self): if self._stop_bulge_point is None: angle = self.half_bulge_angle offset = self.direction * self._radius / math.tan( self.half_bulge_angle) self._stop_bulge_point = self.start_point + sign(angle) * offset return self._stop_bulge_point
def angle_with(self, direction): """Return the angle of self on direction""" angle = math.acos(self.cos_with(direction)) angle_sign = sign(self.sin_with(direction)) return angle_sign * math.degrees(angle)
def _test_is_convex(self): # https://en.wikipedia.org/wiki/Convex_polygon # http://mathworld.wolfram.com/ConvexPolygon.html if not self.is_simple: return False edges = list(self.edges) # a polygon is convex if all turns from one edge vector to the next have the same sense # sign = edges[-1].perp_dot(edges[0]) sign0 = sign(edges[-1].cross(edges[0])) for i in range(len(edges)): if sign(edges[i].cross(edges[i+1])) != sign0: return False return True
def bulge_start_point(self): if self._start_bulge_point is None: angle = self.half_bulge_angle offset = self.prev_part.direction * self._radius / math.tan(angle) self._start_bulge_point = self.start_point - sign(angle) * offset # Note: -offset create internal loop return self._start_bulge_point
def geometry(self): items = [] for vertex1, vertex2 in self.iter_on_segment(): segment = Segment2D(vertex1.point, vertex2.point) if vertex1.bulge: segment_center = segment.center direction = vertex1.segment_vector.normalise() normal = direction.normal # offset = vertex1.bulge_radius - vertex1.sagitta offset = vertex1.sagitta_dual center = segment_center + normal * sign(vertex1.bulge) * offset arc = Circle2D(center, vertex1.bulge_radius) start_angle, stop_angle = [ arc.angle_for_point(vertex.point) for vertex in (vertex1, vertex2) ] if start_angle < 0: start_angle += 360 if stop_angle < 0: stop_angle += 360 if vertex1.bulge < 0: start_angle, stop_angle = stop_angle, start_angle # print('bulb', vertex1, vertex2, vertex1.bulge, start_angle, stop_angle) arc.domain = AngularDomain(start_angle, stop_angle) # arc = Circle2D(center, vertex1.bulge_radius, domain=AngularDomain(start_angle, stop_angle)) items.append(arc) else: items.append(segment) return items
def distance_to_point(self, point, return_point=False, is_inside=False): # Fixme: can be transform the problem to a circle using transformation ??? point_in_frame = self.point_in_ellipse_frame(point) point_in_frame_abs = self.__vector_cls__(abs(point_in_frame.x), abs(point_in_frame.y)) distance, point_in_ellipse = self._eberly_distance(point_in_frame_abs) if is_inside: # Fixme: right ??? return ((point_in_frame_abs - self._center).magnitude_square <= (point_in_ellipse - self._center).magnitude_square) elif return_point: point_in_ellipse = self.__vector_cls__( sign(point_in_frame.x) * (point_in_ellipse.x), sign(point_in_frame.y) * (point_in_ellipse.y), ) point_in_ellipse = self.point_from_ellipse_frame(point_in_ellipse) return distance, point_in_ellipse else: return distance
def intersect_circle(self, circle): # Fixme: check domain !!! v = circle.center - self.center d = sign(v.x) * v.magnitude x = (d**2 - circle.radius**2 + self.radius**2) / (2 * d) y2 = self.radius**2 - x**2 if y2 < 0: return None else: p = self.center + v.normalise() * x if y2 == 0: return p else: n = v.normal() * sqrt(y2) return p - n, p - n
def intersect_segment(self, segment): """Compute the intersection of a circle and a segment.""" # Fixme: check domain !!! dx = segment.vector.x dy = segment.vector.y dr2 = dx**2 + dy**2 p0 = segment.p0 - self.center p1 = segment.p1 - self.center D = p0.cross(p1) # Fixme: fixed typo _product ?? # from sympy import * # x, y, dx, dy, D, r = symbols('x y dx dy D r') # system = [x**2 + y**2 - r**2, dx*y - dy*x + D] # vars = [x, y] # solution = nonlinsolve(system, vars) # solution.subs(dx**2 + dy**2, dr**2) Vector2D = self.__vector_cls__ discriminant = self.radius**2 * dr2 - D**2 if discriminant < 0: return None elif discriminant == 0: # tangent line x = (D * dy) / dr2 y = (-D * dx) / dr2 return Vector2D(x, y) + self.center else: # intersection x_a = D * dy y_a = -D * dx x_b = sign(dy) * dx * sqrt(discriminant) y_b = fabs(dy) * sqrt(discriminant) x0 = (x_a - x_b) / dr2 y0 = (y_a - y_b) / dr2 x1 = (x_a + x_b) / dr2 y1 = (y_a + y_b) / dr2 p0 = Vector2D(x0, y0) + self.center p1 = Vector2D(x1, y1) + self.center return p0, p1
def _compute_area_barycenter(self): """Compute polygon area and barycenter.""" if not self.is_simple: return None # area = self._points[-1].cross(self._points[0]) # for i in range(self.number_of_points): # area *= self._points[i].cross(self._points[i+1]) # P0, P1, Pn-1, P0 points = self.closed_point_array # from 0 to n-1 : P0, ..., Pn-1 xi = points[0, :-1] yi = points[1, :-1] # from 1 to n : P1, ..., Pn-1, P0 xi1 = points[0, 1:] yi1 = points[1, 1:] # Fixme: np.cross ??? cross = xi * yi1 - xi1 * yi self._cross = cross area = .5 * np.sum(cross) if area == 0: # print('Null area') self._area = 0 self._barycenter = self.start_point else: factor = 1 / (6 * area) x = factor * np.sum((xi + xi1) * cross) y = factor * np.sum((yi + yi1) * cross) # area of a convex polygon is defined to be positive if the points are arranged in a # counterclockwise order, and negative if they are in clockwise order (Beyer 1987). self._area = abs(area) self._area_sign = sign(area) self._barycenter = self.__vector_cls__(x, y)