Merge branch 'master' into adam-urbanczyk-OCC-version-update

This commit is contained in:
Adam Urbańczyk
2019-01-12 14:39:47 +01:00
committed by GitHub
60 changed files with 1360 additions and 1024 deletions

View File

@ -1066,6 +1066,45 @@ class Workplane(CQ):
return self.pushPoints(lpoints)
def polarArray(self, radius, startAngle, angle, count, fill=True):
"""
Creates an polar array of points and pushes them onto the stack.
The 0 degree reference angle is located along the local X-axis.
:param radius: Radius of the array.
:param startAngle: Starting angle (degrees) of array. 0 degrees is
situated along local X-axis.
:param angle: The angle (degrees) to fill with elements. A positive
value will fill in the counter-clockwise direction. If fill is
false, angle is the angle between elements.
:param count: Number of elements in array. ( > 0 )
"""
if count <= 0:
raise ValueError("No elements in array")
# First element at start angle, convert to cartesian coords
x = radius * math.cos(math.radians(startAngle))
y = radius * math.sin(math.radians(startAngle))
points = [(x, y)]
# Calculate angle between elements
if fill:
if angle % 360 == 0:
angle = angle / count
elif count > 1:
# Inclusive start and end
angle = angle / (count - 1)
# Add additional elements
for i in range(1, count):
phi = math.radians(startAngle + (angle * i))
x = radius * math.cos(phi)
y = radius * math.sin(phi)
points.append((x, y))
return self.pushPoints(points)
def pushPoints(self, pntList):
"""
Pushes a list of points onto the stack as vertices.
@ -1204,6 +1243,35 @@ class Workplane(CQ):
p = self._findFromPoint(True)
return self.lineTo(xCoord, p.y, forConstruction)
def polarLine(self, distance, angle, forConstruction=False):
"""
Make a line of the given length, at the given angle from the current point
:param float distance: distance of the end of the line from the current point
:param float angle: angle of the vector to the end of the line with the x-axis
:return: the Workplane object with the current point at the end of the new line
"""
x = math.cos(math.radians(angle)) * distance
y = math.sin(math.radians(angle)) * distance
return self.line(x, y, forConstruction)
def polarLineTo(self, distance, angle, forConstruction=False):
"""
Make a line from the current point to the given polar co-ordinates
Useful if it is more convenient to specify the end location rather than
the distance and angle from the current point
:param float distance: distance of the end of the line from the origin
:param float angle: angle of the vector to the end of the line with the x-axis
:return: the Workplane object with the current point at the end of the new line
"""
x = math.cos(math.radians(angle)) * distance
y = math.sin(math.radians(angle)) * distance
return self.lineTo(x, y, forConstruction)
# absolute move in current plane, not drawing
def moveTo(self, x=0, y=0):
"""
@ -1315,6 +1383,66 @@ class Workplane(CQ):
return self.newObject([arc])
def sagittaArc(self, endPoint, sag, forConstruction=False):
"""
Draw an arc from the current point to endPoint with an arc defined by the sag (sagitta).
:param endPoint: end point for the arc
:type endPoint: 2-tuple, in workplane coordinates
:param sag: the sagitta of the arc
:type sag: float, perpendicular distance from arc center to arc baseline.
:return: a workplane with the current point at the end of the arc
The sagitta is the distance from the center of the arc to the arc base.
Given that a closed contour is drawn clockwise;
A positive sagitta means convex arc and negative sagitta means concave arc.
See "https://en.wikipedia.org/wiki/Sagitta_(geometry)" for more information.
"""
startPoint = self._findFromPoint(useLocalCoords=True)
endPoint = Vector(endPoint)
midPoint = endPoint.add(startPoint).multiply(0.5)
sagVector = endPoint.sub(startPoint).normalized().multiply(abs(sag))
if(sag > 0):
sagVector.x, sagVector.y = -sagVector.y, sagVector.x # Rotate sagVector +90 deg
else:
sagVector.x, sagVector.y = sagVector.y, -sagVector.x # Rotate sagVector -90 deg
sagPoint = midPoint.add(sagVector)
return self.threePointArc(sagPoint, endPoint, forConstruction)
def radiusArc(self, endPoint, radius, forConstruction=False):
"""
Draw an arc from the current point to endPoint with an arc defined by the sag (sagitta).
:param endPoint: end point for the arc
:type endPoint: 2-tuple, in workplane coordinates
:param radius: the radius of the arc
:type radius: float, the radius of the arc between start point and end point.
:return: a workplane with the current point at the end of the arc
Given that a closed contour is drawn clockwise;
A positive radius means convex arc and negative radius means concave arc.
"""
startPoint = self._findFromPoint(useLocalCoords=True)
endPoint = Vector(endPoint)
# Calculate the sagitta from the radius
length = endPoint.sub(startPoint).Length / 2.0
try:
sag = abs(radius) - math.sqrt(radius**2 - length**2)
except ValueError:
raise ValueError("Arc radius is not large enough to reach the end point.")
# Return a sagittaArc
if radius > 0:
return self.sagittaArc(endPoint, sag, forConstruction)
else:
return self.sagittaArc(endPoint, -sag, forConstruction)
def rotateAndCopy(self, matrix):
"""
Makes a copy of all edges on the stack, rotates them according to the
@ -1746,7 +1874,14 @@ class Workplane(CQ):
s = Workplane().lineTo(1,0).lineTo(1,1).close().extrude(0.2)
"""
self.lineTo(self.ctx.firstPoint.x, self.ctx.firstPoint.y)
endPoint = self._findFromPoint(True)
startPoint = self.ctx.firstPoint
# Check if there is a distance between startPoint and endPoint
# that is larger than what is considered a numerical error.
# If so; add a line segment between endPoint and startPoint
if endPoint.sub(startPoint).Length > 1e-6:
self.lineTo(self.ctx.firstPoint.x, self.ctx.firstPoint.y)
# Need to reset the first point after closing a wire
self.ctx.firstPoint = None
@ -2091,24 +2226,25 @@ class Workplane(CQ):
newS = newS.clean()
return newS
def sweep(self, path, makeSolid=True, isFrenet=False, combine=True, clean=True):
def sweep(self, path, sweepAlongWires=False, makeSolid=True, isFrenet=False, combine=True, clean=True):
"""
Use all un-extruded wires in the parent chain to create a swept solid.
:param path: A wire along which the pending wires will be swept
:param boolean sweepAlongWires:
False to create multiple swept from wires on the chain along path
True to create only one solid swept along path with shape following the list of wires on the chain
:param boolean combine: True to combine the resulting solid with parent solids if found.
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
:return: a CQ object with the resulting solid selected.
"""
# returns a Solid (or a compound if there were multiple)
r = self._sweep(path.wire(), makeSolid, isFrenet)
r = self._sweep(path.wire(), sweepAlongWires, makeSolid, isFrenet) # returns a Solid (or a compound if there were multiple)
if combine:
newS = self._combineWithBase(r)
else:
newS = self.newObject([r])
if clean:
newS = newS.clean()
if clean: newS = newS.clean()
return newS
def _combineWithBase(self, obj):
@ -2223,6 +2359,43 @@ class Workplane(CQ):
return self.newObject([newS])
def intersect(self, toIntersect, combine=True, clean=True):
"""
Intersects the provided solid from the current solid.
if combine=True, the result and the original are updated to point to the new object
if combine=False, the result will be on the stack, but the original is unmodified
:param toIntersect: object to intersect
:type toIntersect: a solid object, or a CQ object having a solid,
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
:raises: ValueError if there is no solid to intersect with in the chain
:return: a CQ object with the resulting object selected
"""
# look for parents to intersect with
solidRef = self.findSolid(searchStack=True, searchParents=True)
if solidRef is None:
raise ValueError("Cannot find solid to intersect with")
solidToIntersect = None
if isinstance(toIntersect, CQ):
solidToIntersect = toIntersect.val()
elif isinstance(toIntersect, Solid) or isinstance(toIntersect, Compound):
solidToIntersect = toIntersect
else:
raise ValueError("Cannot intersect type '{}'".format(type(toIntersect)))
newS = solidRef.intersect(solidToIntersect)
if clean: newS = newS.clean()
if combine:
solidRef.wrapped = newS.wrapped
return self.newObject([newS])
def cutBlind(self, distanceToCut, clean=True):
"""
Use all un-extruded wires in the parent chain to create a prismatic cut from existing solid.
@ -2386,27 +2559,37 @@ class Workplane(CQ):
return Compound.makeCompound(toFuse)
def _sweep(self, path, makeSolid=True, isFrenet=False):
def _sweep(self, path, sweepAlongWires=False, makeSolid=True, isFrenet=False):
"""
Makes a swept solid from an existing set of pending wires.
:param path: A wire along which the pending wires will be swept
:param boolean sweepAlongWires:
False to create multiple swept from wires on the chain along path
True to create only one solid swept along path with shape following the list of wires on the chain
:return:a FreeCAD solid, suitable for boolean operations
"""
# group wires together into faces based on which ones are inside the others
# result is a list of lists
s = time.time()
wireSets = sortWiresByBuildOrder(
list(self.ctx.pendingWires), self.plane, [])
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires), self.plane, [])
# print "sorted wires in %d sec" % ( time.time() - s )
# now all of the wires have been used to create an extrusion
self.ctx.pendingWires = []
self.ctx.pendingWires = [] # now all of the wires have been used to create an extrusion
toFuse = []
for ws in wireSets:
thisObj = Solid.sweep(
ws[0], ws[1:], path.val(), makeSolid, isFrenet)
if not sweepAlongWires:
for ws in wireSets:
thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid, isFrenet)
toFuse.append(thisObj)
else:
section = []
for ws in wireSets:
for i in range(0, len(ws)):
section.append(ws[i])
# implementation
thisObj = Solid.sweep_multi(section, path.val(), makeSolid, isFrenet)
toFuse.append(thisObj)
return Compound.makeCompound(toFuse)

View File

@ -19,22 +19,26 @@ class Vector(object):
* a gp_Vec
* a vector ( in which case it is copied )
* a 3-tuple
* three float values, x, y, and z
* a 2-tuple (z assumed to be 0)
* three float values: x, y, and z
* two float values: x,y
"""
def __init__(self, *args):
if len(args) == 3:
fV = gp_Vec(*args)
elif len(args) == 2:
fV = gp_Vec(*args,0)
elif len(args) == 1:
if isinstance(args[0], Vector):
fV = gp_Vec(args[0].wrapped.XYZ())
elif isinstance(args[0], (tuple, list)):
fV = gp_Vec(*args[0])
elif isinstance(args[0], gp_Vec):
fV = gp_Vec(args[0].XYZ())
elif isinstance(args[0], gp_Pnt):
fV = gp_Vec(args[0].XYZ())
elif isinstance(args[0], gp_Dir):
arg = args[0]
if len(arg)==3:
fV = gp_Vec(*arg)
elif len(arg)==2:
fV = gp_Vec(*arg,0)
elif isinstance(args[0], (gp_Vec, gp_Pnt, gp_Dir)):
fV = gp_Vec(args[0].XYZ())
elif isinstance(args[0], gp_XYZ):
fV = gp_Vec(args[0])
@ -51,14 +55,26 @@ class Vector(object):
def x(self):
return self.wrapped.X()
@x.setter
def x(self,value):
self.wrapped.SetX(value)
@property
def y(self):
return self.wrapped.Y()
@y.setter
def y(self,value):
self.wrapped.SetY(value)
@property
def z(self):
return self.wrapped.Z()
@z.setter
def z(self,value):
self.wrapped.SetZ(value)
@property
def Length(self):
return self.wrapped.Magnitude()
@ -199,20 +215,21 @@ class Matrix:
self.wrapped = gp_Trsf()
elif isinstance(matrix, gp_Trsf):
self.wrapped = matrix
elif isinstance(matrix, list):
elif isinstance(matrix, (list, tuple)):
# Validate matrix size & 4x4 last row value
valid_sizes = all(
(isinstance(row, (list, tuple)) and (len(row) == 4))
for row in matrix
) and len(matrix) in (3, 4)
if not valid_sizes:
raise TypeError("Matrix constructor requires 2d list of 4x3 or 4x4, but got: {!r}".format(matrix))
elif (len(matrix) == 4) and (tuple(matrix[3]) != (0,0,0,1)):
raise ValueError("Expected the last row to be [0,0,0,1], but got: {!r}".format(matrix[3]))
# Assign values to matrix
self.wrapped = gp_Trsf()
if len(matrix) == 3:
flattened = [e for row in matrix for e in row]
self.wrapped.SetValues(*flattened)
elif len(matrix) == 4:
# Only use first 3 rows - the last must be [0, 0, 0, 1].
lastRow = matrix[3]
if lastRow != [0., 0., 0., 1.]:
raise ValueError("Expected the last row to be [0,0,0,1], but got: {}".format(lastRow))
flattened = [e for row in matrix[0:3] for e in row]
self.wrapped.SetValues(*flattened)
else:
raise TypeError("Matrix constructor requires list of length 12 or 16")
flattened = [e for row in matrix[:3] for e in row]
self.wrapped.SetValues(*flattened)
else:
raise TypeError(
"Invalid param to matrix constructor: {}".format(matrix))
@ -266,18 +283,18 @@ class Matrix:
and column parameters start at zero, which is consistent with most
python libraries, but is counter to gp_Trsf(), which is 1-indexed.
"""
if len(rc) != 2:
if not isinstance(rc, tuple) or (len(rc) != 2):
raise IndexError("Matrix subscript must provide (row, column)")
r, c = rc[0], rc[1]
if r >= 0 and r < 4 and c >= 0 and c < 4:
(r, c) = rc
if (0 <= r <= 3) and (0 <= c <= 3):
if r < 3:
return self.wrapped.Value(r+1,c+1)
return self.wrapped.Value(r + 1, c + 1)
else:
# gp_Trsf doesn't provide access to the 4th row because it has
# an implied value as below:
return [0., 0., 0., 1.][c]
else:
raise IndexError("Out of bounds access into 4x4 matrix: {}".format(rc))
raise IndexError("Out of bounds access into 4x4 matrix: {!r}".format(rc))
class Plane(object):
@ -293,6 +310,10 @@ class Plane(object):
created automatically from faces.
"""
# equality tolerances
_eq_tolerance_origin = 1e-6
_eq_tolerance_dot = 1e-6
@classmethod
def named(cls, stdName, origin=(0, 0, 0)):
"""Create a predefined Plane based on the conventional names.
@ -442,6 +463,23 @@ class Plane(object):
self._setPlaneDir(xDir)
self.origin = origin
def _eq_iter(self, other):
"""Iterator to successively test equality"""
cls = type(self)
yield isinstance(other, Plane) # comparison is with another Plane
# origins are the same
yield abs(self.origin - other.origin) < cls._eq_tolerance_origin
# z-axis vectors are parallel (assumption: both are unit vectors)
yield abs(self.zDir.dot(other.zDir) - 1) < cls._eq_tolerance_dot
# x-axis vectors are parallel (assumption: both are unit vectors)
yield abs(self.xDir.dot(other.xDir) - 1) < cls._eq_tolerance_dot
def __eq__(self, other):
return all(self._eq_iter(other))
def __ne__(self, other):
return not self.__eq__(other)
@property
def origin(self):
return self._origin
@ -774,10 +812,11 @@ class BoundBox(object):
return None
@classmethod
def _fromTopoDS(cls, shape, tol=TOL, optimal=False):
def _fromTopoDS(cls, shape, tol=None, optimal=False):
'''
Constructs a bounnding box from a TopoDS_Shape
Constructs a bounding box from a TopoDS_Shape
'''
tol = TOL if tol is None else tol # tol = TOL (by default)
bbox = Bnd_Box()
bbox.SetGap(tol)
if optimal:
@ -791,6 +830,14 @@ class BoundBox(object):
return cls(bbox)
def isInside(self, anotherBox):
def isInside(self, b2):
"""Is the provided bounding box inside this one?"""
return not anotherBox.wrapped.IsOut(self.wrapped)
if (b2.xmin > self.xmin and
b2.ymin > self.ymin and
b2.zmin > self.zmin and
b2.xmax < self.xmax and
b2.ymax < self.ymax and
b2.zmax < self.zmax):
return True
else:
return False

View File

@ -4,7 +4,6 @@ from .shapes import Shape
import sys
import os
import urllib as urlreader
import tempfile
from OCC.Core.STEPControl import STEPControl_Reader
@ -58,18 +57,3 @@ def importStep(fileName):
solids.append(Shape.cast(shape))
return cadquery.Workplane("XY").newObject(solids)
# Loads a STEP file from an URL into a CQ.Workplane object
def importStepFromURL(url):
# Now read and return the shape
try:
webFile = urlreader.urlopen(url)
tempFile = tempfile.NamedTemporaryFile(suffix='.step', delete=False)
tempFile.write(webFile.read())
webFile.close()
tempFile.close()
return importStep(tempFile.name)
except:
raise ValueError("STEP File from the URL: " +
url + " Could not be loaded")

View File

@ -2,10 +2,10 @@ from OCC.Core.Display.WebGl.x3dom_renderer import X3DExporter
from OCC.Core.gp import gp_Quaternion, gp_Vec
from uuid import uuid4
from math import tan
from xml.etree import ElementTree
from .geom import BoundBox
N_HEADER_LINES = 10
BOILERPLATE = \
'''
<link rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'></link>
@ -47,7 +47,7 @@ FOV = 0.2
def add_x3d_boilerplate(src, height=400, center=(0,0,0), d=(0,0,15), fov=FOV, rot='{} {} {} {} '.format(*ROT)):
return BOILERPLATE.format(src=src,
id=uuid1(),
id=uuid4(),
height=height,
x=d[0],
y=d[1],
@ -70,6 +70,7 @@ def x3d_display(shape,
line_width=2.,
mesh_quality=.3):
# Export to XML <Scene> tag
exporter = X3DExporter(shape,
vertex_shader,
fragment_shader,
@ -83,9 +84,11 @@ def x3d_display(shape,
mesh_quality)
exporter.compute()
x3d_str = exporter.to_x3dfile_string()
x3d_str = '\n'.join(x3d_str.splitlines()[N_HEADER_LINES:])
x3d_str = exporter.to_x3dfile_string(shape_id=0)
xml_et = ElementTree.fromstring(x3d_str)
scene_tag = xml_et.find('./Scene')
# Viewport Parameters
bb = BoundBox._fromTopoDS(shape)
d = max(bb.xlen,bb.ylen,bb.zlen)
c = bb.center
@ -94,6 +97,7 @@ def x3d_display(shape,
quat = gp_Quaternion(*ROT)
vec = quat*(vec) + c.wrapped
return add_x3d_boilerplate(x3d_str,
# return boilerplate + Scene
return add_x3d_boilerplate(ElementTree.tostring(scene_tag).decode('utf-8'),
d=(vec.X(),vec.Y(),vec.Z()),
center=(c.x,c.y,c.z))

View File

@ -1364,23 +1364,50 @@ class Solid(Shape, Mixin3D):
:param path: The wire to sweep the face resulting from the wires over
:return: a Solid object
"""
if path.ShapeType() == 'Edge':
path = Wire.assembleEdges([path, ])
if makeSolid:
face = Face.makeFromWires(outerWire, innerWires)
builder = BRepOffsetAPI_MakePipe(path.wrapped, face.wrapped)
rv = cls(builder.Shape())
else:
builder = BRepOffsetAPI_MakePipeShell(path.wrapped)
builder.Add(outerWire.wrapped)
for w in innerWires:
shapes = []
for w in [outerWire]+innerWires:
builder = BRepOffsetAPI_MakePipeShell(path.wrapped)
builder.SetMode(isFrenet)
builder.Add(w.wrapped)
shapes.append(cls(builder.Shape()))
rv = Compound.makeCompound(shapes)
return rv
@classmethod
def sweep_multi(cls, profiles, path, makeSolid=True, isFrenet=False):
"""
Multi section sweep. Only single outer profile per section is allowed.
:param profiles: list of profiles
:param path: The wire to sweep the face resulting from the wires over
:return: a Solid object
"""
if path.ShapeType() == 'Edge':
path = Wire.assembleEdges([path, ])
builder = BRepOffsetAPI_MakePipeShell(path.wrapped)
for p in profiles:
builder.Add(p.wrapped)
builder.SetMode(isFrenet)
builder.Build()
if makeSolid:
builder.MakeSolid()
return cls(builder.Shape())

