Curves and Profiles#

Parametric curves in 3D, with planar curves as a special case, and the frames that carry a profile along them. Points can be passed as tuples, lists, Vec3, Vec2, Point2 or NumPy arrays; batched results come back as NumPy arrays. Curves never change after construction, so one curve can be shared by many nodes.

Curves#

class pyvcad.Curve#

Base class of all curves: an immutable parametric curve in 3D.

A curve maps a parameter t in domain to a point. Planar curves are the case where every point lies in one plane; 2D input is placed at z = 0. Points can be passed as tuples, lists, Vec3, Vec2, Point2, or NumPy arrays; batched results are NumPy arrays.

Accuracy: closest points are exact closed forms for LineSegment, PolylineCurve and ArcCurve. For other curves they come from a box-pruned search refined with Newton’s method, exact to about 1e-12 relative. Near the centre of curvature of a curved piece the search can settle on a local minimum; the distance is then within twice the piece’s deviation from its chord of the true minimum (for BezierCurve and BSplineCurve, at most 1e-5 of the control-point box diagonal per piece). Arc lengths use Gauss-Legendre quadrature.

Example

>>> import pyvcad as pv
>>> curve = pv.BSplineCurve.interpolate([(0, 0), (10, 5), (20, 0)])
>>> hit = curve.closest_point((10, 0, 0))
bounding_box(self: pyvcad.pyvcad.Curve) → tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64]]#

A box (minimum, maximum) that contains the whole curve.

Example

>>> low, high = curve.bounding_box()
closest_point(self: pyvcad.pyvcad.Curve, point: object) → pyvcad.pyvcad.CurveHit#

Find the point of the curve closest to a point.

Parameters:

point (tuple | Vec3 | numpy.ndarray) – The query point.

Returns:

The parameter, point and distance.

Return type:

CurveHit

Example

>>> hit = curve.closest_point((1.0, 2.0, 0.0))
>>> hit.parameter, hit.distance
derivative(self: pyvcad.pyvcad.Curve, t: SupportsFloat | SupportsIndex, order: SupportsInt | SupportsIndex = 1) → numpy.typing.NDArray[numpy.float64]#

Evaluate a derivative with respect to t.

Parameters:
  • t (float) – The parameter.

  • order (int) – 1 or 2. At a corner, the derivative of the piece starting at t is returned.

Example

>>> velocity = curve.derivative(0.25)
distance(self: pyvcad.pyvcad.Curve, point: object) → float#

Distance from a point to the curve.

Parameters:

point (tuple | Vec3 | numpy.ndarray) – The query point.

Example

>>> d = curve.distance((1.0, 2.0, 0.0))
distance_field(self: pyvcad.pyvcad.Curve) → FloatAttribute#

A field whose value is the distance to this curve (exact), for grading such as “thicker near this path”.

Example

>>> modulus = path.distance_field().map_range(0.0, 10.0, 1000.0, 100.0, clamp=True)
distances(self: pyvcad.pyvcad.Curve, points: object) → numpy.typing.NDArray[numpy.float64]#

Distances from many points to the curve.

Parameters:

points (numpy.ndarray) – An (N, 2) or (N, 3) array of points.

Example

>>> d = curve.distances(numpy.random.rand(100, 3))
property domain#

The parameter domain (start, end).

frames(self: pyvcad.pyvcad.Curve, count: SupportsInt | SupportsIndex, up: object = None) → dict#

Rotation-minimizing frames at points spaced evenly in arc length (see CurveFrames).

Parameters:
  • count (int) – The number of frames.

  • up (tuple | Vec3 | None) – Initial binormal direction. None uses the plane normal of a planar curve, otherwise the world axis least aligned with the start tangent.

Returns:

NumPy arrays ‘parameters’ (N,), and ‘origins’, ‘tangents’, ‘normals’, ‘binormals’ (N, 3).

Return type:

dict

Example

>>> frames = curve.frames(32)
>>> frames['normals'].shape
(32, 3)
property is_closed#

True when the curve ends where it starts.

length(self: pyvcad.pyvcad.Curve) → float#

Total arc length.

Example

>>> pv.ArcCurve.circle((0, 0, 0), 1.0).length()  # 2 pi
length_at(self: pyvcad.pyvcad.Curve, t: SupportsFloat | SupportsIndex) → float#

Arc length from the start of the curve to parameter t.

Parameters:

t (float) – The parameter.

Example

>>> s = curve.length_at(0.5)
length_field(self: pyvcad.pyvcad.Curve, normalized: bool = False) → FloatAttribute#

A field whose value is the arc length from the curve start to each point’s closest curve point.

Parameters:

normalized (bool) – Divide by the curve length, giving 0 at the start and 1 at the end.

Example

>>> taper = path.length_field(normalized=True).map_range(0.0, 1.0, 3.0, 1.0)
mirrored(self: pyvcad.pyvcad.Curve, point: object, normal: object) → pyvcad.pyvcad.Curve#

The curve reflected in a plane.

For a curve in the XY plane, a normal with z = 0 mirrors across the line through point perpendicular to the normal.

Parameters:
  • point (tuple | Vec3) – A point on the mirror plane.

  • normal (tuple | Vec3) – The mirror plane’s normal.

Example

>>> left = curve.mirrored((0, 0), (1, 0))  # across the Y axis
parameter_at_length(self: pyvcad.pyvcad.Curve, length: SupportsFloat | SupportsIndex) → float#

Parameter at which the arc length from the start equals length (arc-length parameterisation).

Parameters:

length (float) – Arc length, clamped to [0, length()] (wrapped for closed curves).

Example

>>> t_middle = curve.parameter_at_length(0.5 * curve.length())
parameter_field(self: pyvcad.pyvcad.Curve) → FloatAttribute#

A field whose value is the curve parameter of each point’s closest curve point.

Example

>>> t = path.parameter_field()
property plane_normal#

Unit normal of the plane holding the curve, or None for straight or non-planar curves.

point(self: pyvcad.pyvcad.Curve, t: SupportsFloat | SupportsIndex) → numpy.typing.NDArray[numpy.float64]#

Evaluate the curve.

Parameters:

t (float) – The parameter. Open curves clamp it to the domain; closed curves wrap it.

Example

>>> p = pv.LineSegment((0, 0, 0), (10, 0, 0)).point(0.5)
points(self: pyvcad.pyvcad.Curve, parameters: collections.abc.Sequence[SupportsFloat | SupportsIndex]) → numpy.typing.NDArray[numpy.float64]#

Evaluate the curve at many parameters.

Parameters:

parameters (list[float] | numpy.ndarray) – The parameters.

Example

>>> xyz = curve.points(numpy.linspace(*curve.domain, 50))  # shape (50, 3)
reversed(self: pyvcad.pyvcad.Curve) → pyvcad.pyvcad.Curve#

The same curve traversed from end to start.

Example

