def filter_toolpath(self, toolpath): new_path = [] last_pos = None optional_moves = [] for move_type, args in toolpath: if move_type in (MOVE_STRAIGHT, MOVE_STRAIGHT_RAPID): if last_pos: # find all remaining pieces of this line inner_lines = [] for polygon in self.settings["polygons"]: inner, outer = polygon.split_line(Line(last_pos, args)) inner_lines.extend(inner) # turn these lines into moves for line in inner_lines: if pdist(line.p1, last_pos) > epsilon: new_path.append((MOVE_SAFETY, None)) new_path.append((move_type, line.p1)) else: # we continue were we left if optional_moves: new_path.extend(optional_moves) optional_moves = [] new_path.append((move_type, line.p2)) last_pos = line.p2 optional_moves = [] # finish the line by moving to its end (if necessary) if pdist(last_pos, args) > epsilon: optional_moves.append((MOVE_SAFETY, None)) optional_moves.append((move_type, args)) last_pos = args elif move_type == MOVE_SAFETY: optional_moves = [] else: new_path.append((move_type, args)) return new_path
def filter_toolpath(self, toolpath): feedrate = min_feedrate = 1 new_path = [] last_pos = None limit = self.settings["timelimit"] duration = 0 for move_type, args in toolpath: if move_type in (MOVE_STRAIGHT, MOVE_STRAIGHT_RAPID): if last_pos: new_distance = pdist(args, last_pos) new_duration = new_distance / max(feedrate, min_feedrate) if (new_duration > 0) and (duration + new_duration > limit): partial = (limit - duration) / new_duration destination = padd(last_pos, pmul(psub(args, last_pos), partial)) duration = limit else: destination = args duration += new_duration else: destination = args new_path.append((move_type, destination)) last_pos = args if (move_type == MACHINE_SETTING) and (args[0] == "feedrate"): feedrate = args[1] if duration >= limit: break return new_path
def is_near_list(point_list, point, distance): for p in point_list: if pdist(p, point) <= distance: return True return False
def simplify_polygon_intersections(lines): new_group = lines[:] # remove all non-adjacent intersecting lines (this splits the group) if len(new_group) > 0: group_starts = [] index1 = 0 while index1 < len(new_group): index2 = 0 while index2 < len(new_group): index_distance = min( abs(index2 - index1), abs(len(new_group) - (index2 - index1))) # skip neighbours if index_distance > 1: line1 = new_group[index1] line2 = new_group[index2] intersection, factor = line1.get_intersection( line2) if intersection and (pdist(intersection, line1.p1) > epsilon) \ and (pdist(intersection, line1.p2) > epsilon): del new_group[index1] new_group.insert(index1, Line(line1.p1, intersection)) new_group.insert(index1 + 1, Line(intersection, line1.p2)) # Shift all items in "group_starts" by one if # they reference a line whose index changed. for i in range(len(group_starts)): if group_starts[i] > index1: group_starts[i] += 1 if index1 + 1 not in group_starts: group_starts.append(index1 + 1) # don't update index2 -> maybe there are other hits elif intersection and (pdist( intersection, line1.p1) < epsilon): if index1 not in group_starts: group_starts.append(index1) index2 += 1 else: index2 += 1 else: index2 += 1 index1 += 1 # The lines intersect each other # We need to split the group. if len(group_starts) > 0: group_starts.sort() groups = [] last_start = 0 for group_start in group_starts: transfer_group = new_group[last_start:group_start] # add only non-empty groups if transfer_group: groups.append(transfer_group) last_start = group_start # Add the remaining lines to the first group or as a new # group. if groups[0][0].p1 == new_group[-1].p2: groups[0] = new_group[last_start:] + groups[0] else: groups.append(new_group[last_start:]) # try to find open groups that can be combined combined_groups = [] for index, current_group in enumerate(groups): # Check if the group is not closed: try to add it to # other non-closed groups. if current_group[0].p1 == current_group[-1].p2: # a closed group combined_groups.append(current_group) else: # the current group is open for other_group in groups[index + 1:]: if other_group[0].p1 != other_group[-1].p2: # This group is also open - a candidate # for merging? if other_group[0].p1 == current_group[ -1].p2: current_group.reverse() for line in current_group: other_group.insert(0, line) break if other_group[-1].p2 == current_group[ 0].p1: other_group.extend(current_group) break else: # not suitable open group found combined_groups.append(current_group) return combined_groups else: # just return one group without intersections return [new_group] else: return None
def dist_to_point(self, p): return pdist(p, self.closest_point(p))
def get_cost(self, other): return pdist(other.start, self.end)
def test_point_near(p, others): for o in others: if pdist(p, o) < max_dist: return True return False
def get_offset_polygons_validated(self, offset): if self.is_outer(): inside_shifting = max(0, -offset) else: inside_shifting = max(0, offset) if inside_shifting * 2 >= self.get_max_inside_distance(): # no polygons will be left return [] points = [] for index in range(len(self._points)): points.append(self.get_shifted_vertex(index, offset)) max_dist = 1000 * epsilon def test_point_near(p, others): for o in others: if pdist(p, o) < max_dist: return True return False reverse_lines = [] shifted_lines = [] for index, p1 in enumerate(points): next_index = (index + 1) % len(points) p2 = points[next_index] diff = psub(p2, p1) old_dir = pnormalized( psub(self._points[next_index], self._points[index])) if pnormalized(diff) != old_dir: # the direction turned around if pnorm(diff) > max_dist: # the offset was too big return None else: reverse_lines.append(index) shifted_lines.append((True, Line(p1, p2))) else: shifted_lines.append((False, Line(p1, p2))) # look for reversed lines index = 0 while index < len(shifted_lines): line_reverse, line = shifted_lines[index] if line_reverse: prev_index = (index - 1) % len(shifted_lines) next_index = (index + 1) % len(shifted_lines) prev_reverse, prev_line = shifted_lines[prev_index] while prev_reverse and (prev_index != next_index): prev_index = (prev_index - 1) % len(shifted_lines) prev_reverse, prev_line = shifted_lines[prev_index] if prev_index == next_index: # no lines are left print("out 1") return [] next_reverse, next_line = shifted_lines[next_index] while next_reverse and (prev_index != next_index): next_index = (next_index + 1) % len(shifted_lines) next_reverse, next_line = shifted_lines[next_index] if prev_index == next_index: # no lines are left print("out 2") return [] if pdist(prev_line.p2, next_line.p1) > max_dist: cp, dist = prev_line.get_intersection(next_line) else: cp = prev_line.p2 if cp: shifted_lines[prev_index] = (False, Line(prev_line.p1, cp)) shifted_lines[next_index] = (False, Line(cp, next_line.p2)) else: cp, dist = prev_line.get_intersection(next_line, infinite_lines=True) raise BaseException( "Expected intersection not found: %s - %s - %s(%d) / %s(%d)" % (cp, shifted_lines[prev_index + 1:next_index], prev_line, prev_index, next_line, next_index)) if index > next_index: # we wrapped around the end of the list break else: index = next_index + 1 else: index += 1 non_reversed = [ one_line for rev, one_line in shifted_lines if not rev and one_line.len > 0 ] # split the list of lines into groups (based on intersections) split_points = [] index = 0 while index < len(non_reversed): other_index = 0 while other_index < len(non_reversed): other_line = non_reversed[other_index] if (other_index == index) \ or (other_index == ((index - 1) % len(non_reversed))) \ or (other_index == ((index + 1) % len(non_reversed))): # skip neighbours other_index += 1 continue line = non_reversed[index] cp, dist = line.get_intersection(other_line) if cp: if not test_point_near( cp, (line.p1, line.p2, other_line.p1, other_line.p2)): # the collision is not close to an end of the line return None elif (cp == line.p1) or (cp == line.p2): # maybe we have been here before if cp not in split_points: split_points.append(cp) elif (pdist(cp, line.p1) < max_dist) or (pdist( cp, line.p2) < max_dist): if pdist(cp, line.p1) < pdist(cp, line.p2): non_reversed[index] = Line(cp, line.p2) else: non_reversed[index] = Line(line.p1, cp) non_reversed.pop(other_index) non_reversed.insert(other_index, Line(other_line.p1, cp)) non_reversed.insert(other_index + 1, Line(cp, other_line.p2)) split_points.append(cp) if other_index < index: index += 1 # skip the second part of this line other_index += 1 else: # the split of 'other_line' will be handled later pass other_index += 1 index += 1 groups = [[]] current_group = 0 split_here = False for line in non_reversed: if line.p1 in split_points: split_here = True if split_here: split_here = False # check if any preceding group fits to the point for index, group in enumerate(groups): if not group: continue if index == current_group: continue if group[0].p1 == group[-1].p2: # the group is already closed continue if line.p1 == group[-1].p2: current_group = index groups[current_group].append(line) break else: current_group = len(groups) groups.append([line]) else: groups[current_group].append(line) if line.p2 in split_points: split_here = True # try to combine open groups for index1, group1 in enumerate(groups): if not group1: continue for index2, group2 in enumerate(groups): if not group2: continue if index2 <= index1: continue if (group1[-1].p2 == group2[0].p1) \ and (group1[0].p1 == group2[-1].p2): group1.extend(group2) groups[index2] = [] break result_polygons = [] print("********** GROUPS **************") for a in groups: print(a) for group in groups: if len(group) <= 2: continue poly = Polygon(self.plane) for line in group: try: poly.append(line) except ValueError: print("NON_REVERSED") for a in non_reversed: print(a) print(groups) print(split_points) print(poly) print(line) raise if self.is_closed and ((not poly.is_closed) or (self.is_outer() != poly.is_outer())): continue elif (not self.is_closed) and (poly.get_area() != 0): continue else: result_polygons.append(poly) return result_polygons
def get_distance_between_groups(group1, group2): forward = pdist(group1[-1].p2, group2[0].p1) backward = pdist(group2[-1].p2, group1[0].p1) return min(forward, backward)
def get_free_paths_triangles(models, cutter, p1, p2, return_triangles=False): if (len(models) == 0) or ((len(models) == 1) and (models[0] is None)): return (p1, p2) elif len(models) == 1: # only one model is left - just continue model = models[0] else: # multiple models were given - process them in layers result = get_free_paths_triangles(models[:1], cutter, p1, p2, return_triangles) # group the result into pairs of two points (start/end) point_pairs = [] while result: pair1 = result.pop(0) pair2 = result.pop(0) point_pairs.append((pair1, pair2)) all_results = [] for pair in point_pairs: one_result = get_free_paths_triangles(models[1:], cutter, pair[0], pair[1], return_triangles) all_results.extend(one_result) return all_results backward = pnormalized(psub(p1, p2)) forward = pnormalized(psub(p2, p1)) xyz_dist = pdist(p2, p1) minx = min(p1[0], p2[0]) maxx = max(p1[0], p2[0]) miny = min(p1[1], p2[1]) maxy = max(p1[1], p2[1]) minz = min(p1[2], p2[2]) # find all hits along scan line hits = [] triangles = model.triangles(minx - cutter.distance_radius, miny - cutter.distance_radius, minz, maxx + cutter.distance_radius, maxy + cutter.distance_radius, INFINITE) for t in triangles: (cl1, d1, cp1) = cutter.intersect(backward, t, start=p1) if cl1: hits.append(Hit(cl1, cp1, t, -d1, backward)) (cl2, d2, cp2) = cutter.intersect(forward, t, start=p1) if cl2: hits.append(Hit(cl2, cp2, t, d2, forward)) # sort along the scan direction hits.sort(key=lambda h: h.d) count = 0 points = [] for h in hits: if h.dir == forward: if count == 0: if -epsilon <= h.d <= xyz_dist + epsilon: if len(points) == 0: points.append((p1, None, None)) points.append((h.cl, h.t, h.cp)) count += 1 else: if count == 1: if -epsilon <= h.d <= xyz_dist + epsilon: points.append((h.cl, h.t, h.cp)) count -= 1 if len(points) % 2 == 1: points.append((p2, None, None)) if len(points) == 0: # check if the path is completely free or if we are inside of the model inside_counter = 0 for h in hits: if -epsilon <= h.d: # we reached the outer limit of the model break if h.dir == forward: inside_counter += 1 else: inside_counter -= 1 if inside_counter <= 0: # we are not inside of the model points.append((p1, None, None)) points.append((p2, None, None)) if return_triangles: return points else: # return only the cutter locations (without triangles) return [cut_info[0] for cut_info in points]