Esempio n. 1
0
def get_lines_layer(lines, z, last_z=None, step_width=None,
                    milling_style=MillingStyle.CONVENTIONAL):
    get_proj_point = lambda proj_point: (proj_point[0], proj_point[1], z)
    projected_lines = []
    for line in lines:
        if (last_z is not None) and (last_z < line.minz):
            # the line was processed before
            continue
        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[2]) / (line.p2[2] - line.p1[2])
            plane_point = padd(line.p1, pmul(line.vector, factor))
            if line.p1[2] < z:
                p1 = get_proj_point(line.p1)
                p2 = line.p2
            else:
                p1 = line.p1
                p2 = get_proj_point(line.p2)
            projected_lines.append(Line(p1, plane_point))
            yield Line(plane_point, p2)
        elif line.minz < last_z < line.maxz:
            plane = Plane((0, 0, last_z), (0, 0, 1, 'v'))
            cp = plane.intersect_point(line.dir, line.p1)[0]
            # we can be sure that there is an intersection
            if line.p1[2] > last_z:
                p1, p2 = cp, line.p2
            else:
                p1, p2 = line.p1, cp
            projected_lines.append(Line(p1, p2))
        else:
            if line.maxz <= z:
                # the line is completely below z
                projected_lines.append(Line(get_proj_point(line.p1), get_proj_point(line.p2)))
            elif line.minz >= z:
                projected_lines.append(line)
            else:
                _log.warn("Unexpected condition 'get_lines_layer': %s / %s / %s / %s",
                          line.p1, line.p2, z, last_z)
    # process all projected lines
    for line in projected_lines:
        points = []
        if step_width is None:
            points.append(line.p1)
            points.append(line.p2)
        else:
            if isiterable(step_width):
                steps = step_width
            else:
                steps = floatrange(0.0, line.len, inc=step_width)
            for step in steps:
                next_point = padd(line.p1, pmul(line.dir, step))
                points.append(next_point)
        yield points
Esempio n. 2
0
 def intersect_cylinder_line(self, direction, edge, start=None):
     if start is None:
         start = self.location
     (ccp, cp, l) = intersect_cylinder_line(
         padd(psub(start, self.location), self.center), self.axis,
         self.distance_radius, self.distance_radiussq, direction, edge)
     # offset intersection
     if ccp:
         cl = padd(start, psub(cp, ccp))
         return (cl, ccp, cp, l)
     return (None, None, None, INFINITE)
Esempio n. 3
0
 def intersect_sphere_point(self, direction, point, start=None):
     if start is None:
         start = self.location
     (ccp, cp, l) = intersect_sphere_point(padd(psub(start, self.location), self.center),
                                           self.distance_radius, self.distance_radiussq,
                                           direction, point)
     # offset intersection
     cl = None
     if cp:
         cl = padd(start, pmul(direction, l))
     return (cl, ccp, cp, l)
Esempio n. 4
0
 def intersect_sphere_plane(self, direction, triangle, start=None):
     if start is None:
         start = self.location
     (ccp, cp, d) = intersect_sphere_plane(
         padd(psub(start, self.location), self.center),
         self.distance_radius, direction, triangle)
     # offset intersection
     if ccp:
         cl = padd(cp, psub(start, ccp))
         return (cl, ccp, cp, d)
     return (None, None, None, INFINITE)
Esempio n. 5
0
 def intersect_torus_plane(self, direction, triangle, start=None):
     if start is None:
         start = self.location
     (ccp, cp, l) = intersect_torus_plane(
         padd(psub(start, self.location),
              self.center), self.axis, self.distance_majorradius,
         self.distance_minorradius, direction, triangle)
     if cp:
         cl = padd(cp, psub(start, ccp))
         return (cl, ccp, cp, l)
     return (None, None, None, INFINITE)
Esempio n. 6
0
 def intersect_torus_point(self, direction, point, start=None):
     if start is None:
         start = self.location
     (ccp, cp, l) = intersect_torus_point(
         padd(psub(start, self.location),
              self.center), self.axis, self.distance_majorradius,
         self.distance_minorradius, self.distance_majorradiussq,
         self.distance_minorradiussq, direction, point)
     if ccp:
         cl = padd(point, psub(start, ccp))
         return (cl, ccp, point, l)
     return (None, None, None, INFINITE)
