"""
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 Sept 27, 2016
@author: jrm
"""
from math import cos, pi, sin
from typing import Any, Optional
from atom.api import (
Bool,
Coerced,
Dict,
Enum,
Float,
ForwardTyped,
Instance,
Int,
List,
Range,
Str,
Typed,
observe,
)
from enaml.core.declarative import d_
from OCCT.TopoDS import TopoDS_Face, TopoDS_Shape
from .geom import Direction, Point, coerce_direction, coerce_point
from .shape import ProxyShape, Shape
class ProxyPlane(ProxyShape):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Plane)
def set_bounds(self, bounds):
raise NotImplementedError
class ProxyVertex(ProxyShape):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Vertex)
def set_marker(self, marker: str):
raise NotImplementedError
def set_marker_scale(self, scale: float):
raise NotImplementedError
class ProxyEdge(ProxyShape):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Edge)
def set_surface(self, surface):
raise NotImplementedError
def set_as_wire(self, enabled: bool):
raise NotImplementedError
def set_line_style(self, style: str):
raise NotImplementedError
def set_line_width(self, width: float):
raise NotImplementedError
def get_value_at(self, t: float, derivative=0):
raise NotImplementedError
def set_solve(self, params):
raise NotImplementedError
def set_reverse(self, reverse: bool):
raise NotImplementedError
class ProxyLine(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Line)
def set_points(self, points):
raise NotImplementedError
class ProxySegment(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Segment)
class ProxyArc(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Arc)
def set_radius(self, r: float):
raise NotImplementedError
def set_alpha1(self, a: float):
raise NotImplementedError
def set_alpha2(self, a: float):
raise NotImplementedError
def set_clockwise(self, clockwise: bool):
raise NotImplementedError
class ProxyCircle(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Circle)
def set_radius(self, r: float):
raise NotImplementedError
def set_as_face(self, as_face: bool):
raise NotImplementedError
class ProxyEllipse(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Ellipse)
def set_major_radius(self, r: float):
raise NotImplementedError
def set_minor_radius(self, r: float):
raise NotImplementedError
def set_as_face(self, as_face: bool):
raise NotImplementedError
class ProxyHyperbola(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Hyperbola)
def set_major_radius(self, r: float):
raise NotImplementedError
def set_minor_radius(self, r: float):
raise NotImplementedError
class ProxyParabola(ProxyEdge):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Parabola)
def set_focal_length(self, length: float):
raise NotImplementedError
class ProxyBSpline(ProxyLine):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: BSpline)
class ProxyBezier(ProxyLine):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Bezier)
class ProxyTrimmedCurve(ProxyLine):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: TrimmedCurve)
def set_shape(self, shape):
raise NotImplementedError
def set_u(self, u: float):
raise NotImplementedError
def set_v(self, v: float):
raise NotImplementedError
class ProxyText(ProxyShape):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Text)
def set_text(self, text: str):
raise NotImplementedError
def set_font(self, font: str):
raise NotImplementedError
def set_size(self, size: float):
raise NotImplementedError
def set_style(self, style: str):
raise NotImplementedError
def set_center(self, center: bool):
raise NotImplementedError
def set_composite(self, composite: bool):
raise NotImplementedError
def set_vertical_alignment(self, alignment: str):
raise NotImplementedError
def set_horizontal_alignment(self, alignment: str):
raise NotImplementedError
class ProxyWire(ProxyEdge):
declaration = ForwardTyped(lambda: Wire)
class ProxyPolyline(ProxyWire):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Polyline)
def set_closed(self, closed: bool):
raise NotImplementedError
def set_as_wire(self, as_wire: bool):
raise NotImplementedError
class ProxyCircuit(ProxyWire):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Circuit)
def set_offset(self, offset: float):
raise NotImplementedError
def set_circles(self, circles: list):
raise NotImplementedError
class ProxyRectangle(ProxyWire):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Rectangle)
def set_rx(self, rx: float):
raise NotImplementedError
def set_ry(self, ry: float):
raise NotImplementedError
def set_width(self, w: float):
raise NotImplementedError
def set_height(self, h: float):
raise NotImplementedError
def set_as_face(self, as_face: bool):
raise NotImplementedError
class ProxySvg(ProxyWire):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Svg)
def set_source(self, source: str):
raise NotImplementedError
def set_mirror(self, mirror: bool):
raise NotImplementedError
def set_fill_mode(self, mode: str):
raise NotImplementedError
def set_center(self, center: bool):
raise NotImplementedError
def set_scale(self, scale: float):
raise NotImplementedError
class ProxyPdf(ProxyWire):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: Pdf)
def set_source(self, source: str):
raise NotImplementedError
def set_mirror(self, mirror: bool):
raise NotImplementedError
def set_fill_mode(self, mode: str):
raise NotImplementedError
def set_center(self, center: bool):
raise NotImplementedError
def set_scale(self, scale: float):
raise NotImplementedError
class ProxyMiddlePath(ProxyWire):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: MiddlePath)
def set_shapes(self, shapes: list):
raise NotImplementedError
def set_mode(self, mode: str):
raise NotImplementedError
def set_join_type(self, join_type: str):
raise NotImplementedError
class ProxyBSplineSurface(ProxyShape):
#: A reference to the shape declaration.
declaration = ForwardTyped(lambda: BSplineSurface)
def set_points(self, points: list[Point]):
raise NotImplementedError
def set_deg_min(self, deg: int):
raise NotImplementedError
def set_deg_max(self, deg: int):
raise NotImplementedError
def set_continuity(self, continuity: Optional[int]):
raise NotImplementedError
def set_interpolate(self, interpolate: bool):
raise NotImplementedError
def set_periodic(self, periodic: bool):
raise NotImplementedError
[docs]
class Plane(Shape):
"""A Point at a specific position.
Examples
--------
Plane:
position = (10, 100, 0)
direction = (0, 0, 1)
"""
proxy = Typed(ProxyPlane)
#: Bounds of plane (optional) a tuple of [(umin, umin), (vmax, vmax)]
bounds = d_(List(Coerced(Point, coercer=coerce_point)))
[docs]
class Vertex(Shape):
"""A Vertex at a specific position.
Examples
--------
Vertex:
position = (10, 100, 0)
"""
proxy = Typed(ProxyVertex)
#: Style marker
#: TODO: Custom
marker = d_(
Enum(
"plus",
"point",
"dot",
"star",
"cross",
"circle",
"point-in-circle",
"star-in-circle",
"plus-in-circle",
"cross-in-circle",
"large-ring",
"medium-ring",
"small-ring",
"ball",
)
)
#: Display scale
marker_scale = d_(Float(1.0, strict=False))
@observe("marker", "marker_scale")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Edge(Shape):
"""An Edge is a base class for Lines and Wires."""
proxy = Typed(ProxyEdge)
#: The parametric surface to wrap this edge on
surface = d_(Instance(TopoDS_Face))
#: Style for line
line_style = d_(Enum("solid", "dashed", "dotted", "dot_dash"))
#: Line width style
line_width = d_(Float(strict=False))
#: If True, convert the edge into a wire
as_wire = d_(Bool())
#: Solver parameters
#: When given these will be use to solve for inputs to the shape
solve = d_(Dict())
#: Reverse
reverse = d_(Bool()).tag(view=True)
@observe("reverse", "as_wire")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
def get_value_at(self, t: float, derivative: int = 0):
"""Get the value of the curve derivative at t. If the edge has no
internal parametric curve representation this will throw an error.
Parameter
---------
t: Float
The parametric value
derivative: Int
The derivative to get (use 0 for position).
Returns
-------
value: Tuple or Float
"""
return self.proxy.get_value_at(t, derivative)
[docs]
class Line(Edge):
"""Creates a Line passing through the position and parallel to vector
given by the direction.
Attributes
----------
position: Tuple
The position of the line.
direction: Tuple
The direction of the line.
Examples
--------
Line:
position = (10, 10, 10)
direction = (0, 0, 1)
"""
proxy = Typed(ProxyLine)
#: List of points
points = d_(List(Coerced(Point, coercer=coerce_point)))
@property
def start(self) -> Point:
return coerce_point(self.proxy.curve.StartPoint())
@property
def end(self) -> Point:
return coerce_point(self.proxy.curve.EndPoint())
@observe("points")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Segment(Line):
"""Creates a line Segment from two or child points. If a position
and direction are given the points are transformed to align with the
plane defined by the given position and direction.
Examples
--------
Segment:
points = ((0, 0, 0), (10, 0, 0)]
"""
proxy = Typed(ProxySegment)
[docs]
class Arc(Line):
"""Creates an Arc that can be used to build a Wire.
Attributes
----------
radius: Float, optional
The radius of the arc.
alpha1: Float, optional
The starting angle of the arc.
alpha2: Float, optional
The ending angle of the arc.
clockwise: Bool
If using two points this gives the interpolation direction
Notes
------
An arc can be constructed using:
1. three child Points
2. axis, radius, alpha 1, alpha 2
3. axis, radius, and two child Points
4. axis, radius, one child Point and alpha 1
Examples
---------
import math
Wire:
Arc:
attr deg = 5
radius = 1
alpha1 = math.radians(deg)
alpha2 = math.radians(deg+2)
Wire:
Arc:
points = (
(1, 0, 0),
(2, 5, 0),
(3, 0, 0)
)
Wire:
Arc:
solve = (
curve.topology.start_tangent,
end_point,
)
"""
proxy = Typed(ProxyArc)
#: Radius of the circle (optional)
radius = d_(Float(0, strict=False)).tag(view=True)
#: Angle circle (optional)
alpha1 = d_(Float(0, strict=False)).tag(view=True)
#: 2nd Angle circle (optional)
alpha2 = d_(Float(0, strict=False)).tag(view=True)
#: Clockwise (sweep direction, when using two points)
clockwise = d_(Bool()).tag(view=True)
[docs]
class Circle(Edge):
"""Creates a Circle.
Attributes
----------
radius: Float
The radius of the circle
Examples
--------
Circle:
radius = 5
position = (0, 0, 10)
direction = (1, 0, 0)
"""
proxy = Typed(ProxyCircle)
#: Radius of the circle
radius = d_(Float(1, strict=False)).tag(view=True)
#: Convert into a face
as_face = d_(Bool())
@observe("radius", "as_face")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Ellipse(Edge):
"""Creates an Ellipse.
Attributes
----------
major_radius: Float
The radius of the circle
minor_radius: Float
The second radius of the circle
Examples
--------
Ellipse:
major_radius = 5
minor_radius = 7
"""
proxy = Typed(ProxyEllipse)
#: Radius of the ellipse
major_radius = d_(Float(1, strict=False)).tag(view=True)
#: Minor radius of the ellipse
minor_radius = d_(Float(1, strict=False)).tag(view=True)
#: Convert into a face
as_face = d_(Bool())
@observe("major_radius", "minor_radius", "as_face")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Hyperbola(Edge):
"""Creates a Hyperbola.
Attributes
----------
major_radius: Float
The major radius of the hyperbola
minor_radius: Float
The minor radius of the hyperbola
Notes
------
The hyperbola is positioned in the space by the coordinate system A2 such
that:
- the origin of A2 is the center of the hyperbola,
- the "X Direction" of A2 defines the major axis of the hyperbola, that is,
the major radius MajorRadius is measured along this axis, and
- the "Y Direction" of A2 defines the minor axis of the hyperbola, that is,
the minor radius MinorRadius is measured along this axis.
This class does not prevent the creation of a hyperbola where:
- MajorAxis is equal to MinorAxis, or
- MajorAxis is less than MinorAxis. Exceptions Standard_ConstructionError
if MajorAxis or MinorAxis is negative.
Raises ConstructionError if MajorRadius < 0.0 or MinorRadius < 0.0 Raised
if MajorRadius < 0.0 or MinorRadius < 0.0
Examples
--------
Wire:
Hyperbola:
major_radius = 5
minor_radius = 3
"""
proxy = Typed(ProxyHyperbola)
#: Major radius of the hyperbola
major_radius = d_(Float(1, strict=False)).tag(view=True)
#: Minor radius of the hyperbola
minor_radius = d_(Float(1, strict=False)).tag(view=True)
@property
def start(self) -> Point:
return coerce_point(self.proxy.curve.StartPoint())
@property
def end(self) -> Point:
return coerce_point(self.proxy.curve.EndPoint())
@observe("major_radius", "minor_radius")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Parabola(Edge):
"""Creates a Parabola with its local coordinate system given by the
`position` and `direction` and it's focal length `focal_length`.
Attributes
----------
focal_length: Float
The focal length of the parabola.
Notes
-----
The XDirection of A2 defines the axis of symmetry of the parabola.
The YDirection of A2 is parallel to the directrix of the parabola.
The Location point of A2 is the vertex of the parabola
Raises ConstructionError if Focal < 0.0 Raised if Focal < 0.0.
Examples
---------
Wire:
Parabola:
focal_length = 10
"""
proxy = Typed(ProxyParabola)
#: Focal length of the parabola
focal_length = d_(Float(1, strict=False)).tag(view=True)
@property
def start(self) -> Point:
return coerce_point(self.proxy.curve.StartPoint())
@property
def end(self) -> Point:
return coerce_point(self.proxy.curve.EndPoint())
@observe("focal_length")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class BSpline(Line):
"""A BSpline built by approximation from the given points.
Examples
---------
Wire:
BSpline:
points = [(0, 0, 0), (10, 0, 0), (10, 10, 0), (0, 10, 0)]
"""
proxy = Typed(ProxyBSpline)
#: Use interpolation method. This will automatically be used if periodic
#: is True or tangents are given.
interpolate = d_(Bool())
#: Whether the spline will be periodic
periodic = d_(Bool())
# Min and max degree
deg_min = d_(Int(3))
deg_max = d_(Int(8))
#: Degree of continuity
continuity = d_(Enum(2, 0, 1, 3, None))
#: List of tangent vectors for each point (optional)
tangents = d_(List(Coerced(Direction, coercer=coerce_direction)))
#: List of parameters
parameters = d_(List(Float(strict=False)))
[docs]
class Bezier(Line):
"""A Bezier curve built by approximation from the given points.
Examples
---------
Wire:
Bezier:
points = [(0, 0, 0), (10, 0, 0), (10, 10, 0), (0, 10, 0)]
"""
proxy = Typed(ProxyBezier)
[docs]
class TrimmedCurve(Edge):
"""A TrimmedCurve built from a curve limited by two parameters.
Examples
---------
Wire:
TrimmedCurve:
u = 0
v = 2*pi
Ellipse:
major_radius = 2*pi
minor_radius = pi/3
"""
proxy = Typed(ProxyTrimmedCurve)
#: Shape to trim. If absent the first child will be used.
shape = d_(Instance((Shape, TopoDS_Shape)))
#: First Parameter
u = d_(Float(0, strict=False))
#: Second Parameter
v = d_(Float(0, strict=False))
[docs]
class Wire(Edge):
"""A Wire is a Path or series of Segment, Arcs, etc... All child items
must be connected or an error will be thrown.
Attributes
----------
edges: List, optional
Edges used to build the wire.
Examples
---------
Wire:
Polyline:
closed = True
points = [(0, 0, 0), (10, 0, 0), (10, 10, 0), (0, 10, 0)]
"""
proxy = Typed(ProxyWire)
#: Fixed
as_wire = True
#: Edges used to create this wire
edges = d_(List())
@observe("edges")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
def points_of_discontinuity(self, tolerance: float = 0.5) -> list[Point]:
"""Find points of discontinuity
Parameters
----------
tolerance: Float
The tolerance to use
Returns
-------
points: List[Point]
List of points where there is C1 discontinuity
"""
points: list[Point] = []
# Map of vertex to map of curve an derivative
data: dict[Point, list[tuple[Any, Point]]] = {}
if not self.proxy_is_active:
self.render()
for d in self.topology.curves:
curve = d["curve"]
for t in (curve.FirstParameter(), curve.LastParameter()):
p, v = self.topology.get_value_at(curve, t=t, derivative=1)
r = data.get(p)
if r is None:
# Hashing doesn't work so check for point equality
for k in data:
if k.is_equal(p):
r = data[k]
break
if r is None:
r = data[p] = []
r.append((curve, v))
for p, curves in data.items():
if len(curves) < 2:
continue
v1, v2 = curves[0][1], curves[1][1]
if v1.is_parallel(v2, tolerance):
continue # Continuous
points.append(p)
return points
[docs]
class Polyline(Wire):
"""A Polyline that can be built from any number of points or vertices,
and consists of a sequence of connected rectilinear edges. If a position
and direction are given the points are transformed to align with the
plane defined by the given position and direction.
Attributes
----------
points: List[Point]
The points of the polygon.
closed: Bool
Automatically close the polygon
as_face: Bool
Convert to face if forms a closed wire.
Examples
---------
Wire:
Polyline:
closed = True
points = [(0, 0, 0), (10, 0, 0), (10, 10, 0), (0, 10, 0)]
"""
proxy = Typed(ProxyPolyline)
#: Polyline is closed
closed = d_(Bool(False)).tag(view=True)
#: List of points
points = d_(List(Coerced(Point, coercer=coerce_point)))
#: Convert into a face
as_face = d_(Bool())
@property
def start(self) -> Point:
return coerce_point(self.proxy.curve.StartPoint())
@property
def end(self) -> Point:
return coerce_point(self.proxy.curve.EndPoint())
@observe("closed", "points", "as_face")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Polygon(Polyline):
"""A polyline that follows points on a circle of a given inscribed or
circumscribed radius.
Attributes
----------
radius: Float
Radius of the polygon
count: Int
Number of points in the polygon, must be 3 or more.
inscribed: Bool
Whether the radius should be interpreted as an "inscribed" or
"circumscribed" radius. The default is "circumscribed" meaning the
points will be on the given radius (inside the circle). If
`inscribed=True` then the midpoint of each segment will be on the
circle of the given radius (outside the circle).
Examples
---------
Wire:
Polygon: # A hexagon of radius 6
radius = 4
count = 6
"""
#: This is fixed
closed = True
#: Radius is inscribed
inscribed = d_(Bool())
#: Radius of the polygon
radius = d_(Float(1, strict=False)).tag(view=True)
#: Number of points
count = d_(Range(low=3)).tag(view=True)
@observe("radius", "inscribed", "count")
def _update_points(self, change: dict[str, Any]):
self.points = self._default_points()
def _default_points(self) -> list[Point]:
n = self.count
r = self.radius
a = 2 * pi / n
if self.inscribed:
r /= cos(pi / n)
return [Point(x=cos(i * a) * r, y=sin(i * a) * r) for i in range(n)]
def coerce_circle(value: Any) -> Circle:
from declaracad.occ.api import Topology
if isinstance(value, (tuple, list)):
radius = None
reverse = False
position = None
for arg in value:
if isinstance(arg, (int, float)):
radius = arg
elif isinstance(arg, bool):
reverse = arg
else:
position = arg
return Circle(radius=radius, reverse=reverse, position=position)
if isinstance(value, dict):
return Circle(**value)
if isinstance(value, Circle):
return value
if isinstance(value, Shape) and getattr(value, "radius", None):
return Circle(
radius=value.radius, direction=value.direction, position=value.position
)
if Topology.is_circle(value):
from OCCT.Geom import Geom_Circle
curve: Geom_Circle = Topology.cast_curve(value)
return Circle(radius=curve.Radius(), position=value.Location())
raise TypeError(f"Cannot coerce {value} to Circle")
[docs]
class Circuit(Wire):
"""Creates a circuit around a set of circles. Typically
used for creating belts or chains. A circle a negative radius is used
to indicate the circuit should go around the "inside". The reverse
argument on each circle can be used to reverse the arc direction.
Examples
--------
Circuit:
circles = [
(2, (0, 0)),
(5, (10, 10)),
(5, (0, 20)),
]
"""
proxy = Typed(ProxyCircuit)
#: Offset value
offset = d_(Float(strict=False))
#: List of tuple of (radius, Point). If radius is negative it is assumed
#: To mean the inside.
circles = d_(List(Coerced(Circle, coercer=coerce_circle)))
@observe("offset", "circles")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Rectangle(Wire):
"""A Wire in the shape of a Rectangle with optional radius'd corners.
Examples
---------
Wire:
Rectangle:
width = 10
height = 5
"""
proxy = Typed(ProxyRectangle)
#: Width of the rectangle
width = d_(Float(1, strict=False)).tag(view=True)
#: Height of the rectangle
height = d_(Float(1, strict=False)).tag(view=True)
#: Radius of the corner
rx = d_(Float(0, strict=False)).tag(view=True)
ry = d_(Float(0, strict=False)).tag(view=True)
#: Convert into a face
as_face = d_(Bool())
@observe("width", "height", "rx", "ry", "as_face")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Text(Shape):
"""Create a shape from a text of a given font.
Attributes
----------
text: String
The text to create
font: String
The font family to use
size: Float
The font size.
style: String
Font style
composite: Bool
Create a composite curve.
Examples
--------
Text:
text = "Hello world!"
font = "Georgia"
position = (10, 100, 0)
"""
#: Proxy shape
proxy = Typed(ProxyText)
#: Center text
center = d_(Bool())
#: Text to display
text = d_(Str())
#: Font to use
font = d_(Str())
#: Font size
size = d_(Float(12.0, strict=False))
#: Font style
style = d_(Enum("regular", "bold", "italic", "bold-italic"))
#: Font alignment
horizontal_alignment = d_(Enum("left", "center", "right"))
vertical_alignment = d_(Enum("bottom", "center", "top", "topfirstline"))
#: Composite curve
composite = d_(Bool(True))
@observe(
"text",
"font",
"center",
"size",
"style",
"composite",
"horizontal_alignment",
"vertical_alignment",
)
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Svg(Wire):
"""Creates a wire from a SVG document.
Attributes
----------
source: String
Path or svg text to parse
Examples
--------
Svg:
source = "path/to/file.svg"
position = (10, 100, 0)
"""
#: Proxy shape
proxy = Typed(ProxySvg)
#: Source file or text
source = d_(Str())
#: Mirror y-axis
mirror = d_(Bool(True))
#: Automatically shift to center
center = d_(Bool(False))
#: Scale. Use this to convert units
scale = d_(Float(1, strict=False))
#: Whether to create faces from nodes
fill_mode = d_(Enum("auto", "always", "never"))
@observe("source", "fill_mode", "mirror", "center", "scale")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class Pdf(Wire):
"""Creates a wire from a PDF document.
Attributes
----------
source: String
Path or pdf to parse
Examples
--------
Svg:
source = "path/to/file.pdf"
position = (10, 100, 0)
"""
#: Proxy shape
proxy = Typed(ProxyPdf)
#: Source file or text
source = d_(Str())
#: Mirror y-axis
mirror = d_(Bool(False))
#: Automatically shift to center
center = d_(Bool(False))
#: Scale. Use this to convert units
scale = d_(Float(25.4 / 72.0, strict=False))
#: Whether to create faces from nodes
fill_mode = d_(Enum("auto", "always", "never"))
@observe("source", "fill_mode", "mirror", "center", "scale")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class MiddlePath(Wire):
"""Create a middle path"""
proxy = Typed(ProxyMiddlePath)
#: Must be either empty 1 to 3 elements.
#: The last two elements represent the start and ending faces or wires.
shapes = d_(List((Shape, TopoDS_Shape)))
#: Mode for 2d paths. Normal trims and keep touching edges.
#: No-trim ignores edge parameters. No touching removes edges touching
#: the face edge.
mode = d_(Enum("normal", "not-touching"))
#: Joint type for 2d paths.
join_type = d_(Enum("arc", "tangent", "intersection"))
@observe("shapes", "mode", "join_type")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)
[docs]
class BSplineSurface(Shape):
"""Create a BSpline surface from a grid of points"""
#: A reference to the shape declaration.
proxy = Typed(ProxyBSplineSurface)
#: List of list of points
points = d_(List(List(Coerced(Point, coercer=coerce_point))))
#: Use interpolation method. This will automatically be used if periodic
#: is True or tangents are given.
interpolate = d_(Bool())
#: Whether the spline will be periodic
periodic = d_(Bool())
# Min and max degree
deg_min = d_(Int(3))
deg_max = d_(Int(8))
#: Degree of continuity
continuity = d_(Enum(2, 0, 1, 3, None))
@observe("points", "deg_min", "deg_max", "continuity", "interpolate", "periodic")
def _update_proxy(self, change: dict[str, Any]):
super()._update_proxy(change)