Esempio n. 1
0
 def _update_caches(self):
     if self._use_kdtree:
         self._t_kdtree = TriangleKdtree(self.triangles())
     self.__uuid = str(uuid.uuid4())
     self.__flat_groups_cache = {}
     # the kdtree is up-to-date again
     self._dirty = False
Esempio n. 2
0
class Model(BaseModel):
    def __init__(self, use_kdtree=True):
        import pycam.Exporters.STLExporter
        super(Model, self).__init__()
        self._triangles = []
        self._item_groups.append(self._triangles)
        self._export_function = pycam.Exporters.STLExporter.STLExporter
        # marker for state of kdtree and uuid
        self._dirty = True
        # enable/disable kdtree
        self._use_kdtree = use_kdtree
        self._t_kdtree = None
        self.__uuid = None

    def __len__(self):
        """ Return the number of available items in the model.
        This is mainly useful for evaluating an empty model as False.
        """
        return len(self._triangles)

    def copy(self):
        result = self.__class__(use_kdtree=self._use_kdtree)
        for triangle in self.triangles():
            result.append(triangle.copy())
        return result

    @property
    def uuid(self):
        if (self.__uuid is None) or self._dirty:
            self._update_caches()
        return self.__uuid

    def append(self, item):
        super(Model, self).append(item)
        if isinstance(item, Triangle):
            self._triangles.append(item)
            # we assume, that the kdtree needs to be rebuilt again
            self._dirty = True

    def reset_cache(self):
        super(Model, self).reset_cache()
        # the triangle kdtree needs to be reset after transforming the model
        self._update_caches()

    def _update_caches(self):
        if self._use_kdtree:
            self._t_kdtree = TriangleKdtree(self.triangles())
        self.__uuid = str(uuid.uuid4())
        # the kdtree is up-to-date again
        self._dirty = False

    def triangles(self,
                  minx=-INFINITE,
                  miny=-INFINITE,
                  minz=-INFINITE,
                  maxx=+INFINITE,
                  maxy=+INFINITE,
                  maxz=+INFINITE):
        if (minx == miny == minz == -INFINITE) and (maxx == maxy == maxz ==
                                                    +INFINITE):
            return self._triangles
        if self._use_kdtree:
            # update the kdtree, if new triangles were added meanwhile
            if self._dirty:
                self._update_caches()
            return self._t_kdtree.Search(minx, maxx, miny, maxy)
        return self._triangles

    def get_waterline_contour(self, plane, callback=None):
        collision_lines = []
        progress_max = 2 * len(self._triangles)
        counter = 0
        for t in self._triangles:
            if callback and callback(percent=100.0 * counter / progress_max):
                return
            collision_line = plane.intersect_triangle(t,
                                                      counter_clockwise=True)
            if collision_line is not None:
                collision_lines.append(collision_line)
            else:
                counter += 1
            counter += 1
        # combine these lines into polygons
        contour = ContourModel(plane=plane)
        for line in collision_lines:
            if callback and callback(percent=100.0 * counter / progress_max):
                return
            contour.append(line)
            counter += 1
        log.debug("Waterline: %f - %d - %s", plane.p[2],
                  len(contour.get_polygons()),
                  [len(p.get_lines()) for p in contour.get_polygons()])
        return contour
Esempio n. 3
0
class Model(BaseModel):
    def __init__(self, use_kdtree=True):
        super(Model, self).__init__()
        self._triangles = []
        self._item_groups.append(self._triangles)
        self._export_function = pycam.Exporters.STLExporter.STLExporter
        # marker for state of kdtree and uuid
        self._dirty = True
        # enable/disable kdtree
        self._use_kdtree = use_kdtree
        self._t_kdtree = None
        self.__flat_groups_cache = {}
        self.__uuid = None

    def __len__(self):
        """ Return the number of available items in the model.
        This is mainly useful for evaluating an empty model as False.
        """
        return len(self._triangles)

    @property
    def uuid(self):
        if (self.__uuid is None) or self._dirty:
            self._update_caches()
        return self.__uuid

    def append(self, item):
        super(Model, self).append(item)
        if isinstance(item, Triangle):
            self._triangles.append(item)
            # we assume, that the kdtree needs to be rebuilt again
            self._dirty = True

    def reset_cache(self):
        super(Model, self).reset_cache()
        # the triangle kdtree needs to be reset after transforming the model
        self._update_caches()

    def _update_caches(self):
        if self._use_kdtree:
            self._t_kdtree = TriangleKdtree(self.triangles())
        self.__uuid = str(uuid.uuid4())
        self.__flat_groups_cache = {}
        # the kdtree is up-to-date again
        self._dirty = False

    def triangles(self,
                  minx=-INFINITE,
                  miny=-INFINITE,
                  minz=-INFINITE,
                  maxx=+INFINITE,
                  maxy=+INFINITE,
                  maxz=+INFINITE):
        if (minx == miny == minz == -INFINITE) \
                and (maxx == maxy == maxz == +INFINITE):
            return self._triangles
        if self._use_kdtree:
            # update the kdtree, if new triangles were added meanwhile
            if self._dirty:
                self._update_caches()
            return self._t_kdtree.Search(minx, maxx, miny, maxy)
        return self._triangles

    def get_waterline_contour(self, plane):
        collision_lines = []
        for t in self._triangles:
            collision_line = plane.intersect_triangle(t,
                                                      counter_clockwise=True)
            if not collision_line is None:
                collision_lines.append(collision_line)
        # combine these lines into polygons
        contour = ContourModel(plane=plane)
        for line in collision_lines:
            contour.append(line)
        log.debug("Waterline: %f - %d - %s" %
                  (plane.p.z, len(contour.get_polygons()),
                   [len(p.get_lines()) for p in contour.get_polygons()]))
        return contour

    def get_flat_areas(self, min_area=None):
        """ Find plane areas (combinations of triangles) bigger than 'min_area'
        and ignore vertical planes. The result is cached.
        """
        if not self.__flat_groups_cache.has_key(min_area):

            def has_shared_edge(t1, t2):
                count = 0
                for p in (t1.p1, t1.p2, t1.p3):
                    if p in (t2.p1, t2.p2, t2.p3):
                        count += 1
                return count >= 2

            groups = []
            for t in self.triangles():
                # Find all groups with the same direction (see 'normal') that
                # share at least one edge with the current triangle.
                touch_groups = []
                if t.normal.z == 0:
                    # ignore vertical triangles
                    continue
                for group_index, group in enumerate(groups):
                    if t.normal == group[0].normal:
                        for group_t in group:
                            if has_shared_edge(t, group_t):
                                touch_groups.append(group_index)
                                break
                if len(touch_groups) > 1:
                    # combine multiple areas with this new triangle
                    touch_groups.reverse()
                    combined = [t]
                    for touch_group_index in touch_groups:
                        combined.extend(groups.pop(touch_group_index))
                    groups.append(combined)
                elif len(touch_groups) == 1:
                    groups[touch_groups[0]].append(t)
                else:
                    groups.append([t])
            # check the size of each area
            if not min_area is None:
                groups = [
                    group for group in groups
                    if sum([t.get_area() for t in group]) >= min_area
                ]
            self.__flat_groups_cache[min_area] = groups
        return self.__flat_groups_cache[min_area]