>>> backwards = curve.reversed()
rotated(self: pyvcad.pyvcad.Curve, degrees: SupportsFloat | SupportsIndex, axis: object = (0.0, 0.0, 1.0), center: object = (0.0, 0.0, 0.0)) → pyvcad.pyvcad.Curve#

The curve turned about an axis through a centre.

Parameters:
  • degrees (float) – The angle, counter-clockwise looking down the axis.

  • axis (tuple | Vec3) – The rotation axis. Defaults to +Z.

  • center (tuple | Vec3) – A point on the axis. Defaults to the origin.

Example

>>> turned = curve.rotated(90.0)
sample(self: pyvcad.pyvcad.Curve, count: SupportsInt | SupportsIndex) → numpy.typing.NDArray[numpy.float64]#

Points spaced evenly in arc length, as an (N, 3) array (see sample_parameters).

Parameters:

count (int) – The number of points.

Example

>>> xyz = curve.sample(64)
sample_parameters(self: pyvcad.pyvcad.Curve, count: SupportsInt | SupportsIndex) → numpy.typing.NDArray[numpy.float64]#

Parameters spaced evenly in arc length. Open curves include both ends; closed curves do not repeat the start.

Parameters:

count (int) – The number of parameters.

Example

>>> ts = curve.sample_parameters(20)
scaled(self: pyvcad.pyvcad.Curve, factor: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0, 0.0)) → pyvcad.pyvcad.Curve#

The curve scaled uniformly about a centre.

Parameters:
  • factor (float) – The scale factor, positive.

  • center (tuple | Vec3) – The fixed point. Defaults to the origin.

Example

>>> bigger = curve.scaled(2.0)
tangent(self: pyvcad.pyvcad.Curve, t: SupportsFloat | SupportsIndex) → numpy.typing.NDArray[numpy.float64]#

Unit tangent at t.

Parameters:

t (float) – The parameter.

Example

>>> direction = curve.tangent(0.0)
tessellate(self: pyvcad.pyvcad.Curve, tolerance: SupportsFloat | SupportsIndex = 0.001) → numpy.typing.NDArray[numpy.float64]#

Points of a polyline that stays within tolerance of the curve, as an (N, 3) array.

Parameters:

tolerance (float) – Largest allowed distance between the polyline and the curve.

Example

>>> outline = curve.tessellate(0.01)
transformed(self: pyvcad.pyvcad.Curve, matrix: object) → pyvcad.pyvcad.Curve#

The curve with every point mapped by an affine transform; the parameterization and the curve type are kept.

Lines, polylines, Bezier curves and B-splines take any affine transform. Arcs (and curves that contain them) need a similarity: rotations, reflections, a uniform scale and a translation.

Parameters:

matrix (numpy.ndarray) – A 4 x 4 matrix applied to points as matrix @ (x, y, z, 1); the last row must be (0, 0, 0, 1).

Example