Esempio n. 7
0
 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
Esempio n. 8
0
def intersect_cylinder_point(center, axis, radius, radiussq, direction, point):
    # take a plane along direction and axis
    n = pnormalized(pcross(direction, axis))
    # distance of the point to this plane
    d = pdot(n, point) - pdot(n, center)
    if abs(d) > radius - epsilon:
        return (None, None, INFINITE)
    # ccl is on cylinder
    d2 = sqrt(radiussq - d * d)
    ccl = padd(padd(center, pmul(n, d)), pmul(direction, d2))
    # take plane through ccl and axis
    plane = Plane(ccl, direction)
    # intersect point with plane
    (ccp, l) = plane.intersect_point(direction, point)
    return (ccp, point, -l)
Esempio n. 9
0
 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
Esempio n. 10
0
 def calc_normal(main, normals):
     suitable = (0, 0, 0, 'v')
     for normal, weight in normals:
         dot = pdot(main, normal)
         if dot > 0:
             suitable = padd(suitable, pmul(normal, weight * dot))
     return pnormalized(suitable)
Esempio n. 11
0
    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)
Esempio n. 12
0
 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)
Esempio n. 13
0
 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
Esempio n. 14
0
 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
Esempio n. 15
0
 def filter_toolpath(self, toolpath):
     feedrate = min_feedrate = 1
     new_path = []
     last_pos = None
     limit = self.settings["timelimit"]
     duration = 0
     for step in toolpath:
         if step.action in MOVES_LIST:
             if last_pos:
                 new_distance = pdist(step.position, 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(step.position, last_pos), partial))
                     duration = limit
                 else:
                     destination = step.position
                     duration += new_duration
             else:
                 destination = step.position
             new_path.append(ToolpathSteps.get_step_class_by_action(step.action)(destination))
             last_pos = step.position
         if (step.action == MACHINE_SETTING) and (step.key == "feedrate"):
             feedrate = step.value
         if duration >= limit:
             break
     return new_path
Esempio n. 16
0
def get_shifted_waterline(up_vector, waterline, cutter_location):
    # Project the waterline and the cutter location down to the slice plane.
    # This is necessary for calculating the horizontal distance between the
    # cutter and the triangle waterline.
    plane = Plane(cutter_location, up_vector)
    wl_proj = plane.get_line_projection(waterline)
    if wl_proj.len < epsilon:
        return None
    offset = wl_proj.dist_to_point(cutter_location)
    if offset < epsilon:
        return wl_proj
    # shift both ends of the waterline towards the cutter location
    shift = psub(cutter_location, wl_proj.closest_point(cutter_location))
    # increase the shift width slightly to avoid "touch" collisions
    shift = pmul(shift, 1.0 + epsilon)
    shifted_waterline = Line(padd(wl_proj.p1, shift), padd(wl_proj.p2, shift))
    return shifted_waterline
Esempio n. 17
0
 def intersect_cylinder_vertex(self, direction, point, start=None):
     if start is None:
         start = self.location
     (cl, ccp, cp, l) = self.intersect_cylinder_point(direction,
                                                      point,
                                                      start=start)
     if ccp and ccp[2] < padd(psub(start, self.location), self.center)[2]:
         return (None, INFINITE, None)
     return (cl, l, cp)
Esempio n. 18
0
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)
Esempio n. 19
0
 def get_shifted_vertex(self, index, offset):
     p1 = self._points[index]
     p2 = self._points[(index + 1) % len(self._points)]
     cross_offset = pnormalized(pcross(psub(p2, p1), self.plane.n))
     bisector_normalized = self.get_bisector(index)
     factor = pdot(cross_offset, bisector_normalized)
     if factor != 0:
         bisector_sized = pmul(bisector_normalized, offset / factor)
         return padd(p1, bisector_sized)
     else:
         return p2
