Beispiel #1
0
def set_wait_time_on_sharp_corners(print_organizer, threshold=0.5 * math.pi, wait_time=0.3):
    """
    Sets a wait time at the sharp corners of the path, based on the angle threshold.

    Parameters
    ----------
    print_organizer: :class:`compas_slicer.print_organization.BasePrintOrganizer`
    threshold: float
        angle_threshold
    wait_time: float
        Time in seconds to introduce to add as a wait time
    """
    number_of_wait_points = 0
    for printpoint, i, j, k in print_organizer.printpoints_indices_iterator():
        neighbors = print_organizer.get_printpoint_neighboring_items('layer_%d' % i, 'path_%d' % j, k)
        prev_ppt = neighbors[0]
        next_ppt = neighbors[1]

        if prev_ppt and next_ppt:
            v_to_prev = normalize_vector(Vector.from_start_end(printpoint.pt, prev_ppt.pt))
            v_to_next = normalize_vector(Vector.from_start_end(printpoint.pt, next_ppt.pt))
            a = abs(Vector(*v_to_prev).angle(v_to_next))

            if a < threshold:
                printpoint.wait_time = wait_time
                printpoint.blend_radius = 0.0  # 0.0 blend radius for points where the robot will wait
                number_of_wait_points += 1
    logger.info('Added wait times for %d points' % number_of_wait_points)
Beispiel #2
0
def is_cww(pt_A: Tuple[float], pt_B: Tuple[float], pt_C: Tuple[float]) -> bool:
    """ Checks if vector AB is oriented clockwise or counter-clockwise to
        vector AC.

        Parameters
        ----------
        pt_a, pt_b, pt_c : list or tuple of floats

        Returns
        -------
        bool

        >>> is_cww((20,30), (40,10), (12,25))
        False
    """
    # create the two vectors, but one is rotated 90 degrees
    # based on https://gamedev.stackexchange.com/questions/45412/
    vector_AC = Vector.from_start_end((*pt_A, 0), (*pt_C, 0))

    rot_B = rotate_points_xy([pt_B], radians(90), pt_A)
    rotated_AB = Vector.from_start_end((*pt_A, 0), *rot_B)

    # positive dot product ccw
    angle_between = dot_vectors_xy(rotated_AB, vector_AC)
    if angle_between > 0:
        return True
    if angle_between < 0:
        return False

    raise ValueError('Vectors have the same direction')
Beispiel #3
0
def is_ccw(A, B, C):
    Vector_AB = Vector.from_start_end(A, B)
    Vector_AC = Vector.from_start_end(A, C)

    #Tests whether rotation from AB onto AC is ccw in the xy-plane
    angle = Vector.angle_signed(Vector_AB, Vector_AC, (0, 0, 1))
    if angle < 0:
        return False
    else:
        return True
def get_area(polygon):

    center = polygon.center
    Atot = 0
    points = polygon.points
    points.append(points[0])

    for i in range(len(points) - 1):
        u = Vector.from_start_end(points[i], points[i + 1])
        v = Vector.from_start_end(points[i], center)
        uxv = u.cross(v)
        Atot += uxv.length / 2

    return Atot