View File

@ -0,0 +1,19 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
length = 80.0 # Length of the block
height = 60.0 # Height of the block
thickness = 10.0 # Thickness of the block
# Create a 3D block based on the dimension variables above.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the X and Y origins to define the workplane, meaning that the
# positive Z direction is "up", and the negative Z direction is "down".
result = cq.Workplane("XY").box(length, height, thickness)
# The following method is now outdated, but can still be used to display the
# results of the script if you want
# from Helpers import show
# show(result) # Render the result of this script
show_object(result)

View File

@ -0,0 +1,20 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
length = 80.0 # Length of the block
height = 60.0 # Height of the block
thickness = 10.0 # Thickness of the block
center_hole_dia = 22.0 # Diameter of center hole in block
# Create a block based on the dimensions above and add a 22mm center hole.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the X and Y origins to define the workplane, meaning that the
# positive Z direction is "up", and the negative Z direction is "down".
# 2. The highest (max) Z face is selected and a new workplane is created on it.
# 3. The new workplane is used to drill a hole through the block.
# 3a. The hole is automatically centered in the workplane.
result = cq.Workplane("XY").box(length, height, thickness) \
.faces(">Z").workplane().hole(center_hole_dia)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,34 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
length = 80.0 # Length of the block
height = 60.0 # Height of the block
thickness = 10.0 # Thickness of the block
center_hole_dia = 22.0 # Diameter of center hole in block
cbore_hole_diameter = 2.4 # Bolt shank/threads clearance hole diameter
cbore_diameter = 4.4 # Bolt head pocket hole diameter
cbore_depth = 2.1 # Bolt head pocket hole depth
# Create a 3D block based on the dimensions above and add a 22mm center hold
# and 4 counterbored holes for bolts
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the X and Y origins to define the workplane, meaning that the
# positive Z direction is "up", and the negative Z direction is "down".
# 2. The highest(max) Z face is selected and a new workplane is created on it.
# 3. The new workplane is used to drill a hole through the block.
# 3a. The hole is automatically centered in the workplane.
# 4. The highest(max) Z face is selected and a new workplane is created on it.
# 5. A for-construction rectangle is created on the workplane based on the
# block's overall dimensions.
# 5a. For-construction objects are used only to place other geometry, they
# do not show up in the final displayed geometry.
# 6. The vertices of the rectangle (corners) are selected, and a counter-bored
# hole is placed at each of the vertices (all 4 of them at once).
result = cq.Workplane("XY").box(length, height, thickness) \
.faces(">Z").workplane().hole(center_hole_dia) \
.faces(">Z").workplane() \
.rect(length - 8.0, height - 8.0, forConstruction=True) \
.vertices().cboreHole(cbore_hole_diameter, cbore_diameter, cbore_depth)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,29 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
circle_radius = 50.0 # Radius of the plate
thickness = 13.0 # Thickness of the plate
rectangle_width = 13.0 # Width of rectangular hole in cylindrical plate
rectangle_length = 19.0 # Length of rectangular hole in cylindrical plate
# Extrude a cylindrical plate with a rectangular hole in the middle of it.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. The 2D geometry for the outer circle is created at the same time as the
# rectangle that will create the hole in the center.
# 2a. The circle and the rectangle will be automatically centered on the
# workplane.
# 2b. Unlike some other functions like the hole(), circle() takes
# a radius and not a diameter.
# 3. The circle and rectangle are extruded together, creating a cylindrical
# plate with a rectangular hole in the center.
# 3a. circle() and rect() could be changed to any other shape to completely
# change the resulting plate and/or the hole in it.
result = cq.Workplane("front").circle(circle_radius) \
.rect(rectangle_width, rectangle_length) \
.extrude(thickness)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,45 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
width = 2.0 # Overall width of the plate
thickness = 0.25 # Thickness of the plate
# Extrude a plate outline made of lines and an arc
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Draws a line from the origin to an X position of the plate's width.
# 2a. The starting point of a 2D drawing like this will be at the center of the
# workplane (0, 0) unless the moveTo() function moves the starting point.
# 3. A line is drawn from the last position straight up in the Y direction
# 1.0 millimeters.
# 4. An arc is drawn from the last point, through point (1.0, 1.5) which is
# half-way back to the origin in the X direction and 0.5 mm above where
# the last line ended at. The arc then ends at (0.0, 1.0), which is 1.0 mm
# above (in the Y direction) where our first line started from.
# 5. An arc is drawn from the last point that ends on (-0.5, 1.0), the sag of
# the curve 0.2 determines that the curve is concave with the midpoint 0.1 mm
# from the arc baseline. If the sag was -0.2 the arc would be convex.
# This convention is valid when the profile is drawn counterclockwise.
# The reverse is true if the profile is drawn clockwise.
# Clockwise: +sag => convex, -sag => concave
# Counterclockwise: +sag => concave, -sag => convex
# 6. An arc is drawn from the last point that ends on (-0.7, -0.2), the arc is
# determined by the radius of -1.5 mm.
# Clockwise: +radius => convex, -radius => concave
# Counterclockwise: +radius => concave, -radius => convex
# 7. close() is called to automatically draw the last line for us and close
# the sketch so that it can be extruded.
# 7a. Without the close(), the 2D sketch will be left open and the extrude
# operation will provide unpredictable results.
# 8. The 2D sketch is extruded into a solid object of the specified thickness.
result = cq.Workplane("front").lineTo(width, 0) \
.lineTo(width, 1.0) \
.threePointArc((1.0, 1.5), (0.0, 1.0)) \
.sagittaArc((-0.5, 1.0), 0.2) \
.radiusArc((-0.7, -0.2), -1.5) \
.close().extrude(thickness)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,35 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
circle_radius = 3.0 # The outside radius of the plate
thickness = 0.25 # The thickness of the plate
# Make a plate with two cutouts in it by moving the workplane center point
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 1b. The initial workplane center point is the center of the circle, at (0,0).
# 2. A circle is created at the center of the workplane
# 2a. Notice that circle() takes a radius and not a diameter
result = cq.Workplane("front").circle(circle_radius)
# 3. The work center is movide to (1.5, 0.0) by calling center().
# 3a. The new center is specified relative to the previous center,not
# relative to global coordinates.
# 4. A 0.5mm x 0.5mm 2D square is drawn inside the circle.
# 4a. The plate has not been extruded yet, only 2D geometry is being created.
result = result.center(1.5, 0.0).rect(0.5, 0.5)
# 5. The work center is moved again, this time to (-1.5, 1.5).
# 6. A 2D circle is created at that new center with a radius of 0.25mm.
result = result.center(-1.5, 1.5).circle(0.25)
# 7. All 2D geometry is extruded to the specified thickness of the plate.
# 7a. The small circle and the square are enclosed in the outer circle of the
# plate and so it is assumed that we want them to be cut out of the plate.
# A separate cut operation is not needed.
result = result.extrude(thickness)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,32 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
plate_radius = 2.0 # The radius of the plate that will be extruded
hole_pattern_radius = 0.25 # Radius of circle where the holes will be placed
thickness = 0.125 # The thickness of the plate that will be extruded
# Make a plate with 4 holes in it at various points in a polar arrangement from
# the center of the workplane.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. A 2D circle is drawn that will become though outer profile of the plate.
r = cq.Workplane("front").circle(plate_radius)
# 3. Push 4 points on the stack that will be used as the center points of the
# holes.
r = r.pushPoints([(1.5, 0), (0, 1.5), (-1.5, 0), (0, -1.5)])
# 4. This circle() call will operate on all four points, putting a circle at
# each one.
r = r.circle(hole_pattern_radius)
# 5. All 2D geometry is extruded to the specified thickness of the plate.
# 5a. The small hole circles are enclosed in the outer circle of the plate and
# so it is assumed that we want them to be cut out of the plate. A
# separate cut operation is not needed.
result = r.extrude(thickness)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,39 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
width = 3.0 # The width of the plate
height = 4.0 # The height of the plate
thickness = 0.25 # The thickness of the plate
polygon_sides = 6 # The number of sides that the polygonal holes should have
polygon_dia = 1.0 # The diameter of the circle enclosing the polygon points
# Create a plate with two polygons cut through it
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. A 3D box is created in one box() operation to represent the plate.
# 2a. The box is centered around the origin, which creates a result that may
# be unituitive when the polygon cuts are made.
# 3. 2 points are pushed onto the stack and will be used as centers for the
# polygonal holes.
# 4. The two polygons are created, on for each point, with one call to
# polygon() using the number of sides and the circle that bounds the
# polygon.
# 5. The polygons are cut thru all objects that are in the line of extrusion.
# 5a. A face was not selected, and so the polygons are created on the
# workplane. Since the box was centered around the origin, the polygons end
# up being in the center of the box. This makes them cut from the center to
# the outside along the normal (positive direction).
# 6. The polygons are cut through all objects, starting at the center of the
# box/plate and going "downward" (opposite of normal) direction. Functions
# like cutBlind() assume a positive cut direction, but cutThruAll() assumes
# instead that the cut is made from a max direction and cuts downward from
# that max through all objects.
result = cq.Workplane("front").box(width, height, thickness) \
.pushPoints([(0, 0.75), (0, -0.75)]) \
.polygon(polygon_sides, polygon_dia) \
.cutThruAll()
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,39 @@
import cadquery as cq
# These can be modified rather than hardcoding values for each dimension.
# Define up our Length, Height, Width, and thickness of the beam
(L, H, W, t) = (100.0, 20.0, 20.0, 1.0)
# Define the points that the polyline will be drawn to/thru
pts = [
(W/2.0, H/2.0),
(W/2.0, (H/2.0 - t)),
(t/2.0, (H/2.0-t)),
(t/2.0, (t - H/2.0)),
(W/2.0, (t - H/2.0)),
(W/2.0, H/-2.0),
(0, H/-2.0)
]
# We generate half of the I-beam outline and then mirror it to create the full
# I-beam.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. moveTo() is used to move the first point from the origin (0, 0) to
# (0, 10.0), with 10.0 being half the height (H/2.0). If this is not done
# the first line will start from the origin, creating an extra segment that
# will cause the extrude to have an invalid shape.
# 3. The polyline function takes a list of points and generates the lines
# through all the points at once.
# 3. Only half of the I-beam profile has been drawn so far. That half is
# mirrored around the Y-axis to create the complete I-beam profile.
# 4. The I-beam profile is extruded to the final length of the beam.
result = cq.Workplane("front").moveTo(0, H/2.0) \
.polyline(pts) \
.mirrorY() \
.extrude(L)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,27 @@
import cadquery as cq
# 1. Establishes a workplane to create the spline on to extrude.
# 1a. Uses the X and Y origins to define the workplane, meaning that the
# positive Z direction is "up", and the negative Z direction is "down".
s = cq.Workplane("XY")
# The points that the spline will pass through
sPnts = [
(2.75, 1.5),
(2.5, 1.75),
(2.0, 1.5),
(1.5, 1.0),
(1.0, 1.25),
(0.5, 1.0),
(0, 1.0)
]
# 2. Generate our plate with the spline feature and make sure it is a
# closed entity
r = s.lineTo(3.0, 0).lineTo(3.0, 1.0).spline(sPnts).close()
# 3. Extrude to turn the wire into a plate
result = r.extrude(0.5)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,20 @@
import cadquery as cq
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. A horizontal line is drawn on the workplane with the hLine function.
# 2a. 1.0 is the distance, not coordinate. hLineTo allows using xCoordinate
# not distance.
r = cq.Workplane("front").hLine(1.0)
# 3. Draw a series of vertical and horizontal lines with the vLine and hLine
# functions.
r = r.vLine(0.5).hLine(-0.25).vLine(-0.25).hLineTo(0.0)
# 4. Mirror the geometry about the Y axis and extrude it into a 3D object.
result = r.mirrorY().extrude(0.25)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,16 @@
import cadquery as cq
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a 3D box that will have a hole placed in it later.
result = cq.Workplane("front").box(2, 3, 0.5)
# 3. Find the top-most face with the >Z max selector.
# 3a. Establish a new workplane to build geometry on.
# 3b. Create a hole down into the box.
result = result.faces(">Z").workplane().hole(0.5)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,21 @@
import cadquery as cq
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a 3D box that will have a hole placed in it later.
result = cq.Workplane("front").box(3, 2, 0.5)
# 3. Select the lower left vertex and make a workplane.
# 3a. The top-most Z face is selected using the >Z selector.
# 3b. The lower-left vertex of the faces is selected with the <XY selector.
# 3c. A new workplane is created on the vertex to build future geometry on.
result = result.faces(">Z").vertices("<XY").workplane()
# 4. A circle is drawn with the selected vertex as its center.
# 4a. The circle is cut down through the box to cut the corner out.
result = result.circle(1.0).cutThruAll()
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,20 @@
import cadquery as cq
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a 3D box that will have geometry based off it later.
result = cq.Workplane("front").box(3, 2, 0.5)
# 3. The lowest face in the X direction is selected with the <X selector.
# 4. A new wokrplane is created
# 4a.The workplane is offset from the object surface so that it is not touching
# the original box.
result = result.faces("<X").workplane(offset=0.75)
# 5. Creates a thin disc on the offset workplane that is floating near the box.
result = result.circle(1.0).extrude(0.5)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,22 @@
import cadquery as cq
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects the top-most Z face of the box.
# 4. Creates a new workplane and then moves and rotates it with the
# transformed function.
# 5. Creates a for-construction rectangle that only exists to use for placing
# other geometry.
# 6. Selects the vertices of the for-construction rectangle.
# 7. Places holes at the center of each selected vertex.
# 7a. Since the workplane is rotated, this results in angled holes in the face.
result = cq.Workplane("front").box(4.0, 4.0, 0.25).faces(">Z") \
.workplane() \
.transformed(offset=(0, -1.5, 1.0), rotate=(60, 0, 0)) \
.rect(1.5, 1.5, forConstruction=True).vertices().hole(0.25)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,21 @@
import cadquery as cq
# Create a block with holes in each corner of a rectangle on that workplane.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects the top-most Z face of the box.
# 4. Creates a new workplane to build new geometry on.
# 5. Creates a for-construction rectangle that only exists to use for placing
# other geometry.
# 6. Selects the vertices of the for-construction rectangle.
# 7. Places holes at the center of each selected vertex.
result = cq.Workplane("front").box(2, 2, 0.5)\
.faces(">Z").workplane() \
.rect(1.5, 1.5, forConstruction=True).vertices() \
.hole(0.125)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,14 @@
import cadquery as cq
# Create a hollow box that's open on both ends with a thin wall.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects faces with normal in +z direction.
# 4. Create a shell by cutting out the top-most Z face.
result = cq.Workplane("front").box(2, 2, 2).faces("+Z").shell(0.05)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,20 @@
import cadquery as cq
# Create a lofted section between a rectangle and a circular section.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the named plane orientation "front" to define the workplane, meaning
# that the positive Z direction is "up", and the negative Z direction
# is "down".
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects the top-most Z face of the box.
# 4. Draws a 2D circle at the center of the the top-most face of the box.
# 5. Creates a workplane 3 mm above the face the circle was drawn on.
# 6. Draws a 2D circle on the new, offset workplane.
# 7. Creates a loft between the circle and the rectangle.
result = cq.Workplane("front").box(4.0, 4.0, 0.25).faces(">Z") \
.circle(1.5).workplane(offset=3.0) \
.rect(0.75, 0.5) \
.loft(combine=True)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,19 @@
import cadquery as cq
# Create a plate with 4 counter-sunk holes in it.
# 1. Establishes a workplane using an XY object instead of a named plane.
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects the top-most face of the box and established a workplane on that.
# 4. Draws a for-construction rectangle on the workplane which only exists for
# placing other geometry.
# 5. Selects the corner vertices of the rectangle and places a counter-sink
# hole, using each vertex as the center of a hole using the cskHole()
# function.
# 5a. When the depth of the counter-sink hole is set to None, the hole will be
# cut through.
result = cq.Workplane(cq.Plane.XY()).box(4, 2, 0.5).faces(">Z") \
.workplane().rect(3.5, 1.5, forConstruction=True) \
.vertices().cskHole(0.125, 0.25, 82.0, depth=None)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,13 @@
import cadquery as cq
# Create a plate with 4 rounded corners in the Z-axis.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the X and Y origins to define the workplane, meaning that the
# positive Z direction is "up", and the negative Z direction is "down".
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects all edges that are parallel to the Z axis.
# 4. Creates fillets on each of the selected edges with the specified radius.
result = cq.Workplane("XY").box(3, 3, 0.5).edges("|Z").fillet(0.125)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,25 @@
import cadquery as cq
# Create a simple block with a hole through it that we can split.
# 1. Establishes a workplane that an object can be built on.
# 1a. Uses the X and Y origins to define the workplane, meaning that the
# positive Z direction is "up", and the negative Z direction is "down".
# 2. Creates a plain box to base future geometry on with the box() function.
# 3. Selects the top-most face of the box and establishes a workplane on it
# that new geometry can be built on.
# 4. Draws a 2D circle on the new workplane and then uses it to cut a hole
# all the way through the box.
c = cq.Workplane("XY").box(1, 1, 1).faces(">Z").workplane() \
.circle(0.25).cutThruAll()
# 5. Selects the face furthest away from the origin in the +Y axis direction.
# 6. Creates an offset workplane that is set in the center of the object.
# 6a. One possible improvement to this script would be to make the dimensions
# of the box variables, and then divide the Y-axis dimension by 2.0 and
# use that to create the offset workplane.
# 7. Uses the embedded workplane to split the object, keeping only the "top"
# portion.
result = c.faces(">Y").workplane(-0.5).split(keepTop=True)
# Displays the result of this script
show_object(result)

