cq.vis.show improvements (#1726)
* Handle vtkActors * Mypy fix * Update Showables * Add specular lighting * Make show non-blocking and add title * Return only qwin * Revert * Test fix * Better coverage * Adding vis docs * Add references * Primer tweaks * Cleanup * Correct file names * Tweaks * Try with -q * Try with always_yes * Remove -q * Try with mamba * Use mamba run * Set MAMBA_ROOT_PREFIX * Fix prefix * Apply suggestions from code review Co-authored-by: Jeremy Wright <wrightjmf@gmail.com> * Do not use full screen * State correct version --------- Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>
This commit is contained in:
14
appveyor.yml
14
appveyor.yml
@ -20,25 +20,27 @@ init:
|
|||||||
- cmd: Miniforge.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%MINICONDA_DIRNAME%
|
- cmd: Miniforge.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%MINICONDA_DIRNAME%
|
||||||
- cmd: set "PATH=%MINICONDA_DIRNAME%;%MINICONDA_DIRNAME%\\Scripts;%PATH%"
|
- cmd: set "PATH=%MINICONDA_DIRNAME%;%MINICONDA_DIRNAME%\\Scripts;%PATH%"
|
||||||
- cmd: activate
|
- cmd: activate
|
||||||
|
- cmd: set MAMBA_ROOT_PREFIX=C:/Miniforge/Library
|
||||||
- sh: curl -sL https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$OS-x86_64.sh > miniconda.sh
|
- sh: curl -sL https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$OS-x86_64.sh > miniconda.sh
|
||||||
- sh: bash miniconda.sh -b -p $HOME/miniconda;
|
- sh: bash miniconda.sh -b -p $HOME/miniconda;
|
||||||
- sh: export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH";
|
- sh: export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH";
|
||||||
- sh: source $HOME/miniconda/bin/activate
|
- sh: source $HOME/miniconda/bin/activate
|
||||||
|
- sh: export MAMBA_ROOT_PREFIX=$HOME/miniconda
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- conda config --set always_yes yes
|
||||||
- mamba env create -f environment.yml
|
- mamba env create -f environment.yml
|
||||||
- conda activate cadquery
|
- mamba list -n cadquery
|
||||||
- conda list
|
|
||||||
|
|
||||||
build: false
|
build: false
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- black . --diff --check
|
- mamba run -n cadquery black . --diff --check
|
||||||
- mypy cadquery
|
- mamba run -n cadquery mypy cadquery
|
||||||
- pytest -v --cov
|
- mamba run -n cadquery pytest -v --cov
|
||||||
|
|
||||||
on_success:
|
on_success:
|
||||||
- codecov
|
- mamba run -n cadquery codecov
|
||||||
|
|
||||||
#on_finish:
|
#on_finish:
|
||||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
from . import Shape, Workplane, Assembly, Sketch, Compound, Color, Vector, Location
|
from . import Shape, Workplane, Assembly, Sketch, Compound, Color, Vector, Location
|
||||||
from .occ_impl.exporters.assembly import _vtkRenderWindow
|
from .occ_impl.assembly import _loc2vtk, toVTK
|
||||||
from .occ_impl.assembly import _loc2vtk
|
|
||||||
|
|
||||||
from typing import Union, Any, List, Tuple
|
from typing import Union, Any, List, Tuple
|
||||||
|
|
||||||
@ -15,8 +14,10 @@ from vtkmodules.vtkRenderingCore import (
|
|||||||
vtkMapper,
|
vtkMapper,
|
||||||
vtkRenderWindowInteractor,
|
vtkRenderWindowInteractor,
|
||||||
vtkActor,
|
vtkActor,
|
||||||
|
vtkProp,
|
||||||
vtkPolyDataMapper,
|
vtkPolyDataMapper,
|
||||||
vtkAssembly,
|
vtkAssembly,
|
||||||
|
vtkRenderWindow,
|
||||||
)
|
)
|
||||||
from vtkmodules.vtkCommonCore import vtkPoints
|
from vtkmodules.vtkCommonCore import vtkPoints
|
||||||
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData
|
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData
|
||||||
@ -27,8 +28,14 @@ DEFAULT_COLOR = [1, 0.8, 0, 1]
|
|||||||
DEFAULT_PT_SIZE = 7.5
|
DEFAULT_PT_SIZE = 7.5
|
||||||
DEFAULT_PT_COLOR = "darkviolet"
|
DEFAULT_PT_COLOR = "darkviolet"
|
||||||
|
|
||||||
|
SPECULAR = 0.3
|
||||||
|
SPECULAR_POWER = 100
|
||||||
|
SPECULAR_COLOR = vtkNamedColors().GetColor3d("White")
|
||||||
|
|
||||||
ShapeLike = Union[Shape, Workplane, Assembly, Sketch, TopoDS_Shape]
|
ShapeLike = Union[Shape, Workplane, Assembly, Sketch, TopoDS_Shape]
|
||||||
Showable = Union[ShapeLike, List[ShapeLike], Vector, List[Vector]]
|
Showable = Union[
|
||||||
|
ShapeLike, List[ShapeLike], Vector, List[Vector], vtkProp, List[vtkProp]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def _to_assy(*objs: ShapeLike, alpha: float = 1) -> Assembly:
|
def _to_assy(*objs: ShapeLike, alpha: float = 1) -> Assembly:
|
||||||
@ -50,7 +57,9 @@ def _to_assy(*objs: ShapeLike, alpha: float = 1) -> Assembly:
|
|||||||
return assy
|
return assy
|
||||||
|
|
||||||
|
|
||||||
def _split_showables(objs) -> Tuple[List[ShapeLike], List[Vector], List[Location]]:
|
def _split_showables(
|
||||||
|
objs,
|
||||||
|
) -> Tuple[List[ShapeLike], List[Vector], List[Location], List[vtkProp]]:
|
||||||
"""
|
"""
|
||||||
Split into showables and others.
|
Split into showables and others.
|
||||||
"""
|
"""
|
||||||
@ -58,6 +67,7 @@ def _split_showables(objs) -> Tuple[List[ShapeLike], List[Vector], List[Location
|
|||||||
rv_s: List[ShapeLike] = []
|
rv_s: List[ShapeLike] = []
|
||||||
rv_v: List[Vector] = []
|
rv_v: List[Vector] = []
|
||||||
rv_l: List[Location] = []
|
rv_l: List[Location] = []
|
||||||
|
rv_a: List[vtkProp] = []
|
||||||
|
|
||||||
for el in objs:
|
for el in objs:
|
||||||
if instance_of(el, ShapeLike):
|
if instance_of(el, ShapeLike):
|
||||||
@ -66,21 +76,24 @@ def _split_showables(objs) -> Tuple[List[ShapeLike], List[Vector], List[Location
|
|||||||
rv_v.append(el)
|
rv_v.append(el)
|
||||||
elif isinstance(el, Location):
|
elif isinstance(el, Location):
|
||||||
rv_l.append(el)
|
rv_l.append(el)
|
||||||
|
elif isinstance(el, vtkProp):
|
||||||
|
rv_a.append(el)
|
||||||
elif isinstance(el, list):
|
elif isinstance(el, list):
|
||||||
tmp1, tmp2, tmp3 = _split_showables(el) # split recursively
|
tmp1, tmp2, tmp3, tmp4 = _split_showables(el) # split recursively
|
||||||
|
|
||||||
rv_s.extend(tmp1)
|
rv_s.extend(tmp1)
|
||||||
rv_v.extend(tmp2)
|
rv_v.extend(tmp2)
|
||||||
rv_l.extend(tmp3)
|
rv_l.extend(tmp3)
|
||||||
|
rv_a.extend(tmp4)
|
||||||
|
|
||||||
return rv_s, rv_v, rv_l
|
return rv_s, rv_v, rv_l, rv_a
|
||||||
|
|
||||||
|
|
||||||
def _to_vtk_pts(
|
def _to_vtk_pts(
|
||||||
vecs: List[Vector], size: float = DEFAULT_PT_SIZE, color: str = DEFAULT_PT_COLOR
|
vecs: List[Vector], size: float = DEFAULT_PT_SIZE, color: str = DEFAULT_PT_COLOR
|
||||||
) -> vtkActor:
|
) -> vtkActor:
|
||||||
"""
|
"""
|
||||||
Convert vectors to vtkActor.
|
Convert Vectors to vtkActor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rv = vtkActor()
|
rv = vtkActor()
|
||||||
@ -110,7 +123,7 @@ def _to_vtk_pts(
|
|||||||
|
|
||||||
def _to_vtk_axs(locs: List[Location], scale: float = 0.1) -> vtkActor:
|
def _to_vtk_axs(locs: List[Location], scale: float = 0.1) -> vtkActor:
|
||||||
"""
|
"""
|
||||||
Convert vectors to vtkActor.
|
Convert Locations to vtkActor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
rv = vtkAssembly()
|
rv = vtkAssembly()
|
||||||
@ -135,6 +148,8 @@ def show(
|
|||||||
alpha: float = 1,
|
alpha: float = 1,
|
||||||
tolerance: float = 1e-3,
|
tolerance: float = 1e-3,
|
||||||
edges: bool = False,
|
edges: bool = False,
|
||||||
|
specular: bool = True,
|
||||||
|
title: str = "CQ viewer",
|
||||||
**kwrags: Any,
|
**kwrags: Any,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -142,7 +157,7 @@ def show(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# split objects
|
# split objects
|
||||||
shapes, vecs, locs = _split_showables(objs)
|
shapes, vecs, locs, props = _split_showables(objs)
|
||||||
|
|
||||||
# construct the assy
|
# construct the assy
|
||||||
assy = _to_assy(*shapes, alpha=alpha)
|
assy = _to_assy(*shapes, alpha=alpha)
|
||||||
@ -151,19 +166,28 @@ def show(
|
|||||||
pts = _to_vtk_pts(vecs)
|
pts = _to_vtk_pts(vecs)
|
||||||
axs = _to_vtk_axs(locs, scale=scale)
|
axs = _to_vtk_axs(locs, scale=scale)
|
||||||
|
|
||||||
# create a VTK window
|
# assy+renderer
|
||||||
win = _vtkRenderWindow(assy, tolerance=tolerance)
|
renderer = toVTK(assy, tolerance=tolerance)
|
||||||
|
|
||||||
win.SetWindowName("CQ viewer")
|
# VTK window boilerplate
|
||||||
|
win = vtkRenderWindow()
|
||||||
|
win.SetWindowName(title)
|
||||||
|
win.AddRenderer(renderer)
|
||||||
|
|
||||||
# get renderer and actor
|
# get renderer and actor
|
||||||
|
for act in renderer.GetActors():
|
||||||
|
|
||||||
|
propt = act.GetProperty()
|
||||||
|
|
||||||
if edges:
|
if edges:
|
||||||
ren = win.GetRenderers().GetFirstRenderer()
|
propt.EdgeVisibilityOn()
|
||||||
for act in ren.GetActors():
|
|
||||||
act.GetProperty().EdgeVisibilityOn()
|
if specular:
|
||||||
|
propt.SetSpecular(SPECULAR)
|
||||||
|
propt.SetSpecularPower(SPECULAR_POWER)
|
||||||
|
propt.SetSpecularColor(SPECULAR_COLOR)
|
||||||
|
|
||||||
# rendering related settings
|
# rendering related settings
|
||||||
win.SetMultiSamples(16)
|
|
||||||
vtkMapper.SetResolveCoincidentTopologyToPolygonOffset()
|
vtkMapper.SetResolveCoincidentTopologyToPolygonOffset()
|
||||||
vtkMapper.SetResolveCoincidentTopologyPolygonOffsetParameters(1, 0)
|
vtkMapper.SetResolveCoincidentTopologyPolygonOffsetParameters(1, 0)
|
||||||
vtkMapper.SetResolveCoincidentTopologyLineOffsetParameters(-1, 0)
|
vtkMapper.SetResolveCoincidentTopologyLineOffsetParameters(-1, 0)
|
||||||
@ -193,7 +217,7 @@ def show(
|
|||||||
orient_widget.InteractiveOff()
|
orient_widget.InteractiveOff()
|
||||||
|
|
||||||
# use gradient background
|
# use gradient background
|
||||||
renderer = win.GetRenderers().GetFirstRenderer()
|
renderer.SetBackground(1, 1, 1)
|
||||||
renderer.GradientBackgroundOn()
|
renderer.GradientBackgroundOn()
|
||||||
|
|
||||||
# use FXXAA
|
# use FXXAA
|
||||||
@ -209,9 +233,15 @@ def show(
|
|||||||
renderer.AddActor(pts)
|
renderer.AddActor(pts)
|
||||||
renderer.AddActor(axs)
|
renderer.AddActor(axs)
|
||||||
|
|
||||||
|
# add other vtk actors
|
||||||
|
for p in props:
|
||||||
|
renderer.AddActor(p)
|
||||||
|
|
||||||
# initialize and set size
|
# initialize and set size
|
||||||
inter.Initialize()
|
inter.Initialize()
|
||||||
win.SetSize(*win.GetScreenSize())
|
|
||||||
|
w, h = win.GetScreenSize()
|
||||||
|
win.SetSize((w // 2, h // 2))
|
||||||
win.SetPosition(-10, 0)
|
win.SetPosition(-10, 0)
|
||||||
|
|
||||||
# show and return
|
# show and return
|
||||||
|
|||||||
BIN
doc/_static/show.PNG
vendored
Normal file
BIN
doc/_static/show.PNG
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 314 KiB |
BIN
doc/_static/show_demo.PNG
vendored
Normal file
BIN
doc/_static/show_demo.PNG
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 290 KiB |
BIN
doc/_static/show_jupyter.PNG
vendored
Normal file
BIN
doc/_static/show_jupyter.PNG
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
BIN
doc/_static/show_vtk.PNG
vendored
Normal file
BIN
doc/_static/show_vtk.PNG
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
@ -41,6 +41,7 @@ Table Of Contents
|
|||||||
sketch.rst
|
sketch.rst
|
||||||
assy.rst
|
assy.rst
|
||||||
free-func.rst
|
free-func.rst
|
||||||
|
vis.rst
|
||||||
fileformat.rst
|
fileformat.rst
|
||||||
examples.rst
|
examples.rst
|
||||||
apireference.rst
|
apireference.rst
|
||||||
|
|||||||
293
doc/primer.rst
293
doc/primer.rst
@ -27,157 +27,24 @@ kernel, there is another set of Geometrical constructs involved as well. For exa
|
|||||||
hold a reference to an underlying curve that is a full circle, and each linear edge holds underneath it the equation
|
hold a reference to an underlying curve that is a full circle, and each linear edge holds underneath it the equation
|
||||||
for a line. CadQuery shields you from these constructs.
|
for a line. CadQuery shields you from these constructs.
|
||||||
|
|
||||||
|
|
||||||
Workplane class
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
The Workplane class contains the currently selected objects (a list of Shapes, Vectors or Locations
|
|
||||||
in the :attr:`~cadquery.Workplane.objects` attribute), the modelling context (in the
|
|
||||||
:attr:`~cadquery.Workplane.ctx` attribute), and CadQuery's fluent api methods. It is the main class
|
|
||||||
that users will instantiate.
|
|
||||||
|
|
||||||
See :ref:`apireference` to learn more.
|
|
||||||
|
|
||||||
|
|
||||||
Workplanes
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Most CAD programs use the concept of Workplanes. If you have experience with other CAD programs you will probably
|
|
||||||
feel comfortable with CadQuery's Workplanes, but if you don't have experience then they are an essential concept to
|
|
||||||
understand.
|
|
||||||
|
|
||||||
Workplanes represent a plane in space, from which other features can be located. They have a center point and a local
|
|
||||||
coordinate system. Most methods that create an object do so relative to the current workplane.
|
|
||||||
|
|
||||||
Usually the first workplane created is the "XY" plane, also known as the "front" plane. Once a solid is defined the most
|
|
||||||
common way to create a workplane is to select a face on the solid that you intend to modify and create a new workplane
|
|
||||||
relative to it. You can also create new workplanes in anywhere in world coordinate system, or relative to other planes
|
|
||||||
using offsets or rotations.
|
|
||||||
|
|
||||||
The most powerful feature of workplanes is that they allow you to work in 2D space in the coordinate system of the
|
|
||||||
workplane, and then CadQuery will transform these points from the workplane coordinate system to the world coordinate
|
|
||||||
system so your 3D features are located where you intended. This makes scripts much easier to create and maintain.
|
|
||||||
|
|
||||||
See :py:class:`cadquery.Workplane` to learn more.
|
|
||||||
|
|
||||||
|
|
||||||
2D Construction
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Once you create a workplane, you can work in 2D, and then later use the features you create to make 3D objects.
|
|
||||||
You'll find all of the 2D constructs you expect -- circles, lines, arcs, mirroring, points, etc.
|
|
||||||
|
|
||||||
See :ref:`2dOperations` to learn more.
|
|
||||||
|
|
||||||
|
|
||||||
3D Construction
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
You can construct 3D primitives such as boxes, wedges, cylinders and spheres directly. You can also sweep, extrude,
|
|
||||||
and loft 2D geometry to form 3D features. Of course the basic primitive operations are also available.
|
|
||||||
|
|
||||||
See :ref:`3doperations` to learn more.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Selectors
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Selectors allow you to select one or more features, in order to define new features. As an example, you might
|
|
||||||
extrude a box, and then select the top face as the location for a new feature. Or, you might extrude a box, and
|
|
||||||
then select all of the vertical edges so that you can apply a fillet to them.
|
|
||||||
|
|
||||||
You can select Vertices, Edges, Faces, Solids, and Wires using selectors.
|
|
||||||
|
|
||||||
Think of selectors as the equivalent of your hand and mouse, if you were to build an object using a conventional CAD system.
|
|
||||||
|
|
||||||
See :ref:`selectors` to learn more.
|
|
||||||
|
|
||||||
|
|
||||||
Construction Geometry
|
|
||||||
---------------------------
|
|
||||||
Construction geometry are features that are not part of the object, but are only defined to aid in building the object.
|
|
||||||
A common example might be to define a rectangle, and then use the corners to define the location of a set of holes.
|
|
||||||
|
|
||||||
Most CadQuery construction methods provide a ``forConstruction`` keyword, which creates a feature that will only be used
|
|
||||||
to locate other features.
|
|
||||||
|
|
||||||
|
|
||||||
The Stack
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
As you work in CadQuery, each operation returns a new Workplane object with the result of that
|
|
||||||
operations. Each Workplane object has a list of objects, and a reference to its parent.
|
|
||||||
|
|
||||||
You can always go backwards to older operations by removing the current object from the stack. For example::
|
|
||||||
|
|
||||||
Workplane(someObject).faces(">Z").first().vertices()
|
|
||||||
|
|
||||||
returns a CadQuery object that contains all of the vertices on the highest face of someObject. But you can always move
|
|
||||||
backwards in the stack to get the face as well::
|
|
||||||
|
|
||||||
Workplane(someObject).faces(">Z").first().vertices().end()
|
|
||||||
|
|
||||||
You can browse stack access methods here: :ref:`stackMethods`.
|
|
||||||
|
|
||||||
|
|
||||||
.. _chaining:
|
|
||||||
|
|
||||||
Chaining
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
All Workplane methods return another Workplane object, so that you can chain the methods together
|
|
||||||
fluently. Use the core Workplane methods to get at the objects that were created.
|
|
||||||
|
|
||||||
Each time a new Workplane object is produced during these chained calls, it has a
|
|
||||||
:attr:`~cadquery.Workplane.parent` attribute that points to the Workplane object that created it.
|
|
||||||
Several CadQuery methods search this parent chain, for example when searching for the context solid.
|
|
||||||
You can also give a Workplane object a tag, and further down your chain of calls you can refer back
|
|
||||||
to this particular object using its tag.
|
|
||||||
|
|
||||||
|
|
||||||
The Context Solid
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Most of the time, you are building a single object, and adding features to that single object. CadQuery watches
|
|
||||||
your operations, and defines the first solid object created as the 'context solid'. After that, any features
|
|
||||||
you create are automatically combined (unless you specify otherwise) with that solid. This happens even if the
|
|
||||||
solid was created a long way up in the stack. For example::
|
|
||||||
|
|
||||||
Workplane("XY").box(1, 2, 3).faces(">Z").circle(0.25).extrude(1)
|
|
||||||
|
|
||||||
Will create a 1x2x3 box, with a cylindrical boss extending from the top face. It was not necessary to manually
|
|
||||||
combine the cylinder created by extruding the circle with the box, because the default behavior for extrude is
|
|
||||||
to combine the result with the context solid. The hole() method works similarly -- CadQuery presumes that you want
|
|
||||||
to subtract the hole from the context solid.
|
|
||||||
|
|
||||||
If you want to avoid this, you can specify ``combine=False``, and CadQuery will create the solid separately.
|
|
||||||
|
|
||||||
|
|
||||||
Iteration
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
CAD models often have repeated geometry, and its really annoying to resort to for loops to construct features.
|
|
||||||
Many CadQuery methods operate automatically on each element on the stack, so that you don't have to write loops.
|
|
||||||
For example, this::
|
|
||||||
|
|
||||||
Workplane("XY").box(1, 2, 3).faces(">Z").vertices().circle(0.5)
|
|
||||||
|
|
||||||
Will actually create 4 circles, because ``vertices()`` selects 4 vertices of a rectangular face, and the ``circle()`` method
|
|
||||||
iterates on each member of the stack.
|
|
||||||
|
|
||||||
This is really useful to remember when you author your own plugins. :py:meth:`cadquery.Workplane.each` is useful for this purpose.
|
|
||||||
|
|
||||||
CadQuery API layers
|
CadQuery API layers
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Once you start to dive a bit more into CadQuery, you may find yourself a bit confused juggling between different types of objects the CadQuery APIs can return.
|
Once you start to dive a bit more into CadQuery, you may find yourself a bit confused juggling between different types of objects the CadQuery APIs can return.
|
||||||
This chapter aims to give an explanation on this topic and to provide background on the underlying implementation and kernel layers so you can leverage more of CadQuery functionality.
|
This chapter aims to give an explanation on this topic and to provide background on the underlying implementation and kernel layers so you can leverage more of CadQuery functionality.
|
||||||
|
|
||||||
CadQuery is composed of 3 different API, which are implemented on top of each other.
|
CadQuery is composed of 4 different API, which are implemented on top of each other.
|
||||||
|
|
||||||
1. The Fluent API
|
1. The Fluent API
|
||||||
|
#. :class:`~cadquery.Workplane`
|
||||||
|
#. :class:`~cadquery.Sketch`
|
||||||
|
#. :class:`~cadquery.Assembly`
|
||||||
2. The Direct API
|
2. The Direct API
|
||||||
|
#. :class:`~cadquery.Shape`
|
||||||
|
2. The Geometry API
|
||||||
|
#. :class:`~cadquery.Vector`
|
||||||
|
#. :class:`~cadquery.Plane`
|
||||||
|
#. :class:`~cadquery.Location`
|
||||||
3. The OCCT API
|
3. The OCCT API
|
||||||
|
|
||||||
The Fluent API
|
The Fluent API
|
||||||
@ -799,6 +666,148 @@ that is shown in the 3D view above.
|
|||||||
rather than the object from the previous step.
|
rather than the object from the previous step.
|
||||||
|
|
||||||
|
|
||||||
|
Workplane class
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
The Workplane class contains the currently selected objects (a list of Shapes, Vectors or Locations
|
||||||
|
in the :attr:`~cadquery.Workplane.objects` attribute), the modelling context (in the
|
||||||
|
:attr:`~cadquery.Workplane.ctx` attribute), and CadQuery's fluent api methods. It is the main class
|
||||||
|
that users will instantiate.
|
||||||
|
|
||||||
|
See :ref:`apireference` to learn more.
|
||||||
|
|
||||||
|
|
||||||
|
Workplanes
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Most CAD programs use the concept of Workplanes. If you have experience with other CAD programs you will probably
|
||||||
|
feel comfortable with CadQuery's Workplanes, but if you don't have experience then they are an essential concept to
|
||||||
|
understand.
|
||||||
|
|
||||||
|
Workplanes represent a plane in space, from which other features can be located. They have a center point and a local
|
||||||
|
coordinate system. Most methods that create an object do so relative to the current workplane.
|
||||||
|
|
||||||
|
Usually the first workplane created is the "XY" plane, also known as the "front" plane. Once a solid is defined the most
|
||||||
|
common way to create a workplane is to select a face on the solid that you intend to modify and create a new workplane
|
||||||
|
relative to it. You can also create new workplanes anywhere in the world coordinate system, or relative to other planes
|
||||||
|
using offsets or rotations.
|
||||||
|
|
||||||
|
The most powerful feature of workplanes is that they allow you to work in 2D space in the coordinate system of the
|
||||||
|
workplane, and then CadQuery will transform these points from the workplane coordinate system to the world coordinate
|
||||||
|
system so your 3D features are located where you intended. This makes scripts much easier to create and maintain.
|
||||||
|
|
||||||
|
See :py:class:`cadquery.Workplane` to learn more.
|
||||||
|
|
||||||
|
|
||||||
|
2D Construction
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Once you create a workplane, you can work in 2D, and then later use the features you create to make 3D objects.
|
||||||
|
You'll find all of the 2D constructs you expect -- circles, lines, arcs, mirroring, points, etc.
|
||||||
|
|
||||||
|
See :ref:`2dOperations` to learn more.
|
||||||
|
|
||||||
|
|
||||||
|
3D Construction
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
You can construct 3D primitives such as boxes, wedges, cylinders and spheres directly. You can also sweep, extrude,
|
||||||
|
and loft 2D geometry to form 3D features. Of course the basic primitive operations are also available.
|
||||||
|
|
||||||
|
See :ref:`3doperations` to learn more.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Selectors
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Selectors allow you to select one or more features, in order to define new features. As an example, you might
|
||||||
|
extrude a box, and then select the top face as the location for a new feature. Or, you might extrude a box, and
|
||||||
|
then select all of the vertical edges so that you can apply a fillet to them.
|
||||||
|
|
||||||
|
You can select Vertices, Edges, Faces, Solids, and Wires using selectors.
|
||||||
|
|
||||||
|
Think of selectors as the equivalent of your hand and mouse, if you were to build an object using a conventional CAD system.
|
||||||
|
|
||||||
|
See :ref:`selectors` to learn more.
|
||||||
|
|
||||||
|
|
||||||
|
Construction Geometry
|
||||||
|
---------------------------
|
||||||
|
Construction geometry are features that are not part of the object, but are only defined to aid in building the object.
|
||||||
|
A common example might be to define a rectangle, and then use the corners to define the location of a set of holes.
|
||||||
|
|
||||||
|
Most CadQuery construction methods provide a ``forConstruction`` keyword, which creates a feature that will only be used
|
||||||
|
to locate other features.
|
||||||
|
|
||||||
|
|
||||||
|
The Stack
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
As you work in CadQuery, each operation returns a new Workplane object with the result of that
|
||||||
|
operations. Each Workplane object has a list of objects, and a reference to its parent.
|
||||||
|
|
||||||
|
You can always go backwards to older operations by removing the current object from the stack. For example::
|
||||||
|
|
||||||
|
Workplane(someObject).faces(">Z").first().vertices()
|
||||||
|
|
||||||
|
returns a CadQuery object that contains all of the vertices on the highest face of someObject. But you can always move
|
||||||
|
backwards in the stack to get the face as well::
|
||||||
|
|
||||||
|
Workplane(someObject).faces(">Z").first().vertices().end()
|
||||||
|
|
||||||
|
You can browse stack access methods here: :ref:`stackMethods`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _chaining:
|
||||||
|
|
||||||
|
Chaining
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
All Workplane methods return another Workplane object, so that you can chain the methods together
|
||||||
|
fluently. Use the core Workplane methods to get at the objects that were created.
|
||||||
|
|
||||||
|
Each time a new Workplane object is produced during these chained calls, it has a
|
||||||
|
:attr:`~cadquery.Workplane.parent` attribute that points to the Workplane object that created it.
|
||||||
|
Several CadQuery methods search this parent chain, for example when searching for the context solid.
|
||||||
|
You can also give a Workplane object a tag, and further down your chain of calls you can refer back
|
||||||
|
to this particular object using its tag.
|
||||||
|
|
||||||
|
|
||||||
|
The Context Solid
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Most of the time, you are building a single object, and adding features to that single object. CadQuery watches
|
||||||
|
your operations, and defines the first solid object created as the 'context solid'. After that, any features
|
||||||
|
you create are automatically combined (unless you specify otherwise) with that solid. This happens even if the
|
||||||
|
solid was created a long way up in the stack. For example::
|
||||||
|
|
||||||
|
Workplane("XY").box(1, 2, 3).faces(">Z").circle(0.25).extrude(1)
|
||||||
|
|
||||||
|
Will create a 1x2x3 box, with a cylindrical boss extending from the top face. It was not necessary to manually
|
||||||
|
combine the cylinder created by extruding the circle with the box, because the default behavior for extrude is
|
||||||
|
to combine the result with the context solid. The hole() method works similarly -- CadQuery presumes that you want
|
||||||
|
to subtract the hole from the context solid.
|
||||||
|
|
||||||
|
If you want to avoid this, you can specify ``combine=False``, and CadQuery will create the solid separately.
|
||||||
|
|
||||||
|
|
||||||
|
Iteration
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
CAD models often have repeated geometry, and its really annoying to resort to for loops to construct features.
|
||||||
|
Many CadQuery methods operate automatically on each element on the stack, so that you don't have to write loops.
|
||||||
|
For example, this::
|
||||||
|
|
||||||
|
Workplane("XY").box(1, 2, 3).faces(">Z").vertices().circle(0.5)
|
||||||
|
|
||||||
|
Will actually create 4 circles, because ``vertices()`` selects 4 vertices of a rectangular face, and the ``circle()`` method
|
||||||
|
iterates on each member of the stack.
|
||||||
|
|
||||||
|
This is really useful to remember when you author your own plugins. :py:meth:`cadquery.Workplane.each` is useful for this purpose.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Assemblies
|
Assemblies
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
94
doc/vis.rst
Normal file
94
doc/vis.rst
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
.. _vis:
|
||||||
|
|
||||||
|
===========================
|
||||||
|
Visualization
|
||||||
|
===========================
|
||||||
|
|
||||||
|
|
||||||
|
Pure Python
|
||||||
|
===========
|
||||||
|
|
||||||
|
Since version 2.4 CadQuery supports visualization without any external tools. Those facilities are based on the VTK library
|
||||||
|
and are not tied to any external tool.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from cadquery import *
|
||||||
|
from cadquery.vis import show
|
||||||
|
|
||||||
|
w = Workplane().sphere(1).split(keepBottom=True) - Workplane().sphere(0.5)
|
||||||
|
r = w.faces('>Z').fillet(0.1)
|
||||||
|
|
||||||
|
# Show the result
|
||||||
|
show(r, alpha=0.5)
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: _static/show.PNG
|
||||||
|
|
||||||
|
|
||||||
|
One can visualize objects of type :class:`~cadquery.Workplane`, :class:`~cadquery.Sketch`, :class:`~cadquery.Assembly`, :class:`~cadquery.Shape`,
|
||||||
|
:class:`~cadquery.Vector`, :class:`~cadquery.Location` and lists thereof.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from cadquery import *
|
||||||
|
from cadquery.occ_impl.shapes import *
|
||||||
|
from cadquery.vis import show
|
||||||
|
|
||||||
|
w = Workplane().sphere(0.5).split(keepTop=True)
|
||||||
|
sk = Sketch().rect(1.5, 1.5)
|
||||||
|
sh = torus(5, 0.5)
|
||||||
|
|
||||||
|
r = rect(2, 2)
|
||||||
|
c = circle(2)
|
||||||
|
|
||||||
|
N = 50
|
||||||
|
params = [i/N for i in range(N)]
|
||||||
|
|
||||||
|
vecs = r.positions(params)
|
||||||
|
locs = c.locations(params)
|
||||||
|
|
||||||
|
# Render the solid
|
||||||
|
show(w, sk, sh, vecs, locs)
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: _static/show_demo.PNG
|
||||||
|
|
||||||
|
|
||||||
|
Additionally it is possible to integrate with other libraries using VTK and display any `vtkProp` object.
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from cadquery.vis import show
|
||||||
|
from cadquery.occ_impl.shapes import torus
|
||||||
|
|
||||||
|
from vtkmodules.vtkRenderingAnnotation import vtkAnnotatedCubeActor
|
||||||
|
|
||||||
|
|
||||||
|
a = vtkAnnotatedCubeActor()
|
||||||
|
t = torus(5,1)
|
||||||
|
|
||||||
|
show(t, a)
|
||||||
|
|
||||||
|
.. image:: _static/show_vtk.PNG
|
||||||
|
|
||||||
|
|
||||||
|
Note that currently the show function is blocking.
|
||||||
|
|
||||||
|
|
||||||
|
Jupyter/JupterLab
|
||||||
|
=================
|
||||||
|
|
||||||
|
There is also more limited support for displaying :class:`~cadquery.Workplane`, :class:`~cadquery.Sketch`, :class:`~cadquery.Assembly`,
|
||||||
|
:class:`~cadquery.Shape` in Jupyter and JupyterLab. This functionality is implemented using VTK.js.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from cadquery import *
|
||||||
|
|
||||||
|
Workplane().sphere(1).split(keepTop=True)
|
||||||
|
|
||||||
|
.. image:: _static/show_jupyter.PNG
|
||||||
|
|
||||||
@ -1,12 +1,12 @@
|
|||||||
from cadquery import Workplane, Assembly, Sketch, Location, Vector, Location
|
from cadquery import Workplane, Assembly, Sketch, Location, Vector
|
||||||
from cadquery.vis import show, show_object
|
from cadquery.vis import show, show_object, vtkAxesActor
|
||||||
|
|
||||||
import cadquery.occ_impl.exporters.assembly as assembly
|
|
||||||
import cadquery.vis as vis
|
import cadquery.vis as vis
|
||||||
|
|
||||||
from vtkmodules.vtkRenderingCore import vtkRenderWindow, vtkRenderWindowInteractor
|
from vtkmodules.vtkRenderingCore import vtkRenderWindow, vtkRenderWindowInteractor
|
||||||
|
from vtkmodules.vtkRenderingAnnotation import vtkAnnotatedCubeActor
|
||||||
|
|
||||||
from pytest import fixture, raises
|
from pytest import fixture
|
||||||
|
|
||||||
|
|
||||||
@fixture
|
@fixture
|
||||||
@ -59,7 +59,7 @@ def test_show(wp, assy, sk, monkeypatch):
|
|||||||
|
|
||||||
# use some dummy vtk objects
|
# use some dummy vtk objects
|
||||||
monkeypatch.setattr(vis, "vtkRenderWindowInteractor", FakeInteractor)
|
monkeypatch.setattr(vis, "vtkRenderWindowInteractor", FakeInteractor)
|
||||||
monkeypatch.setattr(assembly, "vtkRenderWindow", FakeWindow)
|
monkeypatch.setattr(vis, "vtkRenderWindow", FakeWindow)
|
||||||
|
|
||||||
# simple smoke test
|
# simple smoke test
|
||||||
show(wp)
|
show(wp)
|
||||||
@ -89,3 +89,6 @@ def test_show(wp, assy, sk, monkeypatch):
|
|||||||
|
|
||||||
# for now a workaround to be compatible with more complicated CQ-editor invocations
|
# for now a workaround to be compatible with more complicated CQ-editor invocations
|
||||||
show(1)
|
show(1)
|
||||||
|
|
||||||
|
# show a raw vtkProp
|
||||||
|
show(vtkAxesActor(), [vtkAnnotatedCubeActor()])
|
||||||
|
|||||||
Reference in New Issue
Block a user