Example #1
0
def _add_aligned_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz):
    points = ((minx, miny, minz), (maxx, miny, minz), (maxx, maxy, minz),
              (minx, maxy, minz), (minx, miny, maxz), (maxx, miny, maxz),
              (maxx, maxy, maxz), (minx, maxy, maxz))
    triangles = []
    # lower face
    triangles.extend(
        _get_triangles_for_face((points[0], points[1], points[2], points[3])))
    # upper face
    triangles.extend(
        _get_triangles_for_face((points[7], points[6], points[5], points[4])))
    # front face
    triangles.extend(
        _get_triangles_for_face((points[0], points[4], points[5], points[1])))
    # back face
    triangles.extend(
        _get_triangles_for_face((points[2], points[6], points[7], points[3])))
    # right face
    triangles.extend(
        _get_triangles_for_face((points[1], points[5], points[6], points[2])))
    # left face
    triangles.extend(
        _get_triangles_for_face((points[3], points[7], points[4], points[0])))
    # add all triangles to the model
    model = Model()
    for t in triangles:
        model.append(t)
    return model
Example #2
0
def _add_aligned_cuboid_to_model(minx, maxx, miny, maxy, minz, maxz):
    points = (
            Point(minx, miny, minz),
            Point(maxx, miny, minz),
            Point(maxx, maxy, minz),
            Point(minx, maxy, minz),
            Point(minx, miny, maxz),
            Point(maxx, miny, maxz),
            Point(maxx, maxy, maxz),
            Point(minx, maxy, maxz))
    triangles = []
    # lower face
    triangles.extend(_get_triangles_for_face(
            (points[0], points[1], points[2], points[3])))
    # upper face
    triangles.extend(_get_triangles_for_face(
            (points[7], points[6], points[5], points[4])))
    # front face
    triangles.extend(_get_triangles_for_face(
            (points[0], points[4], points[5], points[1])))
    # back face
    triangles.extend(_get_triangles_for_face(
            (points[2], points[6], points[7], points[3])))
    # right face
    triangles.extend(_get_triangles_for_face(
            (points[1], points[5], points[6], points[2])))
    # left face
    triangles.extend(_get_triangles_for_face(
            (points[3], points[7], points[4], points[0])))
    # add all triangles to the model
    model = Model()
    for t in triangles:
        model.append(t)
    return model
Example #3
0
def get_test_model():
    points = []
    points.append(Point(-2, 1, 4))
    points.append(Point(2, 1, 4))
    points.append(Point(0, -2, 4))
    points.append(Point(-5, 2, 2))
    points.append(Point(-1, 3, 2))
    points.append(Point(5, 2, 2))
    points.append(Point(4, -1, 2))
    points.append(Point(2, -4, 2))
    points.append(Point(-2, -4, 2))
    points.append(Point(-3, -2, 2))

    lines = []
    lines.append(Line(points[0], points[1]))
    lines.append(Line(points[1], points[2]))
    lines.append(Line(points[2], points[0]))
    lines.append(Line(points[0], points[3]))
    lines.append(Line(points[3], points[4]))
    lines.append(Line(points[4], points[0]))
    lines.append(Line(points[4], points[1]))
    lines.append(Line(points[4], points[5]))
    lines.append(Line(points[5], points[1]))
    lines.append(Line(points[5], points[6]))
    lines.append(Line(points[6], points[1]))
    lines.append(Line(points[6], points[2]))
    lines.append(Line(points[6], points[7]))
    lines.append(Line(points[7], points[2]))
    lines.append(Line(points[7], points[8]))
    lines.append(Line(points[8], points[2]))
    lines.append(Line(points[8], points[9]))
    lines.append(Line(points[9], points[2]))
    lines.append(Line(points[9], points[0]))
    lines.append(Line(points[9], points[3]))

    model = Model()
    for p1, p2, p3, l1, l2, l3 in (
            (0, 1, 2, 0, 1, 2),
            (0, 3, 4, 3, 4, 5),
            (0, 4, 1, 5, 6, 0),
            (1, 4, 5, 6, 7, 8),
            (1, 5, 6, 8, 9, 10),
            (1, 6, 2, 10, 11, 1),
            (2, 6, 7, 11, 12, 13),
            (2, 7, 8, 13, 14, 15),
            (2, 8, 9, 15, 16, 17),
            (2, 9, 0, 17, 18, 2),
            (0, 9, 3, 18, 19, 3)):
        model.append(Triangle(points[p1], points[p2], points[p3]))
    return model
