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: set "PATH=%MINICONDA_DIRNAME%;%MINICONDA_DIRNAME%\\Scripts;%PATH%"
|
||||
- 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: bash miniconda.sh -b -p $HOME/miniconda;
|
||||
- sh: export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH";
|
||||
- sh: source $HOME/miniconda/bin/activate
|
||||
- sh: export MAMBA_ROOT_PREFIX=$HOME/miniconda
|
||||
|
||||
install:
|
||||
- conda config --set always_yes yes
|
||||
- mamba env create -f environment.yml
|
||||
- conda activate cadquery
|
||||
- conda list
|
||||
- mamba list -n cadquery
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- black . --diff --check
|
||||
- mypy cadquery
|
||||
- pytest -v --cov
|
||||
- mamba run -n cadquery black . --diff --check
|
||||
- mamba run -n cadquery mypy cadquery
|
||||
- mamba run -n cadquery pytest -v --cov
|
||||
|
||||
on_success:
|
||||
- codecov
|
||||
- mamba run -n cadquery codecov
|
||||
|
||||
#on_finish:
|
||||
# - 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 .occ_impl.exporters.assembly import _vtkRenderWindow
|
||||
from .occ_impl.assembly import _loc2vtk
|
||||
from .occ_impl.assembly import _loc2vtk, toVTK
|
||||
|
||||
from typing import Union, Any, List, Tuple
|
||||
|
||||
@ -15,8 +14,10 @@ from vtkmodules.vtkRenderingCore import (
|
||||
vtkMapper,
|
||||
vtkRenderWindowInteractor,
|
||||
vtkActor,
|
||||
vtkProp,
|
||||
vtkPolyDataMapper,
|
||||
vtkAssembly,
|
||||
vtkRenderWindow,
|
||||
)
|
||||
from vtkmodules.vtkCommonCore import vtkPoints
|
||||
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData
|
||||
@ -27,8 +28,14 @@ DEFAULT_COLOR = [1, 0.8, 0, 1]
|
||||
DEFAULT_PT_SIZE = 7.5
|
||||
DEFAULT_PT_COLOR = "darkviolet"
|
||||
|
||||
SPECULAR = 0.3
|
||||
SPECULAR_POWER = 100
|
||||
SPECULAR_COLOR = vtkNamedColors().GetColor3d("White")
|
||||
|
||||
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:
|
||||
@ -50,7 +57,9 @@ def _to_assy(*objs: ShapeLike, alpha: float = 1) -> Assembly:
|
||||
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.
|
||||
"""
|
||||
@ -58,6 +67,7 @@ def _split_showables(objs) -> Tuple[List[ShapeLike], List[Vector], List[Location
|
||||
rv_s: List[ShapeLike] = []
|
||||
rv_v: List[Vector] = []
|
||||
rv_l: List[Location] = []
|
||||
rv_a: List[vtkProp] = []
|
||||
|
||||
for el in objs:
|
||||
if instance_of(el, ShapeLike):
|
||||
@ -66,21 +76,24 @@ def _split_showables(objs) -> Tuple[List[ShapeLike], List[Vector], List[Location
|
||||
rv_v.append(el)
|
||||
elif isinstance(el, Location):
|
||||
rv_l.append(el)
|
||||
elif isinstance(el, vtkProp):
|
||||
rv_a.append(el)
|
||||
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_v.extend(tmp2)
|
||||
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(
|
||||
vecs: List[Vector], size: float = DEFAULT_PT_SIZE, color: str = DEFAULT_PT_COLOR
|
||||
) -> vtkActor:
|
||||
"""
|
||||
Convert vectors to vtkActor.
|
||||
Convert Vectors to vtkActor.
|
||||
"""
|
||||
|
||||
rv = vtkActor()
|
||||
@ -110,7 +123,7 @@ def _to_vtk_pts(
|
||||
|
||||
def _to_vtk_axs(locs: List[Location], scale: float = 0.1) -> vtkActor:
|
||||
"""
|
||||
Convert vectors to vtkActor.
|
||||
Convert Locations to vtkActor.
|
||||
"""
|
||||
|
||||
rv = vtkAssembly()
|
||||
@ -135,6 +148,8 @@ def show(
|
||||
alpha: float = 1,
|
||||
tolerance: float = 1e-3,
|
||||
edges: bool = False,
|
||||
specular: bool = True,
|
||||
title: str = "CQ viewer",
|
||||
**kwrags: Any,
|
||||
):
|
||||
"""
|
||||
@ -142,7 +157,7 @@ def show(
|
||||
"""
|
||||
|
||||
# split objects
|
||||
shapes, vecs, locs = _split_showables(objs)
|
||||
shapes, vecs, locs, props = _split_showables(objs)
|
||||
|
||||
# construct the assy
|
||||
assy = _to_assy(*shapes, alpha=alpha)
|
||||
@ -151,19 +166,28 @@ def show(
|
||||
pts = _to_vtk_pts(vecs)
|
||||
axs = _to_vtk_axs(locs, scale=scale)
|
||||
|
||||
# create a VTK window
|
||||
win = _vtkRenderWindow(assy, tolerance=tolerance)
|
||||
# assy+renderer
|
||||
renderer = toVTK(assy, tolerance=tolerance)
|
||||
|
||||
win.SetWindowName("CQ viewer")
|
||||
# VTK window boilerplate
|
||||
win = vtkRenderWindow()
|
||||
win.SetWindowName(title)
|
||||
win.AddRenderer(renderer)
|
||||
|
||||
# get renderer and actor
|
||||
if edges:
|
||||
ren = win.GetRenderers().GetFirstRenderer()
|
||||
for act in ren.GetActors():
|
||||
act.GetProperty().EdgeVisibilityOn()
|
||||
for act in renderer.GetActors():
|
||||
|
||||
propt = act.GetProperty()
|
||||
|
||||
if edges:
|
||||
propt.EdgeVisibilityOn()
|
||||
|
||||
if specular:
|
||||
propt.SetSpecular(SPECULAR)
|
||||
propt.SetSpecularPower(SPECULAR_POWER)
|
||||
propt.SetSpecularColor(SPECULAR_COLOR)
|
||||
|
||||
# rendering related settings
|
||||
win.SetMultiSamples(16)
|
||||
vtkMapper.SetResolveCoincidentTopologyToPolygonOffset()
|
||||
vtkMapper.SetResolveCoincidentTopologyPolygonOffsetParameters(1, 0)
|
||||
vtkMapper.SetResolveCoincidentTopologyLineOffsetParameters(-1, 0)
|
||||
@ -193,7 +217,7 @@ def show(
|
||||
orient_widget.InteractiveOff()
|
||||
|
||||
# use gradient background
|
||||
renderer = win.GetRenderers().GetFirstRenderer()
|
||||
renderer.SetBackground(1, 1, 1)
|
||||
renderer.GradientBackgroundOn()
|
||||
|
||||
# use FXXAA
|
||||
@ -209,9 +233,15 @@ def show(
|
||||
renderer.AddActor(pts)
|
||||
renderer.AddActor(axs)
|
||||
|
||||
# add other vtk actors
|
||||
for p in props:
|
||||
renderer.AddActor(p)
|
||||
|
||||
# initialize and set size
|
||||
inter.Initialize()
|
||||
win.SetSize(*win.GetScreenSize())
|
||||
|
||||
w, h = win.GetScreenSize()
|
||||
win.SetSize((w // 2, h // 2))
|
||||
win.SetPosition(-10, 0)
|
||||
|
||||
# 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
|
||||
assy.rst
|
||||
free-func.rst
|
||||
vis.rst
|
||||
fileformat.rst
|
||||
examples.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
|
||||
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
|
||||
---------------------------
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
#. :class:`~cadquery.Workplane`
|
||||
#. :class:`~cadquery.Sketch`
|
||||
#. :class:`~cadquery.Assembly`
|
||||
2. The Direct API
|
||||
#. :class:`~cadquery.Shape`
|
||||
2. The Geometry API
|
||||
#. :class:`~cadquery.Vector`
|
||||
#. :class:`~cadquery.Plane`
|
||||
#. :class:`~cadquery.Location`
|
||||
3. The OCCT API
|
||||
|
||||
The Fluent API
|
||||
@ -799,6 +666,148 @@ that is shown in the 3D view above.
|
||||
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
|
||||
----------
|
||||
|
||||
|
||||
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.vis import show, show_object
|
||||
from cadquery import Workplane, Assembly, Sketch, Location, Vector
|
||||
from cadquery.vis import show, show_object, vtkAxesActor
|
||||
|
||||
import cadquery.occ_impl.exporters.assembly as assembly
|
||||
import cadquery.vis as vis
|
||||
|
||||
from vtkmodules.vtkRenderingCore import vtkRenderWindow, vtkRenderWindowInteractor
|
||||
from vtkmodules.vtkRenderingAnnotation import vtkAnnotatedCubeActor
|
||||
|
||||
from pytest import fixture, raises
|
||||
from pytest import fixture
|
||||
|
||||
|
||||
@fixture
|
||||
@ -59,7 +59,7 @@ def test_show(wp, assy, sk, monkeypatch):
|
||||
|
||||
# use some dummy vtk objects
|
||||
monkeypatch.setattr(vis, "vtkRenderWindowInteractor", FakeInteractor)
|
||||
monkeypatch.setattr(assembly, "vtkRenderWindow", FakeWindow)
|
||||
monkeypatch.setattr(vis, "vtkRenderWindow", FakeWindow)
|
||||
|
||||
# simple smoke test
|
||||
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
|
||||
show(1)
|
||||
|
||||
# show a raw vtkProp
|
||||
show(vtkAxesActor(), [vtkAnnotatedCubeActor()])
|
||||
|
||||
Reference in New Issue
Block a user