Skip to content

Commit 1508f15

Browse files
Merge branch 'master' into master
2 parents e87279d + 94030ad commit 1508f15

30 files changed

+1576
-270
lines changed

.github/ISSUE_TEMPLATE/modelling-question.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ are appreciated.
1818
If you want to discuss something in a more casual environment, we have other options under [Getting help in the README.md](https://github.com/CadQuery/cadquery#getting-help). There are also additional examples in the [cadquery-contrib repository](https://github.com/CadQuery/cadquery-contrib).
1919
-->
2020

21-
<!-- We are all volunteers here, you can help us help you by making this question quick to answer. Minimal examples, trimmed of all unreleated code are appreciated. It also helps if you provide code that can be cut and pasted into CQ-Editor. ie. instead of:
21+
<!-- We are all volunteers here, you can help us help you by making this question quick to answer. Minimal examples, trimmed of all unrelated code are appreciated. It also helps if you provide code that can be cut and pasted into CQ-Editor. ie. instead of:
2222
2323
> I have a box, how to I fillet a top corner?
2424

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ target/*
99
MANIFEST
1010
out.*
1111
res?.dxf
12+
.~*
13+
assy.wrl
14+
assy.xml
15+
assy.zip
16+
nested.step
17+
simple.caf
18+
simple.step
19+
simple.stp
20+
simple.xml
21+
test.brep

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ CadQuery is an intuitive, easy-to-use Python module for building parametric 3D C
1515
CadQuery is often compared to [OpenSCAD](http://www.openscad.org/). Like OpenSCAD, CadQuery is an open-source, script based, parametric model generator. However, CadQuery stands out in many ways and has several key advantages:
1616

1717
1. The scripts use a standard programming language, Python, and thus can benefit from the associated infrastructure. This includes many standard libraries and IDEs.
18-
2. CadQuery's CAD kernel Open CASCADE Technology (OCCT) is much more powerful than CGAL. Features supported natively by OCCT include NURBS, splines, surface sewing, STL repair, STEP import/export, and other complex operations, in addition to the standard CSG operations supported by CGAL
19-
3. Ability to import/export STEP and the ability to begin with a STEP model, created in a CAD package, and then add parametric features. This is possible in OpenSCAD using STL, but STL is a lossy format.
18+
2. CadQuery's CAD kernel Open CASCADE Technology ([OCCT](https://en.wikipedia.org/wiki/Open_Cascade_Technology)) is much more powerful than the [CGAL](https://en.wikipedia.org/wiki/CGAL) used by OpenSCAD. Features supported natively by OCCT include NURBS, splines, surface sewing, STL repair, STEP import/export, and other complex operations, in addition to the standard CSG operations supported by CGAL
19+
3. Ability to import/export [STEP](https://en.wikipedia.org/wiki/ISO_10303) and the ability to begin with a STEP model, created in a CAD package, and then add parametric features. This is possible in OpenSCAD using STL, but STL is a lossy format.
2020
4. CadQuery scripts require less code to create most objects, because it is possible to locate features based on the position of other features, workplanes, vertices, etc.
2121
5. CadQuery scripts can build STL, STEP, and AMF faster than OpenSCAD.
2222

@@ -25,7 +25,7 @@ CadQuery is often compared to [OpenSCAD](http://www.openscad.org/). Like OpenSCA
2525
* Create parametric models that can be very easily customized by end users.
2626
* Output high quality (loss-less) CAD formats like STEP and DXF in addition to STL, VRML and AMF.
2727
* Provide a non-proprietary, plain text model format that can be edited and executed with only a web browser.
28-
* Offer advanced modeling capabilities such as fillets, curvelinear extrudes, parametric curves and lofts.
28+
* Offer advanced modeling capabilities such as fillets, curvilinear extrudes, parametric curves and lofts.
2929
* Build nested assemblies out of individual parts and other assemblies.
3030

3131
### Why this fork
@@ -175,7 +175,7 @@ If you are going to contribute code, make sure to follow this steps:
175175
start working on your changes
176176
- Create a conda development environment with something like:
177177
- `conda env create -n cq-dev -f environment.yml`
178-
- Activate the new conda enviornment:
178+
- Activate the new conda environment:
179179
- `conda activate cq-dev`
180180
- If desired, install the master branch of cq-editor (Note; a release version may not be compatible with the master branch of cadquery):
181181
- `conda install -c cadquery -c conda-forge cq-editor=master`

cadquery/assembly.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
PATH_DELIM = "/"
2828

29-
# enitity selector grammar definiiton
29+
# entity selector grammar definiiton
3030
def _define_grammar():
3131

3232
from pyparsing import (
@@ -83,11 +83,11 @@ def __init__(
8383
"""
8484
Construct a constraint.
8585
86-
:param objects: object names refernced in the constraint
86+
:param objects: object names referenced in the constraint
8787
:param args: subshapes (e.g. faces or edges) of the objects
8888
:param sublocs: locations of the objects (only relevant if the objects are nested in a sub-assembly)
8989
:param kind: constraint kind
90-
:param param: optional arbitrary paramter passed to the solver
90+
:param param: optional arbitrary parameter passed to the solver
9191
"""
9292

9393
self.objects = objects
@@ -357,7 +357,7 @@ def _subloc(self, name: str) -> Tuple[Location, str]:
357357
"""
358358
Calculate relative location of an object in a subassembly.
359359
360-
Returns the relative posiitons as well as the name of the top assembly.
360+
Returns the relative positions as well as the name of the top assembly.
361361
"""
362362

363363
rv = Location()
@@ -411,7 +411,7 @@ def constrain(self, *args, param=None):
411411
elif len(args) == 6:
412412
id1, s1, id2, s2, kind, param = args
413413
else:
414-
raise ValueError(f"Incompatibile arguments: {args}")
414+
raise ValueError(f"Incompatible arguments: {args}")
415415

416416
loc1, id1_top = self._subloc(id1)
417417
loc2, id2_top = self._subloc(id2)

cadquery/cq.py

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def popPendingWires(self, errorOnEmpty: bool = True) -> List[Wire]:
130130

131131
class Workplane(object):
132132
"""
133-
Defines a coordinate system in space, in which 2-d coordinates can be used.
133+
Defines a coordinate system in space, in which 2D coordinates can be used.
134134
135135
:param plane: the plane in which the workplane will be done
136136
:type plane: a Plane object, or a string in (XY|YZ|XZ|front|back|top|bottom|left|right)
@@ -305,7 +305,7 @@ def split(self: T, *args, **kwargs) -> T:
305305
)
306306
rv = [solid.split(*tools)]
307307

308-
# split using the current wokrplane
308+
# split using the current workplane
309309
else:
310310

311311
# boilerplate for arg/kwarg parsing
@@ -501,7 +501,7 @@ def workplane(
501501
origin: Optional[VectorLike] = None,
502502
) -> T:
503503
"""
504-
Creates a new 2-D workplane, located relative to the first face on the stack.
504+
Creates a new 2D workplane, located relative to the first face on the stack.
505505
506506
:param offset: offset for the work plane in the Z direction. Default
507507
:param invert: invert the Z direction from that of the face.
@@ -518,7 +518,7 @@ def workplane(
518518
item on the chain immediately before the vertex must be a
519519
face.
520520
521-
The result will be a 2-d working plane
521+
The result will be a 2D working plane
522522
with a new coordinate system set up as follows:
523523
524524
* The centerOption parameter sets how the center is defined.
@@ -1087,7 +1087,7 @@ def rotateAboutCenter(self: T, axisEndPoint: VectorLike, angleDegrees: float) ->
10871087
Future Enhancements:
10881088
* A version of this method that returns a transformed copy, rather than modifying
10891089
the originals
1090-
* This method doesnt expose a very good interface, because the axis of rotation
1090+
* This method doesn't expose a very good interface, because the axis of rotation
10911091
could be inconsistent between multiple objects. This is because the beginning
10921092
of the axis is variable, while the end is fixed. This is fine when operating on
10931093
one object, but is not cool for multiple.
@@ -1212,7 +1212,7 @@ def shell(
12121212
12131213
:param thickness: a positive float, representing the thickness of the desired shell.
12141214
Negative values shell inwards, positive values shell outwards.
1215-
:param kind: kind of joints, intersetion or arc (default: arc).
1215+
:param kind: kind of joints, intersection or arc (default: arc).
12161216
:raises ValueError: if the current stack contains objects that are not faces of a solid
12171217
further up in the chain.
12181218
:returns: a CQ object with the resulting shelled solid selected.
@@ -1531,7 +1531,7 @@ def polarArray(
15311531
def pushPoints(self: T, pntList: Iterable[Union[VectorLike, Location]]) -> T:
15321532
"""
15331533
Pushes a list of points onto the stack as vertices.
1534-
The points are in the 2-d coordinate space of the workplane face
1534+
The points are in the 2D coordinate space of the workplane face
15351535
15361536
:param pntList: a list of points to push onto the stack
15371537
:type pntList: list of 2-tuples, in *local* coordinates
@@ -1687,7 +1687,7 @@ def polarLineTo(
16871687
self: T, distance: float, angle: float, forConstruction: bool = False
16881688
) -> T:
16891689
"""
1690-
Make a line from the current point to the given polar co-ordinates
1690+
Make a line from the current point to the given polar coordinates
16911691
16921692
Useful if it is more convenient to specify the end location rather than
16931693
the distance and angle from the current point
@@ -1817,11 +1817,11 @@ def spline(
18171817
where a tangent constraint is specified.
18181818
:param periodic: creation of periodic curves
18191819
:param parameters: the value of the parameter at each interpolation point.
1820-
(The intepolated curve is represented as a vector-valued function of a
1820+
(The interpolated curve is represented as a vector-valued function of a
18211821
scalar parameter.)
18221822
18231823
If periodic == True, then len(parameters) must be
1824-
len(intepolation points) + 1, otherwise len(parameters) must be equal to
1824+
len(interpolation points) + 1, otherwise len(parameters) must be equal to
18251825
len(interpolation points).
18261826
:param scale: whether to scale the specified tangent vectors before
18271827
interpolating.
@@ -2356,11 +2356,11 @@ def wire(self: T, forConstruction: bool = False) -> T:
23562356
:param forConstruction: whether the wire should be used to make a solid, or if it is just
23572357
for reference
23582358
2359-
This method is primarily of use to plugin developers making utilities for 2-d construction.
2360-
This method should be called when a user operation implies that 2-d construction is
2359+
This method is primarily of use to plugin developers making utilities for 2D construction.
2360+
This method should be called when a user operation implies that 2D construction is
23612361
finished, and we are ready to begin working in 3d.
23622362
2363-
SEE '2-d construction concepts' for a more detailed explanation of how CadQuery handles
2363+
SEE '2D construction concepts' for a more detailed explanation of how CadQuery handles
23642364
edges, wires, etc.
23652365
23662366
Any non edges will still remain.
@@ -2693,11 +2693,11 @@ def polyline(
26932693

26942694
def close(self: T) -> T:
26952695
"""
2696-
End 2-d construction, and attempt to build a closed wire.
2696+
End 2D construction, and attempt to build a closed wire.
26972697
26982698
:return: a CQ object with a completed wire on the stack, if possible.
26992699
2700-
After 2-d drafting with methods such as lineTo, threePointArc,
2700+
After 2D drafting with methods such as lineTo, threePointArc,
27012701
tangentArcPoint and polyline, it is necessary to convert the edges
27022702
produced by these into one or more wires.
27032703
@@ -2710,7 +2710,7 @@ def close(self: T) -> T:
27102710
endPoint = self._findFromPoint(True)
27112711

27122712
if self.ctx.firstPoint is None:
2713-
raise ValueError("Not start point specified - cannot close")
2713+
raise ValueError("No start point specified - cannot close")
27142714
else:
27152715
startPoint = self.ctx.firstPoint
27162716

@@ -2767,7 +2767,7 @@ def cutEach(
27672767

27682768
return self.newObject([s])
27692769

2770-
# but parameter list is different so a simple function pointer wont work
2770+
# but parameter list is different so a simple function pointer won't work
27712771
def cboreHole(
27722772
self: T,
27732773
diameter: float,
@@ -2813,7 +2813,7 @@ def cboreHole(
28132813
# first make the hole
28142814
hole = Solid.makeCylinder(
28152815
diameter / 2.0, depth, center, boreDir
2816-
) # local coordianates!
2816+
) # local coordinates!
28172817

28182818
# add the counter bore
28192819
cbore = Solid.makeCylinder(cboreDiameter / 2.0, cboreDepth, Vector(), boreDir)
@@ -2822,7 +2822,7 @@ def cboreHole(
28222822
return self.cutEach(lambda loc: r.moved(loc), True, clean)
28232823

28242824
# TODO: almost all code duplicated!
2825-
# but parameter list is different so a simple function pointer wont work
2825+
# but parameter list is different so a simple function pointer won't work
28262826
def cskHole(
28272827
self: T,
28282828
diameter: float,
@@ -2879,7 +2879,7 @@ def cskHole(
28792879
return self.cutEach(lambda loc: res.moved(loc), True, clean)
28802880

28812881
# TODO: almost all code duplicated!
2882-
# but parameter list is different so a simple function pointer wont work
2882+
# but parameter list is different so a simple function pointer won't work
28832883
def hole(
28842884
self: T, diameter: float, depth: Optional[float] = None, clean: bool = True,
28852885
) -> T:
@@ -3463,7 +3463,7 @@ def _extrude(
34633463
eDir = self.plane.zDir.multiply(distance)
34643464

34653465
# one would think that fusing faces into a compound and then extruding would work,
3466-
# but it doesnt-- the resulting compound appears to look right, ( right number of faces, etc)
3466+
# but it doesn't-- the resulting compound appears to look right, ( right number of faces, etc)
34673467
# but then cutting it from the main solid fails with BRep_NotDone.
34683468
# the work around is to extrude each and then join the resulting solids, which seems to work
34693469

@@ -3589,15 +3589,15 @@ def interpPlate(
35893589
maxSegments: int = 9,
35903590
) -> T:
35913591
"""
3592-
Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. Using pushpoints directly with interpPlate and combine=True, can be very ressources intensive depending on the complexity of the shape. In this case set combine=False.
3592+
Returns a plate surface that is 'thickness' thick, enclosed by 'surf_edge_pts' points, and going through 'surf_pts' points. Using pushpoints directly with interpPlate and combine=True, can be very resources intensive depending on the complexity of the shape. In this case set combine=False.
35933593
35943594
:param surf_edges
35953595
:type 1 surf_edges: list of [x,y,z] float ordered coordinates
35963596
:type 2 surf_edges: list of ordered or unordered CadQuery wires
35973597
:param surf_pts = [] (uses only edges if [])
35983598
:type surf_pts: list of [x,y,z] float coordinates
35993599
:param thickness = 0 (returns 2D surface if 0)
3600-
:type thickness: float (may be negative or positive depending on thicknening direction)
3600+
:type thickness: float (may be negative or positive depending on thickening direction)
36013601
:param combine: should the results be combined with other solids on the stack
36023602
(and each other)?
36033603
:type combine: true to combine shapes, false otherwise.
@@ -3608,8 +3608,8 @@ def interpPlate(
36083608
:type: NbPtsOnCur Integer >= 15
36093609
:param NbIter = 2 (OCCT default)
36103610
:type: NbIterInteger >= 2
3611-
:param Anisotropie = False (OCCT default)
3612-
:type Anisotropie: Boolean
3611+
:param anisotropy = False (OCCT default)
3612+
:type anisotropy: Boolean
36133613
:param: Tol2d = 0.00001 (OCCT default)
36143614
:type Tol2d: float > 0
36153615
:param Tol3d = 0.0001 (OCCT default)
@@ -3800,6 +3800,69 @@ def sphere(
38003800
else:
38013801
return self.union(spheres, clean=clean)
38023802

3803+
def cylinder(
3804+
self: T,
3805+
height: float,
3806+
radius: float,
3807+
direct: Vector = Vector(0, 0, 1),
3808+
angle: float = 360,
3809+
centered: Union[bool, Tuple[bool, bool, bool]] = True,
3810+
combine: bool = True,
3811+
clean: bool = True,
3812+
) -> T:
3813+
"""
3814+
Returns a cylinder with the specified radius and height for each point on the stack
3815+
3816+
:param height: The height of the cylinder
3817+
:type height: float > 0
3818+
:param radius: The radius of the cylinder
3819+
:type radius: float > 0
3820+
:param direct: The direction axis for the creation of the cylinder
3821+
:type direct: A three-tuple
3822+
:param angle: The angle to sweep the cylinder arc through
3823+
:type angle: float > 0
3824+
:param centered: If True, the cylinder will be centered around the reference point. If False,
3825+
the corner of a bounding box around the cylinder will be on the reference point and it
3826+
will extend in the positive x, y and z directions. Can also use a 3-tuple to specify
3827+
centering along each axis.
3828+
:param combine: Whether the results should be combined with other solids on the stack
3829+
(and each other)
3830+
:type combine: true to combine shapes, false otherwise
3831+
:param clean: call :py:meth:`clean` afterwards to have a clean shape
3832+
:return: A cylinder object for each point on the stack
3833+
3834+
One cylinder is created for each item on the current stack. If no items are on the stack, one
3835+
cylinder is created using the current workplane center.
3836+
3837+
If combine is true, the result will be a single object on the stack. If a solid was found
3838+
in the chain, the result is that solid with all cylinders produced fused onto it otherwise,
3839+
the result is the combination of all the produced cylinders.
3840+
3841+
If combine is false, the result will be a list of the cylinders produced.
3842+
"""
3843+
3844+
if isinstance(centered, bool):
3845+
centered = (centered, centered, centered)
3846+
3847+
offset = Vector()
3848+
if not centered[0]:
3849+
offset += Vector(radius, 0, 0)
3850+
if not centered[1]:
3851+
offset += Vector(0, radius, 0)
3852+
if centered[2]:
3853+
offset += Vector(0, 0, -height / 2)
3854+
3855+
s = Solid.makeCylinder(radius, height, offset, direct, angle)
3856+
3857+
# We want a cylinder for each point on the workplane
3858+
cylinders = self.eachpoint(lambda loc: s.moved(loc), True)
3859+
3860+
# If we don't need to combine everything, just return the created cylinders
3861+
if not combine:
3862+
return cylinders
3863+
else:
3864+
return self.union(cylinders, clean=clean)
3865+
38033866
def wedge(
38043867
self: T,
38053868
dx: float,

cadquery/cqgi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def __init__(self):
153153
class BuildResult(object):
154154
"""
155155
The result of executing a CadQuery script.
156-
The success property contains whether the exeuction was successful.
156+
The success property contains whether the execution was successful.
157157
158158
If successful, the results property contains a list of all results,
159159
and the first_result property contains the first result.

cadquery/occ_impl/exporters/dxf.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,23 @@ def _dxf_spline(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
7676
curve, adaptor.FirstParameter(), adaptor.LastParameter(), CURVE_TOLERANCE
7777
)
7878

79+
# need to apply the transform on the geometry level
80+
spline.Transform(plane.fG.wrapped.Trsf())
81+
7982
order = spline.Degree() + 1
8083
knots = list(spline.KnotSequence())
8184
poles = [(p.X(), p.Y(), p.Z()) for p in spline.Poles()]
85+
weights = (
86+
[spline.Weight(i) for i in range(1, spline.NbPoles() + 1)]
87+
if spline.IsRational()
88+
else None
89+
)
8290

83-
weights = list(spline.Weights()) if spline.IsRational() else None
91+
if spline.IsPeriodic():
92+
pad = spline.NbKnots() - spline.LastUKnotIndex()
93+
poles += poles[:pad]
8494

85-
if spline.IsClosed():
86-
dxf_spline = ezdxf.math.BSplineClosed(poles, order, knots, weights)
87-
else:
88-
dxf_spline = ezdxf.math.BSpline(poles, order, knots, weights)
95+
dxf_spline = ezdxf.math.BSpline(poles, order, knots, weights)
8996

9097
msp.add_spline().apply_construction_tool(dxf_spline)
9198

0 commit comments

Comments
 (0)