>>> import numpy as np
>>> shifted = curve.transformed(np.array([[1, 0, 0, 5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]))
translated(self: pyvcad.pyvcad.Curve, offset: object) → pyvcad.pyvcad.Curve#

The curve moved by an offset.

Parameters:

offset (tuple | Vec3) – The translation; 2D offsets get z = 0.

Example

>>> moved = curve.translated((10, 0))
property type_name#

The curve type, such as ‘BSplineCurve’.

class pyvcad.CurveHit#

The point on a curve closest to a query point, from Curve.closest_point().

property distance#

Distance from the query point to the curve.

property parameter#

Curve parameter of the closest point.

property point#

The closest point as a NumPy array of shape (3,).

class pyvcad.LineSegment#

A straight line segment between two points, with t in [0, 1]. Exact.

__init__(self: pyvcad.pyvcad.LineSegment, start: object, end: object) → None#

Create a line segment.

Parameters:
  • start (tuple | Vec3) – The point at t = 0.

  • end (tuple | Vec3) – The point at t = 1.

Example

>>> import pyvcad as pv
>>> segment = pv.LineSegment((0, 0, 0), (10, 0, 0))
property end#

The point at t = 1.

property start#

The point at t = 0.

class pyvcad.PolylineCurve#

A chain of straight segments through a list of points, open or closed.

The parameter runs over [0, n] for n segments: t = i + s lies on segment i at fraction s. Closest points and lengths are exact; a tree over the segments keeps queries fast for long polylines.

__init__(self: pyvcad.pyvcad.PolylineCurve, points: object, closed: bool = False) → None#

Create a polyline.

Parameters:
  • points (numpy.ndarray | list) – (N, 2) or (N, 3) points; at least 2 (3 when closed).

  • closed (bool) – Join the last point back to the first. A repeated first point is dropped.

Example

>>> import pyvcad as pv
>>> square = pv.PolylineCurve([(0, 0), (10, 0), (10, 10), (0, 10)], closed=True)
property segment_count#

The number of segments.

property vertices#

The vertices as an (N, 3) array (a closed polyline does not repeat the first).

class pyvcad.ArcCurve#

A circular arc or full circle, with t in [0, 1]. Exact.

Angles are in degrees, measured in the arc’s plane from world +X projected into the plane (world +Y when the normal is along X), counter-clockwise about the normal. With the default normal +Z they are ordinary polar angles in the XY plane. A negative sweep runs clockwise; a 360 degree sweep is a closed circle.

__init__(self: pyvcad.pyvcad.ArcCurve, center: object, radius: SupportsFloat | SupportsIndex, start_angle: SupportsFloat | SupportsIndex, end_angle: SupportsFloat | SupportsIndex, normal: object = (0.0, 0.0, 1.0)) → None#

Create an arc.

Parameters:
  • center (tuple | Vec3) – Centre of the circle.

  • radius (float) – Radius, positive.

  • start_angle (float) – Angle at t = 0, in degrees.

  • end_angle (float) – Angle at t = 1, in degrees; abs(end - start) in (0, 360].

  • normal (tuple | Vec3) – Plane normal. Defaults to +Z.

Example

>>> import pyvcad as pv
>>> quarter = pv.ArcCurve((0, 0, 0), 5.0, 0.0, 90.0)
property center#

The centre.

static circle(center: object, radius: SupportsFloat | SupportsIndex, normal: object = (0.0, 0.0, 1.0)) → pyvcad.pyvcad.ArcCurve#

Create a closed circle starting at angle 0.

Parameters:
  • center (tuple | Vec3) – The centre.

  • radius (float) – The radius, positive.

  • normal (tuple | Vec3) – Plane normal. Defaults to +Z.

Example

>>> ring = pv.ArcCurve.circle((0, 0, 0), 10.0)
property end_angle#

Angle at t = 1, in degrees.

static from_ends(start: object, end: object, radius: SupportsFloat | SupportsIndex, counter_clockwise: bool = True, large: bool = False, normal: object = (0.0, 0.0, 1.0)) → pyvcad.pyvcad.ArcCurve#

The arc of a given radius between two points.

Four arcs of one radius join two points; counter_clockwise (about the normal) and large (longer than a half circle) pick one.

Parameters:
  • start (tuple | Vec3) – The first point (t = 0).

  • end (tuple | Vec3) – The last point (t = 1).

  • radius (float) – The radius; at least half the distance between the points.

  • counter_clockwise (bool) – The turning direction. Defaults to True.

  • large (bool) – True for the arc longer than a half circle. Defaults to False.

  • normal (tuple | Vec3) – The plane normal. Defaults to +Z.

Example

>>> bulge = pv.ArcCurve.from_ends((0, 0), (10, 0), 8.0, counter_clockwise=False)
static from_tangent(start: object, direction: object, end: object) → pyvcad.pyvcad.ArcCurve#

The arc that leaves start along direction and ends at end.

Its start tangent is direction, so it continues a line or curve without a kink.

Parameters:
  • start (tuple | Vec3) – The first point (t = 0).

  • direction (tuple | Vec3) – The tangent at the start.

  • end (tuple | Vec3) – The last point (t = 1); not straight ahead.

Example

>>> hook = pv.ArcCurve.from_tangent((10, 0), (1, 0), (10, 8))
property normal#

The unit plane normal.

property radius#

The radius.

property start_angle#

Angle at t = 0, in degrees.

static through(start: object, middle: object, end: object) → pyvcad.pyvcad.ArcCurve#

The arc from start through middle to end.

The arc lies in the plane of the three points, with its normal on the side of positive z (for points in the XY plane, +Z), so a clockwise arc there has a negative sweep.

Parameters:
  • start (tuple | Vec3) – The first point (t = 0).

  • middle (tuple | Vec3) – A point the arc passes through between the ends.

  • end (tuple | Vec3) – The last point (t = 1).

Example

>>> import pyvcad as pv
>>> cap = pv.ArcCurve.through((10, 0), (15, 5), (10, 10))
class pyvcad.BezierCurve#

A polynomial Bezier curve of any degree (cubic is the usual case), with t in [0, 1].

The curve is closed when its first and last control points coincide.

__init__(self: pyvcad.pyvcad.BezierCurve, control_points: object) → None#

Create a Bezier curve; the degree is the number of control points minus 1.

Parameters:

control_points (numpy.ndarray | list) – (N, 2) or (N, 3) control points, at least 2.

Example

>>> import pyvcad as pv
>>> s_curve = pv.BezierCurve([(0, 0), (5, 10), (10, -10), (15, 0)])
property control_points#

The control points as an (N, 3) array.

property degree#

The polynomial degree.

class pyvcad.BSplineCurve#

A non-rational B-spline curve of any degree, open (clamped) or closed (periodic).

Open curves default to clamped uniform knots on [0, 1], so they start and end at the first and last control points. Closed curves reuse their first degree control points after the last one. Use BSplineCurve.interpolate() to pass through given points.

__init__(self: pyvcad.pyvcad.BSplineCurve, control_points: object, degree: SupportsInt | SupportsIndex = 3, knots: collections.abc.Sequence[SupportsFloat | SupportsIndex] = [], closed: bool = False) → None#

Create a B-spline from control points.

Parameters:
  • control_points (numpy.ndarray | list) – (N, 2) or (N, 3) control points. Open curves need at least degree + 1; closed curves at least max(3, degree + 1), without repeating the first at the end.

  • degree (int) – The degree, at least 1. Defaults to 3.

  • knots (list[float]) – Optional knots: N + degree + 1 values (open) or N + 2 * degree + 1 values that repeat with the period of the domain (closed). Empty uses uniform knots.

  • closed (bool) – True for a closed, periodic curve.

Example

>>> import pyvcad as pv
>>> spline = pv.BSplineCurve([(0, 0), (5, 8), (10, -4), (15, 6), (20, 0)], degree=3)
property control_points#

The control points as an (N, 3) array (without the periodic wrap).

property degree#

The polynomial degree.

static interpolate(points: object, degree: SupportsInt | SupportsIndex = 3, closed: bool = False) → pyvcad.pyvcad.BSplineCurve#

Create a B-spline that passes through the given points.

Parameters follow chord length. Open curves start and end at the first and last point. Closed curves are periodic; for odd degrees they start at the first point. The degree is lowered when there are too few points for it.

Parameters:
  • points (numpy.ndarray | list) – (N, 2) or (N, 3) points; consecutive points must differ. Closed curves need at least 3 and may repeat the first point at the end.

  • degree (int) – The degree, at least 1. Defaults to 3.

  • closed (bool) – True for a closed, periodic curve.

Example

>>> path = pv.BSplineCurve.interpolate([(0, 0, 0), (10, 5, 2), (20, 0, 4)])
property knots#

The knot vector in use.

class pyvcad.CompositeCurve#

A chain of curves joined end to start, with t in [0, n] for n curves.

Curve i covers [i, i + 1]. Each curve must start where the previous one ends, within 1e-6 times the largest bounding-box diagonal of the member curves; the chain is closed when it returns to its start within the same tolerance. Corners are allowed where curves meet.

__init__(self: pyvcad.pyvcad.CompositeCurve, curves: collections.abc.Sequence[pyvcad.pyvcad.Curve]) → None#

Create a composite curve.

Parameters:

curves (list[Curve]) – The curves in order, at least one.

Example

>>> import pyvcad as pv
>>> path = pv.CompositeCurve([pv.LineSegment((0, 0), (10, 0)), pv.ArcCurve((10, 5, 0), 5.0, -90, 90)])
property curves#

The member curves.

class pyvcad.TrimmedCurve#

The part of another curve between two of its parameters (the parameter is the base curve’s own).

On a closed base curve the interval may run past the end and wrap around the seam. Profile booleans use trimmed curves to keep the exact pieces of their inputs.

__init__(self: pyvcad.pyvcad.TrimmedCurve, base: pyvcad.pyvcad.Curve, start: SupportsFloat | SupportsIndex, end: SupportsFloat | SupportsIndex) → None#

Trim a curve.

Parameters:
  • base (Curve) – The curve to trim.

  • start (float) – First parameter, in the base curve’s domain.

  • end (float) – Last parameter, greater than start (up to one period past the end of a closed base).

Example

>>> import pyvcad as pv
>>> quarter = pv.TrimmedCurve(pv.ArcCurve.circle((0, 0, 0), 5.0), 0.0, 0.25)
property base#

The base curve.

Frames#

class pyvcad.CurveFrames#

Rotation-minimizing frames along a curve.

The frames turn as little as possible about the tangent, so a profile carried along the curve does not twist on its own (double-reflection method). On a closed curve the leftover rotation after one loop (the holonomy) is spread evenly in arc length, so the frames match at the seam.

Example

>>> frames = pv.CurveFrames(curve, up=(0, 0, 1))
>>> frame = frames.frame(0.5)
>>> frame.normal, frame.binormal
__init__(self: pyvcad.pyvcad.CurveFrames, curve: pyvcad.pyvcad.Curve, up: object = None) → None#

Compute rotation-minimizing frames.

Parameters:
  • curve (Curve) – The curve; it must have positive length.

  • up (tuple | Vec3 | None) – Initial binormal direction. None uses the plane normal of a planar curve, otherwise the world axis least aligned with the start tangent.

Example

>>> frames = pv.CurveFrames(pv.ArcCurve.circle((0, 0, 0), 5.0))
frame(self: pyvcad.pyvcad.CurveFrames, t: SupportsFloat | SupportsIndex) → pyvcad.pyvcad.CurveFrame#

The frame at parameter t.

Parameters:

t (float) – The curve parameter.

Example

>>> frames.frame(0.0).tangent
property holonomy#

Rotation spread along a closed curve to close the frames, in degrees (0 for open curves).

class pyvcad.CurveFrame#

An orthonormal frame on a curve, from CurveFrames.frame(). normal x binormal = tangent.

property binormal#

the profile v axis in a sweep.

Type:

Unit binormal

property normal#

the profile u axis in a sweep.

Type:

Unit normal

property origin#

The curve point.

property tangent#

Unit tangent.

Profiles#

class pyvcad.Profile#

An immutable closed region of the XY plane, bounded by closed curves, possibly with holes.

A profile is the cross-section that Extrude, Revolve and Sweep turn into solids. Loops may be any closed curve at z = 0 and must not cross themselves or each other. Nesting decides what is inside: a loop inside an even number of other loops is an outline, otherwise a hole.

Signed distance is exact, computed from the curves (negative inside). Booleans (|, &, -) run on tessellations for robust topology and then rebuild the result from pieces of the input curves, so arcs and splines stay exact. Round offsets of profiles made of straight edges and arcs are exact; other offsets are polygons within tolerance (default min(0.001, 1e-4 x size)). fillet() rounds corners with exact arcs.

Example

>>> import pyvcad as pv
>>> plate = pv.Profile.rounded_rectangle(40, 20, 3) - pv.Profile.circle(4, center=(10, 0))
>>> plate.area()
class Join#

How Profile.offset() joins edges at corners.

Members:

round : Circular arcs: the true offset.

miter : Edges extended until they meet (limited to twice the distance).

square : Corners cut square at the offset distance.

__init__(self: pyvcad.pyvcad.Profile.Join, value: SupportsInt | SupportsIndex) → None#
miter = <Join.miter: 1>#
Profile.Join.name -> str
round = <Join.round: 0>#
square = <Join.square: 2>#
property value#
__init__(*args, **kwargs)#

Overloaded function.

  1. __init__(self: pyvcad.pyvcad.Profile, loops: collections.abc.Sequence[pyvcad.pyvcad.Curve]) -> None

Create a profile from closed curves in the XY plane.

Parameters:

loops (list[Curve]) – Closed curves at z = 0. Any winding; nesting decides outlines and holes. Loops may touch but must not cross themselves or each other (raises RuntimeError); join overlapping shapes with the profile booleans instead.

Example

>>> import pyvcad as pv
>>> ring = pv.Profile([pv.ArcCurve.circle((0, 0, 0), 10.0), pv.ArcCurve.circle((0, 0, 0), 6.0)])
  1. __init__(self: pyvcad.pyvcad.Profile, polygon: pyvcad.pyvcad.Polygon2) -> None

Create a profile from a Polygon2 and its holes.

Parameters:

polygon (Polygon2) – The polygon.

Example

>>> profile = pv.Profile(polygon2)
area(self: pyvcad.pyvcad.Profile) → float#

The enclosed area, holes subtracted.

bounding_box(self: pyvcad.pyvcad.Profile) → tuple[numpy.typing.NDArray[numpy.float64], numpy.typing.NDArray[numpy.float64]]#

The box (minimum, maximum) containing the profile, as two arrays of shape (2,).

chamfer(self: pyvcad.pyvcad.Profile, distance: SupportsFloat | SupportsIndex, at: object = None) → pyvcad.pyvcad.Profile#

The profile with corners cut by straight edges.

Each chosen corner between straight edges and arcs is cut between the points distance from it along each side (arc length on arcs). A distance too large for the sides is reduced.

Parameters:
  • distance (float) – The distance along each side, positive.

  • at (list[tuple] | None) – Points picking the corners to cut (see fillet()); None cuts every corner.

Example

>>> bevelled = pv.Profile.rectangle(20, 10).chamfer(2.0)
static circle(radius: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0)) → pyvcad.pyvcad.Profile#