Beispiel #5
0
def get_normal_of_path_on_xy_plane(k, point, path, mesh):
    """
    Finds the normal of the curve that lies on the xy plane at the point with index k

    Parameters
    ----------
    k: int, index of the point
    point: :class: 'compas.geometry.Point'
    path: :class: 'compas_slicer.geometry.Path'
    mesh: :class: 'compas.datastructures.Mesh'

    Returns
    ----------
    :class: 'compas.geometry.Vector'
    """

    # find mesh normal is not really needed in the 2D case of planar slicer
    # instead we only need the normal of the curve based on the neighboring pts
    if (0 < k < len(path.points) - 1) or path.is_closed:
        prev_pt = path.points[k - 1]
        next_pt = path.points[(k + 1) % len(path.points)]
        v1 = np.array(normalize_vector(Vector.from_start_end(prev_pt, point)))
        v2 = np.array(normalize_vector(Vector.from_start_end(point, next_pt)))
        v = (v1 + v2) * 0.5
        normal = [-v[1], v[0],
                  v[2]]  # rotate 90 degrees COUNTER-clockwise on the xy plane

    else:
        if k == 0:
            next_pt = path.points[k + 1]
            v = normalize_vector(Vector.from_start_end(point, next_pt))
            normal = [-v[1], v[0], v[2]
                      ]  # rotate 90 degrees COUNTER-clockwise on the xy plane
        else:  # k == len(path.points)-1:
            prev_pt = path.points[k - 1]
            v = normalize_vector(Vector.from_start_end(point, prev_pt))
            normal = [v[1], -v[0],
                      v[2]]  # rotate 90 degrees clockwise on the xy plane

    if length_vector(normal) == 0:
        # When the neighboring elements happen to cancel out, then search for the true normal,
        # and project it on the xy plane for consistency
        normal = get_closest_mesh_normal_to_pt(mesh, point)
        normal = [normal[0], normal[1], 0]

    normal = normalize_vector(normal)
    normal = Vector(*list(normal))
    return normal
def convex_polygon_area_compas(polygon_vertices):
    """Compute the area of a convex polygon using compas.

    Parameters
    ----------
    polygon : sequence
        The XY coordinates of the vertices/corners of the polygon.
        The vertices are assumed to be in order.
        The polygon is assumed to be closed:
        the first and last vertex in the sequence should not be the same.

    Returns
    -------
    float
        The area of the polygon.
    """

    polygon = Polygon(polygon_vertices)
    if not polygon.is_convex:
        sys.exit("the polygon is not convex.")

    vectors = []
    centroid = polygon.centroid
    area = 0.0
    for p in polygon.points:
        vectors.append(Vector.from_start_end(centroid, p))
    for i in range(len(vectors)):
        area += cross_vectors(vectors[i - 1], vectors[i])[2] / 2

    return area
def convex_polygon_area(polygon):
    """Compute the area of a convex polygon.

    Parameters
    ----------
    polygon: compas.geometry.Polygon
        An ordered list of points of a convex polygon.

    Returns
    -------
    Float
        Area of the polygon.
    """
    # find vectors from centroid to all corner points.
    centroid = polygon.centroid
    vectors = []
    for vertex in polygon: 
        vectors.append(Vector.from_start_end(centroid, vertex))
    areas = []
    # the magnitude of the cross product equals the area of a parallelogram, thus half of it equals the triangle.
    for u, v in pairwise(vectors + vectors[:1]):
        uxv = u.cross(v)
        a = uxv.length / 2
        areas.append(a)
    return sum(areas)
def set_blend_radius(print_organizer, d_fillet=10, buffer=0.3):
    """Sets the blend radius (filleting) for the robotic motion.

    Parameters
    ----------
    print_organizer: :class:`compas_slicer.slicers.BasePrintOrganizer`
    d_fillet: float
        Value to attempt to fillet with. Defaults to 10 mm.
    buffer: float
        Buffer to make sure that the blend radius is never too big.
        Defaults to 0.3.
    """

    logger.info("Setting blend radius")

    for printpoint, i, j, k in print_organizer.printpoints_indices_iterator():
        layer_key = 'layer_%d' % i
        path_key = 'path_%d' % j
        neighboring_items = print_organizer.get_printpoint_neighboring_items(
            layer_key, path_key, k)

        if not printpoint.wait_time:
            radius = d_fillet
            if neighboring_items[0]:
                radius = min(
                    radius,
                    norm_vector(
                        Vector.from_start_end(neighboring_items[0].pt,
                                              printpoint.pt)) * buffer)

            if neighboring_items[1]:
                radius = min(
                    radius,
                    norm_vector(
                        Vector.from_start_end(neighboring_items[1].pt,
                                              printpoint.pt)) * buffer)

            radius = round(radius, 5)

        else:
            radius = 0.0  # 0.0 blend radius for points where the robot will pause and wait

        printpoint.blend_radius = radius
 def find_zero_crossing_data(self, u, v):
     """ Finds the position of the zero-crossing on the edge u,v. """
     dist_a, dist_b = self.mesh.vertex[u]['scalar_field'], self.mesh.vertex[
         v]['scalar_field']
     if abs(dist_a) + abs(dist_b) > 0:
         v_coords_a, v_coords_b = self.mesh.vertex_coordinates(
             u), self.mesh.vertex_coordinates(v)
         vec = Vector.from_start_end(v_coords_a, v_coords_b)
         vec = scale_vector(vec, abs(dist_a) / (abs(dist_a) + abs(dist_b)))
         pt = add_vectors(v_coords_a, vec)
         return pt
