Exemple #1
0
 def _get_free_paths(self, cutter, models, p1, p2):
     if self.physics:
         return get_free_paths_ode(self.physics,
                                   p1,
                                   p2,
                                   depth=self._physics_maxdepth)
     else:
         return get_free_paths_triangles(models, cutter, p1, p2)
Exemple #2
0
    def GenerateToolPath(self, motion_grid, draw_callback=None):
        # calculate the number of steps

        # Transfer the grid (a generator) into a list of lists and count the
        # items.
        grid = []
        num_of_grid_positions = 0
        for layer in motion_grid:
            lines = []
            for line in layer:
                # convert the generator to a list
                lines.append(list(line))
            num_of_grid_positions += len(lines)
            grid.append(lines)

        num_of_layers = len(grid)

        progress_counter = ProgressCounter(num_of_grid_positions,
                                           draw_callback)

        current_layer = 0
        for layer_grid in grid:
            # update the progress bar and check, if we should cancel the process
            if draw_callback and draw_callback(text="PushCutter: processing" \
                        + " layer %d/%d" % (current_layer + 1, num_of_layers)):
                # cancel immediately
                break

            self.pa.new_direction(0)
            self.GenerateToolPathSlice(layer_grid, draw_callback,
                                       progress_counter)
            self.pa.end_direction()
            self.pa.finish()

            current_layer += 1

        if self._use_polygon_extractor and (len(self.models) > 1):
            other_models = self.models[1:]
            # TODO: this is complicated and hacky :(
            # we don't use parallelism or ODE (for the sake of simplicity)
            final_pa = pycam.PathProcessors.SimpleCutter.SimpleCutter(
                reverse=self.pa.reverse)
            for path in self.pa.paths:
                final_pa.new_scanline()
                pairs = []
                for index in range(len(path.points) - 1):
                    pairs.append((path.points[index], path.points[index + 1]))
                for p1, p2 in pairs:
                    free_points = get_free_paths_triangles(
                        other_models, self.cutter, p1, p2)
                    for point in free_points:
                        final_pa.append(point)
                final_pa.end_scanline()
            final_pa.finish()
            return final_pa.paths
        else:
            return self.pa.paths
Exemple #3
0
    def GenerateToolPath(self, motion_grid, draw_callback=None):
        # calculate the number of steps

        # Transfer the grid (a generator) into a list of lists and count the
        # items.
        grid = []
        num_of_grid_positions = 0
        for layer in motion_grid:
            lines = []
            for line in layer:
                # convert the generator to a list
                lines.append(list(line))
            num_of_grid_positions += len(lines)
            grid.append(lines)

        num_of_layers = len(grid)

        progress_counter = ProgressCounter(num_of_grid_positions, draw_callback)

        current_layer = 0
        for layer_grid in grid:
            # update the progress bar and check, if we should cancel the process
            if draw_callback and draw_callback(text="PushCutter: processing" \
                        + " layer %d/%d" % (current_layer + 1, num_of_layers)):
                # cancel immediately
                break

            self.pa.new_direction(0)
            self.GenerateToolPathSlice(layer_grid, draw_callback,
                    progress_counter)
            self.pa.end_direction()
            self.pa.finish()

            current_layer += 1

        if self._use_polygon_extractor and (len(self.models) > 1):
            other_models = self.models[1:]
            # TODO: this is complicated and hacky :(
            # we don't use parallelism or ODE (for the sake of simplicity)
            final_pa = pycam.PathProcessors.SimpleCutter.SimpleCutter(
                    reverse=self.pa.reverse)
            for path in self.pa.paths:
                final_pa.new_scanline()
                pairs = []
                for index in range(len(path.points) - 1):
                    pairs.append((path.points[index], path.points[index + 1]))
                for p1, p2 in pairs:
                    free_points = get_free_paths_triangles(other_models,
                            self.cutter, p1, p2)
                    for point in free_points:
                        final_pa.append(point)
                final_pa.end_scanline()
            final_pa.finish()
            return final_pa.paths
        else:
            return self.pa.paths
Exemple #4
0
def _process_one_line((p1, p2, depth, models, cutter, physics)):
    #prof = cProfile.Profile()
    #prof.enable()
    if physics:
        points = get_free_paths_ode(physics, p1, p2, depth=depth)
    else:
        #points = prof.runcall(get_free_paths_triangles, *(models, cutter, p1, p2))
        points = get_free_paths_triangles(models, cutter, p1, p2)
    #prof.disable()
    #prof.print_stats(2)
    return points