A circle.

Parameters:
  • radius (float) – The radius.

  • center (tuple) – The centre (x, y).

Example

>>> disc = pv.Profile.circle(5.0)
closest_point(self: pyvcad.pyvcad.Profile, point: object) → pyvcad.pyvcad.ProfileHit#

The nearest boundary point, with the loop, curve parameter and signed distance.

Parameters:

point (tuple) – The point (x, y).

Example

>>> hit = plate.closest_point((0, 0))
contains(self: pyvcad.pyvcad.Profile, point: object) → bool#

True when the point is inside or on the boundary.

Parameters:

point (tuple) – The point (x, y).

Example

>>> plate.contains((0, 0))
corners(self: pyvcad.pyvcad.Profile, min_angle: SupportsFloat | SupportsIndex = 0.0) → numpy.typing.NDArray[numpy.float64]#

The corner points: where a loop’s tangent jumps, in loop order.

Parameters:

min_angle (float) – Only corners where the tangent turns by more than this many degrees count. A polygon approximation of curves (an offset can be one) has a slight corner at every vertex; a few degrees skips those.

Returns:

An (N, 2) array. Pick rows with ordinary Python to choose corners for fillet() and chamfer().

Return type:

numpy.ndarray

Example

>>> pv.Profile.rectangle(4, 2).corners().shape  # (4, 2)
static cross_section(node: pyvcad.pyvcad.Node, z: SupportsFloat | SupportsIndex, voxel_size: SupportsFloat | SupportsIndex) → pyvcad.pyvcad.Profile#

The cross-section of a node at height z, traced with marching squares.

The node is cloned and prepared at voxel_size, which is also the marching-squares cell size. The loops are polylines on the node’s surface, accurate to about voxel_size.

Parameters:
  • node (Node) – The node.

  • z (float) – Height of the section.

  • voxel_size (float) – Sampling resolution in mm (the marching-squares cell size).

Example