View File

@ -0,0 +1,21 @@
import cadquery as cq
# The dimensions of the model. These can be modified rather than changing the
# shape's code directly.
rectangle_width = 10.0
rectangle_length = 10.0
angle_degrees = 360.0
# Revolve a cylinder from a rectangle
# Switch comments around in this section to try the revolve operation with different parameters
result = cq.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
# Revolve a donut with square walls
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
# Displays the result of this script
show_object(result)

40
examples/Ex023_Sweep.py Normal file
View File

@ -0,0 +1,40 @@
import cadquery as cq
# Points we will use to create spline and polyline paths to sweep over
pts = [
(0, 1),
(1, 2),
(2, 4)
]
# Spline path generated from our list of points (tuples)
path = cq.Workplane("XZ").spline(pts)
# Sweep a circle with a diameter of 1.0 units along the spline path we just created
defaultSweep = cq.Workplane("XY").circle(1.0).sweep(path)
# Sweep defaults to making a solid and not generating a Frenet solid. Setting Frenet to True helps prevent creep in
# the orientation of the profile as it is being swept
frenetShell = cq.Workplane("XY").circle(1.0).sweep(path, makeSolid=True, isFrenet=True)
# We can sweep shapes other than circles
defaultRect = cq.Workplane("XY").rect(1.0, 1.0).sweep(path)
# Switch to a polyline path, but have it use the same points as the spline
path = cq.Workplane("XZ").polyline(pts)
# Using a polyline path leads to the resulting solid having segments rather than a single swept outer face
plineSweep = cq.Workplane("XY").circle(1.0).sweep(path)
# Switch to an arc for the path
path = cq.Workplane("XZ").threePointArc((1.0, 1.5), (0.0, 1.0))
# Use a smaller circle section so that the resulting solid looks a little nicer
arcSweep = cq.Workplane("XY").circle(0.5).sweep(path)
# Translate the resulting solids so that they do not overlap and display them left to right
show_object(defaultSweep)
show_object(frenetShell.translate((5, 0, 0)))
show_object(defaultRect.translate((10, 0, 0)))
show_object(plineSweep.translate((15, 0, 0)))
show_object(arcSweep.translate((20, 0, 0)))

