Source code for declaracad.occ.shape

# -*- coding: utf-8 -*-
"""
Copyright (c) 2016-2018, CodeLV.

Distributed under the terms of the GPL v3 License.

The full license is in the file LICENSE, distributed with this software.

Created on Sep 30, 2016

@author: jrm
"""
from math import pi
from typing import Any, ClassVar, Optional

from atom.api import (
    Bool,
    Coerced,
    Dict,
    Event,
    Float,
    FloatRange,
    ForwardInstance,
    ForwardTyped,
    Instance,
    List,
    Property,
    Str,
    Tuple,
    Typed,
    observe,
)
from enaml.colors import Color, ColorMember
from enaml.core.declarative import d_
from enaml.widgets.control import ProxyControl
from enaml.widgets.toolkit_object import ToolkitObject

#: TODO: This breaks the proxy pattern
from OCCT.TopoDS import TopoDS_Face, TopoDS_Shape, TopoDS_Shell

from declaracad.core.utils import log, process_events

from .geom import (
    BBox,
    Direction,
    Point,
    coerce_direction,
    coerce_point,
    coerce_rotation,
    settings,
)
from .materials import Material, Texture


class ProxyExport(ProxyControl):
    #: A reference to the Shape declaration.
    declaration = ForwardTyped(lambda: Export)


class ProxyShape(ProxyControl):
    #: A reference to the Shape declaration.
    declaration = ForwardTyped(lambda: Shape)

    def set_position(self, position: Point):
        pass

    def set_direction(self, direction: Direction):
        pass

    def set_axis(self, axis):
        raise NotImplementedError

    def set_color(self, color: Color):
        pass

    def set_transparency(self, alpha: float):
        pass

    def set_texture(self, texture):
        pass

    def get_bounding_box(self) -> BBox:
        raise NotImplementedError


class ProxyPart(ProxyShape):
    #: A reference to the Shape declaration.
    declaration = ForwardTyped(lambda: Part)

    def set_transform(self, ops):
        raise NotImplementedError


class ProxyFace(ProxyShape):
    #: A reference to the Shape declaration.
    declaration = ForwardTyped(lambda: Face)

    def set_wire(self, wire):
        raise NotImplementedError


class ProxyBox(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Box)

    def set_dx(self, dx: float):
        raise NotImplementedError

    def set_dy(self, dy: float):
        raise NotImplementedError

    def set_dz(self, dz: float):
        raise NotImplementedError


class ProxyCone(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Cone)

    def set_radius(self, r: float):
        raise NotImplementedError

    def set_radius2(self, r: float):
        raise NotImplementedError

    def set_height(self, height: float):
        raise NotImplementedError

    def set_angle(self, angle: float):
        raise NotImplementedError

    def get_apex(self) -> Point:
        raise NotImplementedError


class ProxyCylinder(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Cylinder)

    def set_radius(self, r):
        raise NotImplementedError

    def set_height(self, height):
        raise NotImplementedError

    def set_angle(self, angle):
        raise NotImplementedError


class ProxyTube(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Tube)

    def set_radius(self, r):
        raise NotImplementedError

    def set_radius2(self, r):
        raise NotImplementedError

    def set_height(self, height):
        raise NotImplementedError

    def set_angle(self, angle):
        raise NotImplementedError


class ProxyHalfSpace(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: HalfSpace)

    def set_surface(self, surface):
        raise NotImplementedError

    def set_side(self, side):
        raise NotImplementedError


class ProxyPrism(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Prism)

    def set_shape(self, surface):
        raise NotImplementedError

    def set_vector(self, vector):
        raise NotImplementedError

    def set_infinite(self, infinite):
        raise NotImplementedError

    def set_canonize(self, canonize):
        raise NotImplementedError


class ProxySphere(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Sphere)

    def set_radius(self, r):
        raise NotImplementedError

    def set_angle(self, a):
        raise NotImplementedError

    def set_angle2(self, a):
        raise NotImplementedError

    def set_angle3(self, a):
        raise NotImplementedError


class ProxyTorus(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Torus)

    def set_radius(self, r):
        raise NotImplementedError

    def set_radius2(self, r):
        raise NotImplementedError

    def set_angle(self, a):
        raise NotImplementedError

    def set_angle2(self, a):
        raise NotImplementedError

    def set_angle3(self, a):
        raise NotImplementedError