Exemple #5
0
def _process_one_line((p1, p2, depth, models, cutter, physics)):
    #prof = cProfile.Profile()
    #prof.enable()
    if physics:
        points = get_free_paths_ode(physics, p1, p2, depth=depth)
    else:
        #points = prof.runcall(get_free_paths_triangles, *(models, cutter, p1, p2))
        points = get_free_paths_triangles(models, cutter, p1, p2)
    #prof.disable()
    #prof.print_stats(2)
    return points
Exemple #6
0
 def GenerateToolPathLinePush(self, pa, line, z, previous_z,
         draw_callback=None):
     if previous_z <= line.minz:
         # the line is completely above the previous level
         pass
     elif line.minz < z < line.maxz:
         # Split the line at the point at z level and do the calculation
         # for both point pairs.
         factor = (z - line.p1.z) / (line.p2.z - line.p1.z)
         plane_point = line.p1.add(line.vector.mul(factor))
         self.GenerateToolPathLinePush(pa, Line(line.p1, plane_point), z,
                 previous_z, draw_callback=draw_callback)
         self.GenerateToolPathLinePush(pa, Line(plane_point, line.p2), z,
                 previous_z, draw_callback=draw_callback)
     elif line.minz < previous_z < line.maxz:
         plane = Plane(Point(0, 0, previous_z), Vector(0, 0, 1))
         cp = plane.intersect_point(line.dir, line.p1)[0]
         # we can be sure that there is an intersection
         if line.p1.z > previous_z:
             p1, p2 = cp, line.p2
         else:
             p1, p2 = line.p1, cp
         self.GenerateToolPathLinePush(pa, Line(p1, p2), z, previous_z,
                 draw_callback=draw_callback)
     else:
         if line.maxz <= z:
             # the line is completely below z
             p1 = Point(line.p1.x, line.p1.y, z)
             p2 = Point(line.p2.x, line.p2.y, z)
         elif line.minz >= z:
             p1 = line.p1
             p2 = line.p2
         else:
             log.warn("Unexpected condition EC_GTPLP: %s / %s / %s / %s" % \
                     (line.p1, line.p2, z, previous_z))
             return
         # no model -> no possible obstacles
         # model is completely below z (e.g. support bridges) -> no obstacles
         relevant_models = [m for m in self.models if m.maxz >= z]
         if not relevant_models:
             points = [p1, p2]
         elif self.physics:
             points = get_free_paths_ode(self.physics, p1, p2)
         else:
             points = get_free_paths_triangles(relevant_models, self.cutter,
                     p1, p2)
         if points:
             for point in points:
                 pa.append(point)
             if draw_callback:
                 draw_callback(tool_position=points[-1], toolpath=pa.paths)
 def GenerateToolPathLinePush(self, pa, line, z, previous_z,
         draw_callback=None):
     if previous_z <= line.minz:
         # the line is completely above the previous level
         pass
     elif line.minz < z < line.maxz:
         # Split the line at the point at z level and do the calculation
         # for both point pairs.
         factor = (z - line.p1.z) / (line.p2.z - line.p1.z)
         plane_point = line.p1.add(line.vector.mul(factor))
         self.GenerateToolPathLinePush(pa, Line(line.p1, plane_point), z,
                 previous_z, draw_callback=draw_callback)
         self.GenerateToolPathLinePush(pa, Line(plane_point, line.p2), z,
                 previous_z, draw_callback=draw_callback)
     elif line.minz < previous_z < line.maxz:
         plane = Plane(Point(0, 0, previous_z), Vector(0, 0, 1))
         cp = plane.intersect_point(line.dir, line.p1)[0]
         # we can be sure that there is an intersection
         if line.p1.z > previous_z:
             p1, p2 = cp, line.p2
         else:
             p1, p2 = line.p1, cp
         self.GenerateToolPathLinePush(pa, Line(p1, p2), z, previous_z,
                 draw_callback=draw_callback)
     else:
         if line.maxz <= z:
             # the line is completely below z
             p1 = Point(line.p1.x, line.p1.y, z)
             p2 = Point(line.p2.x, line.p2.y, z)
         elif line.minz >= z:
             p1 = line.p1
             p2 = line.p2
         else:
             log.warn("Unexpected condition EC_GTPLP: %s / %s / %s / %s" % \
                     (line.p1, line.p2, z, previous_z))
             return
         # no model -> no possible obstacles
         # model is completely below z (e.g. support bridges) -> no obstacles
         relevant_models = [m for m in self.models if m.maxz >= z]
         if not relevant_models:
             points = [p1, p2]
         elif self.physics:
             points = get_free_paths_ode(self.physics, p1, p2)
         else:
             points = get_free_paths_triangles(relevant_models, self.cutter,
                     p1, p2)
         if points:
             for point in points:
                 pa.append(point)
             if draw_callback:
                 draw_callback(tool_position=points[-1], toolpath=pa.paths)