View File

@ -0,0 +1,47 @@
import cadquery as cq
# X axis line length 20.0
path = cq.Workplane("XZ").moveTo(-10, 0).lineTo(10, 0)
# Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0
defaultSweep = cq.Workplane("YZ").workplane(offset=-10.0).circle(2.0). \
workplane(offset=10.0).circle(1.0). \
workplane(offset=10.0).circle(2.0).sweep(path, sweepAlongWires=True)
# We can sweep thrue different shapes
recttocircleSweep = cq.Workplane("YZ").workplane(offset=-10.0).rect(2.0, 2.0). \
workplane(offset=8.0).circle(1.0).workplane(offset=4.0).circle(1.0). \
workplane(offset=8.0).rect(2.0, 2.0).sweep(path, sweepAlongWires=True)
circletorectSweep = cq.Workplane("YZ").workplane(offset=-10.0).circle(1.0). \
workplane(offset=7.0).rect(2.0, 2.0).workplane(offset=6.0).rect(2.0, 2.0). \
workplane(offset=7.0).circle(1.0).sweep(path, sweepAlongWires=True)
# Placement of the Shape is important otherwise could produce unexpected shape
specialSweep = cq.Workplane("YZ").circle(1.0).workplane(offset=10.0).rect(2.0, 2.0). \
sweep(path, sweepAlongWires=True)
# Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0
path = cq.Workplane("XZ").moveTo(-5, 4).lineTo(0, 4). \
threePointArc((4, 0), (0, -4)).lineTo(-5, -4)
# Placement of different shapes should follow the path
# cylinder r=1.5 along first line
# then sweep allong arc from r=1.5 to r=1.0
# then cylinder r=1.0 along last line
arcSweep = cq.Workplane("YZ").workplane(offset=-5).moveTo(0, 4).circle(1.5). \
workplane(offset=5).circle(1.5). \
moveTo(0, -8).circle(1.0). \
workplane(offset=-5).circle(1.0). \
sweep(path, sweepAlongWires=True)
# Translate the resulting solids so that they do not overlap and display them left to right
show_object(defaultSweep)
show_object(circletorectSweep.translate((0, 5, 0)))
show_object(recttocircleSweep.translate((0, 10, 0)))
show_object(specialSweep.translate((0, 15, 0)))
show_object(arcSweep.translate((0, -5, 0)))