class ProxyWedge(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Wedge)

    def set_dx(self, dx):
        raise NotImplementedError

    def set_dy(self, dy):
        raise NotImplementedError

    def set_dz(self, dz):
        raise NotImplementedError

    def set_itx(self, itx):
        raise NotImplementedError


class ProxyRevol(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: Revol)

    def set_shape(self, shape):
        raise NotImplementedError

    def set_angle(self, angle: float):
        raise NotImplementedError


class ProxyRawShape(ProxyShape):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: RawShape)

    def get_shape(self):
        raise NotImplementedError


class ProxyRawPart(ProxyPart):
    #: A reference to the shape declaration.
    declaration = ForwardTyped(lambda: RawPart)

    def get_shapes(self):
        raise NotImplementedError


[docs] class Shape(ToolkitObject): """Abstract shape component that can be displayed on the screen and represented by the framework. Notes ------ This shape's proxy holds an internal reference to the underlying shape which can be accessed using `self.proxy.shape` if needed. The topology of the shape can be accessed using the `self.proxy.topology` attribute. """ #: Reference to the implementation control proxy = Typed(ProxyShape) #: Whether the shape should be displayed and exported when in a part #: If set to false this shape will be excluded from the rendered part display = d_(Bool(True)) #: Description to display in the tooltip when clicked description = d_(Str()) #: The tolerance to use for operations that may require it. tolerance = d_(Float(strict=False)) def _default_tolerance(self) -> float: return settings.tolerance #: Material material = d_(Coerced(Material)) #: A string representing the color of the shape. color = d_(ColorMember()) #: A string representing the line color of the shape in HLR mode. line_color = d_(ColorMember()) #: A string representing the hidden line color of the shape in wireframe mode. wireframe_line_color = d_(ColorMember()) #: The opacity of the shape used for display. transparency = d_(FloatRange(0.0, 1.0, 0.0)) #: Texture to apply to the shape texture = d_(Instance(Texture)) #: Position alias def _get_x(self) -> float: return self.position.x @observe("position.x") def _update_x(self, change: dict[str, Any]): self.notify("x", change) def _set_x(self, v: float): self.position.x = v x = d_(Property(_get_x, _set_x)) def _get_y(self) -> float: return self.position.y @observe("position.y") def _update_y(self, change: dict[str, Any]): self.notify("y", change) def _set_y(self, v: float): self.position.y = v y = d_(Property(_get_y, _set_y)) def _get_z(self) -> float: return self.position.z @observe("position.z") def _update_z(self, change: dict[str, Any]): self.notify("z", change) def _set_z(self, v: float): self.position.z = v z = d_(Property(_get_z, _set_z)) #: A tuple or list of the (x, y, z) position of this shape. This is #: coerced into a Point. position = d_(Coerced(Point, coercer=coerce_point)) def _default_position(self) -> Point: return Point(0, 0, 0) #: A tuple or list of the (u, v, w) normal vector of this shape. This is #: coerced into a Vector setting the orentation of the shape. #: This is effectively the working plane. direction = d_(Coerced(Direction, coercer=coerce_direction)) def _default_direction(self) -> Direction: return Direction(0, 0, 1) #: Rotation about the normal vector in radians rotation = d_(Coerced(float, coercer=coerce_rotation)) def _get_axis(self): return (self.position, self.direction, self.rotation) def _set_axis(self, axis): self.position, self.direction, self.rotation = axis #: A tuple or list of the (u, v, w) axis of this shape. This is #: coerced into a Vector that defines the x, y, and z orientation of #: this shape. axis = d_(Property(_get_axis, _set_axis)) def _get_topology(self): if not self.proxy_is_active: self.render() return self.proxy.topology #: A read only property that accesses the topology of the shape such #: as edges, faces, shells, solids, etc.... topology = Property(_get_topology, cached=True) def _get_bounding_box(self) -> Optional[BBox]: if self.proxy.shape: try: return self.proxy.get_bounding_box() except Exception as e: log.warning(e) return None #: Bounding box of this shape bbox = Property(_get_bounding_box, cached=True) @observe( "color", "transparency", "display", "texture", "position", "direction", "rotation", ) def _update_proxy(self, change: dict[str, Any]): super()._update_proxy(change) @observe("proxy.shape") def _update_properties(self, change: dict[str, Any]): """Clear the cached references when the shape changes.""" for k in ("bbox", "topology"): self.get_member(k).reset(self) self.constructed() #: Triggered when the shape is constructed constructed = d_(Event(), writable=False) def activate_proxy(self): """Activate the proxy object tree. This method should be called by a node to activate the proxy tree by making two initialization passes over the tree, from this node downward. This method is automatically at the proper times and should not normally need to be invoked by user code. """ self.activate_top_down() for child in self.children: # Make sure each is initialized upon activation if not child.is_initialized: child.initialize() if isinstance(child, ToolkitObject): if not child.proxy_is_active: child.activate_proxy() # Generating the model can take a lot of time # so process events inbetween to keep the UI from freezing process_events() self.activate_bottom_up() self.proxy_is_active = True self.activated() def render(self) -> TopoDS_Shape: """Generates and returns the actual shape from the declaration. Enaml does this automatically when it's included in the viewer so this is only neede when working with shapes manually. Returns ------- shape: TopoDS_Shape The shape generated by this declaration. """ if not self.is_initialized: self.initialize() if not self.proxy_is_active: self.activate_proxy() return self.proxy.shape def __repr__(self) -> str: qualname = self.__class__.__qualname__ addr = f"0x{hex(id(self))}" name = self.name return f"<{qualname} name={name} at {addr}>"
[docs] class Part(Shape): """A Part is a compound shape. It may contain any number of nested parts and is typically subclassed. Attributes ---------- name: String An optional name for the part description: String An optional description for the part Examples -------- enamldef Case(Part): TopCover: # etc.. BottomCover: # etc.. """ #: Reference to the implementation control proxy = Typed(ProxyPart) #: Optional name of the part name = d_(Str()) #: Optional description of the part description = d_(Str()) #: Transform operations transform = d_(List()) #: Set to true to prevent destruction of the shape when removed #: from the viewer. You need to manually manage it otherwise it'll #: create a memory leak cached = d_(Bool(False)) #: Static cache to store parts in. cached_parts: ClassVar[dict[str, "Part"]] = {} #: Key use for caching. If you create multiple instances of the same #: part use this to distingish between them cache_key = d_(Str()) #: If true, force delete the cache to reload the cached part reload = d_(Bool()) #: Reference to the cached part. If cached is not True this will default #: to `self` making it safe to always use this for references. cache = ForwardInstance(lambda: Part) #: Triggered when the part is rendered in the viewer rendered = d_(Event(), writable=False) def _default_cache(self): return self @property def shapes(self) -> list[Shape]: return [child for child in self.children if isinstance(child, Shape)] def initialize(self) -> None: if self.cached: self.is_initialized = True self.initialized() self.insert_cached_part() else: super().initialize() def insert_cached_part(self) -> None: """Create and insert the cached part into the parent.""" key = f"{self.__class__.__qualname__}.{self.cache_key}" cached_part = Part.cached_parts.get(key) if self.reload and cached_part is not None: cached_part.cached = False cached_part.destroy() cached_part = None if cached_part is None or cached_part.proxy is None: # Create a new part and place in new cached part cached_part = self.__class__() cached_part.insert_children(self, self.children) cached_part.initialize() Part.cached_parts[key] = cached_part cached_part.cached = True # Make sure parent does not delete again else: # Remove children so they do not generate again for c in self.children: c.destroy() del self._children self.parent.insert_children(self, [cached_part]) self.cache = cached_part self.proxy = cached_part.proxy def destroy(self) -> None: """A reimplemented destructor. If cached is set to True, destroy removes the shape from the view but does not destroy it. """ cached = self.cached if cached and self.cache is self: # Clear the parent but do not actually destroy self.set_parent(None) return elif cached: # This is the part that was used as a placholder for the cached # part. Unset the proxy and then destroy so the cached part is # left intact. self.proxy = None super().destroy()
[docs] class Face(Shape): """A Face turns it's first child Wire into a surface. Examples -------- Add a Wire as a child Face: Wire: # etc.. """ #: Reference to the implementation control proxy = Typed(ProxyFace) #: List of wires to use wires = d_(List())
[docs] class Box(Shape): """A primitive Box shape. Attributes ---------- dx: Float Size or width of the box along the x-axis dy: Float Size or height of the box along the y-axis dz: Float Size or depth of the box along the z-axis Examples -------- Box: dx = 3 dy = 10 # dx, dy, and dz are all 1 by default if omitted """ #: Proxy shape proxy = Typed(ProxyBox) #: x size dx = d_(Float(1, strict=False)).tag(view=True) #: y size dy = d_(Float(1, strict=False)).tag(view=True) #: z size dz = d_(Float(1, strict=False)).tag(view=True) # TODO: Handle other constructors @observe("dx", "dy", "dz") def _update_proxy(self, change: dict[str, Any]): super()._update_proxy(change)
[docs] class Cone(Shape): """A primitive Cone shape. Attributes ---------- height: Float Height of the cone radius: Float Radius of the base of the cone radius2: Float Second radius of the base of the cone (to make it oval) angle: The angle to revolve (in radians) the base profile Examples -------- Cone: height = 10 radius = 5 angle = math.pi/2 """ #: Proxy shape proxy = Typed(ProxyCone) #: Radius radius = d_(Float(1, strict=False)).tag(view=True) #: Radius 2 size radius2 = d_(Float(0, strict=False)).tag(view=True) #: Height height = d_(Float(1, strict=False)).tag(view=True) #: Angle angle = d_(Float(0, strict=False)).tag(view=True) #: Return the apex apex = Property(lambda self: self.proxy.get_apex()) @observe("radius", "radius2", "height", "angle") def _update_proxy(self, change: dict[str, Any]): super()._update_proxy(change)
[docs] class Cylinder(Shape): """A primitive Cylinder shape. Attributes ---------- height: Float Height of the cylinder radius: Float Radius of the base of the cylinder angle: The angle to revolve (in radians) the base profile. Examples -------- Cone: height = 10 radius = 5 """ #: Proxy shape proxy = Typed(ProxyCylinder) #: Radius radius = d_(Float(1, strict=False)).tag(view=True) #: Height height = d_(Float(1, strict=False)).tag(view=True) #: Angle angle = d_(Float(0, strict=False)).tag(view=True) @observe("radius", "height", "angle") def _update_proxy(self, change: dict[str, Any]): super()._update_proxy(change)
[docs] class Tube(Shape): """A tube is a cylinder with the inside cut out. The smaller of radius and radius2 will be used as the inner radius. Attributes ---------- height: Float Height of the tube radius: Float Outer radius of the base of the tube radius2: Float Inner radius of the base of the tube angle: The angle to revolve (in radians) the base profile. Examples -------- Tube: height = 5 radius = 1 radius2 = 0.75 """ #: Proxy shape proxy = Typed(ProxyTube) #: Radius radius = d_(Float(1, strict=False)).tag(view=True) #: Radius 2 size radius2 = d_(Float(0, strict=False)).tag(view=True) #: Height height = d_(Float(1, strict=False)).tag(view=True) #: Angle angle = d_(Float(0, strict=False)).tag(view=True) @observe("radius", "radius2", "height", "angle") def _update_proxy(self, change: dict[str, Any]): super()._update_proxy(change)
[docs] class HalfSpace(Shape): """An infinite solid limited by a surface. Attributes ---------- surface: Face or Shell Surface to divide side: Point A point on the side of the surface where the half space should be Notes ----- A half-space is an infinite solid, limited by a surface. It is built from a face or a shell, which bounds it, and with a reference point, which specifies the side of the surface where the matter of the half-space is located. A half-space is a tool commonly used in topological operations to cut another shape Examples -------- HalfSpace: # Plane at orgiin in z direction position = (0, 0, 0) direction = (0, 0, 1) side = (0, 0, -1) # Negative z side """ #: Proxy shape proxy = Typed(ProxyHalfSpace) #: Surface that is either a face or a Shell surface = d_(Instance((TopoDS_Face, TopoDS_Shell))) #: Side of surface where the space is located side = d_(Coerced(Point, coercer=coerce_point)) @observe("surface", "side") def _update_proxy(self, change: dict[str, Any]): super(HalfSpace, self)._update_proxy(change)
[docs] class Prism(Shape): """A Prism extrudes a Face into a solid or a Wire into a surface along the given vector. Attributes ---------- shape: Shape to extrude or None Reference to the shape to extrude. vector: Tuple of (x, y, z) The extrusion vector. infinite: Bool Whether to extrude an infinte distance along the given vector. canonize: Bool Attempt to canonize in simple shapes Notes ----- The first child node will be used as the shape if none is given. Examples -------- Prism: Wire: Polyline: points = [(0,5,0), (2,6,0), (5,4,0), (0,5,0)] """ #: Proxy shape proxy = Typed(ProxyPrism) #: Shape to build prism from shape = d_(Instance((Shape, TopoDS_Shape))).tag(view=True) #: Vector to build prism from, ignored if infinite is true vector = d_(Tuple((float, int), default=(0, 0, 1))).tag(view=True) #: Infinite infinite = d_(Bool(False)).tag(view=True) #: Attempt to canonize canonize = d_(Bool(True)).tag(view=True) @observe("shape", "vector", "infinite", "canonize") def _update_proxy(self, change: dict[str, Any]): super(Prism, self)._update_proxy(change)
[docs] class Sphere(Shape): """A primitive Sphere shape. Attributes ---------- radius: Float Radius of the sphere angle: Float The u-angle to revolve (in radians) along the base profile from 0 to 2pi. angle2: Float The v-min angle to start from (in radians) from -pi/2 to pi/2 angle3: Float The v-max angle to start from (in radians) from -pi/2 to pi/2 Notes -------- Make a sphere of radius R. For all algorithms The resulting shape is composed of: - a lateral spherical face - Two planar faces parallel to the plane z = 0 if the sphere is truncated in the v parametric direction, or only one planar face if angle1 is equal to -p/2 or if angle2 is equal to p/2 (these faces are circles in case of a complete truncated sphere), - and in case of a portion of sphere, two planar faces to shut the shape. (in the planes u = 0 and u = angle). Examples -------- Sphere: radius = 3 Sphere: angle = math.pi/2 """ #: Proxy shape proxy = Typed(ProxySphere) #: Radius of sphere radius = d_(Float(1, strict=False)).tag(view=True) #: Angle of U (fraction of circle) angle = d_(FloatRange(low=0.0, high=2 * pi, value=2 * pi)).tag(view=True) #: Min Angle of V (fraction of circle in normal direction) angle2 = d_(FloatRange(low=-pi / 2, high=pi / 2, value=-pi / 2)).tag(view=True) #: Max Angle of V (fraction of circle in normal direction) angle3 = d_(FloatRange(low=-pi / 2, high=pi / 2, value=pi / 2)).tag(view=True) @observe("radius", "angle", "angle2", "angle3") def _update_proxy(self, change: dict[str, Any]): super(Sphere, self)._update_proxy(change)
[docs] class Torus(Shape): """A primitive Torus shape (a ring like shape). Attributes ---------- radius: Float Radius of the torus radius2: Float Radius of the torus profile angle: Float The angle to revolve the torus (in radians) from 0 to 2 pi. angle2: Float The angle to revolve the torus profile (in radians) from -pi/2 to pi/2. Examples -------- Torus: radius = 5 """ #: Proxy shape proxy = Typed(ProxyTorus) #: Radius of sphere radius = d_(Float(1, strict=False)).tag(view=True) #: Radius 2 radius2 = d_(Float(0, strict=False)).tag(view=True) #: Angle of U (fraction of circle) angle = d_(FloatRange(low=0.0, high=2 * pi, value=2 * pi)).tag(view=True) #: Start Angle of V (fraction of circle in normal direction) angle2 = d_(Float(0, strict=False)).tag(view=True) #: Stop Angle of V (fraction of circle in normal direction) angle3 = d_(Float(0, strict=False)).tag(view=True) @observe("radius", "radius2", "angle", "angle2", "angle3") def _update_proxy(self, change: dict[str, Any]): super(Torus, self)._update_proxy(change)
[docs] class Wedge(Shape): """A primitive Wedge shape. Attributes ---------- dx: Float Size of the wedge along the x-axis dy: Float Size of the wedge along the y-axis dz: Float Size of the wedge along the z-axis ltx: Float Size of the base before the wedge starts. Must be >= 0. Defaults to 0. Examples -------- Wedge: dy = 5 """ #: Proxy shape proxy = Typed(ProxyWedge) #: x size dx = d_(Float(1, strict=False)).tag(view=True) #: y size dy = d_(Float(1, strict=False)).tag(view=True) #: z size dz = d_(Float(1, strict=False)).tag(view=True) #: z size itx = d_(Float(0, strict=False)).tag(view=True) # TODO: Handle other constructors @observe("dx", "dy", "dz", "itx") def _update_proxy(self, change: dict[str, Any]): super(Wedge, self)._update_proxy(change)
[docs] class Revol(Shape): """A Revol creates a shape by revolving a profile about an axis. Attributes ---------- shape: Shape Shape to revolve. If not given, the first child will be used. angle: Float Angle to revolve (in radians) the base profile. Examples -------- # This creates a cone of radius 4 and height 5. Revol: Wire: Polyline: points = [(0,0,0), (0,2,5), (0,5,0), (0,0,0)] """ #: Proxy shape proxy = Typed(ProxyRevol) #: Shape to build prism from shape = d_(Instance(Shape)).tag(view=True) #: Angle to revolve angle = d_(Float(0, strict=False)).tag(view=True) @observe("shape", "angle") def _update_proxy(self, change: dict[str, Any]): super(Revol, self)._update_proxy(change)
[docs] class RawShape(Shape): """A RawShape is a shape that delegates shape creation to the declaration. This allows custom shapes to be added to the 3D model hierarchy. Users should subclass this and implement the `create_shape` method. Examples -------- from OCC.TopoDS import TopoDS_Shape from OCC.StlAPI import StlAPI_Reader class StlShape(RawShape): #: Loads a shape from an stl file def create_shape(self, parent): stl_reader = StlAPI_Reader() shape = TopoDS_Shape() stl_reader.Read(shape, './models/fan.stl') return shape """ #: Reference to the implementation control proxy = Typed(ProxyRawShape) def create_shape(self, parent): """Create the shape for the control. This method should create and initialize the shape. Parameters ---------- parent : shape or None The parent shape for the control. Returns ------- result : shape The shape for the control. """ raise NotImplementedError def get_shape(self): """Retrieve the shape for display. Returns ------- shape : shape or None The toolkit shape that was previously created by the call to 'create_shape' or None if the proxy is not active or the shape has been destroyed. """ if self.proxy_is_active: return self.proxy.get_shape()
[docs] class RawPart(Shape): """A RawPart is a part that delegates creation to the declaration. This allows custom shapes to be added to the 3D model hierarchy. Users should subclass this and implement the `create_shapes` method. Examples -------- from OCC.TopoDS import TopoDS_Shape from OCC.StlAPI import StlAPI_Reader class StlShape(RawShape): #: Loads a shape from an stl file def create_shape(self, parent): stl_reader = StlAPI_Reader() shape = TopoDS_Shape() stl_reader.Read(shape, './models/fan.stl') return shape """ #: Reference to the implementation control proxy = Typed(ProxyRawPart) def create_shapes(self, parent): """Create the shape for the control. This method should create and initialize the shape. Parameters ---------- parent : shape or None The parent shape for the control. Returns ------- result : List[shape] The shapes for the control. """ raise NotImplementedError def get_shapes(self): """Retrieve the shapes for display. Returns ------- shapes : List[shape] or None The toolkit shape that was previously created by the call to 'create_shapes' or None if the proxy is not active or the shape has been destroyed. """ if self.proxy_is_active: return self.proxy.get_shapes()
[docs] class TopoShape(RawShape): """A declaration for inserting an existing TopoDS_Shape somewhere into a DeclaraCAD tree. """ shape = d_(Instance(TopoDS_Shape)) def create_shape(self, parent): return self.shape def get_shape(self): return self.shape
[docs] class Export(ToolkitObject): """A block which exports the shape""" #: Disable export disabled = d_(Bool()) #: Shapes to export shapes = d_(List((Shape, TopoDS_Shape))) #: Export filename filename = d_(Str()) #: Export options options = d_(Dict())