Exemple #8
0
def get_collision_waterline_of_triangle(model, cutter, up_vector, triangle, z):
    # TODO: there are problems with "material allowance > 0"
    plane = Plane(Point(0, 0, z), up_vector)
    if triangle.minz >= z:
        # no point of the triangle is below z
        # try all edges
        # Case (4)
        proj_points = []
        for p in triangle.get_points():
            proj_p = plane.get_point_projection(p)
            if not proj_p in proj_points:
                proj_points.append(proj_p)
        if len(proj_points) == 3:
            edges = []
            for index in range(3):
                edge = Line(proj_points[index - 1], proj_points[index])
                # the edge should be clockwise around the model
                if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
                    edge = Line(edge.p2, edge.p1)
                edges.append((edge, proj_points[index - 2]))
            outer_edges = []
            for edge, other_point in edges:
                # pick only edges, where the other point is on the right side
                if other_point.sub(edge.p1).cross(edge.dir).dot(up_vector) > 0:
                    outer_edges.append(edge)
            if len(outer_edges) == 0:
                # the points seem to be an one line
                # pick the longest edge
                long_edge = edges[0][0]
                for edge, other_point in edges[1:]:
                    if edge.len > long_edge.len:
                        long_edge = edge
                outer_edges = [long_edge]
        else:
            edge = Line(proj_points[0], proj_points[1])
            if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
                edge = Line(edge.p2, edge.p1)
            outer_edges = [edge]
    else:
        # some parts of the triangle are above and some below the cutter level
        # Cases (2a), (2b), (3a) and (3b)
        points_above = [plane.get_point_projection(p)
                for p in triangle.get_points() if p.z > z]
        waterline = plane.intersect_triangle(triangle)
        if waterline is None:
            if len(points_above) == 0:
                # the highest point of the triangle is at z
                outer_edges = []
            else:
                if abs(triangle.minz - z) < epsilon:
                    # This is just an accuracy issue (see the
                    # "triangle.minz >= z" statement above).
                    outer_edges = []
                elif not [p for p in triangle.get_points()
                        if p.z > z + epsilon]:
                    # same as above: fix for inaccurate floating calculations
                    outer_edges = []
                else:
                    # this should not happen
                    raise ValueError(("Could not find a waterline, but " \
                            + "there are points above z level (%f): " \
                            + "%s / %s") % (z, triangle, points_above))
        else:
            # remove points that are not part of the waterline
            points_above = [p for p in points_above
                    if (p != waterline.p1) and (p != waterline.p2)]
            if len(points_above) == 0:
                # part of case (2a)
                outer_edges = [waterline]
            elif len(points_above) == 1:
                other_point = points_above[0]
                dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(
                        up_vector)
                if dot > 0:
                    # Case (2b)
                    outer_edges = [waterline]
                elif dot < 0:
                    # Case (3b)
                    edges = []
                    edges.append(Line(waterline.p1, other_point))
                    edges.append(Line(waterline.p2, other_point))
                    outer_edges = []
                    for edge in edges:
                        if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
                            outer_edges.append(Line(edge.p2, edge.p1))
                        else:
                            outer_edges.append(edge)
                else:
                    # the three points are on one line
                    # part of case (2a)
                    edges = []
                    edges.append(waterline)
                    edges.append(Line(waterline.p1, other_point))
                    edges.append(Line(waterline.p2, other_point))
                    edges.sort(key=lambda x: x.len)
                    edge = edges[-1]
                    if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
                        outer_edges = [Line(edge.p2, edge.p1)]
                    else:
                        outer_edges = [edge]
            else:
                # two points above
                other_point = points_above[0]
                dot = other_point.sub(waterline.p1).cross(waterline.dir).dot(
                        up_vector)
                if dot > 0:
                    # Case (2b)
                    # the other two points are on the right side
                    outer_edges = [waterline]
                elif dot < 0:
                    # Case (3a)
                    edge = Line(points_above[0], points_above[1])
                    if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
                        outer_edges = [Line(edge.p2, edge.p1)]
                    else:
                        outer_edges = [edge]
                else:
                    edges = []
                    # pick the longest combination of two of these points
                    # part of case (2a)
                    # TODO: maybe we should use the waterline instead?
                    # (otherweise the line could be too long and thus
                    # connections to the adjacent waterlines are not discovered?
                    # Test this with an appropriate test model.)
                    points = [waterline.p1, waterline.p2] + points_above
                    for p1 in points:
                        for p2 in points:
                            if not p1 is p2:
                                edges.append(Line(p1, p2))
                    edges.sort(key=lambda x: x.len)
                    edge = edges[-1]
                    if edge.dir.cross(triangle.normal).dot(up_vector) < 0:
                        outer_edges = [Line(edge.p2, edge.p1)]
                    else:
                        outer_edges = [edge]
    # calculate the maximum diagonal length within the model
    x_dim = abs(model.maxx - model.minx)
    y_dim = abs(model.maxy - model.miny)
    z_dim = abs(model.maxz - model.minz)
    max_length = sqrt(x_dim ** 2 + y_dim ** 2 + z_dim ** 2)
    result = []
    for edge in outer_edges:
        direction = up_vector.cross(edge.dir).normalized()
        if direction is None:
            continue
        direction = direction.mul(max_length)
        edge_dir = edge.p2.sub(edge.p1)
        # TODO: Adapt the number of potential starting positions to the length
        # of the line. Don't use 0.0 and 1.0 - this could result in ambiguous
        # collisions with triangles sharing these vertices.
        for factor in (0.5, epsilon, 1.0 - epsilon, 0.25, 0.75):
            start = edge.p1.add(edge_dir.mul(factor))
            # We need to use the triangle collision algorithm here - because we
            # need the point of collision in the triangle.
            collisions = get_free_paths_triangles([model], cutter, start,
                    start.add(direction), return_triangles=True)
            for index, coll in enumerate(collisions):
                if (index % 2 == 0) and (not coll[1] is None) \
                        and (not coll[2] is None) \
                        and (coll[0].sub(start).dot(direction) > 0):
                    cl, hit_t, cp = coll
                    break
            else:
                log.debug("Failed to detect any collision: " \
                        + "%s / %s -> %s" % (edge, start, direction))
                continue
            proj_cp = plane.get_point_projection(cp)
            # e.g. the Spherical Cutter often does not collide exactly above
            # the potential collision line.
            # TODO: maybe an "is cp inside of the triangle" check would be good?
            if (triangle is hit_t) or (edge.is_point_inside(proj_cp)):
                result.append((cl, edge))
                # continue with the next outer_edge
                break
    # Don't check triangles again that are completely above the z level and
    # did not return any collisions.
    if (len(result) == 0) and (triangle.minz > z):
        # None indicates that the triangle needs no further evaluation
        return None
    return result