Beispiel #10
0
    def from_bounding_box(cls, bbox):
        """Construct a box from the result of a bounding box calculation.

        Parameters
        ----------
        bbox : list[[float, float, float] | :class:`compas.geometry.Point`]
            A list of 8 point locations, representing the corners of the bounding box.
            Positions 0, 1, 2, 3 are the bottom corners.
            Positions 4, 5, 6, 7 are the top corners.
            Both the top and bottom face are oriented in CCW direction, starting at the bottom, left-most point.

        Returns
        -------
        :class:`compas.geometry.Box`
            The box shape.

        Examples
        --------
        >>> from compas.geometry import bounding_box
        >>> bbox = bounding_box([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]])
        >>> box = Box.from_bounding_box(bbox)
        >>> box.width
        1.0
        >>> box.height
        1.0
        >>> box.depth
        1.0

        """
        a = bbox[0]
        b = bbox[1]
        d = bbox[3]
        e = bbox[4]
        xaxis = Vector.from_start_end(a, b)
        yaxis = Vector.from_start_end(a, d)
        zaxis = Vector.from_start_end(a, e)
        xsize = xaxis.length
        ysize = yaxis.length
        zsize = zaxis.length
        frame = Frame(centroid_points(bbox), xaxis, yaxis)
        return cls(frame, xsize, ysize, zsize)
Beispiel #11
0
    def get_layer_ppts(self, layer, base_boundary):
        """ Creates the PrintPoints of a single layer."""
        max_layer_height = get_param(self.parameters,
                                     key='max_layer_height',
                                     defaults_type='layers')
        min_layer_height = get_param(self.parameters,
                                     key='min_layer_height',
                                     defaults_type='layers')
        avg_layer_height = get_param(self.parameters, 'avg_layer_height',
                                     'layers')

        all_pts = [pt for path in layer.paths for pt in path.points]
        closest_fks, projected_pts = utils.pull_pts_to_mesh_faces(
            self.slicer.mesh, all_pts)
        normals = [
            Vector(*self.slicer.mesh.face_normal(fkey)) for fkey in closest_fks
        ]

        count = 0
        crv_to_check = Path(
            base_boundary.points,
            True)  # creation of fake path for the lower boundary

        layer_ppts = {}
        for i, path in enumerate(layer.paths):
            layer_ppts['path_%d' % i] = []

            for p in path.points:
                cp = closest_point_on_polyline(p,
                                               Polyline(crv_to_check.points))
                d = distance_point_point(cp, p)

                ppt = PrintPoint(pt=p,
                                 layer_height=avg_layer_height,
                                 mesh_normal=normals[count])

                ppt.closest_support_pt = Point(*cp)
                ppt.distance_to_support = d
                ppt.layer_height = max(min(d, max_layer_height),
                                       min_layer_height)
                ppt.up_vector = Vector(
                    *normalize_vector(Vector.from_start_end(cp, p)))
                ppt.frame = ppt.get_frame()

                layer_ppts['path_%d' % i].append(ppt)
                count += 1

            crv_to_check = path

        return layer_ppts
