# Copyright (c) 2026, pinkfish
#
# Licensed under the BSD 2-Clause License. See the LICENSE file in the project
# root for the full license text.
# SPDX-License-Identifier: BSD-2-Clause
# LibFile: pybosl2/shapes3d/cylinder.py
# FileSummary: Cylinders, cones, shear cylinders and pipe shapes.
# DocCategory: Foundational
# FileGroup: BOSL2
"""Cylinders, cones, shear cylinders and pipe shapes."""
from __future__ import annotations
import math
from typing import TYPE_CHECKING
from pybosl2._backend import backend_only
from pybosl2._edges_lang import Anchor
from pybosl2._native import native
if TYPE_CHECKING:
from collections.abc import Sequence
from pybosl2.textures import TextureType
from pybosl2._helpers import frag_count as _frag_count
from pybosl2._helpers import pick_radius as _pick_radius
from pybosl2._helpers import quantup
from pybosl2.constants import BOTTOM, CENTER
from pybosl2.exceptions import Bosl2ValueError
from pybosl2.groups import resolve_center_anchor
# Import base class and helper functions from shapes3d.base
from .base import (
Bosl2Solid,
_anchor_offset_cyl,
_finish3,
_ocylinder,
_osphere,
)
if TYPE_CHECKING: # real stub-typed imports for the checker (identical to pre-lazy)
from pythonscad import cube as _ocube
from pythonscad import cylinder as _ocylinder_native
from pythonscad import hull as _ohull
from pythonscad import minkowski as _ominkowski
from pythonscad import polyhedron as _opolyhedron
from pythonscad import rotate_extrude as _orotate_extrude
from pythonscad import sphere as _osphere_native
from pythonscad import textmetrics as _otextmetrics
else:
_ocube = native("cube")
_ocylinder_native = native("cylinder")
_ohull = native("hull")
_ominkowski = native("minkowski")
_opolyhedron = native("polyhedron")
_orotate_extrude = native("rotate_extrude")
_osphere_native = native("sphere")
_otextmetrics = native("textmetrics")
# ---------------------------------------------------------------------------
# Section: Cylinders
# ---------------------------------------------------------------------------
[docs]
@backend_only("csg", neutral="pybosl2.solid.cylinder")
def cylinder(
height: float | None = None,
radius: float | None = None,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
circumscribe: bool = False,
realign: bool = False,
shift: Sequence[float] = (0, 0),
anchor: Anchor | Sequence[float] | None = None,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
chamfer_angle: float | None = None,
chamfer_angle1: float | None = None,
chamfer_angle2: float | None = None,
from_end: bool = False,
from_end1: bool | None = None,
from_end2: bool | None = None,
extra: float = 0.0,
extra1: float | None = None,
extra2: float | None = None,
teardrop: float | bool = False,
clip_angle: float = 90.0,
texture: str | TextureType | None = None,
tex_size: float | Sequence[float] | None = None,
tex_reps: int | Sequence[int] | None = None,
tex_depth: float = 1.0,
tex_inset: float | bool = False,
) -> Bosl2Solid:
"""Return a cylinder with optional chamfering/rounding of its end rims, built with.
cube()/cylinder()/sphere()/rotate_extrude().
Positive rounding is built as a minkowski() of a shorter cylinder with a sphere at each
rounded end (an inset fillet, not an outward bulge), matching BOSL2's own rounded-end
geometry. Chamfering builds the exact half-profile (with the requested bevel at each end)
and revolves it with rotate_extrude().
Note: ``texture=`` (VNF surface texturing) is not supported by this pure-Python port.
Args:
length: length of the cylinder along its axis (default 1)
height: length of the cylinder along its axis (default 1)
radius: radius of the cylinder (default 1)
diameter: diameter of the cylinder
radius1: radius of the negative end of the cylinder
radius2: radius of the positive end of the cylinder
diameter1: diameter of the negative end of the cylinder
diameter2: diameter of the positive end of the cylinder
center: if given, overrides anchor (True -> CENTER, False -> BOTTOM)
chamfer: chamfer size on the end rims (overall/negative/positive)
chamfer1: chamfer size on the end rims (overall/negative/positive)
chamfer2: chamfer size on the end rims (overall/negative/positive)
rounding: rounding radius on the end rims (overall/negative/positive)
rounding1: rounding radius on the end rims (overall/negative/positive)
rounding2: rounding radius on the end rims (overall/negative/positive)
circumscribe: circumscribe rather than inscribe the given radius (default False)
realign: shift point alignment (default False)
shift: X/Y offset for the positive end (shear) (default [0,0])
anchor: anchor point (default BOTTOM if center=False, otherwise CENTER)
spin: Z-axis rotation in degrees after anchor (default 0)
orient: direction to rotate the top towards, after spin (default UP)
fn: arc smoothness overrides. Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back
out to fa/fs.
fa: arc smoothness overrides. Omitted, the ambient ``use_defaults(fa=...)`` value applies.
fs: arc smoothness overrides. Omitted, the ambient ``use_defaults(fs=...)`` value applies.
chamfer_angle: chamfer angle in degrees away from ends
chamfer_angle1: chamfer angle in degrees away from ends
chamfer_angle2: chamfer angle in degrees away from ends
from_end: measure chamfer along conic face (default False)
from_end1: measure chamfer along conic face (default False)
from_end2: measure chamfer along conic face (default False)
extra: add extra height at ends (invisible to anchoring)
extra1: add extra height at ends (invisible to anchoring)
extra2: add extra height at ends (invisible to anchoring)
teardrop: limit rounding angle from horizontal
clip_angle: clip rounding arc at bottom of cylinder
texture: named texture to apply to cylinder side surface
tex_size: size of texture tile
tex_reps: number of texture repetitions
tex_depth: depth of the texture
tex_inset: inset the texture
Examples:
A basic cylinder:
.. pythonscad-example::
from pybosl2.solid import cylinder
cylinder(height=30, radius=10).show()
A cylinder with chamfered ends:
.. pythonscad-example::
from pybosl2.solid import cylinder
cylinder(height=40, radius=15, chamfer=2).show()
A cylinder with rounded ends:
.. pythonscad-example::
from pybosl2.solid import cylinder
cylinder(height=30, radius=12, rounding=2).show()
"""
return cyl(
height=height,
radius=radius,
center=center,
length=length,
radius1=radius1,
radius2=radius2,
diameter=diameter,
diameter1=diameter1,
diameter2=diameter2,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
circumscribe=circumscribe,
realign=realign,
shift=list(shift),
anchor=anchor,
spin=spin,
orient=orient,
fn=fn,
fa=fa,
fs=fs,
chamfer_angle=chamfer_angle,
chamfer_angle1=chamfer_angle1,
chamfer_angle2=chamfer_angle2,
from_end=from_end,
from_end1=from_end1,
from_end2=from_end2,
extra=extra,
extra1=extra1,
extra2=extra2,
teardrop=teardrop,
clip_angle=clip_angle,
texture=texture,
tex_size=tex_size,
tex_reps=tex_reps,
tex_depth=tex_depth,
tex_inset=tex_inset,
)
@backend_only("csg", neutral="pybosl2.solid.cyl")
def _textured_cyl(
length: float,
radius1: float,
radius2: float,
tex: "str | TextureType",
*,
tex_size: "float | Sequence[float] | None",
tex_reps: "int | Sequence[int] | None",
tex_depth: float,
tex_inset: float | bool,
anchor: "Anchor | Sequence[float]",
spin: float,
orient: "Anchor | Sequence[float]",
fn: int | None,
fa: float | None,
fs: float | None,
) -> Bosl2Solid:
"""Build a cylinder whose side carries *tex*, as a polyhedron (SPEC S-34, S-35).
The mesh is built in pure Python (:func:`~pybosl2.textures.textured_cylinder_vnf`) and crosses
to geometry once, at the end. When neither *tex_size* nor *tex_reps* is given the repeats are
derived from the facet count the cylinder would have had, so a textured cylinder is as smooth
as the plain one it replaces.
Args:
length: Height of the cylinder.
radius1: Radius at the bottom.
radius2: Radius at the top.
tex: The texture, by name or already built.
tex_size: Size of one tile in millimetres.
tex_reps: Repeat counts, instead of *tex_size*.
tex_depth: How far the texture displaces the surface.
tex_inset: How far the surface is sunk before the texture is added.
anchor: Anchor point.
spin: Z-axis rotation in degrees after anchor.
orient: Direction to rotate the top towards, after spin.
fn: Fixed fragment count, used to derive the repeats when none are given.
fa: Minimum fragment angle in degrees.
fs: Minimum fragment size in millimetres.
Returns:
The textured cylinder.
"""
from pybosl2.textures import default_tex_reps, textured_cylinder_vnf
if tex_size is None and tex_reps is None:
tex_reps = default_tex_reps(length, radius1, radius2)
mesh = textured_cylinder_vnf(
length,
radius1,
radius2,
tex,
tex_size=tex_size,
tex_reps=tex_reps,
tex_depth=tex_depth,
tex_inset=tex_inset,
fn=fn,
fa=fa,
fs=fs,
)
solid = mesh.polyhedron()
offset = _anchor_offset_cyl(radius1, radius2, length, anchor)
return _finish3(Bosl2Solid._unwrap(solid), offset, spin, orient)
def _cyl_anchor(center: bool | None, anchor: "Anchor | Sequence[float] | None") -> "Anchor | Sequence[float]":
"""Resolve `center=`/`anchor=` for the cylinder family, which centres when given neither.
Args:
center: The `center=` shorthand as passed.
anchor: The anchor as passed, or ``None``.
Returns:
The anchor to place with -- ``CENTER`` when the caller named neither (SPEC D-4).
"""
resolved = resolve_center_anchor(center=center, anchor=anchor, centred=Anchor.CENTER, uncentred=BOTTOM)
return CENTER if resolved is None else resolved
[docs]
def cyl(
height: float | None = None,
radius: float | None = None,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
circumscribe: bool = False,
realign: bool = False,
shift: Sequence[float] = (0, 0),
anchor: Anchor | Sequence[float] | None = None,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
# Additional missing args
chamfer_angle: float | None = None,
chamfer_angle1: float | None = None,
chamfer_angle2: float | None = None,
from_end: bool = False,
from_end1: bool | None = None,
from_end2: bool | None = None,
extra: float = 0.0,
extra1: float | None = None,
extra2: float | None = None,
teardrop: float | bool = False,
clip_angle: float = 90.0,
texture: str | TextureType | None = None,
tex_size: float | Sequence[float] | None = None,
tex_reps: int | Sequence[int] | None = None,
tex_depth: float = 1.0,
tex_inset: float | bool = False,
) -> Bosl2Solid:
"""Return a cylinder with optional chamfering/rounding of its end rims, built with.
cube()/cylinder()/sphere()/rotate_extrude().
Positive rounding is built as a minkowski() of a shorter cylinder with a sphere at each
rounded end (an inset fillet, not an outward bulge), matching BOSL2's own rounded-end
geometry. Chamfering builds the exact half-profile (with the requested bevel at each end)
and revolves it with rotate_extrude().
Note: `texture=` (VNF surface texturing) is not supported by this pure-Python port.
Args:
length: length of the cylinder along its axis (default 1)
height: length of the cylinder along its axis (default 1)
radius: radius of the cylinder (default 1)
diameter: diameter of the cylinder
radius1: radius of the negative end of the cylinder
radius2: radius of the positive end of the cylinder
diameter1: diameter of the negative end of the cylinder
diameter2: diameter of the positive end of the cylinder
center: if given, overrides anchor (True -> CENTER, False -> BOTTOM)
chamfer: chamfer size on the end rims (overall/negative/positive)
chamfer1: chamfer size on the end rims (overall/negative/positive)
chamfer2: chamfer size on the end rims (overall/negative/positive)
rounding: rounding radius on the end rims (overall/negative/positive)
rounding1: rounding radius on the end rims (overall/negative/positive)
rounding2: rounding radius on the end rims (overall/negative/positive)
circumscribe: circumscribe rather than inscribe the given radius (default False)
realign: shift point alignment (default False)
shift: X/Y offset for the positive end (shear) (default [0,0])
anchor: anchor point (default CENTER)
spin: Z-axis rotation in degrees after anchor (default 0)
orient: direction to rotate the top towards, after spin (default UP)
fn: arc smoothness overrides. Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back
out to fa/fs.
fa: arc smoothness overrides. Omitted, the ambient ``use_defaults(fa=...)`` value applies.
fs: arc smoothness overrides. Omitted, the ambient ``use_defaults(fs=...)`` value applies.
chamfer_angle: chamfer angle in degrees away from ends
chamfer_angle1: chamfer angle in degrees away from ends
chamfer_angle2: chamfer angle in degrees away from ends
from_end: measure chamfer along conic face (default False)
from_end1: measure chamfer along conic face (default False)
from_end2: measure chamfer along conic face (default False)
extra: add extra height at ends (invisible to anchoring)
extra1: add extra height at ends (invisible to anchoring)
extra2: add extra height at ends (invisible to anchoring)
teardrop: limit rounding angle from horizontal
clip_angle: clip rounding arc at bottom of cylinder
texture: named texture to apply to cylinder side surface
tex_size: size of texture tile
tex_reps: number of texture repetitions
tex_depth: depth of the texture
tex_inset: inset the texture
Examples:
A basic cylinder:
.. pythonscad-example::
from pybosl2 import cyl
shape = cyl(radius=10, height=30)
shape.show()
A cylinder with chamfered ends:
.. pythonscad-example::
from pybosl2 import cyl
shape = cyl(radius=15, height=40, chamfer=2)
shape.show()
A cylinder with rounded ends:
.. pythonscad-example::
from pybosl2 import cyl
shape = cyl(radius=12, height=35, rounding=3)
shape.show()
"""
length_val = next((v for v in (length, height) if v is not None), 1.0)
rad1 = _pick_radius(radius1=radius1, diameter1=diameter1, radius=radius, diameter=diameter, dflt=1)
rad2 = _pick_radius(radius2=radius2, diameter2=diameter2, radius=radius, diameter=diameter, dflt=1)
if circumscribe:
sides = _frag_count(max(rad1, rad2), fn, fa, fs)
sc = 1 / math.cos(math.pi / sides)
rad1 *= sc
rad2 *= sc
use_anchor = _cyl_anchor(center, anchor)
if texture is not None and texture != "none":
return _textured_cyl(
length_val,
rad1,
rad2,
texture,
tex_size=tex_size,
tex_reps=tex_reps,
tex_depth=tex_depth,
tex_inset=tex_inset,
anchor=use_anchor,
spin=spin,
orient=orient,
fn=fn,
fa=fa,
fs=fs,
)
r1v = rounding1 if rounding1 is not None else (rounding if rounding is not None else 0)
r2v = rounding2 if rounding2 is not None else (rounding if rounding is not None else 0)
c1v = chamfer1 if chamfer1 is not None else (chamfer if chamfer is not None else 0)
c2v = chamfer2 if chamfer2 is not None else (chamfer if chamfer is not None else 0)
if (r1v or r2v) and (c1v or c2v):
raise Bosl2ValueError("Cannot specify nonzero value for both chamfer and rounding")
_check_rim_treatments(rad1, rad2, r1v, r2v, c1v, c2v)
cfang1 = chamfer_angle1 if chamfer_angle1 is not None else (chamfer_angle if chamfer_angle is not None else None)
cfang2 = chamfer_angle2 if chamfer_angle2 is not None else (chamfer_angle if chamfer_angle is not None else None)
fe1 = from_end1 if from_end1 is not None else from_end
fe2 = from_end2 if from_end2 is not None else from_end
if not (r1v or r2v or c1v or c2v):
shape = _ocylinder(
height=length_val,
radius1=rad1,
radius2=rad2,
center=True,
fn=fn,
fa=fa,
fs=fs,
)
elif (
rad1 == rad2
and r1v == r2v
and r1v > 0
and not c1v
and not c2v
and (teardrop is False or teardrop is None)
and clip_angle == 90.0
):
# Straight cylinder, uniform rounding on both ends: exact via minkowski(cylinder, sphere).
inner_r = max(0.001, rad1 - r1v)
inner_l = max(0.001, length_val - 2 * r1v)
sphere_fn = int(quantup(_frag_count(r1v, fn, fa, fs), 4))
shape = _ominkowski(
_ocylinder(height=inner_l, radius=inner_r, center=True, fn=fn, fa=fa, fs=fs),
_osphere(radius=r1v, fn=sphere_fn),
)
else:
profile = cyl_profile(
rad1,
rad2,
length_val,
rounding1=r1v,
rounding2=r2v,
chamfer1=c1v,
chamfer2=c2v,
chamfer_angle1=cfang1,
chamfer_angle2=cfang2,
from_end1=fe1,
from_end2=fe2,
fn=fn,
fa=fa,
fs=fs,
teardrop=teardrop,
clip_angle=clip_angle,
)
from pybosl2._native import native
_opolygon = native("polygon")
shape = _orotate_extrude(_opolygon(profile), fn=fn, fa=fa, fs=fs)
if realign:
sides = _frag_count(max(rad1, rad2), fn, fa, fs)
shape = shape.rotate(180 / sides, [0, 0, 1])
if shift[0] or shift[1]:
shear = [
[1, 0, shift[0] / length_val, 0],
[0, 1, shift[1] / length_val, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
]
shape = shape.multmatrix(shear)
extra1_val = extra1 if extra1 is not None else extra
extra2_val = extra2 if extra2 is not None else extra
if extra1_val > 0:
ext1 = _ocylinder(height=extra1_val, radius=rad1, center=False, fn=fn, fa=fa, fs=fs).translate(
[0, 0, -length_val / 2 - extra1_val]
)
shape = shape | ext1
if extra2_val > 0:
ext2 = _ocylinder(height=extra2_val, radius=rad2, center=False, fn=fn, fa=fa, fs=fs).translate(
[0, 0, length_val / 2]
)
shape = shape | ext2
offset = _anchor_offset_cyl(rad1, rad2, length_val, use_anchor)
return _finish3(shape, offset, spin, orient, size=None, anchor=use_anchor)
def _check_rim_treatments(
radius1: float,
radius2: float,
rounding1: float,
rounding2: float,
chamfer1: float,
chamfer2: float,
) -> None:
"""Reject a rim rounding/chamfer too big for the rim it sits on.
A treatment bigger than its end's radius pushes the revolved profile past the axis, and
``rotate_extrude()`` refuses children that cross it -- which used to surface as a raw OpenSCAD
error plus a solid with no bounding box. A cone's top radius is 0, so *any* top treatment did
this: ``cone(height=30, radius=15, chamfer=1)`` produced nothing usable (SPEC E-4).
Args:
radius1: Radius at the bottom end.
radius2: Radius at the top end.
rounding1: Rounding radius at the bottom rim.
rounding2: Rounding radius at the top rim.
chamfer1: Chamfer size at the bottom rim.
chamfer2: Chamfer size at the top rim.
Raises:
ValueError: If a treatment exceeds the radius of the end it is applied to.
"""
for label, treatment, radius in (
("rounding1", rounding1, radius1),
("rounding2", rounding2, radius2),
("chamfer1", chamfer1, radius1),
("chamfer2", chamfer2, radius2),
):
if treatment and treatment > radius:
raise Bosl2ValueError(
f"cyl(): {label}={treatment} is larger than that end's radius ({radius}); "
f"a rim treatment has to fit inside the rim it rounds."
)
[docs]
def cyl_profile(
radius1: float,
radius2: float,
length: float,
rounding1: float = 0,
rounding2: float = 0,
chamfer1: float = 0,
chamfer2: float = 0,
chamfer_angle1: float | None = None,
chamfer_angle2: float | None = None,
from_end1: bool = False,
from_end2: bool = False,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
teardrop: float | bool = False,
clip_angle: float = 90.0,
) -> list[list[float]]:
"""Generate a 2D cylinder profile with optional rounding and chamfering.
Args:
radius1: Radius at the bottom end.
radius2: Radius at the top end.
length: Length of the cylinder along its axis.
rounding1: Rounding radius at the bottom end; negative rounds outward.
rounding2: Rounding radius at the top end; negative rounds outward.
chamfer1: Chamfer size at the bottom end.
chamfer2: Chamfer size at the top end.
chamfer_angle1: Angle of the bottom chamfer in degrees, measured from the end face.
chamfer_angle2: Angle of the top chamfer in degrees, measured from the end face.
from_end1: Measure the bottom chamfer along the end face rather than up the side.
from_end2: Measure the top chamfer along the end face rather than up the side.
fn: Fixed fragment count for curved surfaces. Omitted, the ambient ``use_defaults(fn=...)`` value applies;
``fn=0`` opts back out to fa/fs.
fa: Minimum fragment angle in degrees. Omitted, the ambient ``use_defaults(fa=...)`` value applies.
fs: Minimum fragment size in millimetres. Omitted, the ambient ``use_defaults(fs=...)`` value applies.
teardrop: Make the top a printable teardrop, optionally giving the angle.
clip_angle: Angle at which a teardrop profile is clipped flat, for printability.
"""
from pybosl2._helpers import arc_points as _arc_points
eff_clip = float(clip_angle)
if teardrop is not False and teardrop is not None:
# `bool` is a subclass of `int`, so an `isinstance(teardrop, (int, float))` test answers
# True for `teardrop=True` and takes the boolean itself as the angle -- a **1 degree**
# teardrop, which is a rounding with an imperceptible flat on it rather than the 45 the
# flag is supposed to mean. The bool has to be ruled out before the number is read.
td_ang = 45.0 if isinstance(teardrop, bool) else float(teardrop)
eff_clip = min(eff_clip, 90.0 - td_ang)
_check_rim_treatments(radius1, radius2, rounding1, rounding2, chamfer1, chamfer2)
path = [[0.0, -length / 2]]
if rounding1:
sides = max(3, _frag_count(rounding1, fn, fa, fs) // 4)
center = [radius1 - rounding1, -length / 2 + rounding1]
pts = _arc_points(sides, rounding1, 360 - eff_clip, eff_clip, center)
if eff_clip < 90.0:
path.append([pts[0][0], -length / 2])
path.extend(pts)
elif chamfer1:
angle1 = chamfer_angle1 if chamfer_angle1 is not None else 45.0
if from_end1:
dx = chamfer1 * math.cos(math.radians(angle1))
dy = chamfer1 * math.sin(math.radians(angle1))
else:
dx = chamfer1
dy = chamfer1 * math.tan(math.radians(angle1))
path.append([radius1 - dx, -length / 2])
path.append([radius1, -length / 2 + dy])
else:
path.append([radius1, -length / 2])
if rounding2:
sides = max(3, _frag_count(rounding2, fn, fa, fs) // 4)
center = [radius2 - rounding2, length / 2 - rounding2]
pts = _arc_points(sides, rounding2, 0, eff_clip, center)
path.extend(pts)
if eff_clip < 90.0:
path.append([pts[-1][0], length / 2])
elif chamfer2:
angle2 = chamfer_angle2 if chamfer_angle2 is not None else 45.0
if from_end2:
dx = chamfer2 * math.cos(math.radians(angle2))
dy = chamfer2 * math.sin(math.radians(angle2))
else:
dx = chamfer2
dy = chamfer2 * math.tan(math.radians(angle2))
path.append([radius2, length / 2 - dy])
path.append([radius2 - dx, length / 2])
else:
path.append([radius2, length / 2])
path.append([0.0, length / 2])
return path
[docs]
@backend_only("csg", neutral="pybosl2.solid.xcyl")
def xcyl(
height: float | None = None,
radius: float | None = None,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
circumscribe: bool = False,
realign: bool = False,
shift: Sequence[float] = (0, 0),
anchor: Anchor | Sequence[float] | None = None,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
# Additional missing args
chamfer_angle: float | None = None,
chamfer_angle1: float | None = None,
chamfer_angle2: float | None = None,
from_end: bool = False,
from_end1: bool | None = None,
from_end2: bool | None = None,
extra: float = 0.0,
extra1: float | None = None,
extra2: float | None = None,
teardrop: float | bool = False,
clip_angle: float = 90.0,
texture: str | TextureType | None = None,
tex_size: float | Sequence[float] | None = None,
tex_reps: int | Sequence[int] | None = None,
tex_depth: float = 1.0,
tex_inset: float | bool = False,
) -> Bosl2Solid:
"""Return a cylinder oriented along the X axis. See cyl() for argument details.
Args:
height: Length of the cylinder along its axis (default 1)
radius: Radius of the cylinder (default 1)
center: If given, overrides anchor (True -> CENTER, False -> BOTTOM)
length: Length of the cylinder along its axis (default 1)
radius1: Radius of the negative end of the cylinder.
radius2: Radius of the positive end of the cylinder.
diameter: Diameter of the cylinder.
diameter1: Diameter of the negative end of the cylinder.
diameter2: Diameter of the positive end of the cylinder.
chamfer: Chamfer size on the end rims (overall/negative/positive)
chamfer1: Chamfer size on the end rims (overall/negative/positive)
chamfer2: Chamfer size on the end rims (overall/negative/positive)
rounding: Rounding radius on the end rims (overall/negative/positive)
rounding1: Rounding radius on the end rims (overall/negative/positive)
rounding2: Rounding radius on the end rims (overall/negative/positive)
circumscribe: Circumscribe rather than inscribe the given radius (CSG backend).
realign: Shift point alignment by half a facet (CSG backend).
shift: ``[x, y]`` offset for the positive end, shearing the solid (CSG backend).
anchor: Anchor point (default CENTER)
spin: Z-axis rotation in degrees after anchor (default 0)
orient: Direction to rotate the top towards, after spin (default UP)
fn: Fixed fragment count for curved surfaces; the ambient default applies when omitted, and 0 means "use
fa/fs" (CSG backend). Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back out
to fa/fs.
fa: Minimum fragment angle in degrees; ambient default when omitted (CSG backend). Omitted, the ambient
``use_defaults(fa=...)`` value applies.
fs: Minimum fragment size in millimetres; ambient default when omitted (CSG backend). Omitted, the ambient
``use_defaults(fs=...)`` value applies.
chamfer_angle: End chamfer angle in degrees away from the ends (CSG backend).
chamfer_angle1: Chamfer angle at the bottom end (CSG backend).
chamfer_angle2: Chamfer angle at the top end (CSG backend).
from_end: Measure the chamfer along the conic face rather than the axis (CSG backend).
from_end1: Measure the bottom chamfer along the conic face (CSG backend).
from_end2: Measure the top chamfer along the conic face (CSG backend).
extra: Extra height at both ends, invisible to anchoring (CSG backend).
extra1: Extra height at the bottom end (CSG backend).
extra2: Extra height at the top end (CSG backend).
teardrop: Limit the rounding angle from horizontal, for printability (CSG backend).
clip_angle: Clip the rounding arc at the bottom of the cylinder (CSG backend).
texture: Named texture for the side surface (CSG backend).
tex_size: Size of one texture tile (CSG backend).
tex_reps: Number of texture repetitions (CSG backend).
tex_depth: Depth of the texture (CSG backend).
tex_inset: Inset the texture into the surface (CSG backend).
Examples:
.. pythonscad-example::
from pybosl2.shapes3d.cylinder import xcyl
shape = xcyl(radius=10, height=30)
shape.show()
"""
length_val = next((v for v in (length, height) if v is not None), 1.0)
rad1 = _pick_radius(radius1=radius1, diameter1=diameter1, radius=radius, diameter=diameter, dflt=1)
rad2 = _pick_radius(radius2=radius2, diameter2=diameter2, radius=radius, diameter=diameter, dflt=1)
if circumscribe:
sides = _frag_count(max(rad1, rad2), fn, fa, fs)
sc = 1 / math.cos(math.pi / sides)
rad1 *= sc
rad2 *= sc
use_anchor = _cyl_anchor(center, anchor)
shape = cyl(
length=length_val,
radius1=rad1,
radius2=rad2,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
circumscribe=circumscribe,
realign=realign,
shift=shift,
anchor=CENTER,
fn=fn,
fa=fa,
fs=fs,
chamfer_angle=chamfer_angle,
chamfer_angle1=chamfer_angle1,
chamfer_angle2=chamfer_angle2,
from_end=from_end,
from_end1=from_end1,
from_end2=from_end2,
extra=extra,
extra1=extra1,
extra2=extra2,
teardrop=teardrop,
clip_angle=clip_angle,
texture=texture,
tex_size=tex_size,
tex_reps=tex_reps,
tex_depth=tex_depth,
tex_inset=tex_inset,
).shape.rotate(90, [0, 1, 0])
offset = _anchor_offset_cyl(rad1, rad2, length_val, use_anchor, axis=0)
return _finish3(shape, offset, spin, orient, size=None, anchor=use_anchor)
[docs]
@backend_only("csg", neutral="pybosl2.solid.ycyl")
def ycyl(
height: float | None = None,
radius: float | None = None,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
circumscribe: bool = False,
realign: bool = False,
shift: Sequence[float] = (0, 0),
anchor: Anchor | Sequence[float] | None = None,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
# Additional missing args
chamfer_angle: float | None = None,
chamfer_angle1: float | None = None,
chamfer_angle2: float | None = None,
from_end: bool = False,
from_end1: bool | None = None,
from_end2: bool | None = None,
extra: float = 0.0,
extra1: float | None = None,
extra2: float | None = None,
teardrop: float | bool = False,
clip_angle: float = 90.0,
texture: str | TextureType | None = None,
tex_size: float | Sequence[float] | None = None,
tex_reps: int | Sequence[int] | None = None,
tex_depth: float = 1.0,
tex_inset: float | bool = False,
) -> Bosl2Solid:
"""Return a cylinder oriented along the Y axis. See cyl() for argument details.
Args:
height: Length of the cylinder along its axis (default 1)
radius: Radius of the cylinder (default 1)
center: If given, overrides anchor (True -> CENTER, False -> BOTTOM)
length: Length of the cylinder along its axis (default 1)
radius1: Radius of the negative end of the cylinder.
radius2: Radius of the positive end of the cylinder.
diameter: Diameter of the cylinder.
diameter1: Diameter of the negative end of the cylinder.
diameter2: Diameter of the positive end of the cylinder.
chamfer: Chamfer size on the end rims (overall/negative/positive)
chamfer1: Chamfer size on the end rims (overall/negative/positive)
chamfer2: Chamfer size on the end rims (overall/negative/positive)
rounding: Rounding radius on the end rims (overall/negative/positive)
rounding1: Rounding radius on the end rims (overall/negative/positive)
rounding2: Rounding radius on the end rims (overall/negative/positive)
circumscribe: Circumscribe rather than inscribe the given radius (CSG backend).
realign: Shift point alignment by half a facet (CSG backend).
shift: ``[x, y]`` offset for the positive end, shearing the solid (CSG backend).
anchor: Anchor point (default CENTER)
spin: Z-axis rotation in degrees after anchor (default 0)
orient: Direction to rotate the top towards, after spin (default UP)
fn: Fixed fragment count for curved surfaces; the ambient default applies when omitted, and 0 means "use
fa/fs" (CSG backend). Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back out
to fa/fs.
fa: Minimum fragment angle in degrees; ambient default when omitted (CSG backend). Omitted, the ambient
``use_defaults(fa=...)`` value applies.
fs: Minimum fragment size in millimetres; ambient default when omitted (CSG backend). Omitted, the ambient
``use_defaults(fs=...)`` value applies.
chamfer_angle: End chamfer angle in degrees away from the ends (CSG backend).
chamfer_angle1: Chamfer angle at the bottom end (CSG backend).
chamfer_angle2: Chamfer angle at the top end (CSG backend).
from_end: Measure the chamfer along the conic face rather than the axis (CSG backend).
from_end1: Measure the bottom chamfer along the conic face (CSG backend).
from_end2: Measure the top chamfer along the conic face (CSG backend).
extra: Extra height at both ends, invisible to anchoring (CSG backend).
extra1: Extra height at the bottom end (CSG backend).
extra2: Extra height at the top end (CSG backend).
teardrop: Limit the rounding angle from horizontal, for printability (CSG backend).
clip_angle: Clip the rounding arc at the bottom of the cylinder (CSG backend).
texture: Named texture for the side surface (CSG backend).
tex_size: Size of one texture tile (CSG backend).
tex_reps: Number of texture repetitions (CSG backend).
tex_depth: Depth of the texture (CSG backend).
tex_inset: Inset the texture into the surface (CSG backend).
Examples:
.. pythonscad-example::
from pybosl2.shapes3d.cylinder import ycyl
shape = ycyl(radius=10, height=30)
shape.show()
"""
length_val = next((v for v in (length, height) if v is not None), 1.0)
rad1 = _pick_radius(radius1=radius1, diameter1=diameter1, radius=radius, diameter=diameter, dflt=1)
rad2 = _pick_radius(radius2=radius2, diameter2=diameter2, radius=radius, diameter=diameter, dflt=1)
if circumscribe:
sides = _frag_count(max(rad1, rad2), fn, fa, fs)
sc = 1 / math.cos(math.pi / sides)
rad1 *= sc
rad2 *= sc
use_anchor = _cyl_anchor(center, anchor)
shape = cyl(
length=length_val,
radius1=rad1,
radius2=rad2,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
circumscribe=circumscribe,
realign=realign,
shift=shift,
anchor=CENTER,
fn=fn,
fa=fa,
fs=fs,
chamfer_angle=chamfer_angle,
chamfer_angle1=chamfer_angle1,
chamfer_angle2=chamfer_angle2,
from_end=from_end,
from_end1=from_end1,
from_end2=from_end2,
extra=extra,
extra1=extra1,
extra2=extra2,
teardrop=teardrop,
clip_angle=clip_angle,
texture=texture,
tex_size=tex_size,
tex_reps=tex_reps,
tex_depth=tex_depth,
tex_inset=tex_inset,
).shape.rotate(-90, [1, 0, 0])
offset = _anchor_offset_cyl(rad1, rad2, length_val, use_anchor, axis=1)
return _finish3(shape, offset, spin, orient, size=None, anchor=use_anchor)
[docs]
@backend_only("csg", neutral="pybosl2.solid.zcyl")
def zcyl(
height: float | None = None,
radius: float | None = None,
center: bool | None = None,
length: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
circumscribe: bool = False,
realign: bool = False,
shift: Sequence[float] = (0, 0),
anchor: Anchor | Sequence[float] | None = None,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
# Additional missing args
chamfer_angle: float | None = None,
chamfer_angle1: float | None = None,
chamfer_angle2: float | None = None,
from_end: bool = False,
from_end1: bool | None = None,
from_end2: bool | None = None,
extra: float = 0.0,
extra1: float | None = None,
extra2: float | None = None,
teardrop: float | bool = False,
clip_angle: float = 90.0,
texture: str | TextureType | None = None,
tex_size: float | Sequence[float] | None = None,
tex_reps: int | Sequence[int] | None = None,
tex_depth: float = 1.0,
tex_inset: float | bool = False,
) -> Bosl2Solid:
"""Return a cylinder oriented along the Z axis (same as cyl() with default orientation). See cyl() for.
argument details.
Args:
height: Length of the cylinder along its axis (default 1)
radius: Radius of the cylinder (default 1)
center: If given, overrides anchor (True -> CENTER, False -> BOTTOM)
length: Length of the cylinder along its axis (default 1)
radius1: Radius of the negative end of the cylinder.
radius2: Radius of the positive end of the cylinder.
diameter: Diameter of the cylinder.
diameter1: Diameter of the negative end of the cylinder.
diameter2: Diameter of the positive end of the cylinder.
chamfer: Chamfer size on the end rims (overall/negative/positive)
chamfer1: Chamfer size on the end rims (overall/negative/positive)
chamfer2: Chamfer size on the end rims (overall/negative/positive)
rounding: Rounding radius on the end rims (overall/negative/positive)
rounding1: Rounding radius on the end rims (overall/negative/positive)
rounding2: Rounding radius on the end rims (overall/negative/positive)
circumscribe: Circumscribe rather than inscribe the given radius (CSG backend).
realign: Shift point alignment by half a facet (CSG backend).
shift: ``[x, y]`` offset for the positive end, shearing the solid (CSG backend).
anchor: Anchor point (default CENTER)
spin: Z-axis rotation in degrees after anchor (default 0)
orient: Direction to rotate the top towards, after spin (default UP)
fn: Fixed fragment count for curved surfaces; the ambient default applies when omitted, and 0 means "use
fa/fs" (CSG backend). Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back out
to fa/fs.
fa: Minimum fragment angle in degrees; ambient default when omitted (CSG backend). Omitted, the ambient
``use_defaults(fa=...)`` value applies.
fs: Minimum fragment size in millimetres; ambient default when omitted (CSG backend). Omitted, the ambient
``use_defaults(fs=...)`` value applies.
chamfer_angle: End chamfer angle in degrees away from the ends (CSG backend).
chamfer_angle1: Chamfer angle at the bottom end (CSG backend).
chamfer_angle2: Chamfer angle at the top end (CSG backend).
from_end: Measure the chamfer along the conic face rather than the axis (CSG backend).
from_end1: Measure the bottom chamfer along the conic face (CSG backend).
from_end2: Measure the top chamfer along the conic face (CSG backend).
extra: Extra height at both ends, invisible to anchoring (CSG backend).
extra1: Extra height at the bottom end (CSG backend).
extra2: Extra height at the top end (CSG backend).
teardrop: Limit the rounding angle from horizontal, for printability (CSG backend).
clip_angle: Clip the rounding arc at the bottom of the cylinder (CSG backend).
texture: Named texture for the side surface (CSG backend).
tex_size: Size of one texture tile (CSG backend).
tex_reps: Number of texture repetitions (CSG backend).
tex_depth: Depth of the texture (CSG backend).
tex_inset: Inset the texture into the surface (CSG backend).
Examples:
.. pythonscad-example::
from pybosl2.shapes3d.cylinder import zcyl
shape = zcyl(radius=10, height=30)
shape.show()
"""
return cyl(
height=height,
radius=radius,
center=center,
length=length,
radius1=radius1,
radius2=radius2,
diameter=diameter,
diameter1=diameter1,
diameter2=diameter2,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
circumscribe=circumscribe,
realign=realign,
shift=shift,
anchor=anchor,
spin=spin,
orient=orient,
fn=fn,
fa=fa,
fs=fs,
chamfer_angle=chamfer_angle,
chamfer_angle1=chamfer_angle1,
chamfer_angle2=chamfer_angle2,
from_end=from_end,
from_end1=from_end1,
from_end2=from_end2,
extra=extra,
extra1=extra1,
extra2=extra2,
teardrop=teardrop,
clip_angle=clip_angle,
texture=texture,
tex_size=tex_size,
tex_reps=tex_reps,
tex_depth=tex_depth,
tex_inset=tex_inset,
)
[docs]
@backend_only("csg", neutral="pybosl2.solid.tube")
def tube(
height: float | None = None,
outer_radius: float | None = None,
inner_radius: float | None = None,
center: bool | None = None,
outer_diameter: float | None = None,
inner_diameter: float | None = None,
wall: float | None = None,
outer_radius1: float | None = None,
outer_radius2: float | None = None,
outer_diameter1: float | None = None,
outer_diameter2: float | None = None,
inner_radius1: float | None = None,
inner_radius2: float | None = None,
inner_diameter1: float | None = None,
inner_diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
realign: bool = False,
length: float | None = None,
anchor: Anchor | Sequence[float] = Anchor.CENTER,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
) -> Bosl2Solid:
"""Return a hollow cylindrical tube, with optional chamfer/rounding on end rims.
Note: BOSL2's outer-radius parameters are named ``or``/``or1``/``or2``, which collide with the
Python keyword ``or``; they are exposed here as ``outer_radius``/``outer_radius1``/``outer_radius2`` instead.
Args:
height: height of the tube (default 1)
length: height of the tube (default 1)
outer_radius: outer radius of the tube (BOSL2 ``or``) (default 1)
inner_radius: inner radius of the tube
center: if given, overrides anchor (True -> CENTER, False -> DOWN)
outer_diameter: outer diameter of the tube
inner_diameter: inner diameter of the tube
wall: horizontal wall thickness (default 1)
outer_radius1: outer radius of the bottom/top
outer_radius2: outer radius of the bottom/top
outer_diameter1: outer diameter of the bottom/top
outer_diameter2: outer diameter of the bottom/top
inner_radius1: inner radius of the bottom/top
inner_radius2: inner radius of the bottom/top
inner_diameter1: inner diameter of the bottom/top
inner_diameter2: inner diameter of the bottom/top
chamfer: chamfer size on end rims (overall/bottom/top)
chamfer1: chamfer size on end rims (overall/bottom/top)
chamfer2: chamfer size on end rims (overall/bottom/top)
rounding: rounding radius on end rims (overall/bottom/top)
rounding1: rounding radius on end rims (overall/bottom/top)
rounding2: rounding radius on end rims (overall/bottom/top)
realign: rotate by half the angle of one face (default False)
anchor: anchor point (default CENTER)
spin: Z-axis rotation in degrees after anchor (default 0)
orient: direction to rotate the top towards, after spin (default UP)
fn: arc smoothness overrides. Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back
out to fa/fs.
fa: arc smoothness overrides. Omitted, the ambient ``use_defaults(fa=...)`` value applies.
fs: arc smoothness overrides. Omitted, the ambient ``use_defaults(fs=...)`` value applies.
Examples:
.. pythonscad-example::
from pybosl2 import tube
shape = tube(height=20, outer_radius=15, inner_radius=10)
shape.show()
A tube with chamfered end rims:
.. pythonscad-example::
from pybosl2 import tube
shape = tube(height=20, outer_radius=15, inner_radius=10, chamfer=1)
shape.show()
"""
height = height if height is not None else (length if length is not None else 1)
orr1 = _pick_radius(
radius1=outer_radius1,
diameter1=outer_diameter1,
radius=outer_radius,
diameter=outer_diameter,
dflt=None,
)
orr2 = _pick_radius(
radius1=outer_radius2,
diameter1=outer_diameter2,
radius=outer_radius,
diameter=outer_diameter,
dflt=None,
)
irr1 = _pick_radius(
radius1=inner_radius1,
diameter1=inner_diameter1,
radius=inner_radius,
diameter=inner_diameter,
dflt=None,
)
irr2 = _pick_radius(
radius1=inner_radius2,
diameter1=inner_diameter2,
radius=inner_radius,
diameter=inner_diameter,
dflt=None,
)
wall_v = wall if wall is not None else 1
rad1 = orr1 if orr1 is not None else (irr1 + wall_v if irr1 is not None else None)
rad2 = orr2 if orr2 is not None else (irr2 + wall_v if irr2 is not None else None)
irad1 = irr1 if irr1 is not None else (orr1 - wall_v if orr1 is not None else None)
irad2 = irr2 if irr2 is not None else (orr2 - wall_v if orr2 is not None else None)
if rad1 is None or rad2 is None or irad1 is None or irad2 is None:
raise Bosl2ValueError(
"tube(): needs two of the three sizes -- an inner radius/diameter, an outer "
"radius/diameter, and a wall thickness."
)
if not (irad1 <= rad1):
raise Bosl2ValueError("tube(): inner radius is larger than outer radius.")
if not (irad2 <= rad2):
raise Bosl2ValueError("tube(): inner radius is larger than outer radius.")
use_anchor = resolve_center_anchor(center=center, anchor=anchor, centred=Anchor.CENTER, uncentred=BOTTOM)
# Build outer and inner cylinders via cyl() for chamfer/rounding support
outer = cyl(
height=height,
radius1=rad1,
radius2=rad2,
center=True,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
fn=fn,
fa=fa,
fs=fs,
)
extra_h = max(chamfer or 0, rounding or 0, chamfer1 or 0, rounding1 or 0, chamfer2 or 0, rounding2 or 0)
inner = cyl(
height=height + extra_h + 0.02,
radius1=irad1,
radius2=irad2,
center=True,
fn=fn,
fa=fa,
fs=fs,
)
shape = outer.shape - inner.shape
if realign:
sides = _frag_count(max(rad1, rad2), fn, fa, fs)
shape = shape.rotate(180 / sides, [0, 0, 1])
offset = _anchor_offset_cyl(rad1, rad2, height, use_anchor)
return _finish3(shape, offset, spin, orient, size=None, anchor=use_anchor)
[docs]
@backend_only("csg")
def cone(
height: float | None = None,
radius: float | None = None,
radius1: float | None = None,
radius2: float | None = None,
center: bool | None = None,
diameter: float | None = None,
diameter1: float | None = None,
diameter2: float | None = None,
chamfer: float | None = None,
chamfer1: float | None = None,
chamfer2: float | None = None,
rounding: float | None = None,
rounding1: float | None = None,
rounding2: float | None = None,
length: float | None = None,
anchor: Anchor | Sequence[float] | None = None,
spin: float = 0,
orient: Anchor | Sequence[float] = Anchor.TOP,
fn: int | None = None,
fa: float | None = None,
fs: float | None = None,
) -> Bosl2Solid:
"""Return a cone/truncated cone with optional chamfering or rounding of the end rims.
Convenience wrapper around :func:`cyl` / :func:`cylinder` with ``radius2=0`` by default
for a pointed cone, or with explicit ``radius2`` for a truncated (frustum) form.
Args:
height: height of the cone (default 1)
length: height of the cone (default 1)
radius: base radius (default 1)
radius1: bottom radius (overrides *radius*)
radius2: top radius (default 0 for a pointed cone)
center: if given, overrides anchor
diameter: base diameter
diameter1: bottom diameter
diameter2: top diameter
chamfer: chamfer size on the end rims
chamfer1: chamfer size on the end rims
chamfer2: chamfer size on the end rims
rounding: rounding radius on the end rims
rounding1: rounding radius on the end rims
rounding2: rounding radius on the end rims
anchor: anchor point
spin: Z-axis rotation in degrees after anchor (default 0)
orient: direction to rotate the top towards, after spin (default UP)
fn: arc smoothness overrides. Omitted, the ambient ``use_defaults(fn=...)`` value applies; ``fn=0`` opts back
out to fa/fs.
fa: arc smoothness overrides. Omitted, the ambient ``use_defaults(fa=...)`` value applies.
fs: arc smoothness overrides. Omitted, the ambient ``use_defaults(fs=...)`` value applies.
Examples:
A pointed cone:
.. pythonscad-example::
from pybosl2 import shapes3d as s3
s3.cone(height=30, radius=15).show()
A truncated cone (frustum):
.. pythonscad-example::
from pybosl2 import shapes3d as s3
s3.cone(height=30, radius1=15, radius2=8).show()
A cone with chamfered base:
.. pythonscad-example::
from pybosl2 import shapes3d as s3
s3.cone(height=30, radius1=15, radius2=3, chamfer=2).show()
"""
r1 = _pick_radius(radius1=radius1, diameter1=diameter1, radius=radius, diameter=diameter, dflt=1)
r2 = _pick_radius(radius2=radius2, diameter2=diameter2, dflt=0)
return cyl(
height=height,
radius1=r1,
radius2=r2,
center=center,
length=length,
chamfer=chamfer,
chamfer1=chamfer1,
chamfer2=chamfer2,
rounding=rounding,
rounding1=rounding1,
rounding2=rounding2,
anchor=anchor,
spin=spin,
orient=orient,
fn=fn,
fa=fa,
fs=fs,
)