Exemple #9
0
 def _get_free_paths(self, cutter, models, p1, p2):
     if self.physics:
         return get_free_paths_ode(self.physics, p1, p2,
                 depth=self._physics_maxdepth)
     else:
         return get_free_paths_triangles(models, cutter, p1, p2)
Exemple #10
0
 def _get_free_paths(self, cutter, models, p1, p2):
     return get_free_paths_triangles(models, cutter, p1, p2)
Exemple #11
0
    def GenerateToolPath(self, cutter, models, motion_grid, minz=None, maxz=None, draw_callback=None):
        # Transfer the grid (a generator) into a list of lists and count the
        # items.
        grid = []
        num_of_grid_positions = 0
        for layer in motion_grid:
            lines = []
            for line in layer:
                # convert the generator to a list
                lines.append(list(line))
            num_of_grid_positions += len(lines)
            grid.append(lines)

        num_of_layers = len(grid)

        progress_counter = ProgressCounter(num_of_grid_positions, draw_callback)

        current_layer = 0
        if self.waterlines:
            self.pa = pycam.PathProcessors.ContourCutter.ContourCutter()
        else:
            path = []
        for layer_grid in grid:
            # update the progress bar and check, if we should cancel the process
            if draw_callback and draw_callback(text="PushCutter: processing" \
                        + " layer %d/%d" % (current_layer + 1, num_of_layers)):
                # cancel immediately
                break

            if self.waterlines:
                self.pa.new_direction(0)
            result = self.GenerateToolPathSlice(cutter, models, layer_grid,
                    draw_callback, progress_counter)
            if self.waterlines:
                self.pa.end_direction()
                self.pa.finish()
            else:
                path.extend(result)

            current_layer += 1

        if self.waterlines:
            # TODO: this is complicated and hacky :(
            # we don't use parallelism or ODE (for the sake of simplicity)
            result = []
            # turn the waterline points into cutting segments
            for path in self.pa.paths:
                pairs = []
                for index in range(len(path.points) - 1):
                    pairs.append((path.points[index], path.points[index + 1]))
                if len(models) > 1:
                    # We assume that the first model is used for the waterline and all
                    # other models are obstacles (e.g. a support grid).
                    other_models = models[1:]
                    for p1, p2 in pairs:
                        free_points = get_free_paths_triangles(other_models,
                                cutter, p1, p2)
                        for index in range(len(free_points) / 2):
                            result.append((MOVE_STRAIGHT, free_points[2 * index]))
                            result.append((MOVE_STRAIGHT, free_points[2 * index + 1]))
                            result.append((MOVE_SAFETY, None))
                else:
                    for p1, p2 in pairs:
                        result.append((MOVE_STRAIGHT, p1))
                        result.append((MOVE_STRAIGHT, p2))
                        result.append((MOVE_SAFETY, None))
            return result
        else:
            return path
