Merge pull request #43 from jmwright/master
Suuuhhhh Weet! thanks for contributing this feature!
This commit is contained in:
commit
8041accf9f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
*.pyc
|
||||
doc/_build/*
|
||||
dist/*
|
||||
.idea/*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Metadata-Version: 1.0
|
||||
Metadata-Version: 1.1
|
||||
Name: cadquery
|
||||
Version: 0.1.5
|
||||
Version: 0.1.6
|
||||
Summary: CadQuery is a parametric scripting language for creating and traversing CAD models
|
||||
Home-page: https://github.com/dcowden/cadquery
|
||||
Author: David Cowden
|
||||
|
|
|
@ -16,6 +16,7 @@ cadquery/contrib/__init__.py
|
|||
cadquery/freecad_impl/__init__.py
|
||||
cadquery/freecad_impl/exporters.py
|
||||
cadquery/freecad_impl/geom.py
|
||||
cadquery/freecad_impl/importers.py
|
||||
cadquery/freecad_impl/shapes.py
|
||||
cadquery/freecad_impl/verutil.py
|
||||
cadquery/plugins/__init__.py
|
||||
|
@ -23,6 +24,7 @@ tests/TestCQSelectors.py
|
|||
tests/TestCadObjects.py
|
||||
tests/TestCadQuery.py
|
||||
tests/TestExporters.py
|
||||
tests/TestImporters.py
|
||||
tests/TestImports.py
|
||||
tests/TestWorkplanes.py
|
||||
tests/__init__.py
|
|
@ -1868,6 +1868,55 @@ class Workplane(CQ):
|
|||
else:
|
||||
return self.newObject([r])
|
||||
|
||||
def revolve(self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True):
|
||||
"""
|
||||
Use all un-revolved wires in the parent chain to create a solid.
|
||||
|
||||
:param angleDegrees: the angle to revolve through.
|
||||
:type angleDegrees: float, anything less than 360 degrees will leave the shape open
|
||||
:param axisStart: the start point of the axis of rotation
|
||||
:type axisStart: tuple, a two tuple
|
||||
:param axisEnd: the end point of the axis of rotation
|
||||
:type axisEnd: tuple, a two tuple
|
||||
:param combine: True to combine the resulting solid with parent solids if found.
|
||||
:type combine: boolean, combine with parent solid
|
||||
:return: a CQ object with the resulting solid selected.
|
||||
|
||||
The returned object is always a CQ object, and depends on wither combine is True, and
|
||||
whether a context solid is already defined:
|
||||
|
||||
* if combine is False, the new value is pushed onto the stack.
|
||||
* if combine is true, the value is combined with the context solid if it exists,
|
||||
and the resulting solid becomes the new context solid.
|
||||
"""
|
||||
#Make sure we account for users specifying angles larger than 360 degrees
|
||||
angleDegrees = angleDegrees % 360.0
|
||||
|
||||
#Compensate for FreeCAD not assuming that a 0 degree revolve means a 360 degree revolve
|
||||
angleDegrees = 360.0 if angleDegrees == 0 else angleDegrees
|
||||
|
||||
#The default start point of the vector defining the axis of rotation will be the origin of the workplane
|
||||
if axisStart is None:
|
||||
axisStart = self.plane.toWorldCoords((0,0)).toTuple()
|
||||
else:
|
||||
axisStart = self.plane.toWorldCoords(axisStart).toTuple()
|
||||
|
||||
#The default end point of the vector defining the axis of rotation should be along the normal from the plane
|
||||
if axisEnd is None:
|
||||
#Make sure we match the user's assumed axis of rotation if they specified an start but not an end
|
||||
if axisStart[1] != 0:
|
||||
axisEnd = self.plane.toWorldCoords((0,axisStart[1])).toTuple()
|
||||
else:
|
||||
axisEnd = self.plane.toWorldCoords((0,1)).toTuple()
|
||||
else:
|
||||
axisEnd = self.plane.toWorldCoords(axisEnd).toTuple()
|
||||
|
||||
r = self._revolve(angleDegrees, axisStart, axisEnd) # returns a Solid ( or a compound if there were multiple )
|
||||
if combine:
|
||||
return self._combineWithBase(r)
|
||||
else:
|
||||
return self.newObject([r])
|
||||
|
||||
def _combineWithBase2(self,obj):
|
||||
"""
|
||||
Combines the provided object with the base solid, if one can be found.
|
||||
|
@ -2105,6 +2154,33 @@ class Workplane(CQ):
|
|||
|
||||
return Compound.makeCompound(toFuse)
|
||||
|
||||
def _revolve(self, angleDegrees, axisStart, axisEnd):
|
||||
"""
|
||||
Make a solid from the existing set of pending wires.
|
||||
|
||||
:param angleDegrees: the angle to revolve through.
|
||||
:type angleDegrees: float, anything less than 360 degrees will leave the shape open
|
||||
:param axisStart: the start point of the axis of rotation
|
||||
:type axisStart: tuple, a two tuple
|
||||
:param axisEnd: the end point of the axis of rotation
|
||||
:type axisEnd: tuple, a two tuple
|
||||
:return: a FreeCAD solid, suitable for boolean operations.
|
||||
|
||||
This method is a utility method, primarily for plugin and internal use.
|
||||
"""
|
||||
#We have to gather the wires to be revolved
|
||||
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires),self.plane,[])
|
||||
|
||||
#Mark that all of the wires have been used to create a revolution
|
||||
self.ctx.pendingWires = []
|
||||
|
||||
#Revolve the wires, make a compound out of them and then fuse them
|
||||
toFuse = []
|
||||
for ws in wireSets:
|
||||
thisObj = Solid.revolve(ws[0], ws[1:], angleDegrees, axisStart, axisEnd)
|
||||
toFuse.append(thisObj)
|
||||
|
||||
return Compound.makeCompound(toFuse)
|
||||
|
||||
def box(self,length,width,height,centered=(True,True,True),combine=True):
|
||||
"""
|
||||
|
|
|
@ -51,8 +51,10 @@ from cadquery import Vector,BoundBox
|
|||
import FreeCAD
|
||||
|
||||
from .verutil import fc_import
|
||||
|
||||
FreeCADPart = fc_import("FreeCAD.Part")
|
||||
|
||||
|
||||
class Shape(object):
|
||||
"""
|
||||
Represents a shape in the system.
|
||||
|
@ -101,6 +103,7 @@ class Shape(object):
|
|||
|
||||
tr.forConstruction = forConstruction
|
||||
return tr
|
||||
|
||||
# TODO: all these should move into the exporters folder.
|
||||
# we dont need a bunch of exporting code stored in here!
|
||||
#
|
||||
|
@ -189,6 +192,7 @@ class Shape(object):
|
|||
return Vector(self.wrapped.CenterOfMass)
|
||||
except:
|
||||
pass
|
||||
|
||||
def Closed(self):
|
||||
return self.wrapped.Closed
|
||||
|
||||
|
@ -288,6 +292,7 @@ class Shape(object):
|
|||
def __hash__(self):
|
||||
return self.wrapped.hashCode()
|
||||
|
||||
|
||||
class Vertex(Shape):
|
||||
def __init__(self, obj, forConstruction=False):
|
||||
"""
|
||||
|
@ -308,6 +313,7 @@ class Vertex(Shape):
|
|||
"""
|
||||
return Vector(self.wrapped.Point)
|
||||
|
||||
|
||||
class Edge(Shape):
|
||||
def __init__(self, obj):
|
||||
"""
|
||||
|
@ -631,7 +637,8 @@ class Solid(Shape):
|
|||
Make a wedge located in pnt\nBy default pnt=Vector(0,0,0) and dir=Vec
|
||||
tor(0,0,1)'
|
||||
"""
|
||||
return Shape.cast(FreeCADPart.makeWedge(xmin,ymin,zmin,z2min,x2min,xmax,ymax,zmax,z2max,x2max,pnt,dir))
|
||||
return Shape.cast(
|
||||
FreeCADPart.makeWedge(xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt, dir))
|
||||
|
||||
@classmethod
|
||||
def makeSphere(cls, radius, pnt=None, angleDegrees1=None, angleDegrees2=None, angleDegrees3=None):
|
||||
|
@ -737,6 +744,50 @@ class Solid(Shape):
|
|||
|
||||
return Shape.cast(result)
|
||||
|
||||
@classmethod
|
||||
def revolve(cls, outerWire, innerWires, angleDegrees, axisStart, axisEnd):
|
||||
"""
|
||||
Attempt to revolve the list of wires into a solid in the provided direction
|
||||
|
||||
:param outerWire: the outermost wire
|
||||
:param innerWires: a list of inner wires
|
||||
:param angleDegrees: the angle to revolve through.
|
||||
:type angleDegrees: float, anything less than 360 degrees will leave the shape open
|
||||
:param axisStart: the start point of the axis of rotation
|
||||
:type axisStart: tuple, a two tuple
|
||||
:param axisEnd: the end point of the axis of rotation
|
||||
:type axisEnd: tuple, a two tuple
|
||||
:return: a Solid object
|
||||
|
||||
The wires must not intersect
|
||||
|
||||
* all wires must be closed
|
||||
* there cannot be any intersecting or self-intersecting wires
|
||||
* wires must be listed from outside in
|
||||
* more than one levels of nesting is not supported reliably
|
||||
* the wire(s) that you're revolving cannot be centered
|
||||
|
||||
This method will attempt to sort the wires, but there is much work remaining to make this method
|
||||
reliable.
|
||||
"""
|
||||
freeCADWires = [outerWire.wrapped]
|
||||
|
||||
for w in innerWires:
|
||||
freeCADWires.append(w.wrapped)
|
||||
|
||||
f = FreeCADPart.Face(freeCADWires)
|
||||
|
||||
rotateCenter = FreeCAD.Base.Vector(axisStart)
|
||||
rotateAxis = FreeCAD.Base.Vector(axisEnd)
|
||||
|
||||
#Convert our axis end vector into to something FreeCAD will understand (an axis specification vector)
|
||||
rotateAxis = rotateCenter.sub(rotateAxis)
|
||||
|
||||
#FreeCAD wants a rotation center and then an axis to rotate around rather than an axis of rotation
|
||||
result = f.revolve(rotateCenter, rotateAxis, angleDegrees)
|
||||
|
||||
return Shape.cast(result)
|
||||
|
||||
def tessellate(self, tolerance):
|
||||
return self.wrapped.tessellate(tolerance)
|
||||
|
||||
|
@ -779,6 +830,7 @@ class Solid(Shape):
|
|||
nativeFaces = [f.wrapped for f in faceList]
|
||||
return Shape.cast(self.wrapped.makeThickness(nativeFaces, thickness, tolerance))
|
||||
|
||||
|
||||
class Compound(Shape):
|
||||
def __init__(self, obj):
|
||||
"""
|
||||
|
|
44
examples/FreeCAD/Ex025_Revolution.py
Normal file
44
examples/FreeCAD/Ex025_Revolution.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
#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))
|
||||
|
||||
#Get a cadquery solid object
|
||||
solid = result.val()
|
||||
|
||||
#Use the wrapped property of a cadquery primitive to get a FreeCAD solid
|
||||
Part.show(solid.wrapped)
|
||||
|
||||
#Would like to zoom to fit the part here, but FreeCAD doesn't seem to have that scripting functionality
|
2
setup.py
2
setup.py
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
|
||||
setup(
|
||||
name='cadquery',
|
||||
version='0.1.6',
|
||||
version='0.1.7',
|
||||
url='https://github.com/dcowden/cadquery',
|
||||
license='LGPL',
|
||||
author='David Cowden',
|
||||
|
|
|
@ -219,6 +219,87 @@ class TestCadQuery(BaseTest):
|
|||
#self.assertEqual(1,s.solids().size() )
|
||||
#self.assertEqual(8,s.faces().size() )
|
||||
|
||||
def testRevolveCylinder(self):
|
||||
"""
|
||||
Test creating a solid using the revolve operation.
|
||||
:return:
|
||||
"""
|
||||
#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
|
||||
|
||||
#Test revolve without any options for making a cylinder
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
|
||||
self.assertEqual(3, result.faces().size())
|
||||
self.assertEqual(2, result.vertices().size())
|
||||
self.assertEqual(3, result.edges().size())
|
||||
|
||||
#Test revolve when only setting the angle to revolve through
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
|
||||
self.assertEqual(3, result.faces().size())
|
||||
self.assertEqual(2, result.vertices().size())
|
||||
self.assertEqual(3, result.edges().size())
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(270.0)
|
||||
self.assertEqual(5, result.faces().size())
|
||||
self.assertEqual(6, result.vertices().size())
|
||||
self.assertEqual(9, result.edges().size())
|
||||
|
||||
#Test when passing revolve the angle and the axis of revolution's start point
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
|
||||
self.assertEqual(3, result.faces().size())
|
||||
self.assertEqual(2, result.vertices().size())
|
||||
self.assertEqual(3, result.edges().size())
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(270.0,(-5,-5))
|
||||
self.assertEqual(5, result.faces().size())
|
||||
self.assertEqual(6, result.vertices().size())
|
||||
self.assertEqual(9, result.edges().size())
|
||||
|
||||
#Test when passing revolve the angle and both the start and ends of the axis of revolution
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
|
||||
self.assertEqual(3, result.faces().size())
|
||||
self.assertEqual(2, result.vertices().size())
|
||||
self.assertEqual(3, result.edges().size())
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(270.0,(-5, -5),(-5, 5))
|
||||
self.assertEqual(5, result.faces().size())
|
||||
self.assertEqual(6, result.vertices().size())
|
||||
self.assertEqual(9, result.edges().size())
|
||||
|
||||
#Testing all of the above without combine
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
|
||||
self.assertEqual(3, result.faces().size())
|
||||
self.assertEqual(2, result.vertices().size())
|
||||
self.assertEqual(3, result.edges().size())
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(270.0,(-5,-5),(-5,5), False)
|
||||
self.assertEqual(5, result.faces().size())
|
||||
self.assertEqual(6, result.vertices().size())
|
||||
self.assertEqual(9, result.edges().size())
|
||||
|
||||
def testRevolveDonut(self):
|
||||
"""
|
||||
Test creating a solid donut shape with square walls
|
||||
:return:
|
||||
"""
|
||||
#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
|
||||
|
||||
result = Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
|
||||
self.assertEqual(4, result.faces().size())
|
||||
self.assertEqual(4, result.vertices().size())
|
||||
self.assertEqual(6, result.edges().size())
|
||||
|
||||
def testRevolveCone(self):
|
||||
"""
|
||||
Test creating a solid from a revolved triangle
|
||||
:return:
|
||||
"""
|
||||
result = Workplane("XY").lineTo(0,10).lineTo(5,0).close().revolve()
|
||||
self.assertEqual(2, result.faces().size())
|
||||
self.assertEqual(2, result.vertices().size())
|
||||
self.assertEqual(3, result.edges().size())
|
||||
|
||||
def testRectArray(self):
|
||||
NUMX=3
|
||||
NUMY=3
|
||||
|
|
Loading…
Reference in New Issue
Block a user