Esempio n. 20
0
 def get_intersection(self, line, infinite_lines=False):
     """ Get the point of intersection between two lines. Intersections
     outside the length of these lines are ignored.
     Returns (None, None) if no valid intersection was found.
     Otherwise the result is (CollisionPoint, distance). Distance is between
     0 and 1.
     """
     x1, x2, x3, x4 = self.p1, self.p2, line.p1, line.p2
     a = psub(x2, x1)
     b = psub(x4, x3)
     c = psub(x3, x1)
     # see http://mathworld.wolfram.com/Line-LineIntersection.html (24)
     try:
         factor = pdot(pcross(c, b), pcross(a, b)) / pnormsq(pcross(a, b))
     except ZeroDivisionError:
         # lines are parallel
         # check if they are _one_ line
         if pnorm(pcross(a, c)) != 0:
             # the lines are parallel with a distance
             return None, None
         # the lines are on one straight
         candidates = []
         if self.is_point_inside(x3):
             candidates.append((x3, pnorm(c) / pnorm(a)))
         elif self.is_point_inside(x4):
             candidates.append((x4, pdist(line.p2, self.p1) / pnorm(a)))
         elif line.is_point_inside(x1):
             candidates.append((x1, 0))
         elif line.is_point_inside(x2):
             candidates.append((x2, 1))
         else:
             return None, None
         # return the collision candidate with the lowest distance
         candidates.sort(key=lambda collision: collision[1])
         return candidates[0]
     if infinite_lines or (-epsilon <= factor <= 1 + epsilon):
         intersec = padd(x1, pmul(a, factor))
         # check if the intersection is between x3 and x4
         if infinite_lines:
             return intersec, factor
         elif ((min(x3[0], x4[0]) - epsilon <= intersec[0] <=
                max(x3[0], x4[0]) + epsilon)
               and (min(x3[1], x4[1]) - epsilon <= intersec[1] <=
                    max(x3[1], x4[1]) + epsilon)
               and (min(x3[2], x4[2]) - epsilon <= intersec[2] <=
                    max(x3[2], x4[2]) + epsilon)):
             return intersec, factor
         else:
             # intersection outside of the length of line(x3, x4)
             return None, None
     else:
         # intersection outside of the length of line(x1, x2)
         return None, None
Esempio n. 21
0
 def intersect_point(self, direction, point):
     if (direction is not None) and (pnorm(direction) != 1):
         # calculations will go wrong, if the direction is not a unit vector
         direction = pnormalized(direction)
     if direction is None:
         return (None, INFINITE)
     denom = pdot(self.n, direction)
     if denom == 0:
         return (None, INFINITE)
     l = -(pdot(self.n, point) - pdot(self.n, self.p)) / denom
     cp = padd(point, pmul(direction, l))
     return (cp, l)
Esempio n. 22
0
def intersect_sphere_plane(center, radius, direction, triangle):
    # let n be the normal to the plane
    n = triangle.normal
    if pdot(n, direction) == 0:
        return (None, None, INFINITE)
    # the cutter contact point is on the sphere, where the surface normal is n
    if pdot(n, direction) < 0:
        ccp = psub(center, pmul(n, radius))
    else:
        ccp = padd(center, pmul(n, radius))
    # intersect the plane with a line through the contact point
    (cp, d) = triangle.plane.intersect_point(direction, ccp)
    return (ccp, cp, d)
Esempio n. 23
0
 def intersect_cylinder_edge(self, direction, edge, start=None):
     if start is None:
         start = self.location
     (cl, ccp, cp, l) = self.intersect_cylinder_line(direction,
                                                     edge,
                                                     start=start)
     if not ccp:
         return (None, INFINITE, None)
     m = pdot(psub(cp, edge.p1), edge.dir)
     if (m < -epsilon) or (m > edge.len + epsilon):
         return (None, INFINITE, None)
     if ccp[2] < padd(psub(start, self.location), self.center)[2]:
         return (None, INFINITE, None)
     return (cl, l, cp)