Esempio n. 4
0
class Model(BaseModel):
    def __init__(self, use_kdtree=True):
        import pycam.Exporters.STLExporter
        super().__init__()
        self._triangles = []
        self._item_groups.append(self._triangles)
        self._export_function = pycam.Exporters.STLExporter.STLExporter
        # marker for state of kdtree and uuid
        self._dirty = True
        # enable/disable kdtree
        self._use_kdtree = use_kdtree
        self._t_kdtree = None
        self.__uuid = None

    def __len__(self):
        """ Return the number of available items in the model.
        This is mainly useful for evaluating an empty model as False.
        """
        return len(self._triangles)

    def __iter__(self):
        yield from self._triangles

    def copy(self):
        result = self.__class__(use_kdtree=self._use_kdtree)
        for triangle in self.triangles():
            result.append(triangle.copy())
        return result

    @property
    def uuid(self):
        if (self.__uuid is None) or self._dirty:
            self._update_caches()
        return self.__uuid

    def append(self, item):
        super().append(item)
        if isinstance(item, Triangle):
            self._triangles.append(item)
            # we assume, that the kdtree needs to be rebuilt again
            self._dirty = True

    def reset_cache(self):
        super().reset_cache()
        # the triangle kdtree needs to be reset after transforming the model
        self._update_caches()

    def _update_caches(self):
        if self._use_kdtree:
            self._t_kdtree = TriangleKdtree(self.triangles())
        self.__uuid = str(uuid.uuid4())
        # the kdtree is up-to-date again
        self._dirty = False

    def triangles(self,
                  minx=-INFINITE,
                  miny=-INFINITE,
                  minz=-INFINITE,
                  maxx=+INFINITE,
                  maxy=+INFINITE,
                  maxz=+INFINITE):
        if (minx == miny == minz == -INFINITE) and (maxx == maxy == maxz ==
                                                    +INFINITE):
            return self._triangles
        if self._use_kdtree:
            # update the kdtree, if new triangles were added meanwhile
            if self._dirty:
                self._update_caches()
            return self._t_kdtree.search(minx, maxx, miny, maxy)
        return self._triangles

    def get_waterline_contour(self, plane, callback=None):
        collision_lines = []
        progress_max = 2 * len(self._triangles)
        counter = 0
        for t in self._triangles:
            if callback and callback(percent=100.0 * counter / progress_max):
                return
            collision_line = plane.intersect_triangle(t,
                                                      counter_clockwise=True)
            if collision_line is not None:
                collision_lines.append(collision_line)
            else:
                counter += 1
            counter += 1
        # combine these lines into polygons
        contour = ContourModel(plane=plane)
        for line in collision_lines:
            if callback and callback(percent=100.0 * counter / progress_max):
                return
            contour.append(line)
            counter += 1
        log.debug("Waterline: %f - %d - %s", plane.p[2],
                  len(contour.get_polygons()),
                  [len(p.get_lines()) for p in contour.get_polygons()])
        return contour

    def to_x3d(self, color):
        yield "<Shape>"
        yield "<Appearance>"
        yield ('<Material diffuseColor="{:f} {:f} {:f}" transparency="{:f}" />'
               .format(color["red"], color["green"], color["blue"],
                       1 - color["alpha"]))
        yield "</Appearance>"
        yield "<TriangleSet>"
        yield '<Coordinate point="'
        for triangle in self:
            p1, p2, p3 = triangle.get_points()
            # use the proper direction in order to let normals point outwards
            yield " ".join("{:f} {:f} {:f}".format(*point)
                           for point in (p1, p3, p2))
        yield '"/>'
        yield "</TriangleSet>"
        yield "</Shape>"