Example #4
0
def get_support_distributed(model,
                            z_plane,
                            average_distance,
                            min_bridges_per_polygon,
                            thickness,
                            height,
                            length,
                            bounds=None,
                            start_at_corners=False):
    if (average_distance == 0) or (length == 0) or (thickness == 0) or (height
                                                                        == 0):
        return
    result = Model()
    if not hasattr(model, "get_polygons"):
        model = model.get_waterline_contour(
            Plane((0, 0, max(model.minz, z_plane)), (0, 0, 1, 'v')))
    if model:
        model = model.get_flat_projection(
            Plane((0, 0, z_plane), (0, 0, 1, 'v')))
    if model and bounds:
        model = model.get_cropped_model_by_bounds(bounds)
    if model:
        polygons = model.get_polygons()
    else:
        return None
    # minimum required distance between two bridge start points
    avoid_distance = 1.5 * (abs(length) + thickness)
    if start_at_corners:
        bridge_calculator = _get_corner_bridges
    else:
        bridge_calculator = _get_edge_bridges
    for polygon in polygons:
        # no grid for _small_ inner polygons
        # TODO: calculate a reasonable factor (see below)
        if polygon.is_closed and (not polygon.is_outer()) \
                and (abs(polygon.get_area()) < 25000 * thickness ** 2):
            continue
        bridges = bridge_calculator(polygon, z_plane, min_bridges_per_polygon,
                                    average_distance, avoid_distance)
        for pos, direction in bridges:
            _add_cuboid_to_model(result, pos, pmul(direction, length), height,
                                 thickness)
    return result
Example #5
0
def get_support_grid(minx,
                     maxx,
                     miny,
                     maxy,
                     z_plane,
                     dist_x,
                     dist_y,
                     thickness,
                     height,
                     length,
                     offset_x=0.0,
                     offset_y=0.0,
                     adjustments_x=None,
                     adjustments_y=None):
    lines_x, lines_y = get_support_grid_locations(minx, maxx, miny, maxy,
                                                  dist_x, dist_y, offset_x,
                                                  offset_y, adjustments_x,
                                                  adjustments_y)
    # create all x grid lines
    grid_model = Model()
    # convert all inputs to "number"
    thickness = number(thickness)
    height = number(height)
    # helper variables
    thick_half = thickness / 2
    for line_x in lines_x:
        # we make the grid slightly longer (by thickness) than necessary
        grid_model += _add_aligned_cuboid_to_model(line_x - thick_half,
                                                   line_x + thick_half,
                                                   miny - length,
                                                   maxy + length, z_plane,
                                                   z_plane + height)
    for line_y in lines_y:
        # we make the grid slightly longer (by thickness) than necessary
        grid_model += _add_aligned_cuboid_to_model(minx - length,
                                                   maxx + length,
                                                   line_y - thick_half,
                                                   line_y + thick_half,
                                                   z_plane, z_plane + height)
    return grid_model
Example #6
0
    binary = False
    log.debug("STL import info: %s / %s / %s / %s" % \
            (f.len, numfacets, header.find("solid"), header.find("facet")))

    if f.len == (84 + 50*numfacets):
        binary = True
    elif header.find("solid") >= 0 and header.find("facet") >= 0:
        binary = False
        f.seek(0)
    else:
        log.error("STLImporter: STL binary/ascii detection failed")
        return None

    if use_kdtree:
        kdtree = PointKdtree([], 3, 1, epsilon)
    model = Model(use_kdtree)

    t = None
    p1 = None
    p2 = None
    p3 = None

    if binary:
        for i in range(1, numfacets + 1): 
            if callback and callback():
                log.warn("STLImporter: load model operation cancelled")
                return None
            a1 = unpack("<f", f.read(4))[0] 
            a2 = unpack("<f", f.read(4))[0] 
            a3 = unpack("<f", f.read(4))[0] 