Esempio n. 24
0
 def draw_direction_cone_mesh(self,
                              p1,
                              p2,
                              position=0.5,
                              precision=12,
                              size=0.1):
     distance = psub(p2, p1)
     length = pnorm(distance)
     direction = pnormalized(distance)
     if direction is None or length < 0.5:
         # zero-length line
         return []
     cone_length = length * size
     cone_radius = cone_length / 3.0
     bottom = padd(p1, pmul(psub(p2, p1), position - size / 2))
     top = padd(p1, pmul(psub(p2, p1), position + size / 2))
     # generate a a line perpendicular to this line, cross product is good at this
     cross = pcross(direction, (0, 0, -1))
     conepoints = []
     if pnorm(cross) != 0:
         # The line direction is not in line with the z axis.
         conep1 = padd(bottom, pmul(cross, cone_radius))
         conepoints = [
             self._rotate_point(conep1, bottom, direction, x)
             for x in numpy.linspace(0, 2 * math.pi, precision)
         ]
     else:
         # Z axis
         # just add cone radius to the x axis and rotate the point
         conep1 = (bottom[0] + cone_radius, bottom[1], bottom[2])
         conepoints = [
             self._rotate_point(conep1, p1, direction, x)
             for x in numpy.linspace(0, 2 * math.pi, precision)
         ]
     triangles = [(top, conepoints[idx], conepoints[idx + 1])
                  for idx in range(len(conepoints) - 1)]
     return triangles
Esempio n. 25
0
def intersect_cylinder_line(center, axis, radius, radiussq, direction, edge):
    d = edge.dir
    # take a plane throught the line and along the cylinder axis (1)
    n = pcross(d, axis)
    if pnorm(n) == 0:
        # no contact point, but should check here if cylinder *always*
        # intersects line...
        return (None, None, INFINITE)
    n = pnormalized(n)
    # the contact line between the cylinder and this plane (1)
    # is where the surface normal is perpendicular to the plane
    # so line := ccl + \lambda * axis
    if pdot(n, direction) < 0:
        ccl = psub(center, pmul(n, radius))
    else:
        ccl = padd(center, pmul(n, radius))
    # now extrude the contact line along the direction, this is a plane (2)
    n2 = pcross(direction, axis)
    if pnorm(n2) == 0:
        # no contact point, but should check here if cylinder *always*
        # intersects line...
        return (None, None, INFINITE)
    n2 = pnormalized(n2)
    plane1 = Plane(ccl, n2)
    # intersect this plane with the line, this gives us the contact point
    (cp, l) = plane1.intersect_point(d, edge.p1)
    if not cp:
        return (None, None, INFINITE)
    # now take a plane through the contact line and perpendicular to the
    # direction (3)
    plane2 = Plane(ccl, direction)
    # the intersection of this plane (3) with the line through the contact point
    # gives us the cutter contact point
    (ccp, l) = plane2.intersect_point(direction, cp)
    cp = padd(ccp, pmul(direction, -l))
    return (ccp, cp, -l)
Esempio n. 26
0
def get_bisector(p1, p2, p3, up_vector):
    """ Calculate the bisector between p1, p2 and p3, whereas p2 is the origin
    of the angle.
    """
    d1 = pnormalized(psub(p2, p1))
    d2 = pnormalized(psub(p2, p3))
    bisector_dir = pnormalized(padd(d1, d2))
    if bisector_dir is None:
        # the two vectors pointed to opposite directions
        bisector_dir = pnormalized(pcross(d1, up_vector))
    else:
        skel_up_vector = pcross(bisector_dir, psub(p2, p1))
        if pdot(up_vector, skel_up_vector) < 0:
            # reverse the skeleton vector to point outwards
            bisector_dir = pmul(bisector_dir, -1)
    return bisector_dir
Esempio n. 27
0
def intersect_circle_plane(center, radius, direction, triangle):
    # let n be the normal to the plane
    n = triangle.normal
    if pdot(n, direction) == 0:
        return (None, None, INFINITE)
    # project onto z=0
    n2 = (n[0], n[1], 0)
    if pnorm(n2) == 0:
        (cp, d) = triangle.plane.intersect_point(direction, center)
        ccp = psub(cp, pmul(direction, d))
        return (ccp, cp, d)
    n2 = pnormalized(n2)
    # the cutter contact point is on the circle, where the surface normal is n
    ccp = padd(center, pmul(n2, -radius))
    # intersect the plane with a line through the contact point
    (cp, d) = triangle.plane.intersect_point(direction, ccp)
    return (ccp, cp, d)