Beispiel #12
0
 def get_up_vectors(self):
     """ Finds the up_vectors of each point of the boundary. A smoothing step is also included. """
     up_vectors = []
     for i, p in enumerate(self.points):
         v1 = Vector.from_start_end(p,
                                    self.points[(i + 1) % len(self.points)])
         cross = v1.cross(self.normals[i])
         v = Vector(*normalize_vector(cross))
         if v[2] < 0:
             v.scale(-1)
         up_vectors.append(v)
     up_vectors = utils.smooth_vectors(up_vectors,
                                       strength=0.4,
                                       iterations=3)
     return up_vectors
def seams_smooth(slicer, smooth_distance):
    """Smooths the seams (transition between layers)
    by removing points within a certain distance.

    Parameters
    ----------
    slicer: :class:`compas_slicer.slicers.BaseSlicer`
        An instance of one of the compas_slicer.slicers classes.
    smooth_distance: float
        Distance (in mm) to perform smoothing
    """

    logger.info("Smoothing seams with a distance of %i mm" % smooth_distance)

    for i, layer in enumerate(slicer.layers):
        if len(layer.paths) == 1 or isinstance(
                layer, compas_slicer.geometry.VerticalLayer):
            for path in layer.paths:
                pt0 = path.points[0]
                # only points in the first half of a path should be evaluated
                half_of_path = path.points[:int(len(path.points) / 2)]
                for point in half_of_path:
                    if distance_point_point(pt0, point) < smooth_distance:
                        # remove points if within smooth_distance
                        path.points.pop(0)
                    else:
                        # create new point at a distance of the
                        # 'smooth_distance' from the first point,
                        # so that all seams are of equal length
                        vect = Vector.from_start_end(pt0, point)
                        vect.unitize()
                        new_pt = pt0 + (vect * smooth_distance)
                        path.points.insert(0, new_pt)
                        break
        else:
            logger.warning(
                "Smooth seams only works for layers consisting out of a single path, or for vertical layers."
                "\nPaths were not changed, seam smoothing skipped for layer %i"
                % i)
