コード例 #1
0
class StlModel:
    def __init__(self, filename):
        self.filename = filename
        self.facets = []
        self.parse()
        self.direction = '+Z'
        self.ex = dict()
        self.tree = IntervalTree(0)
        self.sorted_z = []
        self.update()

    def __nonzero__(self):
        return True

    def update(self):
        logging.info("Current scales:")
        self.ex = self.get_extremal()
        self.log_scales()
        logging.info('Making tree for STL-model...')
        self.make_tree()
        logging.info('Finished tree.')

    def make_tree(self):
        i = 0
        self.sorted_z = []
        for facet in self.facets:
            self.sorted_z.append((facet.minz(), i))
            self.sorted_z.append((facet.maxz(), i))
            i += 1
        self.sorted_z.sort()

        self.tree = IntervalTree(len(self.sorted_z))
        for facet in self.facets:
            l = self.find_z(facet.minz(), True)
            r = self.find_z(facet.maxz(), False)
            self.tree.push(l, r - 1, facet)

    def intersect_facets(self, z):
        return self.tree.get(self.find_z(z))

    #if bot: returns first element >= z
    #  else: returns first element > z
    def find_z(self, z, bot=False):
        l = 0
        r = len(self.sorted_z)
        while r > l:
            m = (r + l) // 2
            if bot:
                if self.sorted_z[m][0] + EPS > z:
                    r = m
                else:
                    l = m + 1
            else:
                if self.sorted_z[m][0] - EPS > z:
                    r = m
                else:
                    l = m + 1

        # l == r
        return l

    def read_facet(self, f):
        line = f.readline().strip()
        if line != 'outer loop':
            raise ValueError('Expected "outer loop", got "%s"' % line)

        facet = []
        line = f.readline().strip()
        while line != 'endloop':
            parts = line.split()
            if parts[0] != 'vertex':
                raise ValueError('Expected "vertex x y z", got "%s"' % line)
            facet.append(tuple([float(num) for num in parts[1:]]))

            line = f.readline().strip()
        line = f.readline().strip()
        if line != 'endfacet':
            raise ValueError('Expected "endfacet", got "%s"' % line)
        return Facet(Point3(facet[0]), Point3(facet[1]), Point3(facet[2]))

    def log_scales(self):
        e = self.ex
        logging.info('minx = %.3f \t maxx = %.3f' % (e['minx'], e['maxx']))
        logging.info('miny = %.3f \t maxy = %.3f' % (e['miny'], e['maxy']))
        logging.info('minz = %.3f \t maxz = %.3f' % (e['minz'], e['maxz']))

    def parse_text(self):
        f = open(self.filename, 'r')
        logging.info('Parsing STL text model')
        line = f.readline().strip()
        parts = line.split()
        if parts[0] != 'solid':
            raise FormatSTLError('Expected "solid ...", got "%s"' % line)
        name = ' '.join(parts[1:])

        line = f.readline().strip()
        while line.startswith('facet'):
            try:
                facet = self.read_facet(f)
                self.facets.append(facet)
            except AssertionError:
                pass
            line = f.readline().strip()
        if line != ('endsolid %s' % name) and line != "endsolid":
            raise FormatSTLError('Expected "endsolid %s", got "%s"' % (name, line))

    def parse_bin(self):
        file = open(self.filename, 'rb')
        import struct
        try:
            header = file.read(80)
            logging.info('Parsing STL binary model')
            logging.info('HEADER: %s' % header)
            (count,) = struct.unpack('<I', file.read(4))
            logging.info('COUNT: %d' % count)

            for i in range(count):
                normal = struct.unpack('<fff', file.read(12))
                points = []
                for i in range(3):
                    points.append(struct.unpack('<fff', file.read(12)))

                try:
                    f = Facet(Point3(points[0]), Point3(points[1]), Point3(points[2]) )
                    f.normal = Vector3(Point3(normal))
                    f.normal.normalize()
                    self.facets.append(f)
                except AssertionError:
                    pass
                attribute_byte_count = file.read(2)
        except:
            self.facets = []
            raise FormatSTLError

    def parse(self):
        f = open(self.filename, 'r')
        data = f.read()
        if "facet normal" in data[0:300] and "outer loop" in data[0:300]:
            self.parse_text()
        else:
            self.parse_bin()

    def get_extremal(self):
        rand_point = self.facets[0].points[0]
        extremals = {'minx': rand_point.x, 'maxx': rand_point.x,
                     'miny': rand_point.y, 'maxy': rand_point.y,
                     'minz': rand_point.z, 'maxz': rand_point.z}
        for facet in self.facets:
            for p in facet:
                extremals['minx'] = min(extremals['minx'], p.x)
                extremals['maxx'] = max(extremals['maxx'], p.x)

                extremals['miny'] = min(extremals['miny'], p.y)
                extremals['maxy'] = max(extremals['maxy'], p.y)

                extremals['minz'] = min(extremals['minz'], p.z)
                extremals['maxz'] = max(extremals['maxz'], p.z)
        extremals['xsize'] = extremals['maxx'] - extremals['minx']
        extremals['ysize'] = extremals['maxy'] - extremals['miny']
        extremals['zsize'] = extremals['maxz'] - extremals['minz']
        extremals['diameter'] = math.sqrt(extremals['xsize']**2 + extremals['ysize']**2 + extremals['zsize']**2)
        extremals['xcenter'] = (extremals['maxx'] + extremals['minx']) / 2
        extremals['ycenter'] = (extremals['maxy'] + extremals['miny']) / 2
        extremals['zcenter'] = (extremals['maxz'] + extremals['minz']) / 2
        return extremals

    def changeDirection(self, direction):
        #This strange 3 lines make reverse transformation
        for i in range(3):
            for f in self.facets:
                f.changeDirection(self.direction)

        self.direction = direction
        for f in self.facets:
            f.changeDirection(direction)

        self.update()

    def zoom_x(self, scale):
        for f in self.facets:
            f.zoom_x(scale)
        self.update()

    def zoom_y(self, scale):
        for f in self.facets:
            f.zoom_y(scale)
        self.update()

    def zoom_z(self, scale):
        for f in self.facets:
            f.zoom_z(scale)
        self.update()

    def add_x(self, v):
        for f in self.facets:
            f.add_x(v)

    def add_y(self, v):
        for f in self.facets:
            f.add_y(v)

    def add_z(self, v):
        for f in self.facets:
            f.add_z(v)

    def zoom(self, scale):
        for f in self.facets:
            f.zoom(scale)
        self.update()

    def max_size(self):
        max_v = 0
        for v in (self.ex['minx'], self.ex['maxx'], self.ex['miny'], self.ex['maxy'], self.ex['minz'], self.ex['maxz']):
            max_v = max(max_v, abs(v))
        return max_v

    def centering(self):
        self.add_x(-self.ex['xcenter'])
        self.add_y(-self.ex['ycenter'])
        self.add_z(-self.ex['zcenter'])
        self.update()