Esempio n. 28
0
def _add_cuboid_to_model(model, start, direction, height, width):
    up = pmul((0, 0, 1, 'v'), height)
    ortho_dir = pnormalized(pcross(direction, up))
    start1 = padd(start, pmul(ortho_dir, -width / 2))
    start2 = padd(start1, up)
    start3 = padd(start2, pmul(ortho_dir, width))
    start4 = psub(start3, up)
    end1 = padd(start1, direction)
    end2 = padd(start2, direction)
    end3 = padd(start3, direction)
    end4 = padd(start4, direction)
    faces = ((start1, start2, start3, start4), (start1, end1, end2, start2),
             (start2, end2, end3, start3), (start3, end3, end4, start4),
             (start4, end4, end1, start1), (end4, end3, end2, end1))
    for face in faces:
        t1, t2 = _get_triangles_for_face(face)
        model.append(t1)
        model.append(t2)
Esempio n. 29
0
def intersect_sphere_point(center, radius, radiussq, direction, point):
    # line equation
    # (1) x = p_0 + \lambda * d
    # sphere equation
    # (2) (x-x_0)^2 = R^2
    # (1) in (2) gives a quadratic in \lambda
    p0_x0 = psub(center, point)
    a = pnormsq(direction)
    b = 2 * pdot(p0_x0, direction)
    c = pnormsq(p0_x0) - radiussq
    d = b * b - 4 * a * c
    if d < 0:
        return (None, None, INFINITE)
    if a < 0:
        l = (-b + sqrt(d)) / (2 * a)
    else:
        l = (-b - sqrt(d)) / (2 * a)
    # cutter contact point
    ccp = padd(point, pmul(direction, -l))
    return (ccp, point, l)
Esempio n. 30
0
def get_spiral_layer(minx, maxx, miny, maxy, z, line_distance, step_width,
                     grid_direction, start_position, rounded_corners, reverse):
    current_location = _get_position(minx, maxx, miny, maxy, z, start_position)
    if line_distance > 0:
        line_steps_x = math.ceil((float(maxx - minx) / line_distance))
        line_steps_y = math.ceil((float(maxy - miny) / line_distance))
        line_distance_x = (maxx - minx) / line_steps_x
        line_distance_y = (maxy - miny) / line_steps_y
        lines = get_spiral_layer_lines(minx, maxx, miny, maxy, z,
                                       line_distance_x, line_distance_y,
                                       grid_direction, start_position,
                                       current_location)
        if reverse:
            lines.reverse()
        # turn the lines into steps
        if rounded_corners:
            rounded_lines = []
            previous = None
            for index, (start, end) in enumerate(lines):
                radius = 0.5 * min(line_distance_x, line_distance_y)
                edge_vector = psub(end, start)
                # TODO: ellipse would be better than arc
                offset = pmul(pnormalized(edge_vector), radius)
                if previous:
                    start = padd(start, offset)
                    center = padd(previous, offset)
                    up_vector = pnormalized(
                        pcross(psub(previous, center), psub(start, center)))
                    north = padd(center, (1.0, 0.0, 0.0, 'v'))
                    angle_start = get_angle_pi(
                        north, center, previous, up_vector,
                        pi_factor=True) * 180.0
                    angle_end = get_angle_pi(
                        north, center, start, up_vector,
                        pi_factor=True) * 180.0
                    # TODO: remove these exceptions based on up_vector.z (get_points_of_arc does
                    #       not respect the plane, yet)
                    if up_vector[2] < 0:
                        angle_start, angle_end = -angle_end, -angle_start
                    arc_points = get_points_of_arc(center, radius, angle_start,
                                                   angle_end)
                    if up_vector[2] < 0:
                        arc_points.reverse()
                    for arc_index in range(len(arc_points) - 1):
                        p1_coord = arc_points[arc_index]
                        p2_coord = arc_points[arc_index + 1]
                        p1 = (p1_coord[0], p1_coord[1], z)
                        p2 = (p2_coord[0], p2_coord[1], z)
                        rounded_lines.append((p1, p2))
                if index != len(lines) - 1:
                    end = psub(end, offset)
                previous = end
                rounded_lines.append((start, end))
            lines = rounded_lines
        for start, end in lines:
            points = []
            if step_width is None:
                points.append(start)
                points.append(end)
            else:
                line = Line(start, end)
                if isiterable(step_width):
                    steps = step_width
                else:
                    steps = floatrange(0.0, line.len, inc=step_width)
                for step in steps:
                    next_point = padd(line.p1, pmul(line.dir, step))
                    points.append(next_point)
            if reverse:
                points.reverse()
            yield points