View File

@ -0,0 +1,56 @@
# This script can create any regular rectangular Lego(TM) Brick
import cadquery as cq
#####
# Inputs
######
lbumps = 1 # number of bumps long
wbumps = 1 # number of bumps wide
thin = True # True for thin, False for thick
#
# Lego Brick Constants-- these make a lego brick a lego :)
#
pitch = 8.0
clearance = 0.1
bumpDiam = 4.8
bumpHeight = 1.8
if thin:
height = 3.2
else:
height = 9.6
t = (pitch - (2 * clearance) - bumpDiam) / 2.0
postDiam = pitch - t # works out to 6.5
total_length = lbumps*pitch - 2.0*clearance
total_width = wbumps*pitch - 2.0*clearance
# make the base
s = cq.Workplane("XY").box(total_length, total_width, height)
# shell inwards not outwards
s = s.faces("<Z").shell(-1.0 * t)
# make the bumps on the top
s = s.faces(">Z").workplane(). \
rarray(pitch, pitch, lbumps, wbumps, True).circle(bumpDiam / 2.0) \
.extrude(bumpHeight)
# add posts on the bottom. posts are different diameter depending on geometry
# solid studs for 1 bump, tubes for multiple, none for 1x1
tmp = s.faces("<Z").workplane(invert=True)
if lbumps > 1 and wbumps > 1:
tmp = tmp.rarray(pitch, pitch, lbumps - 1, wbumps - 1, center=True). \
circle(postDiam / 2.0).circle(bumpDiam / 2.0).extrude(height - t)
elif lbumps > 1:
tmp = tmp.rarray(pitch, pitch, lbumps - 1, 1, center=True). \
circle(t).extrude(height - t)
elif wbumps > 1:
tmp = tmp.rarray(pitch, pitch, 1, wbumps - 1, center=True). \
circle(t).extrude(height - t)
else:
tmp = s
# Render the solid
show_object(tmp)

View File