コード例 #2
0
class Slice:
    def __init__(self, model, z, asrt=True):
        print 'new slice %.2f' % z
        self.asrt = asrt
        self.calculated_fully_scan_old = None
        self.calculated_fully_scan = None
        self.calculated_get_loops = None
        self.calculated_get_shape = None
        self.stl_model = model
        self.z = z
        self.lines = []
        self.ex = {'minx': MAXSIZE, 'maxx': -MAXSIZE,
                   'miny': MAXSIZE, 'maxy': -MAXSIZE}

        if len(model.facets) > MAXFACETS:
            logging.error("Cant slice %d facets. The max supposed numbers of facets is %.2f" %
                          (len(model.facets), MAXFACETS))
            raise SizeSliceError("Cant slice so big model")
        for facet in model.intersect_facets(z):
            if facet.isIntersect(z):
                line = facet.intersect(z)
                if line.length > EPS:
                    self.lines.append(line)

        for line in self.lines:
            for p in line:
                self.ex['minx'] = min(self.ex['minx'], p.x)
                self.ex['maxx'] = max(self.ex['maxx'], p.x)
                self.ex['miny'] = min(self.ex['miny'], p.y)
                self.ex['maxy'] = max(self.ex['maxy'], p.y)

        if self.max_size() > MAXSIZE:
            logging.error("Cant slice %.2f model. The max size is %.2f" % (self.max_size(), MAXSIZE))
            raise SizeSliceError("Cant slice so big model")

        self.sorted_y = []
        self.tree_x = IntervalTree(0)
        print "lines in slice: %d" % len(self.lines)

    def max_size(self):
        x = max(-self.ex['minx'], self.ex['maxx'])
        y = max(-self.ex['miny'], self.ex['maxy'])
        return max(x, y)

    def __len__(self):
        return len(self.lines)

    def __nonzero__(self):
        return True

    #Used it for find first index, self.sorted_y[idx] > y.
    #If there are numbers, equal with y, answer may be any index of them.
    def find_y_old(self, y, asrt=True, left=True):
        l = 0
        r = len(self.sorted_y)
        while r > l:
            m = (r + l) // 2
            if asrt and equal(self.sorted_y[m][0], y):
                logging.info('You want find_y(%.3f) with Assert mode, but there are such y' % y)
                assert 0
            if self.sorted_y[m][0] > y:
                r = m
            else:
                l = m + 1

        # l == r
        return l

    #simple fully scan each STEP row
    #returns list[Line2]
    def fully_scan_old(self):
        if len(self.lines) <= 1:
            return []

        if not self.calculated_fully_scan_old is None:
            return self.calculated_fully_scan_old

        i = 0
        self.sorted_y = []
        for line in self.lines:
            self.sorted_y.append((line.p1.y, i))
            self.sorted_y.append((line.p2.y, -1))
            i += 1
        self.sorted_y.sort()
        self.tree_x = IntervalTree(len(self.sorted_y))
        for line in self.lines:
            l = self.find_y_old(line.p1.y, False)
            r = self.find_y_old(line.p2.y, False)
            if l > r:
                (l, r) = (r, l)
            self.tree_x.push(l, r - 1, line)

        miny = self.ex['miny']
        maxy = self.ex['maxy']

        y = miny + CORRECTION
        ans = []
        number_tries = 0
        while y < maxy:
            while self.exist(y):
                logging.info('Correction in fully_scan_old')
                y += CORRECTION

            if number_tries > 3:
                logging.error("I tired to tries so much! ;(")
                if self.asrt:
                    raise stl_utils.FormatSTLError('Cant slice')
            try:
                ans.extend(self.get_lines_in_row_old(y))
                y += STEP
                number_tries = 0
            except AssertionError:
                y += CORRECTION
                number_tries += 1

        self.calculated_fully_scan = ans
        return ans

    #Remeber, it doesnt work if there is edge in the row
    def get_lines_in_row_old(self, y):
        ans = []
        intersects = []
        index = self.find_y_old(y)

        for line in self.tree_x.get(index):
            if line.isIntersect(y):
                intersects.append(line.calcIntersect(y))
            else:
                logging.info('get_lines_in_row: It can not be! ;(')
                assert 0

        if len(intersects) % 2 == 1:
            logging.error('get_lines_in_row: I have odd number of intersects %f slice %f row. Trying to increment less.' % (self.z, y))
            assert 0
        else:
            intersects.sort()
            for i in range(len(intersects) // 2):
                p1 = Point2(intersects[2 * i], y)
                p2 = Point2(intersects[2 * i + 1], y)
                ans.append(Line2(p1, p2))

        return ans

    #this function is not used now
    def get_points_in_row(self, y):
        intersects = []

        for line in self.lines:
            try:
                if line.isIntersect(y):
                    intersects.append(line.calcIntersect(y))
            except AssertionError:
                intersects.append(line.p1.x)
                intersects.append(line.p2.x)

        intersects.sort()
        ans = [intersects[0]]
        last = ans[0]
        for i in intersects[1:]:
            if abs(i - last) > EPS:
                ans.append(i)
                last = i

        return ans

    #find first element, >= y
    def find_y_left(self, y):
        l = 0
        r = len(self.sorted_y)
        while r > l:
            m = (r + l) // 2
            if self.sorted_y[m][0] + EPS > y:
                r = m
            else:
                l = m + 1

        # l == r
        return l

    #find first element, > y
    def find_y_right(self, y):
        l = 0
        r = len(self.sorted_y)
        while r > l:
            m = (r + l) // 2
            if self.sorted_y[m][0] - EPS > y:
                r = m
            else:
                l = m + 1

        # l == r
        return l

    def find_y(self, y):
        l = 0
        r = len(self.sorted_y)
        while r > l:
            m = (r + l) // 2
            if equal(y, self.sorted_y[m][0]):
                return m
            if self.sorted_y[m][0] > y:
                r = m
            else:
                l = m + 1

        if l == len(self.sorted_y):
            assert 0
        if equal(y, self.sorted_y[l][0]):
            return l

        # not found
        assert 0

    def get_loops(self):
        if not self.calculated_get_loops is None:
            return self.calculated_get_loops

        self.sorted_y = []
        i = 0
        for line in self.lines:
            self.sorted_y.append((line.p1.y, i))
            i += 1
        self.sorted_y.sort()

        ans = []

        checked = []
        for j in range(len(self.lines)):
            checked.append(False)

        for j in range(len(self.lines)):
            if checked[j]:
                continue
            checked[j] = True
            line = self.lines[j]

            loop = [line.p1]
            p = line.p2
            missed = 0
            while p.dist(line.p1) > EPS:
                if p.dist(loop[-1]) > 0.5:
                    loop.append(p)
                else:
                    missed += 1
                nearest = False
                dist = 100
                nearest_idx = -1
                i = self.find_y_left(p.y - CORRECTION)
                while (i < len(self.sorted_y)) and ((self.sorted_y[i][0] - CORRECTION) < p.y):
                    if not checked[self.sorted_y[i][1]]:
                        if p.dist(self.lines[self.sorted_y[i][1]].p1) < dist:
                            dist = p.dist(self.lines[self.sorted_y[i][1]].p1)
                            nearest = self.lines[self.sorted_y[i][1]].p2
                            nearest_idx = self.sorted_y[i][1]

                    i += 1
                if dist > CORRECTION:
                    logging.info("Can't find nearest point. Loop is missed.")
                    loop = []
                    break
                p = nearest
                checked[nearest_idx] = True

            print "point in loop %d" % len(loop)
            print "missed point in loop %d" % missed
            print
            if len(loop) > 2:
                ans.append(Loop(loop))

        self.calculated_get_loops = ans
        return ans

    #fing loops first
    def fully_scan(self):
        if not self.calculated_fully_scan is None:
            return self.calculated_fully_scan
        loops = self.get_loops()

        lines = []
        indx = 0
        for loop in loops:
            prev = loop.points[-1]
            for p in loop:
                lines.append((prev, p, indx, loop.is_hole()))
                prev = p
            indx += 1

        self.sorted_y = []
        for (start, end, i, hole) in lines:
            self.sorted_y.append((start.y, i))
        self.sorted_y.sort()
        self.tree_x = IntervalTree(len(self.sorted_y))

        for (start, end, i, hole) in lines:
            l = self.find_y(start.y)
            r = self.find_y(end.y)
            if l > r:
                (l, r) = (r, l)
            self.tree_x.push(l, r, (start, end, i, hole))

        miny = self.ex['miny']
        maxy = self.ex['maxy']

        y = miny
        ans = []
        while y < maxy:
            while self.exist(y):
                logging.info('Correction in fully_scan')
                y += CORRECTION

            ans.extend(self.get_lines_in_row(y))
            y += STEP

        self.calculated_fully_scan = ans
        return ans

    def exist(self, y):
        try:
            self.find_y(y)
            return True
        except AssertionError:
            return False

    def get_lines_in_row(self, y):
        ans = []
        intersects = []
        index = self.find_y_right(y)

        max_i = 0
        for (start, end, i, hole) in self.tree_x.get(index):
            if i > max_i:
                max_i = i
            line = Line2(start, end)
            if line.isIntersect(y):
                intersects.append((line.calcIntersect(y), i, hole))

        assert len(intersects) % 2 == 0
        active_loop = dict()
        for i in range(max_i + 1):
            active_loop[i] = False

        intersects.sort()
        last = []
        for i in range(len(intersects)):
            if not active_loop[intersects[i][1]]:
                active_loop[intersects[i][1]] = True
                last.append(intersects[i][2])
            else:
                active_loop[intersects[i][1]] = False
                assert last.pop() == intersects[i][2]

            if last and not last[-1]:
                p1 = Point2(intersects[i][0], y)
                p2 = Point2(intersects[i + 1][0], y)
                ans.append(Line2(p1, p2))

        return ans

    def get_shape(self):
        if not self.calculated_get_shape is None:
            return self.calculated_get_shape
        loops = self.get_loops()

        ans = []
        for loop in loops:
            prev = loop.points[-1]
            for p in loop:
                ans.append(Line2(prev, p))
                prev = p

        self.calculated_get_shape = ans
        return ans
コード例 #3
0
class Slice:
    def __init__(self, model, z):
        self.stl_model = model
        self.z = z
        self.lines = []
        self.sorted_y = []
        self.ex = {'minx': MAXSIZE, 'maxx': -MAXSIZE,
                   'miny': MAXSIZE, 'maxy': -MAXSIZE}
        if self.stl_model.loaded:
            if  model.max_size() > MAXSIZE:
                logging.error("Cant slice %.2f model. The max size is %.2f" % (model.max_size(), MAXSIZE))
                raise SizeSliceError("Cant slice so big model")
            if len(model.facets) > MAXFACETS:
                logging.error("Cant slice %d facets. The max supposed numbers of facets is %.2f" %
                              (len(model.facets), MAXFACETS))
                raise SizeSliceError("Cant slice so big model")
            for facet in model.facets:
                if facet.isIntersect(z):
                    line = facet.intersect(z)
                    if line.length > EPS:
                        self.lines.append(line)

            for line in self.lines:
                for p in line:
                    self.ex['minx'] = min(self.ex['minx'], p.x)
                    self.ex['maxx'] = max(self.ex['maxx'], p.x)
                    self.ex['miny'] = min(self.ex['miny'], p.y)
                    self.ex['maxy'] = max(self.ex['maxy'], p.y)

            for line in self.lines:
                self.sorted_y.append(line.p1.y)
                self.sorted_y.append(line.p2.y)
            self.sorted_y.sort()

            #making interval tree for fast search intersected lines
            self.tree_x = IntervalTree(len(self.sorted_y))
            for line in self.lines:
                l = self.find_y(line.p1.y, False)
                r = self.find_y(line.p2.y, False)
                if l > r:
                    (l, r) = (r, l)
                self.tree_x.push(l, r - 1, line)


    def setHeight(self, height):
        # Set new height and recalculate list of facets
        self.z = height
        self.lines = []
        if self.stl_model and self.stl_model.max_size() > MAXSIZE:
            logging.error("Cant slice %.2f model. The max size is %.2f" % (model.max_size(), MAXSIZE))
            raise SizeSliceError("Cant slice so big model")
        for facet in self.stl_model.facets:
            if facet.isIntersect(self.z):
                self.lines.append(facet.intersect(self.z))


    def __len__(self):
        return len(self.lines)

    #Used it for find first index, self.sorted_y[idx] > y.
    #If there are numbers, equal with y, answer may be any index of them.
    def find_y(self, y, asrt=True):
        l = 0
        r = len(self.sorted_y)
        while r > l:
            m = (r + l) // 2
            if asrt:
                if equal(self.sorted_y[m], y):
                    logging.info('You want find_y(%.3f) with Assert mode, but there are such y' % y)
                    assert 0
            if self.sorted_y[m] > y:
                r = m
            else:
                l = m + 1

        # l == r
        return l

    #simple fully scan each STEP row
    #returns list[Line2]
    def fully_scan(self):
        if len(self.lines) <= 1:
            return []

        miny = self.ex['miny']
        maxy = self.ex['maxy']

        y = miny + EPS
        ans = []
        number_tries = 0
        while y < maxy:
            if number_tries > 3:
                logging.error("I tired to tries so much! ;(")
                raise stl_utils.FormatSTLError('Cant slice')
            try:
                ans.extend(self.get_lines_in_row(y))
                y += STEP
                number_tries = 0
            except AssertionError:
                y += EPS
                number_tries += 1
        return ans

    #scans only significant rows
    #returns list[tuple[Point2]]
    #it wasn't a good idea. no profit
    def intellectual_scan(self):
        if len(self.lines) <= 1:
            return []

        all_y = []
        for line in self.lines:
            all_y.append(line.p1.y)
            all_y.append(line.p2.y)
        all_y.sort()

        ans = []
        y_prev = all_y[0]
        for y_next in all_y[1:]:
            if y_next - STEP / 5 < y_prev:
                y_prev = y_next
                continue
            try:
                lines_prev = self.get_lines_in_row(y_prev + EPS)
                lines_next = self.get_lines_in_row(y_next - EPS)
            except:
                logging.error("Can't get_lines_in_row. %f slice, %f row" % (self.z, y_next))
                raise stl_utils.FormatSTLError("Can't get_lines_in_row. %f slice, %f row" % (self.z, y_next))
                #continue

            if len(lines_prev) != len(lines_next):
                logging.error("Ooops, the lengths is not equal!")
                raise stl_utils.FormatSTLError("Ooops, the lengths is not equal! Row %f" % y_next)
                #continue

            for i in range(len(lines_prev)):
                ans.append((lines_prev[i].p1, lines_prev[i].p2, lines_next[i].p2, lines_next[i].p1))
            y_prev = y_next

        return ans

    #Remeber, it doesnt work if there is edge in the row
    def get_lines_in_row(self, y):
        ans = []
        intersects = []
        index = self.find_y(y)

        for line in self.tree_x.get(index):
            if line.isIntersect(y):
                intersects.append(line.calcIntersect(y))
            else:
                logging.info('get_lines_in_row: It can not be! ;(')
                assert 0

        if len(intersects) % 2 == 1:
            logging.error('get_lines_in_row: I have odd number of intersects %f slice %f row. Trying to increment less.' % (self.z, y))
            assert 0
        else:
            intersects.sort()
            for i in range(len(intersects) // 2):
                p1 = Point2(intersects[2 * i], y)
                p2 = Point2(intersects[2 * i + 1], y)
                ans.append(Line2(p1, p2))

        return ans

    #this function is not used now
    def get_points_in_row(self, y):
        intersects = []

        for line in self.lines:
            try:
                if line.isIntersect(y):
                    intersects.append(line.calcIntersect(y))
            except AssertionError:
                intersects.append(line.p1.x)
                intersects.append(line.p2.x)

        intersects.sort()
        ans = [intersects[0]]
        last = ans[0]
        for i in intersects[1:]:
            if abs(i - last) > EPS:
                ans.append(i)
                last = i

        return ans

    def make_correct_loops(self):
        print len(self.lines)
        return []
        '''