Example #7
0
def import_model(filename, use_kdtree=True, callback=None, **kwargs):
    global vertices, edges, kdtree
    vertices = 0
    edges = 0
    kdtree = None

    normal_conflict_warning_seen = False

    if hasattr(filename, "read"):
        # make sure that the input stream can seek and has ".len"
        f = BufferedReader(filename)
        # useful for later error messages
        filename = "input stream"
    else:
        try:
            url_file = pycam.Utils.URIHandler(filename).open()
            # urllib.urlopen objects do not support "seek" - so we need a buffered reader
            # Is there a better approach than consuming the whole file at once?
            f = BufferedReader(BytesIO(url_file.read()))
            url_file.close()
        except IOError as exc:
            raise LoadFileError(
                "STLImporter: Failed to read file ({}): {}".format(
                    filename, exc))

    # the facet count is only available for the binary format
    facet_count = get_facet_count_if_binary_format(f)
    is_binary = (facet_count is not None)

    if use_kdtree:
        kdtree = PointKdtree([], 3, 1, epsilon)
    model = Model(use_kdtree)

    t = None
    p1 = None
    p2 = None
    p3 = None

    if is_binary:
        # Skip the header and count fields of binary stl file
        f.seek(HEADER_SIZE + COUNT_SIZE)

        for i in range(1, facet_count + 1):
            if callback and callback():
                raise AbortOperationException(
                    "STLImporter: load model operation cancelled")
            a1 = unpack("<f", f.read(4))[0]
            a2 = unpack("<f", f.read(4))[0]
            a3 = unpack("<f", f.read(4))[0]

            n = (float(a1), float(a2), float(a3), 'v')

            v11 = unpack("<f", f.read(4))[0]
            v12 = unpack("<f", f.read(4))[0]
            v13 = unpack("<f", f.read(4))[0]

            p1 = get_unique_vertex(float(v11), float(v12), float(v13))

            v21 = unpack("<f", f.read(4))[0]
            v22 = unpack("<f", f.read(4))[0]
            v23 = unpack("<f", f.read(4))[0]

            p2 = get_unique_vertex(float(v21), float(v22), float(v23))

            v31 = unpack("<f", f.read(4))[0]
            v32 = unpack("<f", f.read(4))[0]
            v33 = unpack("<f", f.read(4))[0]

            p3 = get_unique_vertex(float(v31), float(v32), float(v33))

            # not used (additional attributes)
            f.read(2)

            dotcross = pdot(n, pcross(psub(p2, p1), psub(p3, p1)))
            if a1 == a2 == a3 == 0:
                dotcross = pcross(psub(p2, p1), psub(p3, p1))[2]
                n = None

            if dotcross > 0:
                # Triangle expects the vertices in clockwise order
                t = Triangle(p1, p3, p2)
            elif dotcross < 0:
                if not normal_conflict_warning_seen:
                    log.warn(
                        "Inconsistent normal/vertices found in facet definition %d of '%s'. "
                        "Please validate the STL file!", i, filename)
                    normal_conflict_warning_seen = True
                t = Triangle(p1, p2, p3)
            else:
                # the three points are in a line - or two points are identical
                # usually this is caused by points, that are too close together
                # check the tolerance value in pycam/Geometry/PointKdtree.py
                log.warn(
                    "Skipping invalid triangle: %s / %s / %s (maybe the resolution of the "
                    "model is too high?)", p1, p2, p3)
                continue
            if n:
                t.normal = n

            model.append(t)
    else:
        # from here on we want to use a text based input stream (not bytes)
        f = TextIOWrapper(f, encoding="utf-8")
        solid = re.compile(r"\s*solid\s+(\w+)\s+.*")
        endsolid = re.compile(r"\s*endsolid\s*")
        facet = re.compile(r"\s*facet\s*")
        normal = re.compile(
            r"\s*facet\s+normal" +
            r"\s+(?P<x>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<y>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<z>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)\s+")
        endfacet = re.compile(r"\s*endfacet\s+")
        loop = re.compile(r"\s*outer\s+loop\s+")
        endloop = re.compile(r"\s*endloop\s+")
        vertex = re.compile(
            r"\s*vertex" +
            r"\s+(?P<x>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<y>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<z>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)\s+")

        current_line = 0

        for line in f:
            if callback and callback():
                raise AbortOperationException(
                    "STLImporter: load model operation cancelled")
            current_line += 1
            m = solid.match(line)
            if m:
                model.name = m.group(1)
                continue

            m = facet.match(line)
            if m:
                m = normal.match(line)
                if m:
                    n = (float(m.group('x')), float(m.group('y')),
                         float(m.group('z')), 'v')
                else:
                    n = None
                continue
            m = loop.match(line)
            if m:
                continue
            m = vertex.match(line)
            if m:
                p = get_unique_vertex(float(m.group('x')), float(m.group('y')),
                                      float(m.group('z')))
                if p1 is None:
                    p1 = p
                elif p2 is None:
                    p2 = p
                elif p3 is None:
                    p3 = p
                else:
                    log.error(
                        "STLImporter: more then 3 points in facet (line %d)",
                        current_line)
                continue
            m = endloop.match(line)
            if m:
                continue
            m = endfacet.match(line)
            if m:
                if None in (p1, p2, p3):
                    log.warn(
                        "Invalid facet definition in line %d of '%s'. Please validate the "
                        "STL file!", current_line, filename)
                    n, p1, p2, p3 = None, None, None, None
                    continue
                if not n:
                    n = pnormalized(pcross(psub(p2, p1), psub(p3, p1)))

                # validate the normal
                # The three vertices of a triangle in an STL file are supposed
                # to be in counter-clockwise order. This should match the
                # direction of the normal.
                if n is None:
                    # invalid triangle (zero-length vector)
                    dotcross = 0
                else:
                    # make sure the points are in ClockWise order
                    dotcross = pdot(n, pcross(psub(p2, p1), psub(p3, p1)))
                if dotcross > 0:
                    # Triangle expects the vertices in clockwise order
                    t = Triangle(p1, p3, p2, n)
                elif dotcross < 0:
                    if not normal_conflict_warning_seen:
                        log.warn(
                            "Inconsistent normal/vertices found in line %d of '%s'. Please "
                            "validate the STL file!", current_line, filename)
                        normal_conflict_warning_seen = True
                    t = Triangle(p1, p2, p3, n)
                else:
                    # The three points are in a line - or two points are
                    # identical. Usually this is caused by points, that are too
                    # close together. Check the tolerance value in
                    # pycam/Geometry/PointKdtree.py.
                    log.warn(
                        "Skipping invalid triangle: %s / %s / %s (maybe the resolution of "
                        "the model is too high?)", p1, p2, p3)
                    n, p1, p2, p3 = (None, None, None, None)
                    continue
                n, p1, p2, p3 = (None, None, None, None)
                model.append(t)
                continue
            m = endsolid.match(line)
            if m:
                continue

    # TODO display unique vertices and edges count - currently not counted
    log.info("Imported STL model: %d triangles", len(model.triangles()))
    vertices = 0
    edges = 0
    kdtree = None

    if not model:
        # no valid items added to the model
        raise LoadFileError(
            "Failed to load model from STL file: no elements found")
    else:
        return model