@ -1,32 +0,0 @@
#File: Ex001_Simple_Block.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex001_Simple_Block
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex001_Simple_Block)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more in-depth explanation of this example at http://parametricparts.com/docs/quickstart.html
import cadquery
import Part
#The dimensions of the box. These can be modified rather than changing the box's code directly.
length = 80.0
height = 60.0
thickness = 10.0
#Create a 3D box based on the dimension variables above
result = cadquery.Workplane("XY").box(length, height, thickness)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,33 +0,0 @@
#File: Ex002_Block_With_Bored_Center_Hole.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex002_Block_With_Bored_Center_Hole
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex002_Block_With_Bored_Center_Hole)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more in-depth explantion of this example at http://parametricparts.com/docs/quickstart.html
import cadquery
import Part
#The dimensions of the box. These can be modified rather than changing the box's code directly.
length = 80.0
height = 60.0
thickness = 10.0
center_hole_dia = 22.0
#Create a 3D box based on the dimension variables above and add a 22mm center hole
result = cadquery.Workplane("XY").box(length, height, thickness) \
.faces(">Z").workplane().hole(center_hole_dia)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,40 +0,0 @@
#File: Ex003_Pillow_Block_With_Counterbored_Holes.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex003_Pillow_Block_With_Counterbored_Holes
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex003_Pillow_Block_With_Counterbored_Holes)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more in-depth explanation of this example at http://parametricparts.com/docs/quickstart.html
import cadquery
import Part
#The dimensions of the box. These can be modified rather than changing the box's code directly.
length = 80.0
height = 60.0
thickness = 10.0
center_hole_dia = 22.0
cbore_hole_diameter = 2.4
cbore_diameter = 4.4
cbore_depth = 2.1
#Create a 3D box based on the dimension variables above and add 4 counterbored holes
result = cadquery.Workplane("XY").box(length, height, thickness) \
.faces(">Z").workplane().hole(center_hole_dia) \
.faces(">Z").workplane() \
.rect(length - 8.0, height - 8.0, forConstruction = True) \
.vertices().cboreHole(cbore_hole_diameter, cbore_diameter, cbore_depth)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,34 +0,0 @@
#File: Ex004_Extruded_Cylindrical_Plate.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex004_Extruded_Cylindrical_Plate
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex004_Extruded_Cylindrical_Plate)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The dimensions of the model. These can be modified rather than changing the box's code directly.
circle_radius = 50.0
rectangle_width = 13.0
rectangle_length = 19.0
thickness = 13.0
#Extrude a cylindrical plate with a rectangular hole in the middle of it
result = cadquery.Workplane("front").circle(circle_radius).rect(rectangle_width, rectangle_length).extrude(thickness)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,33 +0,0 @@
#File: Ex005_Extruded_Lines_and_Arcs.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex005_Extruded_Lines_and_Arcs
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex005_Extruded_Lines_and_Arcs)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
#(Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The dimensions of the model. These can be modified rather than changing the box's code directly.
width = 2.0
thickness = 0.25
#Extrude a plate outline made of lines and an arc
result = cadquery.Workplane("front").lineTo(width, 0).lineTo(width, 1.0).threePointArc((1.0, 1.5),(0.0, 1.0)) \
.close().extrude(thickness)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,38 +0,0 @@
#File: Ex006_Moving_the_Current_Working_Point.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex006_Moving_the_Current_Working_Point
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex006_Moving_the_Current_Working_Point)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The dimensions of the model. These can be modified rather than changing the box's code directly.
circle_radius = 3.0
thickness = 0.25
#Make the plate with two cutouts in it
result = cadquery.Workplane("front").circle(circle_radius) # Current point is the center of the circle, at (0,0)
result = result.center(1.5,0.0).rect(0.5,0.5) # New work center is (1.5,0.0)
result = result.center(-1.5,1.5).circle(0.25) # New work center is ( 0.0,1.5).
#The new center is specified relative to the previous center, not global coordinates!
result = result.extrude(thickness)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,36 +0,0 @@
#File: Ex007_Using_Point_Lists.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex007_Using_Point_Lists
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex007_Using_Point_Lists)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The dimensions of the model. These can be modified rather than changing the box's code directly.
plate_radius = 2.0
hole_pattern_radius = 0.25
thickness = 0.125
#Make the plate with 4 holes in it at various points
r = cadquery.Workplane("front").circle(plate_radius) # Make the base
r = r.pushPoints([(1.5, 0), (0, 1.5), (-1.5, 0), (0, -1.5)]) # Now four points are on the stack
r = r.circle(hole_pattern_radius) # Circle will operate on all four points
result = r.extrude(thickness)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,36 +0,0 @@
#File: Ex008_Polygon_Creation.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex008_Polygon_Creation
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex008_Polygon_Creation)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The dimensions of the model. These can be modified rather than changing the box's code directly.
width = 3.0
height = 4.0
thickness = 0.25
polygon_sides = 6
polygon_dia = 1.0
#Create a plate with two polygons cut through it
result = cadquery.Workplane("front").box(width, height, thickness).pushPoints([(0, 0.75), (0, -0.75)]) \
.polygon(polygon_sides, polygon_dia).cutThruAll()
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,44 +0,0 @@
#File: Ex009_Polylines.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex009_Polylines
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex009_Polylines)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Set up our Length, Height, Width, and thickness that will be used to define the locations that the polyline
#is drawn to/thru
(L, H, W, t) = (100.0, 20.0, 20.0, 1.0)
#Define the locations that the polyline will be drawn to/thru
pts = [
(0, H/2.0),
(W/2.0, H/2.0),
(W/2.0, (H/2.0 - t)),
(t/2.0, (H/2.0-t)),
(t/2.0, (t - H/2.0)),
(W/2.0, (t - H/2.0)),
(W/2.0, H/-2.0),
(0, H/-2.0)
]
#We generate half of the I-beam outline and then mirror it to create the full I-beam
result = cadquery.Workplane("front").polyline(pts).mirrorY().extrude(L)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,45 +0,0 @@
#File: Ex010_Defining_an_Edge_with_a_Spline.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex010_Defining_an_Edge_with_a_Spline
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex010_Defining_an_Edge_with_a_Spline)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The workplane we want to create the spline on to extrude
s = cadquery.Workplane("XY")
#The points that the spline will pass through
sPnts = [
(2.75, 1.5),
(2.5, 1.75),
(2.0, 1.5),
(1.5, 1.0),
(1.0, 1.25),
(0.5, 1.0),
(0, 1.0)
]
#Generate our plate with the spline feature and make sure it's a closed entity
r = s.lineTo(3.0, 0).lineTo(3.0, 1.0).spline(sPnts).close()
#Extrude to turn the wire into a plate
result = r.extrude(0.5)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,34 +0,0 @@
#File: Ex011_Mirroring_Symmetric_Geometry.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex011_Mirroring_Symmetric_Geometry
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex011_Mirroring_Symmetric_Geometry)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#1.0 is the distance, not coordinate
r = cadquery.Workplane("front").hLine(1.0)
#hLineTo allows using xCoordinate not distance
r = r.vLine(0.5).hLine(-0.25).vLine(-0.25).hLineTo(0.0)
#Mirror the geometry and extrude
result = r.mirrorY().extrude(0.25)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,31 +0,0 @@
#File: Ex012_Creating_Workplanes_on_Faces.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex012_Creating_Workplanes_on_Faces
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex012_Creating_Workplanes_on_Faces)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Make a basic prism
result = cadquery.Workplane("front").box(2,3,0.5)
#Find the top-most face and make a hole
result = result.faces(">Z").workplane().hole(0.5)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,34 +0,0 @@
#File: Ex013_Locating_a_Workplane_on_a_Vertex.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex013_Locating_a_Workplane_on_a_Vertex
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex013_Locating_a_Workplane_on_a_Vertex)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Make a basic prism
result = cadquery.Workplane("front").box(3, 2, 0.5)
#Select the lower left vertex and make a workplane
result = result.faces(">Z").vertices("<XY").workplane()
#Cut the corner out
result = result.circle(1.0).cutThruAll()
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,34 +0,0 @@
#File: Ex014_Offset_Workplanes.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex014_Offset_Workplanes
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex014_Offset_Workplanes)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Make a basic prism
result = cadquery.Workplane("front").box(3, 2, 0.5)
#Workplane is offset from the object surface
result = result.faces("<X").workplane(offset=0.75)
#Create a disc
result = result.circle(1.0).extrude(0.5)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,32 +0,0 @@
#File: Ex015_Rotated_Workplanes.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex015_Rotated_Workplanes
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex015_Rotated_Workplanes)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
from cadquery import Vector
import Part
#Create a rotated workplane and put holes in each corner of a rectangle on that workplane, producing angled holes
#in the face
result = cadquery.Workplane("front").box(4.0, 4.0, 0.25).faces(">Z").workplane() \
.transformed(offset=Vector(0, -1.5, 1.0), rotate=Vector(60, 0, 0)) \
.rect(1.5, 1.5, forConstruction=True).vertices().hole(0.25)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,29 +0,0 @@
#File: Ex016_Using_Construction_Geometry.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex016_Using_Construction_Geometry
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex016_Using_Construction_Geometry)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Create a block with holes in each corner of a rectangle on that workplane
result = cadquery.Workplane("front").box(2, 2, 0.5).faces(">Z").workplane() \
.rect(1.5, 1.5, forConstruction=True).vertices().hole(0.125)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,28 +0,0 @@
#File: Ex017_Shelling_to_Create_Thin_Features.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex017_Shelling_to_Create_Thin_Features
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex017_Shelling_to_Create_Thin_Features)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Create a hollow box that's open on both ends with a thin wall
result = cadquery.Workplane("front").box(2, 2, 2).faces("+Z").shell(0.05)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,29 +0,0 @@
#File: Ex018_Making_Lofts.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex018_Making_Lofts
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex018_Making_Lofts)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Create a lofted section between a rectangle and a circular section
result = cadquery.Workplane("front").box(4.0, 4.0, 0.25).faces(">Z").circle(1.5) \
.workplane(offset=3.0).rect(0.75, 0.5).loft(combine=True)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,30 +0,0 @@
#File: Ex019_Counter_Sunk_Holes.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex019_Counter_Sunk_Holes
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex019_Counter_Sunk_Holes)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Create a plate with 4 counter-sunk holes in it
result = cadquery.Workplane(cadquery.Plane.XY()).box(4, 2, 0.5).faces(">Z").workplane() \
.rect(3.5, 1.5, forConstruction=True)\
.vertices().cskHole(0.125, 0.25, 82.0, depth=None)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,28 +0,0 @@
#File: Ex020_Rounding_Corners_with_Fillets.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex020_Rounding_Corners_with_Fillets
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex020_Rounding_Corners_with_Fillets)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Create a plate with 4 rounded corners in the Z-axis
result = cadquery.Workplane("XY").box(3, 3, 0.5).edges("|Z").fillet(0.125)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,31 +0,0 @@
#File: Ex021_Splitting_an_Object.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex021_Splitting_an_Object
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex021_Splitting_an_Object)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Create a simple block with a hole through it that we can split
c = cadquery.Workplane("XY").box(1, 1, 1).faces(">Z").workplane().circle(0.25).cutThruAll()
#Cut the block in half sideways
result = c.faces(">Y").workplane(-0.5).split(keepTop=True)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,40 +0,0 @@
#File: Ex022_Classic_OCC_Bottle.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex022_Classic_OCC_Bottle
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex022_Classic_OCC_Bottle)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Set up the length, width, and thickness
(L,w,t) = (20.0, 6.0, 3.0)
s = cadquery.Workplane("XY")
#Draw half the profile of the bottle and extrude it
p = s.center(-L / 2.0, 0).vLine(w / 2.0) \
.threePointArc((L / 2.0, w / 2.0 + t),(L, w / 2.0)).vLine(-w / 2.0) \
.mirrorX().extrude(30.0, True)
#Make the neck
p.faces(">Z").workplane().circle(3.0).extrude(2.0, True)
#Make a shell
result = p.faces(">Z").shell(0.3)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,102 +0,0 @@
#File: Ex023_Parametric_Enclosure.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex023_Parametric_Enclosure
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex023_Parametric_Enclosure)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#Parameter definitions
p_outerWidth = 100.0 # Outer width of box enclosure
p_outerLength = 150.0 # Outer length of box enclosure
p_outerHeight = 50.0 # Outer height of box enclosure
p_thickness = 3.0 # Thickness of the box walls
p_sideRadius = 10.0 # Radius for the curves around the sides of the bo
p_topAndBottomRadius = 2.0 # Radius for the curves on the top and bottom edges of the box
p_screwpostInset = 12.0 # How far in from the edges the screwposts should be placed
p_screwpostID = 4.0 # Inner diameter of the screwpost holes, should be roughly screw diameter not including threads
p_screwpostOD = 10.0 # Outer diameter of the screwposts. Determines overall thickness of the posts
p_boreDiameter = 8.0 # Diameter of the counterbore hole, if any
p_boreDepth = 1.0 # Depth of the counterbore hole, if
p_countersinkDiameter = 0.0 # Outer diameter of countersink. Should roughly match the outer diameter of the screw head
p_countersinkAngle = 90.0 # Countersink angle (complete angle between opposite sides, not from center to one side)
p_flipLid = True # Whether to place the lid with the top facing down or not.
p_lipHeight = 1.0 # Height of lip on the underside of the lid. Sits inside the box body for a snug fit.
#Outer shell
oshell = cadquery.Workplane("XY").rect(p_outerWidth, p_outerLength).extrude(p_outerHeight + p_lipHeight)
#Weird geometry happens if we make the fillets in the wrong order
if p_sideRadius > p_topAndBottomRadius:
oshell.edges("|Z").fillet(p_sideRadius)
oshell.edges("#Z").fillet(p_topAndBottomRadius)
else:
oshell.edges("#Z").fillet(p_topAndBottomRadius)
oshell.edges("|Z").fillet(p_sideRadius)
#Inner shell
ishell = oshell.faces("<Z").workplane(p_thickness, True)\
.rect((p_outerWidth - 2.0 * p_thickness),(p_outerLength - 2.0 * p_thickness))\
.extrude((p_outerHeight - 2.0 * p_thickness), False) # Set combine false to produce just the new boss
ishell.edges("|Z").fillet(p_sideRadius - p_thickness)
#Make the box outer box
box = oshell.cut(ishell)
#Make the screwposts
POSTWIDTH = (p_outerWidth - 2.0 * p_screwpostInset)
POSTLENGTH = (p_outerLength - 2.0 * p_screwpostInset)
postCenters = box.faces(">Z").workplane(-p_thickness)\
.rect(POSTWIDTH, POSTLENGTH, forConstruction=True)\
.vertices()
for v in postCenters.all():
v.circle(p_screwpostOD / 2.0).circle(p_screwpostID / 2.0)\
.extrude((-1.0) * ((p_outerHeight + p_lipHeight) - (2.0 * p_thickness)), True)
#Split lid into top and bottom parts
(lid, bottom) = box.faces(">Z").workplane(-p_thickness - p_lipHeight).split(keepTop=True, keepBottom=True).all()
#Translate the lid, and subtract the bottom from it to produce the lid inset
lowerLid = lid.translate((0, 0, -p_lipHeight))
cutlip = lowerLid.cut(bottom).translate((p_outerWidth + p_thickness, 0, p_thickness - p_outerHeight + p_lipHeight))
#Compute centers for counterbore/countersink or counterbore
topOfLidCenters = cutlip.faces(">Z").workplane().rect(POSTWIDTH, POSTLENGTH, forConstruction=True).vertices()
#Add holes of the desired type
if p_boreDiameter > 0 and p_boreDepth > 0:
topOfLid = topOfLidCenters.cboreHole(p_screwpostID, p_boreDiameter, p_boreDepth, (2.0) * p_thickness)
elif p_countersinkDiameter > 0 and p_countersinkAngle > 0:
topOfLid = topOfLidCenters.cskHole(p_screwpostID, p_countersinkDiameter, p_countersinkAngle, (2.0) * p_thickness)
else:
topOfLid= topOfLidCenters.hole(p_screwpostID, 2.0 * p_thickness)
#Flip lid upside down if desired
if p_flipLid:
topOfLid.rotateAboutCenter((1, 0, 0), 180)
#Return the combined result
result = topOfLid.combineSolids(bottom)
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -1,41 +0,0 @@
#File: Ex024_Using_FreeCAD_Solids_as_CQ_Objects.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex024_Using_FreeCAD_Solids_as_CQ_Objects
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex024_Using_FreeCAD_Solids_as_CQ_Objects)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery, FreeCAD, Part
#Create a new document that we can draw our model on
newDoc = FreeCAD.newDocument()
#shows a 1x1x1 FreeCAD cube in the display
initialBox = newDoc.addObject("Part::Box","initialBox")
newDoc.recompute()
#Make a CQ object
cqBox = cadquery.CQ(cadquery.Solid(initialBox.Shape))
#Extrude a peg
newThing = cqBox.faces(">Z").workplane().circle(0.5).extrude(0.25)
#Add a FreeCAD object to the tree and then store a CQ object in it
nextShape = newDoc.addObject("Part::Feature", "nextShape")
nextShape.Shape = newThing.val().wrapped
#Rerender the doc to see what the new solid looks like
newDoc.recompute()