>>> section = pv.Profile.cross_section(pv.Sphere(pv.Vec3(0, 0, 0), 10.0), 5.0, 0.1)
difference(self: pyvcad.pyvcad.Profile, other: pyvcad.pyvcad.Profile, tolerance: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

This profile minus another (also the - operator).

Parameters:
  • other (Profile) – The profile to remove.

  • tolerance (float) – Tessellation tolerance for the topology; 0 uses the default.

Example

>>> plate = pv.Profile.rectangle(40, 20) - pv.Profile.circle(4)
fillet(self: pyvcad.pyvcad.Profile, radius: SupportsFloat | SupportsIndex, at: object = None) → pyvcad.pyvcad.Profile#

The profile with corners rounded by exact tangent arcs.

Rounds corners between straight edges and circular arcs, convex and concave. A radius too large for the neighbouring pieces is reduced at that corner. Corners next to Bezier or spline pieces are kept (round_convex() and round_concave() round any corner).

Parameters:
  • radius (float) – The fillet radius, positive.

  • at (list[tuple] | None) – Points picking the corners to round, each its nearest corner (see corners()); None rounds every corner.

Example

>>> soft = pv.Profile.regular_polygon(6, 10.0).fillet(2.0)
>>> top_only = plate.fillet(3.0, at=[c for c in plate.corners() if c[1] > 0])
static from_curves(curves: collections.abc.Sequence[pyvcad.pyvcad.Curve], tolerance: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

Build a profile from loose curves by chaining them end to end into closed loops.

Closed curves are loops on their own. Open curves are joined where their ends lie within tolerance (either may be reversed); small gaps are bridged with a straight segment. Use it for pieces that come in any order and direction, such as an imported drawing.

Parameters:
  • curves (list[Curve]) – The curves, in the XY plane.

  • tolerance (float) – The largest gap to close; 0 uses 1e-6 of the curves’ overall size.

Example

>>> import pyvcad as pv
>>> pieces = [pv.LineSegment((0, 0), (4, 0)), pv.LineSegment((0, 3), (4, 0)), pv.LineSegment((0, 3), (0, 0))]
>>> triangle = pv.Profile.from_curves(pieces)
intersection(self: pyvcad.pyvcad.Profile, other: pyvcad.pyvcad.Profile, tolerance: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

The intersection with another profile (also the & operator).

Parameters:
  • other (Profile) – The other profile.

  • tolerance (float) – Tessellation tolerance for the topology; 0 uses the default.

Example

>>> lens = disc.intersection(other_disc)
property is_empty#

True for the empty profile.

is_hole(self: pyvcad.pyvcad.Profile, index: SupportsInt | SupportsIndex) → bool#

True when loop index is a hole.

Parameters:

index (int) – The loop index.

property loops#

outlines counter-clockwise, holes clockwise.

Type:

The boundary loops

mirrored(self: pyvcad.pyvcad.Profile, point: object = (0.0, 0.0), direction: object = (0.0, 1.0)) → pyvcad.pyvcad.Profile#

The profile reflected across a line.

Parameters:
  • point (tuple) – A point on the mirror line. Defaults to the origin.

  • direction (tuple) – The mirror line’s direction. Defaults to (0, 1), the Y axis, which mirrors left to right.

Example

>>> whole = half | half.mirrored()
offset(self: pyvcad.pyvcad.Profile, distance: SupportsFloat | SupportsIndex, join: pyvcad.pyvcad.Profile.Join = <Join.round: 0>, tolerance: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

The profile grown (positive distance) or shrunk (negative distance).

Round offsets of profiles made of straight edges and arcs are built exactly (and used when they agree with the polygon offset’s loops and area); other offsets are polygons within the tolerance.

Parameters:
  • distance (float) – The offset distance.

  • join (Profile.Join) – How corners are joined: round (default), miter or square.

  • tolerance (float) – Tessellation tolerance; 0 uses the default.

Example

>>> wall = plate - plate.offset(-2.0)
static polygon(points: object) → pyvcad.pyvcad.Profile#

A simple polygon through the given points (either winding; do not repeat the first point).

Parameters:

points (numpy.ndarray | list) – (N, 2) points.

Example

>>> ell = pv.Profile.polygon([(0, 0), (4, 0), (4, 1), (1, 1), (1, 4), (0, 4)])
static rectangle(width: SupportsFloat | SupportsIndex, height: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0)) → pyvcad.pyvcad.Profile#

An axis-aligned rectangle.

Parameters:
  • width (float) – Size along x.

  • height (float) – Size along y.

  • center (tuple) – The centre (x, y).

Example

>>> bar = pv.Profile.rectangle(20.0, 4.0)
static regular_polygon(sides: SupportsInt | SupportsIndex, radius: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0), rotation: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

A regular polygon.

Parameters:
  • sides (int) – Number of corners, at least 3.

  • radius (float) – Distance from the centre to each corner.

  • center (tuple) – The centre (x, y).

  • rotation (float) – Angle of the first corner in degrees, counter-clockwise from +X.

Example

>>> hexagon = pv.Profile.regular_polygon(6, 10.0)
rotated(self: pyvcad.pyvcad.Profile, degrees: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0)) → pyvcad.pyvcad.Profile#

The profile turned counter-clockwise about a centre.

Parameters:
  • degrees (float) – The angle.

  • center (tuple) – The centre of rotation. Defaults to the origin.

Example

>>> spokes = [bar.rotated(a) for a in range(0, 360, 60)]
round_concave(self: pyvcad.pyvcad.Profile, radius: SupportsFloat | SupportsIndex) → pyvcad.pyvcad.Profile#

Round every concave corner, of any curve type, by growing and reshrinking the profile.

The same as offset(radius).offset(-radius): gaps narrower than 2 * radius close.

Parameters:

radius (float) – The rounding radius, positive.

Example

>>> webbed = star.round_concave(1.0)
round_convex(self: pyvcad.pyvcad.Profile, radius: SupportsFloat | SupportsIndex) → pyvcad.pyvcad.Profile#

Round every convex corner, of any curve type, by shrinking and regrowing the profile.

The same as offset(-radius).offset(radius): features thinner than 2 * radius disappear.

Parameters:

radius (float) – The rounding radius, positive.

Example

>>> pebble = star.round_convex(1.0)
static rounded_rectangle(width: SupportsFloat | SupportsIndex, height: SupportsFloat | SupportsIndex, radius: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0)) → pyvcad.pyvcad.Profile#

An axis-aligned rectangle with rounded corners.

Parameters:
  • width (float) – Size along x.

  • height (float) – Size along y.

  • radius (float) – Corner radius, clamped to half the smaller side.

  • center (tuple) – The centre (x, y).

Example

>>> plate = pv.Profile.rounded_rectangle(40.0, 20.0, 3.0)
scaled(self: pyvcad.pyvcad.Profile, factor: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0)) → pyvcad.pyvcad.Profile#

The profile scaled uniformly about a centre.

Parameters:
  • factor (float) – The scale factor, positive.

  • center (tuple) – The fixed point. Defaults to the origin.

Example

>>> half = profile.scaled(0.5)
signed_distance(self: pyvcad.pyvcad.Profile, point: object) → float#

Exact signed distance to the boundary: negative inside.

Parameters:

point (tuple) – The point (x, y).