Example #8
0
def ImportModel(filename, use_kdtree=True, callback=None, **kwargs):
    global vertices, edges, kdtree
    vertices = 0
    edges = 0
    kdtree = None

    normal_conflict_warning_seen = False

    if hasattr(filename, "read"):
        # make sure that the input stream can seek and has ".len"
        f = StringIO(filename.read())
        # useful for later error messages
        filename = "input stream"
    else:
        try:
            url_file = pycam.Utils.URIHandler(filename).open()
            # urllib.urlopen objects do not support "seek" - so we need to read
            # the whole file at once. This is ugly - anyone with a better idea?
            f = StringIO(url_file.read())
            # TODO: the above ".read" may be incomplete - this is ugly
            # see http://patrakov.blogspot.com/2011/03/case-of-non-raised-exception.html
            # and http://stackoverflow.com/questions/1824069/
            url_file.close()
        except IOError as err_msg:
            log.error("STLImporter: Failed to read file (%s): %s", filename,
                      err_msg)
            return None
    # Read the first two lines of (potentially non-binary) input - they should
    # contain "solid" and "facet".
    header_lines = []
    while len(header_lines) < 2:
        line = f.readline(200)
        if len(line) == 0:
            # empty line (not even a line-feed) -> EOF
            log.error("STLImporter: No valid lines found in '%s'", filename)
            return None
        # ignore comment lines
        # note: partial comments (starting within a line) are not handled
        if not line.startswith(";"):
            header_lines.append(line)
    header = "".join(header_lines)
    # read byte 80 to 83 - they contain the "numfacets" value in binary format
    f.seek(80)
    numfacets = unpack("<I", f.read(4))[0]
    binary = False
    log.debug("STL import info: %s / %s / %s / %s", f.len, numfacets,
              header.find("solid"), header.find("facet"))

    if f.len == (84 + 50 * numfacets):
        binary = True
    elif header.find("solid") >= 0 and header.find("facet") >= 0:
        binary = False
        f.seek(0)
    else:
        log.error("STLImporter: STL binary/ascii detection failed")
        return None

    if use_kdtree:
        kdtree = PointKdtree([], 3, 1, epsilon)
    model = Model(use_kdtree)

    t = None
    p1 = None
    p2 = None
    p3 = None

    if binary:
        for i in range(1, numfacets + 1):
            if callback and callback():
                log.warn("STLImporter: load model operation cancelled")
                return None
            a1 = unpack("<f", f.read(4))[0]
            a2 = unpack("<f", f.read(4))[0]
            a3 = unpack("<f", f.read(4))[0]

            n = (float(a1), float(a2), float(a3), 'v')

            v11 = unpack("<f", f.read(4))[0]
            v12 = unpack("<f", f.read(4))[0]
            v13 = unpack("<f", f.read(4))[0]

            p1 = UniqueVertex(float(v11), float(v12), float(v13))

            v21 = unpack("<f", f.read(4))[0]
            v22 = unpack("<f", f.read(4))[0]
            v23 = unpack("<f", f.read(4))[0]

            p2 = UniqueVertex(float(v21), float(v22), float(v23))

            v31 = unpack("<f", f.read(4))[0]
            v32 = unpack("<f", f.read(4))[0]
            v33 = unpack("<f", f.read(4))[0]

            p3 = UniqueVertex(float(v31), float(v32), float(v33))

            # not used (additional attributes)
            f.read(2)

            dotcross = pdot(n, pcross(psub(p2, p1), psub(p3, p1)))
            if a1 == a2 == a3 == 0:
                dotcross = pcross(psub(p2, p1), psub(p3, p1))[2]
                n = None

            if dotcross > 0:
                # Triangle expects the vertices in clockwise order
                t = Triangle(p1, p3, p2)
            elif dotcross < 0:
                if not normal_conflict_warning_seen:
                    log.warn(
                        "Inconsistent normal/vertices found in facet definition %d of '%s'. "
                        "Please validate the STL file!", i, filename)
                    normal_conflict_warning_seen = True
                t = Triangle(p1, p2, p3)
            else:
                # the three points are in a line - or two points are identical
                # usually this is caused by points, that are too close together
                # check the tolerance value in pycam/Geometry/PointKdtree.py
                log.warn(
                    "Skipping invalid triangle: %s / %s / %s (maybe the resolution of the "
                    "model is too high?)", p1, p2, p3)
                continue
            if n:
                t.normal = n

            model.append(t)
    else:
        solid = re.compile(r"\s*solid\s+(\w+)\s+.*")
        endsolid = re.compile(r"\s*endsolid\s*")
        facet = re.compile(r"\s*facet\s*")
        normal = re.compile(
            r"\s*facet\s+normal" +
            r"\s+(?P<x>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<y>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<z>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)\s+")
        endfacet = re.compile(r"\s*endfacet\s+")
        loop = re.compile(r"\s*outer\s+loop\s+")
        endloop = re.compile(r"\s*endloop\s+")
        vertex = re.compile(
            r"\s*vertex" +
            r"\s+(?P<x>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<y>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)" +
            r"\s+(?P<z>[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?)\s+")

        current_line = 0

        for line in f:
            if callback and callback():
                log.warn("STLImporter: load model operation cancelled")
                return None
            current_line += 1
            m = solid.match(line)
            if m:
                model.name = m.group(1)
                continue

            m = facet.match(line)
            if m:
                m = normal.match(line)
                if m:
                    n = (float(m.group('x')), float(m.group('y')),
                         float(m.group('z')), 'v')
                else:
                    n = None
                continue
            m = loop.match(line)
            if m:
                continue
            m = vertex.match(line)
            if m:
                p = UniqueVertex(float(m.group('x')), float(m.group('y')),
                                 float(m.group('z')))
                if p1 is None:
                    p1 = p
                elif p2 is None:
                    p2 = p
                elif p3 is None:
                    p3 = p
                else:
                    log.error(
                        "STLImporter: more then 3 points in facet (line %d)",
                        current_line)
                continue
            m = endloop.match(line)
            if m:
                continue
            m = endfacet.match(line)
            if m:
                if None in (p1, p2, p3):
                    log.warn(
                        "Invalid facet definition in line %d of '%s'. Please validate the "
                        "STL file!", current_line, filename)
                    n, p1, p2, p3 = None, None, None, None
                    continue
                if not n:
                    n = pnormalized(pcross(psub(p2, p1), psub(p3, p1)))

                # validate the normal
                # The three vertices of a triangle in an STL file are supposed
                # to be in counter-clockwise order. This should match the
                # direction of the normal.
                if n is None:
                    # invalid triangle (zero-length vector)
                    dotcross = 0
                else:
                    # make sure the points are in ClockWise order
                    dotcross = pdot(n, pcross(psub(p2, p1), psub(p3, p1)))
                if dotcross > 0:
                    # Triangle expects the vertices in clockwise order
                    t = Triangle(p1, p3, p2, n)
                elif dotcross < 0:
                    if not normal_conflict_warning_seen:
                        log.warn(
                            "Inconsistent normal/vertices found in line %d of '%s'. Please "
                            "validate the STL file!", current_line, filename)
                        normal_conflict_warning_seen = True
                    t = Triangle(p1, p2, p3, n)
                else:
                    # The three points are in a line - or two points are
                    # identical. Usually this is caused by points, that are too
                    # close together. Check the tolerance value in
                    # pycam/Geometry/PointKdtree.py.
                    log.warn(
                        "Skipping invalid triangle: %s / %s / %s (maybe the resolution of "
                        "the model is too high?)", p1, p2, p3)
                    n, p1, p2, p3 = (None, None, None, None)
                    continue
                n, p1, p2, p3 = (None, None, None, None)
                model.append(t)
                continue
            m = endsolid.match(line)
            if m:
                continue

    log.info("Imported STL model: %d vertices, %d edges, %d triangles",
             vertices, edges, len(model.triangles()))
    vertices = 0
    edges = 0
    kdtree = None

    if not model:
        # no valid items added to the model
        return None
    else:
        return model