View File

@ -1,41 +0,0 @@
#File: Ex025_Revolution.py
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
#You run this example by typing the following in the FreeCAD python console, making sure to change
#the path to this example, and the name of the example appropriately.
#import sys
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
#import Ex025_Revolution
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
#reload(Ex025_Revolution)
#You'll need to delete the original shape that was created, and the new shape should be named sequentially
# (Shape001, etc).
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
#You can get a more information on this example at
# http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
import cadquery
import Part
#The dimensions of the model. These can be modified rather than changing the shape's code directly.
rectangle_width = 10.0
rectangle_length = 10.0
angle_degrees = 360.0
#Revolve a cylinder from a rectangle
#Switch comments around in this section to try the revolve operation with different parameters
result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
#Revolve a donut with square walls
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
#Boiler plate code to render our solid in FreeCAD's GUI
Part.show(result.toFreecad())

View File

@ -8,13 +8,14 @@ import unittest
#on py 2.7.x on win
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCadObjects))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadQuery.TestCadQuery))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestExporters.TestExporters))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestImporters.TestImporters))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestJupyter.TestJupyter))
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
if __name__ == '__main__':
result = unittest.TextTestRunner().run(suite)

View File

@ -2,7 +2,7 @@
import sys
import unittest
from tests import BaseTest
from OCC.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_DZ
from OCC.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_DZ, gp_XYZ
from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex,
BRepBuilderAPI_MakeEdge,
BRepBuilderAPI_MakeFace)
@ -24,10 +24,27 @@ class TestCadObjects(BaseTest):
v1 = Vector(1, 2, 3)
v2 = Vector((1, 2, 3))
v3 = Vector(gp_Vec(1, 2, 3))
v4 = Vector([1,2,3])
v5 = Vector(gp_XYZ(1,2,3))
for v in [v1, v2, v3]:
for v in [v1, v2, v3, v4, v5]:
self.assertTupleAlmostEquals((1, 2, 3), v.toTuple(), 4)
v6 = Vector((1,2))
v7 = Vector([1,2])
v8 = Vector(1,2)
for v in [v6, v7, v8]:
self.assertTupleAlmostEquals((1, 2, 0), v.toTuple(), 4)
v9 = Vector()
self.assertTupleAlmostEquals((0, 0, 0), v9.toTuple(), 4)
v9.x = 1.
v9.y = 2.
v9.z = 3.
self.assertTupleAlmostEquals((1, 2, 3), (v9.x, v9.y, v9.z), 4)
def testVertex(self):
"""
Tests basic vertex functions
@ -149,14 +166,19 @@ class TestCadObjects(BaseTest):
[0., 1., 0., 2.],
[0., 0., 1., 3.],
[0., 0., 0., 1.]]
vals4x4_tuple = tuple(tuple(r) for r in vals4x4)
# test constructor with 16-value input
m = Matrix(vals4x4)
self.assertEqual(vals4x4, matrix_vals(m))
m = Matrix(vals4x4_tuple)
self.assertEqual(vals4x4, matrix_vals(m))
# test constructor with 12-value input (the last 4 are an implied
# [0,0,0,1])
m = Matrix(vals4x4[0:12])
m = Matrix(vals4x4[:3])
self.assertEqual(vals4x4, matrix_vals(m))
m = Matrix(vals4x4_tuple[:3])
self.assertEqual(vals4x4, matrix_vals(m))
# Test 16-value input with invalid values for the last 4
@ -167,14 +189,24 @@ class TestCadObjects(BaseTest):
with self.assertRaises(ValueError):
Matrix(invalid)
# Test input with invalid size
# Test input with invalid size / nested types
with self.assertRaises(TypeError):
Matrix([[1, 2, 3, 4], [1, 2, 3], [1, 2, 3, 4]])
with self.assertRaises(TypeError):
Matrix([1,2,3])
# Invalid sub-type
with self.assertRaises(TypeError):
Matrix([[1, 2, 3, 4], 'abc', [1, 2, 3, 4]])
# test out-of-bounds access
m = Matrix()
with self.assertRaises(IndexError):
m[5, 5]
m[0, 4]
with self.assertRaises(IndexError):
m[4, 0]
with self.assertRaises(IndexError):
m['ab']
def testTranslate(self):
@ -188,6 +220,50 @@ class TestCadObjects(BaseTest):
gp_Pnt(1, 1, 0)).Edge())
self.assertEqual(2, len(e.Vertices()))
def testPlaneEqual(self):
# default orientation
self.assertEqual(
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1))
)
# moved origin
self.assertEqual(
Plane(origin=(2,1,-1), xDir=(1,0,0), normal=(0,0,1)),
Plane(origin=(2,1,-1), xDir=(1,0,0), normal=(0,0,1))
)
# moved x-axis
self.assertEqual(
Plane(origin=(0,0,0), xDir=(1,1,0), normal=(0,0,1)),
Plane(origin=(0,0,0), xDir=(1,1,0), normal=(0,0,1))
)
# moved z-axis
self.assertEqual(
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,1,1)),
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,1,1))
)
def testPlaneNotEqual(self):
# type difference
for value in [None, 0, 1, 'abc']:
self.assertNotEqual(
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
value
)
# origin difference
self.assertNotEqual(
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
Plane(origin=(0,0,1), xDir=(1,0,0), normal=(0,0,1))
)
# x-axis difference
self.assertNotEqual(
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
Plane(origin=(0,0,0), xDir=(1,1,0), normal=(0,0,1))
)
# z-axis difference
self.assertNotEqual(
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,1,1))
)
if __name__ == '__main__':
unittest.main()

View File

@ -418,6 +418,54 @@ class TestCadQuery(BaseTest):
self.assertEqual(3, result.faces().size())
self.assertEqual(3, result.edges().size())
def testSweepAlongListOfWires(self):
"""
Tests the operation of sweeping along a list of wire(s) along a path
"""
# X axis line length 20.0
path = Workplane("XZ").moveTo(-10, 0).lineTo(10, 0)
# Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0
defaultSweep = Workplane("YZ").workplane(offset=-10.0).circle(2.0). \
workplane(offset=10.0).circle(1.0). \
workplane(offset=10.0).circle(2.0).sweep(path, sweepAlongWires=True)
# We can sweep thrue different shapes
recttocircleSweep = Workplane("YZ").workplane(offset=-10.0).rect(2.0, 2.0). \
workplane(offset=8.0).circle(1.0).workplane(offset=4.0).circle(1.0). \
workplane(offset=8.0).rect(2.0, 2.0).sweep(path, sweepAlongWires=True)
circletorectSweep = Workplane("YZ").workplane(offset=-10.0).circle(1.0). \
workplane(offset=7.0).rect(2.0, 2.0).workplane(offset=6.0).rect(2.0, 2.0). \
workplane(offset=7.0).circle(1.0).sweep(path, sweepAlongWires=True)
# Placement of the Shape is important otherwise could produce unexpected shape
specialSweep = Workplane("YZ").circle(1.0).workplane(offset=10.0).rect(2.0, 2.0). \
sweep(path, sweepAlongWires=True)
# Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0
path = Workplane("XZ").moveTo(-5, 4).lineTo(0, 4). \
threePointArc((4, 0), (0, -4)).lineTo(-5, -4)
# Placement of different shapes should follow the path
# cylinder r=1.5 along first line
# then sweep allong arc from r=1.5 to r=1.0
# then cylinder r=1.0 along last line
arcSweep = Workplane("YZ").workplane(offset=-5).moveTo(0, 4).circle(1.5). \
workplane(offset=5).circle(1.5). \
moveTo(0, -8).circle(1.0). \
workplane(offset=-5).circle(1.0). \
sweep(path, sweepAlongWires=True)
# Test and saveModel
self.assertEqual(1, defaultSweep.solids().size())
self.assertEqual(1, circletorectSweep.solids().size())
self.assertEqual(1, recttocircleSweep.solids().size())
self.assertEqual(1, specialSweep.solids().size())
self.assertEqual(1, arcSweep.solids().size())
self.saveModel(defaultSweep)
def testTwistExtrude(self):
"""
Tests extrusion while twisting through an angle.
@ -446,6 +494,35 @@ class TestCadQuery(BaseTest):
# 6 faces for the box, 2 faces for each cylinder
self.assertEqual(6 + NUMX * NUMY * 2, s.faces().size())
def testPolarArray(self):
radius = 10
# Test for proper number of elements
s = Workplane("XY").polarArray(radius, 0, 180, 1)
self.assertEqual(1, s.size())
s = Workplane("XY").polarArray(radius, 0, 180, 6)
self.assertEqual(6, s.size())
# Test for proper placement when fill == True
s = Workplane("XY").polarArray(radius, 0, 180, 3)
self.assertAlmostEqual(0, s.objects[1].x)
self.assertAlmostEqual(radius, s.objects[1].y)
# Test for proper placement when angle to fill is multiple of 360 deg
s = Workplane("XY").polarArray(radius, 0, 360, 4)
self.assertAlmostEqual(0, s.objects[1].x)
self.assertAlmostEqual(radius, s.objects[1].y)
# Test for proper placement when fill == False
s = Workplane("XY").polarArray(radius, 0, 90, 3, fill=False)
self.assertAlmostEqual(0, s.objects[1].x)
self.assertAlmostEqual(radius, s.objects[1].y)
# Test for proper operation of startAngle
s = Workplane("XY").polarArray(radius, 90, 180, 3)
self.assertAlmostEqual(0, s.objects[0].x)
self.assertAlmostEqual(radius, s.objects[0].y)
def testNestedCircle(self):
s = Workplane("XY").box(40, 40, 5).pushPoints(
[(10, 0), (0, 10)]).circle(4).circle(2).extrude(4)
@ -642,6 +719,24 @@ class TestCadQuery(BaseTest):
self.assertEqual(10, currentS.faces().size())
def testIntersect(self):
"""
Tests the intersect function.
"""
s = Workplane(Plane.XY())
currentS = s.rect(2.0, 2.0).extrude(0.5)
toIntersect = s.rect(1.0, 1.0).extrude(1)
currentS.intersect(toIntersect.val())
self.assertEqual(6, currentS.faces().size())
self.assertAlmostEqual(currentS.val().Volume(),0.5)
currentS.intersect(toIntersect)
self.assertEqual(6, currentS.faces().size())
self.assertAlmostEqual(currentS.val().Volume(),0.5)
def testBoundingBox(self):
"""
Tests the boudingbox center of a model
@ -803,6 +898,37 @@ class TestCadQuery(BaseTest):
selectors.NearestToPointSelector((0.0, 0.0, 0.0)))
.first().val().Y))
# Test the sagittaArc and radiusArc functions
a1 = Workplane(Plane.YZ()).threePointArc((5, 1), (10, 0))
a2 = Workplane(Plane.YZ()).sagittaArc((10, 0), -1)
a3 = Workplane(Plane.YZ()).threePointArc((6, 2), (12, 0))
a4 = Workplane(Plane.YZ()).radiusArc((12, 0), -10)
assert(a1.edges().first().val().geomType() == "CIRCLE")
assert(a2.edges().first().val().geomType() == "CIRCLE")
assert(a3.edges().first().val().geomType() == "CIRCLE")
assert(a4.edges().first().val().geomType() == "CIRCLE")
assert(a1.edges().first().val().Length() == a2.edges().first().val().Length())
assert(a3.edges().first().val().Length() == a4.edges().first().val().Length())
def testPolarLines(self):
"""
Draw some polar lines and check expected results
"""
# Test the PolarLine* functions
s = Workplane(Plane.XY())
r = s.polarLine(10, 45) \
.polarLineTo(10, -45) \
.polarLine(10, -180) \
.polarLine(-10, -90) \
.close()
# a single wire, 5 edges
self.assertEqual(1, r.wires().size())
self.assertEqual(5, r.wires().edges().size())
def testLargestDimension(self):
"""
Tests the largestDimension function when no solids are on the stack and when there are
@ -1326,6 +1452,80 @@ class TestCadQuery(BaseTest):
line(-10, 0).close().extrude(10, clean=False).clean()
self.assertEqual(6, s.faces().size())
def testPlanes(self):
"""
Test other planes other than the normal ones (XY, YZ)
"""
# ZX plane
s = Workplane(Plane.ZX())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# YX plane
s = Workplane(Plane.YX())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# YX plane
s = Workplane(Plane.YX())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# ZY plane
s = Workplane(Plane.ZY())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# front plane
s = Workplane(Plane.front())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# back plane
s = Workplane(Plane.back())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# left plane
s = Workplane(Plane.left())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# right plane
s = Workplane(Plane.right())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# top plane
s = Workplane(Plane.top())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
# bottom plane
s = Workplane(Plane.bottom())
result = s.rect(2.0, 4.0).extrude(0.5).faces(">Z").workplane()\
.rect(1.5, 3.5, forConstruction=True).vertices().cskHole(0.125, 0.25, 82, depth=None)
self.saveModel(result)
def testIsInside(self):
"""
Testing if one box is inside of another.
"""
box1 = Workplane(Plane.XY()).box(10, 10, 10)
box2 = Workplane(Plane.XY()).box(5, 5, 5)
self.assertFalse(box2.val().BoundingBox().isInside(box1.val().BoundingBox()))
self.assertTrue(box1.val().BoundingBox().isInside(box2.val().BoundingBox()))
def testCup(self):
"""
UOM = "mm"
@ -1486,3 +1686,36 @@ class TestCadQuery(BaseTest):
self.assertTupleAlmostEquals(delta.toTuple(),
(0., 0., 2. * h),
decimal_places)
def testClose(self):
# Close without endPoint and startPoint coincide.
# Create a half-circle
a = Workplane(Plane.XY()).sagittaArc((10, 0), 2).close().extrude(2)
# Close when endPoint and startPoint coincide.
# Create a double half-circle
b = Workplane(Plane.XY()).sagittaArc((10, 0), 2).sagittaArc((0, 0), 2).close().extrude(2)
# The b shape shall have twice the volume of the a shape.
self.assertAlmostEqual(a.val().Volume() * 2.0, b.val().Volume())
# Testcase 3 from issue #238
thickness = 3.0
length = 10.0
width = 5.0
obj1 = Workplane('XY', origin=(0, 0, -thickness / 2)) \
.moveTo(length / 2, 0).threePointArc((0, width / 2), (-length / 2, 0)) \
.threePointArc((0, -width / 2), (length / 2, 0)) \
.close().extrude(thickness)
os_x = 8.0 # Offset in X
os_y = -19.5 # Offset in Y
obj2 = Workplane('YZ', origin=(os_x, os_y, -thickness / 2)) \
.moveTo(os_x + length / 2, os_y).sagittaArc((os_x -length / 2, os_y), width / 2) \
.sagittaArc((os_x + length / 2, os_y), width / 2) \
.close().extrude(thickness)
# The obj1 shape shall have the same volume as the obj2 shape.
self.assertAlmostEqual(obj1.val().Volume(), obj2.val().Volume())

13
tests/TestJupyter.py Normal file
View File

@ -0,0 +1,13 @@
from tests import BaseTest
import cadquery
class TestJupyter(BaseTest):
def test_repr_html(self):
cube = cadquery.Workplane('XY').box(1, 1, 1)
shape = cube.val()
self.assertIsInstance(shape, cadquery.occ_impl.shapes.Solid)
# Test no exception on rendering to html
html = shape._repr_html_()
# TODO: verification improvement: test for valid html

View File

@ -49,5 +49,14 @@ class BaseTest(unittest.TestCase):
self.assertAlmostEqual(i, j, places)
__all__ = ['TestCadObjects', 'TestCadQuery', 'TestCQSelectors', 'TestWorkplanes',
'TestExporters', 'TestCQSelectors', 'TestImporters', 'TestCQGI']
__all__ = [
'TestCadObjects',
'TestCadQuery',
'TestCQGI',
'TestCQSelectors',
'TestCQSelectors',
'TestExporters',
'TestImporters',
'TestJupyter',
'TestWorkplanes',
]