Exemple #12
0
def _process_one_line((p1, p2, depth, models, cutter, physics)):
    if physics:
        points = get_free_paths_ode(physics, p1, p2, depth=depth)
    else:
        points = get_free_paths_triangles(models, cutter, p1, p2)
    return points
Exemple #13
0
    def generate_toolpath(self,
                          cutter,
                          models,
                          motion_grid,
                          minz=None,
                          maxz=None,
                          draw_callback=None):
        # Transfer the grid (a generator) into a list of lists and count the items.
        grid = []
        num_of_grid_positions = 0
        for layer in motion_grid:
            lines = []
            for line in layer:
                # convert the generator to a list
                lines.append(list(line))
            num_of_grid_positions += len(lines)
            grid.append(lines)

        num_of_layers = len(grid)

        progress_counter = ProgressCounter(num_of_grid_positions,
                                           draw_callback)

        current_layer = 0
        if self.waterlines:
            self.pa = pycam.PathProcessors.ContourCutter.ContourCutter()
        else:
            path = []
        for layer_grid in grid:
            # update the progress bar and check, if we should cancel the process
            if draw_callback and draw_callback(
                    text=("PushCutter: processing layer %d/%d" %
                          (current_layer + 1, num_of_layers))):
                # cancel immediately
                break

            if self.waterlines:
                self.pa.new_direction(0)
            result = self.generate_toolpath_slice(cutter, models, layer_grid,
                                                  draw_callback,
                                                  progress_counter)
            if self.waterlines:
                self.pa.end_direction()
                self.pa.finish()
            else:
                path.extend(result)

            current_layer += 1

        if self.waterlines:
            # TODO: this is complicated and hacky :(
            # we don't use parallelism (for the sake of simplicity)
            result = []
            # turn the waterline points into cutting segments
            for path in self.pa.paths:
                pairs = []
                for index in range(len(path.points) - 1):
                    pairs.append((path.points[index], path.points[index + 1]))
                if len(models) > 1:
                    # We assume that the first model is used for the waterline and all
                    # other models are obstacles (e.g. a support grid).
                    other_models = models[1:]
                    for p1, p2 in pairs:
                        free_points = get_free_paths_triangles(
                            other_models, cutter, p1, p2)
                        for index in range(len(free_points) // 2):
                            result.append(MoveStraight(free_points[2 * index]))
                            result.append(
                                MoveStraight(free_points[2 * index + 1]))
                            result.append(MoveSafety())
                else:
                    for p1, p2 in pairs:
                        result.append(MoveStraight(p1))
                        result.append(MoveStraight(p2))
                        result.append(MoveSafety())
            return result
        else:
            return path
Exemple #14
0
def _process_one_line(extra_args):
    p1, p2, models, cutter = extra_args
    points = get_free_paths_triangles(models, cutter, p1, p2)
    return points
Exemple #15
0
def _process_one_line((p1, p2, depth, models, cutter, physics)):
    if physics:
        points = get_free_paths_ode(physics, p1, p2, depth=depth)
    else:
        points = get_free_paths_triangles(models, cutter, p1, p2)
    return points