def _get_edge_bridges(polygon, z_plane, min_bridges, average_distance, avoid_distance): def is_near_list(point_list, point, distance): for p in point_list: if pdist(p, point) <= distance: return True return False lines = polygon.get_lines() poly_lengths = polygon.get_lengths() num_of_bridges = max(min_bridges, int(round(sum(poly_lengths) / average_distance))) real_average_distance = sum(poly_lengths) / num_of_bridges max_line_index = poly_lengths.index(max(poly_lengths)) positions = [] current_line_index = max_line_index distance_processed = poly_lengths[current_line_index] / 2 positions.append(current_line_index) while len(positions) < num_of_bridges: current_line_index += 1 current_line_index %= len(poly_lengths) # skip lines that are not at least twice as long as the grid width while (distance_processed + poly_lengths[current_line_index] < real_average_distance): distance_processed += poly_lengths[current_line_index] current_line_index += 1 current_line_index %= len(poly_lengths) positions.append(current_line_index) distance_processed += poly_lengths[current_line_index] distance_processed %= real_average_distance result = [] bridge_positions = [] for line_index in positions: position = polygon.get_middle_of_line(line_index) # skip bridges that are close to another existing bridge if is_near_list(bridge_positions, position, avoid_distance): line = polygon.get_lines()[line_index] # calculate two alternative points on the same line position1 = pdiv(padd(position, line.p1), 2) position2 = pdiv(padd(position, line.p2), 2) if is_near_list(bridge_positions, position1, avoid_distance): if is_near_list(bridge_positions, position2, avoid_distance): # no valid alternative - we skip this bridge continue else: # position2 is OK position = position2 else: # position1 is OK position = position1 # append the original position (ignoring z_plane) bridge_positions.append(position) # move the point to z_plane position = (position[0], position[1], z_plane) bridge_dir = pnormalized(pcross(lines[line_index].dir, polygon.plane.n)) result.append((position, bridge_dir)) return result
def subdivide(self, depth): sub = [] if depth == 0: sub.append(self) else: p4 = pdiv(padd(self.p1, self.p2), 2) p5 = pdiv(padd(self.p2, self.p3), 2) p6 = pdiv(padd(self.p3, self.p1), 2) sub += Triangle(self.p1, p4, p6).subdivide(depth - 1) sub += Triangle(p6, p5, self.p3).subdivide(depth - 1) sub += Triangle(p6, p4, p5).subdivide(depth - 1) sub += Triangle(p4, self.p2, p5).subdivide(depth - 1) return sub
def reset_cache(self): self.minx = min(self.p1[0], self.p2[0], self.p3[0]) self.miny = min(self.p1[1], self.p2[1], self.p3[1]) self.minz = min(self.p1[2], self.p2[2], self.p3[2]) self.maxx = max(self.p1[0], self.p2[0], self.p3[0]) self.maxy = max(self.p1[1], self.p2[1], self.p3[1]) self.maxz = max(self.p1[2], self.p2[2], self.p3[2]) self.e1 = Line(self.p1, self.p2) self.e2 = Line(self.p2, self.p3) self.e3 = Line(self.p3, self.p1) # calculate normal, if p1-p2-pe are in clockwise order if self.normal is None: self.normal = pnormalized( pcross(psub(self.p3, self.p1), psub(self.p2, self.p1))) if not len(self.normal) > 3: self.normal = (self.normal[0], self.normal[1], self.normal[2], 'v') self.center = pdiv(padd(padd(self.p1, self.p2), self.p3), 3) self.plane = Plane(self.center, self.normal) # calculate circumcircle (resulting in radius and middle) denom = pnorm(pcross(psub(self.p2, self.p1), psub(self.p3, self.p2))) self.radius = (pdist(self.p2, self.p1) * pdist(self.p3, self.p2) * pdist(self.p3, self.p1)) / (2 * denom) self.radiussq = self.radius**2 denom2 = 2 * denom * denom alpha = pdist_sq(self.p3, self.p2) * pdot(psub( self.p1, self.p2), psub(self.p1, self.p3)) / denom2 beta = pdist_sq(self.p1, self.p3) * pdot(psub( self.p2, self.p1), psub(self.p2, self.p3)) / denom2 gamma = pdist_sq(self.p1, self.p2) * pdot(psub( self.p3, self.p1), psub(self.p3, self.p2)) / denom2 self.middle = (self.p1[0] * alpha + self.p2[0] * beta + self.p3[0] * gamma, self.p1[1] * alpha + self.p2[1] * beta + self.p3[1] * gamma, self.p1[2] * alpha + self.p2[2] * beta + self.p3[2] * gamma)
def split_line(self, line): outer = [] inner = [] # project the line onto the polygon's plane proj_line = self.plane.get_line_projection(line) intersections = [] for pline in self.get_lines(): cp, d = proj_line.get_intersection(pline) if cp: intersections.append((cp, d)) # sort the intersections by distance intersections.sort(key=lambda collision: collision[1]) intersections.insert(0, (proj_line.p1, 0)) intersections.append((proj_line.p2, 1)) def get_original_point(d): return padd(line.p1, pmul(line.vector, d)) for index in range(len(intersections) - 1): p1, d1 = intersections[index] p2, d2 = intersections[index + 1] if p1 != p2: middle = pdiv(padd(p1, p2), 2) new_line = Line(get_original_point(d1), get_original_point(d2)) if self.is_point_inside(middle): inner.append(new_line) else: outer.append(new_line) return (inner, outer)
def get_outside_lines(poly1, poly2): result = [] for line in poly1.get_lines(): collisions = [] for o_line in poly2.get_lines(): cp, dist = o_line.get_intersection(line) if (cp is not None) and (0 < dist < 1): collisions.append((cp, dist)) # sort the collisions according to the distance collisions.append((line.p1, 0)) collisions.append((line.p2, 1)) collisions.sort(key=lambda collision: collision[1]) for index in range(len(collisions) - 1): p1 = collisions[index][0] p2 = collisions[index + 1][0] if pdist(p1, p2) < epsilon: # ignore zero-length lines continue # Use the middle between p1 and p2 to check the # inner/outer state. p_middle = pdiv(padd(p1, p2), 2) p_inside = (poly2.is_point_inside(p_middle) and not poly2.is_point_on_outline(p_middle)) if not p_inside: result.append(Line(p1, p2)) return result
def get_middle_of_line(self, index): if (index >= len(self._points)) \ or (not self.is_closed and index == len(self._points) - 1): return None else: return pdiv(padd(self._points[index], self._points[(index + 1) % len(self._points)]), 2)
def intersect_torus_plane(center, axis, majorradius, minorradius, direction, triangle): # take normal to the plane n = triangle.normal if pdot(n, direction) == 0: return (None, None, INFINITE) if pdot(n, axis) == 1: return (None, None, INFINITE) # find place on torus where surface normal is n b = pmul(n, -1) z = axis a = psub(b, pmul(z, pdot(z, b))) a_sq = pnormsq(a) if a_sq <= 0: return (None, None, INFINITE) a = pdiv(a, sqrt(a_sq)) ccp = padd(padd(center, pmul(a, majorradius)), pmul(b, minorradius)) # find intersection with plane (cp, l) = triangle.plane.intersect_point(direction, ccp) return (ccp, cp, l)