Esempio n. 31
0
def get_bezier_lines(points_with_bulge, segments=32):
    # TODO: add a recursive algorithm for more than two points
    if len(points_with_bulge) != 2:
        return []
    else:
        result_points = []
        p1, bulge1 = points_with_bulge[0]
        p2, bulge2 = points_with_bulge[1]
        if not bulge1 and not bulge2:
            # straight line
            return [Line(p1, p2)]
        straight_dir = pnormalized(psub(p2, p1))
        bulge1 = math.atan(bulge1)
        rot_matrix = Matrix.get_rotation_matrix_axis_angle((0, 0, 1),
                                                           -2 * bulge1,
                                                           use_radians=True)
        dir1_mat = Matrix.multiply_vector_matrix(
            (straight_dir[0], straight_dir[1], straight_dir[2]), rot_matrix)
        dir1 = (dir1_mat[0], dir1_mat[1], dir1_mat[2], 'v')
        if bulge2 is None:
            bulge2 = bulge1
        else:
            bulge2 = math.atan(bulge2)
        rot_matrix = Matrix.get_rotation_matrix_axis_angle((0, 0, 1),
                                                           2 * bulge2,
                                                           use_radians=True)
        dir2_mat = Matrix.multiply_vector_matrix(
            (straight_dir[0], straight_dir[1], straight_dir[2]), rot_matrix)
        dir2 = (dir2_mat[0], dir2_mat[1], dir2_mat[2], 'v')
        # interpretation of bulge1 and bulge2:
        # /// taken from http://paulbourke.net/dataformats/dxf/dxf10.html ///
        # The bulge is the tangent of 1/4 the included angle for an arc
        # segment, made negative if the arc goes clockwise from the start
        # point to the end point; a bulge of 0 indicates a straight segment,
        # and a bulge of 1 is a semicircle.
        alpha = 2 * (abs(bulge1) + abs(bulge2))
        dist = pdist(p2, p1)
        # calculate the radius of the circumcircle - avoiding divide-by-zero
        if (abs(alpha) < epsilon) or (abs(math.pi - alpha) < epsilon):
            radius = dist / 2.0
        else:
            # see http://en.wikipedia.org/wiki/Law_of_sines
            radius = abs(dist / math.sin(alpha / 2.0)) / 2.0
        # The calculation of "factor" is based on random guessing - but it
        # seems to work well.
        factor = 4 * radius * math.tan(alpha / 4.0)
        dir1 = pmul(dir1, factor)
        dir2 = pmul(dir2, factor)
        for index in range(segments + 1):
            # t: 0..1
            t = float(index) / segments
            # see: http://en.wikipedia.org/wiki/Cubic_Hermite_spline
            p = padd(
                pmul(p1, 2 * t**3 - 3 * t**2 + 1),
                padd(
                    pmul(dir1, t**3 - 2 * t**2 + t),
                    padd(pmul(p2, -2 * t**3 + 3 * t**2),
                         pmul(dir2, t**3 - t**2))))
            result_points.append(p)
        # create lines
        result = []
        for index in range(len(result_points) - 1):
            result.append(Line(result_points[index], result_points[index + 1]))
        return result