def point_position(self, a_point, perspective_point=Point3(0, 0, 1)): """ Check the position of a_point w.r.t. the segment on the plane formed by the segment and the point, as viewed from the perspective_point :param a_point: a Point object :param perspective_point: observation point (the default value should be used ONLY when working in 2D) :return: False, if the perspective point is COPLANAR with self and the a_point; otherwise, either RIGHT (-1), LEFT (1), BEFORE (-2), BETWEEN (0), or AFTER (2) """ if Point3.are_coplanar(self.start, self.end, a_point, perspective_point): return False return Point3.orientation(self.start, self.end, a_point, perspective_point)
def intersects(self, other): """ :param other: Line or Vector :return: True if the two segments intersect, False otherwise """ if isinstance(other, (Vector2, Vector3)): other = Line([Point3(0, 0, 0), Point3(other)]) if not isinstance(other, Line): raise TypeError( "Line.intersects requires a Line or a Vector as parameter") l1, l2 = self, other # handy aliases for the segments # in order for two segments to intersect they must lie on the same plane if not Point3.are_coplanar(l1.start, l1.end, l2.start, l2.end): print("Segments are not COPLANAR") return False # Ok, the segments are COPLANAR! # now, we have two cases to be treated separately: (1) the segments are COLLINEAR, (2) or not if l1.is_collinear_with(l2): # if COLLINEAR we need to check the relative distances between the extremes of the segments # We have only three possible cases where the segments intersect: # A) l2 is inside l1 (i.e., both l2 extremes are BETWEEN l1 extremes) # B) l1 is inside l2 # C) none of the above, but one extreme of one of the two segments is BETWEEN the extremes of the other # compute the positions of the segments' extremes wrt the other segment l2_start_position = Point3.collinear_position( l1.start, l1.end, l2.start) l2_end_position = Point3.collinear_position( l1.start, l1.end, l2.end) l1_start_position = Point3.collinear_position( l2.start, l2.end, l1.start) l1_end_position = Point3.collinear_position( l2.start, l2.end, l1.end) # case A) if l2_start_position == BETWEEN and l2_end_position == BETWEEN: return True # case B) elif l1_start_position == BETWEEN and l1_end_position == BETWEEN: return True # case C) elif any(pos == BETWEEN for pos in [ l2_start_position, l2_end_position, l1_start_position, l1_end_position ]): return True else: return False else: # if the two segments are not COLLINEAR, it can still be the case that one of the extremes of # one segment is COLLINEAR with the other segment. This case must be treated separately # consider l1 as reference segment and check if the extremes of l2 are COLLINEAR with l1 is_l2_start_collinear = Point3.are_collinear( l1.start, l1.end, l2.start) is_l2_end_collinear = Point3.are_collinear(l1.start, l1.end, l2.end) # consider l2 as reference segment and check if the extremes of l1 are COLLINEAR with l2 is_l1_start_collinear = Point3.are_collinear( l2.start, l2.end, l1.start) is_l1_end_collinear = Point3.are_collinear(l2.start, l2.end, l1.end) if is_l2_start_collinear or is_l2_end_collinear or is_l1_start_collinear or is_l1_end_collinear: # NOTE: at most one of the above variables can be True, otherwise it means that the two # segments are COLLINEAR and we would have entered the if condition above--l1.is_collinear_with(l2). # determine which is the reference segment ref_segment = l1 if is_l2_start_collinear or is_l2_end_collinear else l2 # determine which extreme of the other segment is COLLINEAR with the ref_segment # assume it is l2.start coll_extreme = l2.start if ref_segment is l1: if is_l2_end_collinear: coll_extreme = l2.end else: if is_l1_start_collinear: coll_extreme = l1.start else: coll_extreme = l2.end # the only case when the two segments intersect is when the coll_extreme is positioned # BETWEEN the extremes of the reference segment return Point3.collinear_position(ref_segment.start, ref_segment.end, coll_extreme) == BETWEEN else: # the two segments are in general position. So, to determine if they intersect # we need to check the position of the extremes of each segment wrt the other segment. # To do this in 3-space we need an observation point that is not on the same plane # so, compute an observation point that is not on the same plane # easily, we can take the cross product of the two segments v1 = Vector3(l1.end - l1.start) v2 = Vector3(l2.end - l2.start) v3 = v1.cross(v2) observation_point = Point3(v3) + l1.start # in order to intersect the extremes of each segment must lie on opposite sides of the other segment return \ l1.point_position(l2[0], observation_point) * l1.point_position(l2[1], observation_point) < 0 and \ l2.point_position(l1[0], observation_point) * l2.point_position(l1[1], observation_point) < 0
def get_renderable_arrays(self): """ traverse the DCEL structure and return a list of vertices and a list of elements for the rendering of the DCEL :return: vertices: a list of verices [a, b, c, ...], elements: a list of indices [idx_1, idx_2, idx_3, ...] from the list vertices. if the DCEL represents a set of point, each index refers a point to be rendered if the DCEL represents a linear entity, each pair of indices forms a segment to be rendered if the DCEL represents a polygon or a polyhedron, each triplet of indices form a face to be rendered """ from app.geoms.point import Point3 vertices = list() elements = list() outline_elements = list() if self.exterior_face.isolated_vertices: # if there are isolated vertices it means this DCEL represents a set of points (and nothing more) for v in self.exterior_face.isolated_vertices: elements.append(len(vertices)) vertices.append(v) else: # otherwise we are treating either a linear geometry, a polygon, or a polyhedron visited_faces = set() faces_to_visit = set() faces_to_visit.add(self.exterior_face) while faces_to_visit: face = faces_to_visit.pop() if face not in visited_faces: for ae in face.adjacent_edges: edge_chain = ae.get_chain() # if the first edge in the chain has no prev_edge the chain is open open_chain = True if _is_chain_open(edge_chain) else False v1_idx = None first_edge, last_edge = 0, len(edge_chain)-1 # for i, edge in enumerate(reversed(edge_chain)): # # NOTE: OpenGL requires vertices of faces to be given in CW order, but our polygons # # have been constructed specifying vertices in CCW order, so we need to traverse # # the edge_chain in reverse order to create CW ordered vertices and elements for i, edge in enumerate(edge_chain): if edge.twin_edge.adjacent_face not in visited_faces and \ edge.twin_edge.adjacent_face is not face: faces_to_visit.add(edge.twin_edge.adjacent_face) if i == first_edge: v1 = edge.source_vertex() if v1 in vertices: v1_idx = vertices.index(v1) else: v1_idx = len(vertices) vertices.append(v1) v2 = edge.target_vertex if v2 in vertices: v2_idx = vertices.index(v2) else: v2_idx = len(vertices) vertices.append(v2) if open_chain: # if we are treating an open chain it means we are treating a linear entity # so the elements must be given pair-wise. Each pair denotes the start and the # end of the i-th segment making the linear entity elements.extend([v1_idx, v2_idx]) else: # otherwise we are treating a face. Faces can be only triangles (we assumed this before) if i == first_edge: elements.append(v1_idx) # the last vertex of the last edge is the first vertex of the first edge, # so it was already treated if i != last_edge: elements.append(v2_idx) # treat the outline # take the neighbor face (adjacent to the twin of this edge) # access vertex opposite to the twin (REMEMBER: each face is a triangle) # check if such a vertex is COPLANAR with # get the face adjacent to this face through this edge neighbor_face = edge.twin_edge.adjacent_face # we proceed only if the neighbor face has been visited already # in this way we avoid to add more than once the same part of the outline if neighbor_face in visited_faces: # get the vertex opposite to this edge in the # face adjacent to this face through the current edge other_face_vertex = edge.twin_edge.next_edge.target_vertex # get the three vertices defining the face the current edge belongs to v3 = edge_chain[(i+1) % len(edge_chain)].target_vertex if not Point3.are_coplanar(v1.point, v2.point, v3.point, other_face_vertex.point): outline_elements.extend([v1_idx, v2_idx]) v1, v1_idx = v2, v2_idx # advance the first vertex visited_faces.add(face) return vertices, elements, outline_elements