Merge branch 'master' into adam-urbanczyk-OCC-version-update
This commit is contained in:
211
cadquery/cq.py
211
cadquery/cq.py
@ -1066,6 +1066,45 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.pushPoints(lpoints)
|
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):
|
def pushPoints(self, pntList):
|
||||||
"""
|
"""
|
||||||
Pushes a list of points onto the stack as vertices.
|
Pushes a list of points onto the stack as vertices.
|
||||||
@ -1204,6 +1243,35 @@ class Workplane(CQ):
|
|||||||
p = self._findFromPoint(True)
|
p = self._findFromPoint(True)
|
||||||
return self.lineTo(xCoord, p.y, forConstruction)
|
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
|
# absolute move in current plane, not drawing
|
||||||
def moveTo(self, x=0, y=0):
|
def moveTo(self, x=0, y=0):
|
||||||
"""
|
"""
|
||||||
@ -1315,6 +1383,66 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.newObject([arc])
|
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):
|
def rotateAndCopy(self, matrix):
|
||||||
"""
|
"""
|
||||||
Makes a copy of all edges on the stack, rotates them according to the
|
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)
|
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
|
# Need to reset the first point after closing a wire
|
||||||
self.ctx.firstPoint = None
|
self.ctx.firstPoint = None
|
||||||
@ -2091,24 +2226,25 @@ class Workplane(CQ):
|
|||||||
newS = newS.clean()
|
newS = newS.clean()
|
||||||
return newS
|
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.
|
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 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 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
|
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
|
||||||
:return: a CQ object with the resulting solid selected.
|
:return: a CQ object with the resulting solid selected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# returns a Solid (or a compound if there were multiple)
|
r = self._sweep(path.wire(), sweepAlongWires, makeSolid, isFrenet) # returns a Solid (or a compound if there were multiple)
|
||||||
r = self._sweep(path.wire(), makeSolid, isFrenet)
|
|
||||||
if combine:
|
if combine:
|
||||||
newS = self._combineWithBase(r)
|
newS = self._combineWithBase(r)
|
||||||
else:
|
else:
|
||||||
newS = self.newObject([r])
|
newS = self.newObject([r])
|
||||||
if clean:
|
if clean: newS = newS.clean()
|
||||||
newS = newS.clean()
|
|
||||||
return newS
|
return newS
|
||||||
|
|
||||||
def _combineWithBase(self, obj):
|
def _combineWithBase(self, obj):
|
||||||
@ -2223,6 +2359,43 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.newObject([newS])
|
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):
|
def cutBlind(self, distanceToCut, clean=True):
|
||||||
"""
|
"""
|
||||||
Use all un-extruded wires in the parent chain to create a prismatic cut from existing solid.
|
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)
|
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.
|
Makes a swept solid from an existing set of pending wires.
|
||||||
|
|
||||||
:param path: A wire along which the pending wires will be swept
|
: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
|
:return:a FreeCAD solid, suitable for boolean operations
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# group wires together into faces based on which ones are inside the others
|
# group wires together into faces based on which ones are inside the others
|
||||||
# result is a list of lists
|
# result is a list of lists
|
||||||
s = time.time()
|
s = time.time()
|
||||||
wireSets = sortWiresByBuildOrder(
|
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires), self.plane, [])
|
||||||
list(self.ctx.pendingWires), self.plane, [])
|
|
||||||
# print "sorted wires in %d sec" % ( time.time() - s )
|
# print "sorted wires in %d sec" % ( time.time() - s )
|
||||||
# now all of the wires have been used to create an extrusion
|
self.ctx.pendingWires = [] # now all of the wires have been used to create an extrusion
|
||||||
self.ctx.pendingWires = []
|
|
||||||
|
|
||||||
toFuse = []
|
toFuse = []
|
||||||
for ws in wireSets:
|
if not sweepAlongWires:
|
||||||
thisObj = Solid.sweep(
|
for ws in wireSets:
|
||||||
ws[0], ws[1:], path.val(), makeSolid, isFrenet)
|
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)
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
return Compound.makeCompound(toFuse)
|
return Compound.makeCompound(toFuse)
|
||||||
|
@ -19,22 +19,26 @@ class Vector(object):
|
|||||||
* a gp_Vec
|
* a gp_Vec
|
||||||
* a vector ( in which case it is copied )
|
* a vector ( in which case it is copied )
|
||||||
* a 3-tuple
|
* 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):
|
def __init__(self, *args):
|
||||||
if len(args) == 3:
|
if len(args) == 3:
|
||||||
fV = gp_Vec(*args)
|
fV = gp_Vec(*args)
|
||||||
|
elif len(args) == 2:
|
||||||
|
fV = gp_Vec(*args,0)
|
||||||
elif len(args) == 1:
|
elif len(args) == 1:
|
||||||
if isinstance(args[0], Vector):
|
if isinstance(args[0], Vector):
|
||||||
fV = gp_Vec(args[0].wrapped.XYZ())
|
fV = gp_Vec(args[0].wrapped.XYZ())
|
||||||
elif isinstance(args[0], (tuple, list)):
|
elif isinstance(args[0], (tuple, list)):
|
||||||
fV = gp_Vec(*args[0])
|
arg = args[0]
|
||||||
elif isinstance(args[0], gp_Vec):
|
if len(arg)==3:
|
||||||
fV = gp_Vec(args[0].XYZ())
|
fV = gp_Vec(*arg)
|
||||||
elif isinstance(args[0], gp_Pnt):
|
elif len(arg)==2:
|
||||||
fV = gp_Vec(args[0].XYZ())
|
fV = gp_Vec(*arg,0)
|
||||||
elif isinstance(args[0], gp_Dir):
|
elif isinstance(args[0], (gp_Vec, gp_Pnt, gp_Dir)):
|
||||||
fV = gp_Vec(args[0].XYZ())
|
fV = gp_Vec(args[0].XYZ())
|
||||||
elif isinstance(args[0], gp_XYZ):
|
elif isinstance(args[0], gp_XYZ):
|
||||||
fV = gp_Vec(args[0])
|
fV = gp_Vec(args[0])
|
||||||
@ -51,14 +55,26 @@ class Vector(object):
|
|||||||
def x(self):
|
def x(self):
|
||||||
return self.wrapped.X()
|
return self.wrapped.X()
|
||||||
|
|
||||||
|
@x.setter
|
||||||
|
def x(self,value):
|
||||||
|
self.wrapped.SetX(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y(self):
|
def y(self):
|
||||||
return self.wrapped.Y()
|
return self.wrapped.Y()
|
||||||
|
|
||||||
|
@y.setter
|
||||||
|
def y(self,value):
|
||||||
|
self.wrapped.SetY(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def z(self):
|
def z(self):
|
||||||
return self.wrapped.Z()
|
return self.wrapped.Z()
|
||||||
|
|
||||||
|
@z.setter
|
||||||
|
def z(self,value):
|
||||||
|
self.wrapped.SetZ(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Length(self):
|
def Length(self):
|
||||||
return self.wrapped.Magnitude()
|
return self.wrapped.Magnitude()
|
||||||
@ -199,20 +215,21 @@ class Matrix:
|
|||||||
self.wrapped = gp_Trsf()
|
self.wrapped = gp_Trsf()
|
||||||
elif isinstance(matrix, gp_Trsf):
|
elif isinstance(matrix, gp_Trsf):
|
||||||
self.wrapped = matrix
|
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()
|
self.wrapped = gp_Trsf()
|
||||||
if len(matrix) == 3:
|
flattened = [e for row in matrix[:3] for e in row]
|
||||||
flattened = [e for row in matrix for e in row]
|
self.wrapped.SetValues(*flattened)
|
||||||
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")
|
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"Invalid param to matrix constructor: {}".format(matrix))
|
"Invalid param to matrix constructor: {}".format(matrix))
|
||||||
@ -266,18 +283,18 @@ class Matrix:
|
|||||||
and column parameters start at zero, which is consistent with most
|
and column parameters start at zero, which is consistent with most
|
||||||
python libraries, but is counter to gp_Trsf(), which is 1-indexed.
|
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)")
|
raise IndexError("Matrix subscript must provide (row, column)")
|
||||||
r, c = rc[0], rc[1]
|
(r, c) = rc
|
||||||
if r >= 0 and r < 4 and c >= 0 and c < 4:
|
if (0 <= r <= 3) and (0 <= c <= 3):
|
||||||
if r < 3:
|
if r < 3:
|
||||||
return self.wrapped.Value(r+1,c+1)
|
return self.wrapped.Value(r + 1, c + 1)
|
||||||
else:
|
else:
|
||||||
# gp_Trsf doesn't provide access to the 4th row because it has
|
# gp_Trsf doesn't provide access to the 4th row because it has
|
||||||
# an implied value as below:
|
# an implied value as below:
|
||||||
return [0., 0., 0., 1.][c]
|
return [0., 0., 0., 1.][c]
|
||||||
else:
|
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):
|
class Plane(object):
|
||||||
@ -293,6 +310,10 @@ class Plane(object):
|
|||||||
created automatically from faces.
|
created automatically from faces.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# equality tolerances
|
||||||
|
_eq_tolerance_origin = 1e-6
|
||||||
|
_eq_tolerance_dot = 1e-6
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def named(cls, stdName, origin=(0, 0, 0)):
|
def named(cls, stdName, origin=(0, 0, 0)):
|
||||||
"""Create a predefined Plane based on the conventional names.
|
"""Create a predefined Plane based on the conventional names.
|
||||||
@ -442,6 +463,23 @@ class Plane(object):
|
|||||||
self._setPlaneDir(xDir)
|
self._setPlaneDir(xDir)
|
||||||
self.origin = origin
|
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
|
@property
|
||||||
def origin(self):
|
def origin(self):
|
||||||
return self._origin
|
return self._origin
|
||||||
@ -774,10 +812,11 @@ class BoundBox(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@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 = Bnd_Box()
|
||||||
bbox.SetGap(tol)
|
bbox.SetGap(tol)
|
||||||
if optimal:
|
if optimal:
|
||||||
@ -791,6 +830,14 @@ class BoundBox(object):
|
|||||||
|
|
||||||
return cls(bbox)
|
return cls(bbox)
|
||||||
|
|
||||||
def isInside(self, anotherBox):
|
def isInside(self, b2):
|
||||||
"""Is the provided bounding box inside this one?"""
|
"""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
|
||||||
|
@ -4,7 +4,6 @@ from .shapes import Shape
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import urllib as urlreader
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from OCC.Core.STEPControl import STEPControl_Reader
|
from OCC.Core.STEPControl import STEPControl_Reader
|
||||||
@ -58,18 +57,3 @@ def importStep(fileName):
|
|||||||
solids.append(Shape.cast(shape))
|
solids.append(Shape.cast(shape))
|
||||||
|
|
||||||
return cadquery.Workplane("XY").newObject(solids)
|
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")
|
|
||||||
|
@ -2,10 +2,10 @@ from OCC.Core.Display.WebGl.x3dom_renderer import X3DExporter
|
|||||||
from OCC.Core.gp import gp_Quaternion, gp_Vec
|
from OCC.Core.gp import gp_Quaternion, gp_Vec
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from math import tan
|
from math import tan
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
from .geom import BoundBox
|
from .geom import BoundBox
|
||||||
|
|
||||||
N_HEADER_LINES = 10
|
|
||||||
BOILERPLATE = \
|
BOILERPLATE = \
|
||||||
'''
|
'''
|
||||||
<link rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'></link>
|
<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)):
|
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,
|
return BOILERPLATE.format(src=src,
|
||||||
id=uuid1(),
|
id=uuid4(),
|
||||||
height=height,
|
height=height,
|
||||||
x=d[0],
|
x=d[0],
|
||||||
y=d[1],
|
y=d[1],
|
||||||
@ -70,6 +70,7 @@ def x3d_display(shape,
|
|||||||
line_width=2.,
|
line_width=2.,
|
||||||
mesh_quality=.3):
|
mesh_quality=.3):
|
||||||
|
|
||||||
|
# Export to XML <Scene> tag
|
||||||
exporter = X3DExporter(shape,
|
exporter = X3DExporter(shape,
|
||||||
vertex_shader,
|
vertex_shader,
|
||||||
fragment_shader,
|
fragment_shader,
|
||||||
@ -83,9 +84,11 @@ def x3d_display(shape,
|
|||||||
mesh_quality)
|
mesh_quality)
|
||||||
|
|
||||||
exporter.compute()
|
exporter.compute()
|
||||||
x3d_str = exporter.to_x3dfile_string()
|
x3d_str = exporter.to_x3dfile_string(shape_id=0)
|
||||||
x3d_str = '\n'.join(x3d_str.splitlines()[N_HEADER_LINES:])
|
xml_et = ElementTree.fromstring(x3d_str)
|
||||||
|
scene_tag = xml_et.find('./Scene')
|
||||||
|
|
||||||
|
# Viewport Parameters
|
||||||
bb = BoundBox._fromTopoDS(shape)
|
bb = BoundBox._fromTopoDS(shape)
|
||||||
d = max(bb.xlen,bb.ylen,bb.zlen)
|
d = max(bb.xlen,bb.ylen,bb.zlen)
|
||||||
c = bb.center
|
c = bb.center
|
||||||
@ -94,6 +97,7 @@ def x3d_display(shape,
|
|||||||
quat = gp_Quaternion(*ROT)
|
quat = gp_Quaternion(*ROT)
|
||||||
vec = quat*(vec) + c.wrapped
|
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()),
|
d=(vec.X(),vec.Y(),vec.Z()),
|
||||||
center=(c.x,c.y,c.z))
|
center=(c.x,c.y,c.z))
|
@ -1364,23 +1364,50 @@ class Solid(Shape, Mixin3D):
|
|||||||
:param path: The wire to sweep the face resulting from the wires over
|
:param path: The wire to sweep the face resulting from the wires over
|
||||||
:return: a Solid object
|
:return: a Solid object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if path.ShapeType() == 'Edge':
|
if path.ShapeType() == 'Edge':
|
||||||
path = Wire.assembleEdges([path, ])
|
path = Wire.assembleEdges([path, ])
|
||||||
|
|
||||||
if makeSolid:
|
if makeSolid:
|
||||||
face = Face.makeFromWires(outerWire, innerWires)
|
face = Face.makeFromWires(outerWire, innerWires)
|
||||||
|
|
||||||
builder = BRepOffsetAPI_MakePipe(path.wrapped, face.wrapped)
|
builder = BRepOffsetAPI_MakePipe(path.wrapped, face.wrapped)
|
||||||
|
rv = cls(builder.Shape())
|
||||||
else:
|
else:
|
||||||
builder = BRepOffsetAPI_MakePipeShell(path.wrapped)
|
shapes = []
|
||||||
builder.Add(outerWire.wrapped)
|
for w in [outerWire]+innerWires:
|
||||||
for w in innerWires:
|
builder = BRepOffsetAPI_MakePipeShell(path.wrapped)
|
||||||
|
builder.SetMode(isFrenet)
|
||||||
builder.Add(w.wrapped)
|
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()
|
builder.Build()
|
||||||
|
|
||||||
|
if makeSolid:
|
||||||
|
builder.MakeSolid()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return cls(builder.Shape())
|
return cls(builder.Shape())
|
||||||
|
|
||||||
|
|
||||||
|
19
examples/Ex001_Simple_Block.py
Normal file
19
examples/Ex001_Simple_Block.py
Normal 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)
|
20
examples/Ex002_Block_With_Bored_Center_Hole.py
Normal file
20
examples/Ex002_Block_With_Bored_Center_Hole.py
Normal 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)
|
34
examples/Ex003_Pillow_Block_With_Counterbored_Holes.py
Normal file
34
examples/Ex003_Pillow_Block_With_Counterbored_Holes.py
Normal 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)
|
29
examples/Ex004_Extruded_Cylindrical_Plate.py
Normal file
29
examples/Ex004_Extruded_Cylindrical_Plate.py
Normal 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)
|
45
examples/Ex005_Extruded_Lines_and_Arcs.py
Normal file
45
examples/Ex005_Extruded_Lines_and_Arcs.py
Normal 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)
|
35
examples/Ex006_Moving_the_Current_Working_Point.py
Normal file
35
examples/Ex006_Moving_the_Current_Working_Point.py
Normal 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)
|
32
examples/Ex007_Using_Point_Lists.py
Normal file
32
examples/Ex007_Using_Point_Lists.py
Normal 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)
|
39
examples/Ex008_Polygon_Creation.py
Normal file
39
examples/Ex008_Polygon_Creation.py
Normal 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)
|
39
examples/Ex009_Polylines.py
Normal file
39
examples/Ex009_Polylines.py
Normal 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)
|
27
examples/Ex010_Defining_an_Edge_with_a_Spline.py
Normal file
27
examples/Ex010_Defining_an_Edge_with_a_Spline.py
Normal 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)
|
20
examples/Ex011_Mirroring_Symmetric_Geometry.py
Normal file
20
examples/Ex011_Mirroring_Symmetric_Geometry.py
Normal 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)
|
16
examples/Ex012_Creating_Workplanes_on_Faces.py
Normal file
16
examples/Ex012_Creating_Workplanes_on_Faces.py
Normal 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)
|
21
examples/Ex013_Locating_a_Workplane_on_a_Vertex.py
Normal file
21
examples/Ex013_Locating_a_Workplane_on_a_Vertex.py
Normal 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)
|
20
examples/Ex014_Offset_Workplanes.py
Normal file
20
examples/Ex014_Offset_Workplanes.py
Normal 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)
|
22
examples/Ex015_Rotated_Workplanes.py
Normal file
22
examples/Ex015_Rotated_Workplanes.py
Normal 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)
|
21
examples/Ex016_Using_Construction_Geometry.py
Normal file
21
examples/Ex016_Using_Construction_Geometry.py
Normal 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)
|
14
examples/Ex017_Shelling_to_Create_Thin_Features.py
Normal file
14
examples/Ex017_Shelling_to_Create_Thin_Features.py
Normal 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)
|
20
examples/Ex018_Making_Lofts.py
Normal file
20
examples/Ex018_Making_Lofts.py
Normal 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)
|
19
examples/Ex019_Counter_Sunk_Holes.py
Normal file
19
examples/Ex019_Counter_Sunk_Holes.py
Normal 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)
|
13
examples/Ex020_Rounding_Corners_with_Fillets.py
Normal file
13
examples/Ex020_Rounding_Corners_with_Fillets.py
Normal 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)
|
25
examples/Ex021_Splitting_an_Object.py
Normal file
25
examples/Ex021_Splitting_an_Object.py
Normal 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)
|
21
examples/Ex022_Revolution.py
Normal file
21
examples/Ex022_Revolution.py
Normal 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
40
examples/Ex023_Sweep.py
Normal 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)))
|
47
examples/Ex024_Sweep_Along_List_Of_Wires.py
Normal file
47
examples/Ex024_Sweep_Along_List_Of_Wires.py
Normal 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)))
|
||||||
|
|
||||||
|
|
56
examples/Ex100_Lego_Brick.py
Normal file
56
examples/Ex100_Lego_Brick.py
Normal 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)
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
|
@ -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()
|
|
@ -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())
|
|
@ -8,13 +8,14 @@ import unittest
|
|||||||
#on py 2.7.x on win
|
#on py 2.7.x on win
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
|
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCadObjects))
|
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(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(TestExporters.TestExporters))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestImporters.TestImporters))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestImporters.TestImporters))
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestJupyter.TestJupyter))
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
result = unittest.TextTestRunner().run(suite)
|
result = unittest.TextTestRunner().run(suite)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from tests import BaseTest
|
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,
|
from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex,
|
||||||
BRepBuilderAPI_MakeEdge,
|
BRepBuilderAPI_MakeEdge,
|
||||||
BRepBuilderAPI_MakeFace)
|
BRepBuilderAPI_MakeFace)
|
||||||
@ -24,10 +24,27 @@ class TestCadObjects(BaseTest):
|
|||||||
v1 = Vector(1, 2, 3)
|
v1 = Vector(1, 2, 3)
|
||||||
v2 = Vector((1, 2, 3))
|
v2 = Vector((1, 2, 3))
|
||||||
v3 = Vector(gp_Vec(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)
|
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):
|
def testVertex(self):
|
||||||
"""
|
"""
|
||||||
Tests basic vertex functions
|
Tests basic vertex functions
|
||||||
@ -149,14 +166,19 @@ class TestCadObjects(BaseTest):
|
|||||||
[0., 1., 0., 2.],
|
[0., 1., 0., 2.],
|
||||||
[0., 0., 1., 3.],
|
[0., 0., 1., 3.],
|
||||||
[0., 0., 0., 1.]]
|
[0., 0., 0., 1.]]
|
||||||
|
vals4x4_tuple = tuple(tuple(r) for r in vals4x4)
|
||||||
|
|
||||||
# test constructor with 16-value input
|
# test constructor with 16-value input
|
||||||
m = Matrix(vals4x4)
|
m = Matrix(vals4x4)
|
||||||
self.assertEqual(vals4x4, matrix_vals(m))
|
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
|
# test constructor with 12-value input (the last 4 are an implied
|
||||||
# [0,0,0,1])
|
# [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))
|
self.assertEqual(vals4x4, matrix_vals(m))
|
||||||
|
|
||||||
# Test 16-value input with invalid values for the last 4
|
# Test 16-value input with invalid values for the last 4
|
||||||
@ -167,14 +189,24 @@ class TestCadObjects(BaseTest):
|
|||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
Matrix(invalid)
|
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):
|
with self.assertRaises(TypeError):
|
||||||
Matrix([1,2,3])
|
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
|
# test out-of-bounds access
|
||||||
m = Matrix()
|
m = Matrix()
|
||||||
with self.assertRaises(IndexError):
|
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):
|
def testTranslate(self):
|
||||||
@ -188,6 +220,50 @@ class TestCadObjects(BaseTest):
|
|||||||
gp_Pnt(1, 1, 0)).Edge())
|
gp_Pnt(1, 1, 0)).Edge())
|
||||||
self.assertEqual(2, len(e.Vertices()))
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -418,6 +418,54 @@ class TestCadQuery(BaseTest):
|
|||||||
self.assertEqual(3, result.faces().size())
|
self.assertEqual(3, result.faces().size())
|
||||||
self.assertEqual(3, result.edges().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):
|
def testTwistExtrude(self):
|
||||||
"""
|
"""
|
||||||
Tests extrusion while twisting through an angle.
|
Tests extrusion while twisting through an angle.
|
||||||
@ -446,6 +494,35 @@ class TestCadQuery(BaseTest):
|
|||||||
# 6 faces for the box, 2 faces for each cylinder
|
# 6 faces for the box, 2 faces for each cylinder
|
||||||
self.assertEqual(6 + NUMX * NUMY * 2, s.faces().size())
|
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):
|
def testNestedCircle(self):
|
||||||
s = Workplane("XY").box(40, 40, 5).pushPoints(
|
s = Workplane("XY").box(40, 40, 5).pushPoints(
|
||||||
[(10, 0), (0, 10)]).circle(4).circle(2).extrude(4)
|
[(10, 0), (0, 10)]).circle(4).circle(2).extrude(4)
|
||||||
@ -642,6 +719,24 @@ class TestCadQuery(BaseTest):
|
|||||||
|
|
||||||
self.assertEqual(10, currentS.faces().size())
|
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):
|
def testBoundingBox(self):
|
||||||
"""
|
"""
|
||||||
Tests the boudingbox center of a model
|
Tests the boudingbox center of a model
|
||||||
@ -803,6 +898,37 @@ class TestCadQuery(BaseTest):
|
|||||||
selectors.NearestToPointSelector((0.0, 0.0, 0.0)))
|
selectors.NearestToPointSelector((0.0, 0.0, 0.0)))
|
||||||
.first().val().Y))
|
.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):
|
def testLargestDimension(self):
|
||||||
"""
|
"""
|
||||||
Tests the largestDimension function when no solids are on the stack and when there are
|
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()
|
line(-10, 0).close().extrude(10, clean=False).clean()
|
||||||
self.assertEqual(6, s.faces().size())
|
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):
|
def testCup(self):
|
||||||
"""
|
"""
|
||||||
UOM = "mm"
|
UOM = "mm"
|
||||||
@ -1486,3 +1686,36 @@ class TestCadQuery(BaseTest):
|
|||||||
self.assertTupleAlmostEquals(delta.toTuple(),
|
self.assertTupleAlmostEquals(delta.toTuple(),
|
||||||
(0., 0., 2. * h),
|
(0., 0., 2. * h),
|
||||||
decimal_places)
|
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
13
tests/TestJupyter.py
Normal 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
|
@ -49,5 +49,14 @@ class BaseTest(unittest.TestCase):
|
|||||||
self.assertAlmostEqual(i, j, places)
|
self.assertAlmostEqual(i, j, places)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TestCadObjects', 'TestCadQuery', 'TestCQSelectors', 'TestWorkplanes',
|
__all__ = [
|
||||||
'TestExporters', 'TestCQSelectors', 'TestImporters', 'TestCQGI']
|
'TestCadObjects',
|
||||||
|
'TestCadQuery',
|
||||||
|
'TestCQGI',
|
||||||
|
'TestCQSelectors',
|
||||||
|
'TestCQSelectors',
|
||||||
|
'TestExporters',
|
||||||
|
'TestImporters',
|
||||||
|
'TestJupyter',
|
||||||
|
'TestWorkplanes',
|
||||||
|
]
|
||||||
|
Reference in New Issue
Block a user