Example

>>> pv.Profile.circle(5.0).signed_distance((0, 0))
-5.0
signed_distances(self: pyvcad.pyvcad.Profile, points: object) → numpy.typing.NDArray[numpy.float64]#

Signed distances of many points.

Parameters:

points (numpy.ndarray) – An (N, 2) array of points.

Example

>>> d = profile.signed_distances(numpy.random.rand(100, 2))
property size#

The diagonal of the bounding box.

static slot(length: SupportsFloat | SupportsIndex, width: SupportsFloat | SupportsIndex, center: object = (0.0, 0.0), angle: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

A slot (stadium): two half circles joined by straight sides.

Parameters:
  • length (float) – Overall length, tip to tip; at least the width.

  • width (float) – The width, which is also the diameter of the round ends.

  • center (tuple) – The centre (x, y).

  • angle (float) – Direction of the long axis in degrees, counter-clockwise from +X.

Example

>>> slot = pv.Profile.slot(20.0, 6.0, angle=30.0)
tessellate(self: pyvcad.pyvcad.Profile, tolerance: SupportsFloat | SupportsIndex = 0.0) → list#

Each loop as an (N, 2) array of polygon points within tolerance (0 uses the default).

Parameters:

tolerance (float) – Largest distance between the polygons and the curves.

Example

>>> outlines = plate.tessellate(0.01)
to_polygons(self: pyvcad.pyvcad.Profile, tolerance: SupportsFloat | SupportsIndex = 0.0) → list[pyvcad.pyvcad.Polygon2]#

The profile as Polygon2 outlines, each with its holes.

Parameters:

tolerance (float) – Tessellation tolerance; 0 uses the default.

translated(self: pyvcad.pyvcad.Profile, offset: object) → pyvcad.pyvcad.Profile#

The profile moved by an offset.

Parameters:

offset (tuple) – The translation (dx, dy).

Example

>>> moved = profile.translated((10, 0))
union(self: pyvcad.pyvcad.Profile, other: object, tolerance: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Profile#

The union with another profile or a list of profiles (also the | operator).

Parameters:
  • other (Profile | list[Profile]) – The profiles to add.

  • tolerance (float) – Tessellation tolerance for the topology; 0 uses the default.

Example

>>> both = disc.union(bar)
class pyvcad.ProfileHit#

The boundary point of a profile closest to a query point, from Profile.closest_point().

property distance#

negative inside the profile.

Type:

Signed distance

property loop#

Index of the loop in Profile.loops.

property parameter#

Curve parameter on that loop.

property point#

The closest boundary point as a NumPy array of shape (2,).

Sketches#

Draw profiles and paths step by step with a pen, place them on any plane, and build them from construction geometry. The guide Sketching and Extruding walks through all of these.

class pyvcad.Sketch#

Draw 2D profiles and paths step by step, like a pen: lines, arcs and splines from the current point, with optional fillets and chamfers at the corners.

Each drawing method adds a piece from the pen position and moves the pen to its end, so a path has no gaps. The sketch tracks the current direction (the end tangent of the last piece), which tangent_arc_to() and tangent_arc() continue without a kink. Drawing methods take fillet= or chamfer= to treat the corner at the piece’s end; close() takes them for the corner where the loop closes. Fillets and chamfers work between lines and arcs (not next to Bezier or spline pieces).

close() finishes a loop and move_to() starts a new path: a loop drawn inside another becomes a hole. profile() builds a Profile from the closed loops; curves() and curve() return paths as curves (for example to sweep along). Every drawing method returns the sketch, so calls chain.

Example

>>> import pyvcad as pv
>>> s = pv.Sketch((0, 0))
>>> s.line_to(40, 0).tangent_arc_to(40, 20).line_to(0, 20, fillet=4).close(chamfer=2)
>>> plate = s.profile()
__init__(self: pyvcad.pyvcad.Sketch, start: object = (0.0, 0.0)) → None#

Start a sketch with the pen at a point.

Parameters:

start (tuple) – The first pen position. Defaults to the origin.

Example

>>> s = pv.Sketch((10, 0))
arc_through(self: object, middle: object, end: object, fillet: object = None, chamfer: object = None) → object#

Draw an arc through a point to another.

Parameters:
  • middle (tuple) – A point on the arc.

  • end (tuple) – The end of the arc.

  • fillet (float | None) – Round the corner at the end of this arc with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.arc_through((30, 35), (10, 30))
arc_to(self: object, x: SupportsFloat | SupportsIndex, y: SupportsFloat | SupportsIndex, radius: SupportsFloat | SupportsIndex, counter_clockwise: bool = True, large: bool = False, fillet: object = None, chamfer: object = None) → object#

Draw an arc of a radius to (x, y).

Parameters:
  • x (float) – The end x.

  • y (float) – The end y.

  • radius (float) – The radius; at least half the distance to the end.

  • counter_clockwise (bool) – The turning direction. Defaults to True.

  • large (bool) – True for the arc longer than a half circle. Defaults to False.

  • fillet (float | None) – Round the corner at the end of this arc with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.arc_to(20, 0, 15.0, counter_clockwise=False)
bezier_to(self: object, points: object, fillet: object = None, chamfer: object = None) → object#

Draw a Bezier curve whose first control point is the pen position.

Parameters:
  • points (list[tuple]) – The other control points; the last is the end. Two points give a quadratic, three a cubic.

  • fillet (float | None) – Accepted for symmetry; corners next to Bezier pieces are not treated.

  • chamfer (float | None) – As fillet.

Example

>>> s.bezier_to([(10, 10), (20, -10), (30, 0)])
close(self: object, fillet: object = None, chamfer: object = None) → object#

Close the current path, with a line back to its start if the pen is elsewhere.

Parameters:
  • fillet (float | None) – Round the corner where the loop closes with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.close(fillet=2)
curve(self: pyvcad.pyvcad.Sketch) → pyvcad.pyvcad.Curve#

The sketch’s one path as a curve, with the corners treated (for example a Sweep path).

Example

>>> path = pv.Sketch((0, 0)).line_to(20, 0, fillet=5).line_to(20, 20).curve()
curves(self: pyvcad.pyvcad.Sketch) → list[pyvcad.pyvcad.Curve]#

Every path as one curve, open or closed, with the corners treated, in drawing order.

Example

>>> outline, hole = s.curves()
property direction#

The current direction (unit end tangent of the last piece), or None before the path’s first piece.

line(self: object, dx: SupportsFloat | SupportsIndex = 0.0, dy: SupportsFloat | SupportsIndex = 0.0, fillet: object = None, chamfer: object = None) → object#

Draw a line by an offset from the pen position.

Parameters:
  • dx (float) – The x offset. Defaults to 0.

  • dy (float) – The y offset. Defaults to 0.

  • fillet (float | None) – Round the corner at the end of this line with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.line(dy=20)  # straight up
line_to(self: object, x: SupportsFloat | SupportsIndex, y: SupportsFloat | SupportsIndex, fillet: object = None, chamfer: object = None) → object#

Draw a line to (x, y).

Parameters:
  • x (float) – The end x.

  • y (float) – The end y.

  • fillet (float | None) – Round the corner at the end of this line with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.line_to(40, 0, fillet=3)
move_to(self: object, x: SupportsFloat | SupportsIndex, y: SupportsFloat | SupportsIndex) → object#

Start a new path at (x, y), leaving the current one as it is.

Parameters:
  • x (float) – The new pen x.

  • y (float) – The new pen y.

Example

>>> s.move_to(5, 5)  # then draw a hole
polar(self: object, length: SupportsFloat | SupportsIndex, angle: SupportsFloat | SupportsIndex, fillet: object = None, chamfer: object = None) → object#

Draw a line of a length in a direction.

Parameters:
  • length (float) – The length, positive.

  • angle (float) – The direction in degrees, counter-clockwise from +X.

  • fillet (float | None) – Round the corner at the end of this line with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.polar(10, 60)
property position#

The pen position.

profile(self: pyvcad.pyvcad.Sketch) → pyvcad.pyvcad.Profile#

Build the profile bounded by the closed loops, with the corners treated.

Loops inside other loops become holes. Every path with pieces must be closed.

Example

>>> solid = pv.Extrude(s.profile(), 5.0)
spline_through(self: object, points: object, fillet: object = None, chamfer: object = None) → object#

Draw a cubic spline from the pen position through points.

Parameters:
  • points (list[tuple]) – The points to pass through; the last is the end.

  • fillet (float | None) – Accepted for symmetry; corners next to spline pieces are not treated.

  • chamfer (float | None) – As fillet.

Example

>>> s.spline_through([(10, 5), (20, -5), (30, 0)])
property start#

The start point of the current path.

tangent_arc(self: object, radius: SupportsFloat | SupportsIndex, angle: SupportsFloat | SupportsIndex, fillet: object = None, chamfer: object = None) → object#

Draw an arc of a radius that continues the current direction and turns by an angle.

Parameters:
  • radius (float) – The radius, positive.

  • angle (float) – The turn in degrees; positive turns left, negative right.

  • fillet (float | None) – Round the corner at the end of this arc with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.line(dx=10).tangent_arc(5.0, 90)  # a smooth left turn
tangent_arc_to(self: object, x: SupportsFloat | SupportsIndex, y: SupportsFloat | SupportsIndex, fillet: object = None, chamfer: object = None) → object#

Draw an arc to (x, y) that continues the current direction without a kink.

Parameters:
  • x (float) – The end x.

  • y (float) – The end y; not straight ahead of the pen.

  • fillet (float | None) – Round the corner at the end of this arc with this radius.

  • chamfer (float | None) – Or cut it this far along each side.

Example

>>> s.line_to(40, 0).tangent_arc_to(40, 20)  # a half-round end
class pyvcad.Plane#

A plane to draw a profile on: an origin and right-handed in-plane axes.

A profile’s (x, y) lands at origin + x * x_axis + y * y_axis, and normal = x_axis x y_axis points out of the plane. Extrude and Loft build along the normal; Revolve spins about the plane’s y axis through its origin, so a profile’s x is its distance from that axis.

The standard planes: Plane.xy(z) (x along +X, y along +Y, normal +Z), Plane.xz(y) (x along +X, y along +Z, normal -Y) and Plane.yz(x) (x along +Y, y along +Z, normal +X). Placing a profile with a plane is rigid, so distances stay exact.

Example

>>> import pyvcad as pv
>>> side = pv.Plane.xz()
>>> rib = pv.Extrude(pv.Profile.rectangle(20, 8, center=(0, 4)), 2.0, symmetric=True, plane=side)
__init__(self: pyvcad.pyvcad.Plane, origin: object = (0.0, 0.0, 0.0), normal: object = (0.0, 0.0, 1.0), x_direction: object = None) → None#

Create a plane from an origin and a normal.

Parameters:
  • origin (tuple | Vec3) – Where a profile’s (0, 0) lands. Defaults to the world origin.

  • normal (tuple | Vec3) – The plane normal. Defaults to +Z.

  • x_direction (tuple | Vec3 | None) – The direction of the profile’s x axis, made perpendicular to the normal. None uses world +X projected into the plane (+Y when the normal is along X).

Example

>>> tilted = pv.Plane((0, 0, 10), (0, 1, 1))
property normal#

The unit normal, x_axis x y_axis.

offset(self: pyvcad.pyvcad.Plane, distance: SupportsFloat | SupportsIndex) → pyvcad.pyvcad.Plane#

The same plane moved along its normal.

Parameters:

distance (float) – How far to move it.

Example

>>> higher = pv.Plane.xy().offset(5.0)
property origin#

The origin.

rotated(self: pyvcad.pyvcad.Plane, degrees: SupportsFloat | SupportsIndex, axis: object, center: object = (0.0, 0.0, 0.0)) → pyvcad.pyvcad.Plane#

The same plane turned about an axis through a point.

Parameters:
  • degrees (float) – The angle, counter-clockwise looking down the axis.

  • axis (tuple | Vec3) – The rotation axis.

  • center (tuple | Vec3) – A point on the axis. Defaults to the origin.

Example

>>> angled = pv.Plane.xz().rotated(30.0, (0, 0, 1))
to_local(self: pyvcad.pyvcad.Plane, point: object) → numpy.typing.NDArray[numpy.float64]#

The plane coordinates (x, y, height along the normal) of a world point.

Parameters:

point (tuple | Vec3) – The world point.

Example

>>> x, y, w = plane.to_local((1, 2, 3))
to_world(self: pyvcad.pyvcad.Plane, x: SupportsFloat | SupportsIndex, y: SupportsFloat | SupportsIndex, w: SupportsFloat | SupportsIndex = 0.0) → numpy.typing.NDArray[numpy.float64]#

The world point of profile coordinates (x, y), w above the plane along the normal.

Parameters:
  • x (float) – The profile x.

  • y (float) – The profile y.

  • w (float) – The height along the normal. Defaults to 0.

Example

>>> pv.Plane.xz().to_world(1, 2)  # array([1., 0., 2.])
property x_axis#

The unit direction of the profile’s x axis.

static xy(z: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Plane#

The XY plane at height z: x along +X, y along +Y, normal +Z.

Parameters:

z (float) – The plane’s height. Defaults to 0.

Example

>>> lid = pv.Plane.xy(20.0)
static xz(y: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Plane#

The XZ plane at y: x along +X, y along +Z, normal -Y.

Parameters:

y (float) – The plane’s position along Y. Defaults to 0.

Example

>>> front = pv.Plane.xz()
property y_axis#

The unit direction of the profile’s y axis.

static yz(x: SupportsFloat | SupportsIndex = 0.0) → pyvcad.pyvcad.Plane#

The YZ plane at x: x along +Y, y along +Z, normal +X.

Parameters:

x (float) – The plane’s position along X. Defaults to 0.

Example

>>> right = pv.Plane.yz(15.0)

Construction geometry#

Closed-form intersections, tangents and corner treatments. Lines are infinite lines through two points and circles are a centre and a radius; every function returns all of its solutions, in a stated order, as NumPy arrays.

pyvcad.intersect_lines(a0: object, a1: object, b0: object, b1: object) → list#

The intersection of the line through a0 and a1 with the line through b0 and b1.

The lines are infinite: the points only fix their position and direction.

Parameters:
  • a0 (tuple) – A point on the first line.

  • a1 (tuple) – Another point on the first line.

  • b0 (tuple) – A point on the second line.

  • b1 (tuple) – Another point on the second line.

Returns:

The intersection point, or an empty list for parallel lines.

Return type:

list[numpy.ndarray]

Example

>>> import pyvcad as pv
>>> [p] = pv.intersect_lines((0, 0), (1, 1), (0, 4), (1, 3))  # (2, 2)
pyvcad.intersect_line_circle(a0: object, a1: object, center: object, radius: SupportsFloat | SupportsIndex) → list#

The intersections of the line through a0 and a1 with a circle.

Parameters:
  • a0 (tuple) – A point on the line.

  • a1 (tuple) – Another point on the line.

  • center (tuple) – The circle’s centre.

  • radius (float) – The circle’s radius, positive.

Returns:

Zero, one (tangent) or two points, in order along the line from a0 toward a1.

Return type:

list[numpy.ndarray]

Example

>>> near, far = pv.intersect_line_circle((-10, 1), (10, 1), (0, 0), 5.0)
pyvcad.intersect_circles(center0: object, radius0: SupportsFloat | SupportsIndex, center1: object, radius1: SupportsFloat | SupportsIndex) → list#

The intersections of two circles.

Parameters:
  • center0 (tuple) – The first circle’s centre.

  • radius0 (float) – The first circle’s radius, positive.

  • center1 (tuple) – The second circle’s centre.

  • radius1 (float) – The second circle’s radius, positive.

Returns:

Zero, one (touching) or two points; with two, the one to the left of the line from center0 to center1 comes first.

Return type:

list[numpy.ndarray]

Example

>>> upper, lower = pv.intersect_circles((0, 0), 5.0, (6, 0), 5.0)  # (3, 4) and (3, -4)
pyvcad.tangent_points(point: object, center: object, radius: SupportsFloat | SupportsIndex) → list#

The points where lines from a point touch a circle.

Parameters:
  • point (tuple) – The point the lines start from.

  • center (tuple) – The circle’s centre.

  • radius (float) – The circle’s radius, positive.

Returns:

Two points (the left one first, seen from the point looking at the centre), one when the point is on the circle, none when it is inside.

Return type:

list[numpy.ndarray]

Example

>>> left, right = pv.tangent_points((20, 0), (0, 0), 5.0)
pyvcad.tangent_lines(center0: object, radius0: SupportsFloat | SupportsIndex, center1: object, radius1: SupportsFloat | SupportsIndex) → list#

The lines touching two circles, as pairs of tangent points.

The outer tangents (both circles on the same side of the line) come first, then the inner ones (crossing between the circles). In each kind, the line touching circle 0 on its left (seen from center0 looking at center1) comes first. Overlapping circles have no inner tangents; a circle inside the other has none at all.

Parameters:
  • center0 (tuple) – The first circle’s centre.

  • radius0 (float) – The first circle’s radius, positive.

  • center1 (tuple) – The second circle’s centre.

  • radius1 (float) – The second circle’s radius, positive.

Returns:

(point on circle 0, point on circle 1) for each line.

Return type:

list[tuple[numpy.ndarray, numpy.ndarray]]

Example

>>> (a, b), (c, d), *inner = pv.tangent_lines((0, 0), 10.0, (40, 0), 4.0)
pyvcad.fillet_curves(a: pyvcad.pyvcad.Curve, b: pyvcad.pyvcad.Curve, radius: SupportsFloat | SupportsIndex) → list[pyvcad.pyvcad.Curve]#

Round the corner where curve a ends and curve b starts with an exact tangent arc.

The curves must be lines (LineSegment, PolylineCurve) or arcs in the XY plane, and b must start where a ends. A radius too large for the curves is reduced.

Parameters:
  • a (Curve) – The curve before the corner.

  • b (Curve) – The curve after the corner.

  • radius (float) – The fillet radius, positive.

Returns:

What is left of a, the fillet arc, and what is left of b, in order.

Return type:

list[Curve]

Example

>>> a = pv.LineSegment((0, 0), (10, 0))
>>> b = pv.LineSegment((10, 0), (10, 10))
>>> path = pv.CompositeCurve(pv.fillet_curves(a, b, 2.0))
pyvcad.chamfer_curves(a: pyvcad.pyvcad.Curve, b: pyvcad.pyvcad.Curve, distance: SupportsFloat | SupportsIndex) → list[pyvcad.pyvcad.Curve]#

Cut the corner where curve a ends and curve b starts with a straight edge.

As fillet_curves(), with an edge between the points distance from the corner along each curve.

Parameters:
  • a (Curve) – The curve before the corner.

  • b (Curve) – The curve after the corner.

  • distance (float) – The distance along each curve, positive.

Returns:

What is left of a, the chamfer edge, and what is left of b, in order.

Return type:

list[Curve]

Example

>>> path = pv.CompositeCurve(pv.chamfer_curves(a, b, 1.5))

Curves from data#

Mesh paths (MeshPath.to_curve()), mesh and selection borders (SurfaceMesh.boundary_loops(), MeshSelection.boundary_loops()) and node contours all return ordinary curves. Profile.cross_section() gives the region of a slice instead of its outlines.

pyvcad.contour_curves(node: pyvcad.pyvcad.Node, z: SupportsFloat | SupportsIndex, voxel_size: SupportsFloat | SupportsIndex, iso: SupportsFloat | SupportsIndex = 0.0) → list[pyvcad.pyvcad.Curve]#

Trace the iso-lines of a node on the plane at height z, as curves at that height.

Marching squares on a clone of the node prepared at voxel_size, which is also the cell size. Closed contours come first (closed PolylineCurves, counter-clockwise), then open ones. Accurate to about voxel_size. Use Profile.cross_section() for the region instead of its outlines.

Parameters:
  • node (Node) – The node.

  • z (float) – Height of the plane.

  • voxel_size (float) – Sampling resolution in mm (the marching-squares cell size).

  • iso (float) – The level to trace; 0 is the surface, positive values trace outside it.

Example

>>> import pyvcad as pv
>>> outlines = pv.contour_curves(part, 5.0, 0.1)
>>> rims = pv.Union([pv.Tube(c, 0.5) for c in outlines])