Beispiel #14
0
class SkeletonVol(Mesh):
    """ SkeletonVol is typologically constructed low poly mesh.
    It construct a branch like volumetric mesh from input lines. 
    """
    def __init__(self):
        super(SkeletonVol, self).__init__()

    @classmethod
    def from_skeleton_lines(cls, lines=[]):
        skeleton_vol = cls()

        network = Network.from_lines(lines)
        convex_hull_mesh = get_convex_hull_mesh(points)

    def get_convex_hull_mesh(points):
        faces = convex_hull(points)
        vertices = list(set(flatten(faces)))

        i_index = {i: index for index, i in enumerate(vertices)}
        vertices = [points[index] for index in vertices]
        faces = [[i_index[i] for i in face] for face in faces]
        faces = unify_cycles(vertices, faces)

        mesh = Mesh.from_vertices_and_faces(vertices, faces)

        return mesh

    guids = compas_rhino.select_lines()
    lines = compas_rhino.get_line_coordinates(guids)

    network = Network.from_lines(lines)

    leafs = []
    joints = []
    for key in network.node:
        if network.is_leaf(key):
            leafs.append(key)
        else:
            joints.append(key)

    pt_center = network.node_coordinates(joints[0])
    pts = [network.node_coordinates(key) for key in leafs]

    convex_hull_mesh = get_convex_hull_mesh(pts)

    mesh = Mesh()
    # for key in convex_hull_mesh.vertices():
    #     mesh.add_vertex(key)
    #     mesh.vertex[key].update(convex_hull_mesh.vertex[key])

    descdent_tree = copy.deepcopy(convex_hull_mesh.halfedge)

    for u, v in convex_hull_mesh.edges():
        descdent_tree[u][v] = {'jp': None, 'lp': None}
        descdent_tree[v][u] = {'jp': None, 'lp': None}

    # current_key = convex_hull_mesh.number_of_vertices()
    current_key = 0

    for fkey in convex_hull_mesh.faces():
        f_centroid = convex_hull_mesh.face_centroid(fkey)
        vec = Vector.from_start_end(pt_center, f_centroid)

        # if the branches has a 'convex' corner,
        # flip the vec to the corresponding face.
        f_normal = convex_hull_mesh.face_normal(fkey)
        angle = angle_vectors(f_normal, vec, False)
        if angle > math.pi * 0.5:
            pln = Plane(pt_center, f_normal)
            pt_mirror = mirror_point_plane(f_centroid, pln)
            vec = Vector.from_start_end(pt_center, pt_mirror)

        vec.unitize()
        vec.scale(joint_width)

        pt = add_vectors(pt_center, vec)

        face = convex_hull_mesh.face[fkey]
        v_keys = face + [face[0]]
        for u, v in pairwise(v_keys):
            descdent_tree[u][v].update({'jp': current_key})

        mesh.add_vertex(current_key)
        mesh.vertex[current_key].update({'x': pt[0], 'y': pt[1], 'z': pt[2]})

        current_key += 1

    for key in convex_hull_mesh.vertices():
        nbrs = convex_hull_mesh.vertex_neighbors(key)
        for nbr in nbrs:
            halfedge = (key, nbr)
            pt_joint_descendent = mesh.vertex_coordinates(
                descdent_tree[key][nbr]['jp'])

            vec_edge = Vector.from_start_end(
                pt_center, convex_hull_mesh.vertex_coordinates(key))
            pln_end = Plane(convex_hull_mesh.vertex_coordinates(key), vec_edge)
            pt = project_point_plane(pt_joint_descendent, pln_end)
            vec_leaf = Vector.from_start_end(
                convex_hull_mesh.vertex_coordinates(key), pt)
            vec_leaf.unitize()
            vec_leaf.scale(leaf_width)
            pt = add_vectors(convex_hull_mesh.vertex_coordinates(key),
                             vec_leaf)

            descdent_tree[key][nbr].update({'lp': current_key})

            mesh.add_vertex(current_key)
            mesh.vertex[current_key].update({
                'x': pt[0],
                'y': pt[1],
                'z': pt[2]
            })
            current_key += 1

    for key in convex_hull_mesh.vertices():
        nbrs = convex_hull_mesh.vertex_neighbors(key, ordered=True)
        v_keys = nbrs + [nbrs[0]]
        for a, b in pairwise(v_keys):
            face = [
                descdent_tree[key][a]['lp'], descdent_tree[key][a]['jp'],
                descdent_tree[key][b]['jp'], descdent_tree[key][b]['lp']
            ]
            mesh.add_face(face)

    fixed = list(mesh.vertices_where({'vertex_degree': 3}))
    fixed = list(mesh.vertices())

    mesh = mesh_subdivide_catmullclark(mesh, k=1, fixed=fixed)
    # mesh = mesh_subdivide_quad(mesh, k=1)
    mesh_smooth_centroid(mesh, fixed=fixed)

    artist = MeshArtist(mesh)
    artist.draw_mesh()
Beispiel #15
0
mesh = Mesh()
for key in convex_hull_mesh.vertices():
    mesh.add_vertex(key)
    mesh.vertex[key].update(convex_hull_mesh.vertex[key])

descdent_tree = copy.deepcopy(convex_hull_mesh.halfedge)

for u, v in convex_hull_mesh.edges():
    descdent_tree[u][v] = {'jp': None, 'lp': None}
    descdent_tree[v][u] = {'jp': None, 'lp': None}

current_key = convex_hull_mesh.number_of_vertices()

for fkey in convex_hull_mesh.faces():
    f_centroid = convex_hull_mesh.face_centroid(fkey)
    vec = Vector.from_start_end(pt_center, f_centroid)

    # if the branches has a 'convex' corner,
    # flip the vec to the corresponding face.
    f_normal = convex_hull_mesh.face_normal(fkey)
    angle = angle_vectors(f_normal, vec, False)
    if angle > math.pi * 0.5:
        # vec.scale(-1)
        pln = Plane(pt_center, f_normal)
        pt_mirror = mirror_point_plane(f_centroid, pln)
        vec = Vector.from_start_end(pt_center, pt_mirror)

        # angle = math.pi - angle

    vec.unitize()
    # dist = joint_width / math.cos(angle)
from compas.geometry import Frame
from compas.geometry import Plane
from compas.geometry import Vector

a = Vector(1, 0, 0)
b = Vector.from_start_end([1, 0, 0], [2, 0, 0])
assert a == b

a = Plane([0, 0, 0], [0, 0, 1])
b = Plane.from_three_points([0, 0, 0], [1, 0, 0], [0, 1, 0])
assert a == b

a = Frame([0, 0, 0], [3, 0, 0], [0, 2, 0])
b = Frame.from_points([0, 0, 0], [5, 0, 0], [1, 2, 0])
assert a == b
Beispiel #17
0
        data['positive_y_axis'] = is_positive_y

    # distance from plane - Scalar value (per vertex)
    mesh.update_default_vertex_attributes({'dist_from_plane': 0.0})
    plane = (Point(0.0, 0.0, -30.0), Vector(0.0, 0.5, 0.5))
    for v_key, data in mesh.vertices(data=True):
        v_coord = mesh.vertex_coordinates(v_key, axes='xyz')
        data['dist_from_plane'] = distance_point_plane(v_coord, plane)

    # direction towards point - Vector value (per vertex)
    mesh.update_default_vertex_attributes({'direction_to_pt': 0.0})
    pt = Point(4.0, 1.0, 0.0)
    for v_key, data in mesh.vertices(data=True):
        v_coord = mesh.vertex_coordinates(v_key, axes='xyz')
        data['direction_to_pt'] = np.array(
            normalize_vector(Vector.from_start_end(v_coord, pt)))

    # --------------- Slice mesh
    slicer = PlanarSlicer(mesh, slicer_type="default", layer_height=5.0)
    slicer.slice_model()
    simplify_paths_rdp_igl(slicer, threshold=1.0)
    slicer_utils.save_to_json(slicer.to_data(), OUTPUT_PATH,
                              'slicer_data.json')

    # --------------- Create printpoints
    print_organizer = PlanarPrintOrganizer(slicer)
    print_organizer.create_printpoints()

    # --------------- Transfer mesh attributes to printpoints
    transfer_mesh_attributes_to_printpoints(mesh,
                                            print_organizer.printpoints_dict)
Beispiel #18
0
3. Define a function for computing the cross products of two same-length arrays of vectors by
    a. Prototype in pure Python (loop over the arrays).
    b. Make Numpy equivalent without loops.
"""

# 2. Use the cross product to compute the area of a convex, 2D polygon from the following set of points, 
# and compare your result with using the compas function area_triangle.

from compas.geometry import Vector
from compas.geometry import area_triangle

a = [0.0, 0.0, 0.0]
b = [1.0, 0.0, 0.0]
c = [0.0, 1.0, 0.0]

ab = Vector.from_start_end(a, b) #Vector1
ac = Vector.from_start_end(a, c) #Vector2

#compute the cross product using the Vector function cross cp = a.cross(b)
x = #...

#take te length of the vector
L = #...

#and divide L by 2
A1 = #...
print(A1)

#now compute the area by using the area_triangle function of compas
A2 = #...
print(A2)
Beispiel #19
0
from compas.geometry import Vector
from compas.geometry import area_triangle

a = [0.0, 0.0, 0.0]
b = [1.0, 0.0, 0.0]
c = [0.0, 1.0, 0.0]

ab = Vector.from_start_end(a, b)
ac = Vector.from_start_end(a, c)

L = ab.cross(ac).length
A = area_triangle([a, b, c])

print(0.5 * L == A)