Implemented build and parameter search CQGI functionality, and updated the CadQuery library.
This commit is contained in:
parent
2d7344e07d
commit
4c06752c39
|
@ -9,6 +9,8 @@ import ExportCQ, ImportCQ
|
||||||
import module_locator
|
import module_locator
|
||||||
import Settings
|
import Settings
|
||||||
import Shared
|
import Shared
|
||||||
|
from cadquery import cqgi
|
||||||
|
from Helpers import show
|
||||||
|
|
||||||
# Distinguish python built-in open function from the one declared here
|
# Distinguish python built-in open function from the one declared here
|
||||||
if open.__module__ == '__builtin__':
|
if open.__module__ == '__builtin__':
|
||||||
|
@ -122,9 +124,34 @@ class CadQueryExecuteScript:
|
||||||
# Clear the old render before re-rendering
|
# Clear the old render before re-rendering
|
||||||
Shared.clearActiveDocument()
|
Shared.clearActiveDocument()
|
||||||
|
|
||||||
|
# Check to see if we are executig a CQGI compliant script
|
||||||
|
scriptText = cqCodePane.toPlainText().encode('utf-8')
|
||||||
|
if "build_object(" in scriptText and "# build_object(" not in scriptText and "#build_boject(" not in scriptText:
|
||||||
|
FreeCAD.Console.PrintMessage("Executing CQGI-compliant script.\r\n")
|
||||||
|
|
||||||
|
# A repreentation of the CQ script with all the metadata attached
|
||||||
|
cqModel = cqgi.parse(scriptText)
|
||||||
|
|
||||||
|
# Allows us to present parameters to users later that they can alter
|
||||||
|
parameters = cqModel.metadata.parameters
|
||||||
|
|
||||||
|
FreeCAD.Console.PrintMessage("Script Variables:\r\n")
|
||||||
|
for key, value in parameters.iteritems():
|
||||||
|
FreeCAD.Console.PrintMessage("variable name: " + key + ", variable value: " + str(value.default_value) + "\r\n")
|
||||||
|
|
||||||
|
build_result = cqgi.parse(scriptText).build()
|
||||||
|
|
||||||
|
# Make sure that the build was successful
|
||||||
|
if build_result.success:
|
||||||
|
# Display all the results that the user requested
|
||||||
|
for result in build_result.results:
|
||||||
|
show(result)
|
||||||
|
else:
|
||||||
|
FreeCAD.Console.PrintError("Error executing CQGI-compliant script.\r\n")
|
||||||
|
else:
|
||||||
# Save our code to a tempfile and render it
|
# Save our code to a tempfile and render it
|
||||||
tempFile = tempfile.NamedTemporaryFile(delete=False)
|
tempFile = tempfile.NamedTemporaryFile(delete=False)
|
||||||
tempFile.write(cqCodePane.toPlainText().encode('utf-8'))
|
tempFile.write(scriptText)
|
||||||
tempFile.close()
|
tempFile.close()
|
||||||
|
|
||||||
# Set some environment variables that may help the user
|
# Set some environment variables that may help the user
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,22 +7,23 @@ install:
|
||||||
- gcc --version
|
- gcc --version
|
||||||
- g++ --version
|
- g++ --version
|
||||||
- python ./setup.py install
|
- python ./setup.py install
|
||||||
- pip install coverage
|
- pip install -r requirements-dev.txt
|
||||||
- pip install coveralls
|
|
||||||
- pip install Sphinx==1.3.2
|
|
||||||
- pip install travis-sphinx
|
- pip install travis-sphinx
|
||||||
- pip install pyparsing
|
|
||||||
script:
|
script:
|
||||||
- coverage run --source=cadquery ./runtests.py
|
- coverage run --source=cadquery ./runtests.py
|
||||||
- travis-sphinx --nowarn --source=doc build
|
- travis-sphinx --nowarn --source=doc build
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- coveralls
|
- coveralls
|
||||||
- travis-sphinx deploy
|
- travis-sphinx deploy
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
- pythonocc
|
- pythonocc
|
||||||
- 2_0_branch
|
- 2_0_branch
|
||||||
deploy:
|
|
||||||
|
deploy:
|
||||||
provider: pypi
|
provider: pypi
|
||||||
user: dcowden
|
user: dcowden
|
||||||
password:
|
password:
|
||||||
|
|
|
@ -10,9 +10,9 @@ CadQuery is an intuitive, easy-to-use python based language for building paramet
|
||||||
|
|
||||||
CadQuery has several goals:
|
CadQuery has several goals:
|
||||||
|
|
||||||
* Build models with scripts that are as close as possible to how you'd describe the object to a human.
|
* Build lD models with scripts that are as close as possible to how you'd describe the object to a human.
|
||||||
* Create parametric models that can be very easily customized by end users
|
* Create parametric models that can be very easily customized by end users
|
||||||
* Output high quality CAD formats like STEP and AMF in addition to traditional STL
|
* Output high quality (loss-less) CAD formats like STEP and AMF in addition to traditional STL
|
||||||
* Provide a non-proprietary, plain text model format that can be edited and executed with only a web browser
|
* Provide a non-proprietary, plain text model format that can be edited and executed with only a web browser
|
||||||
|
|
||||||
Using CadQuery, you can write short, simple scripts that produce high quality CAD models. It is easy to make many different objects using a single script that can be customized.
|
Using CadQuery, you can write short, simple scripts that produce high quality CAD models. It is easy to make many different objects using a single script that can be customized.
|
||||||
|
@ -21,7 +21,6 @@ Full Documentation
|
||||||
============================
|
============================
|
||||||
You can find the full cadquery documentation at http://dcowden.github.io/cadquery
|
You can find the full cadquery documentation at http://dcowden.github.io/cadquery
|
||||||
|
|
||||||
|
|
||||||
Getting Started With CadQuery
|
Getting Started With CadQuery
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
@ -113,6 +112,40 @@ The cadquery script is surprisingly short, and allows easily customizing any of
|
||||||
|
|
||||||
Thanks go to cadquery contributor hyOzd ( Altu Technology ) for the example!
|
Thanks go to cadquery contributor hyOzd ( Altu Technology ) for the example!
|
||||||
|
|
||||||
|
Projects Using CadQuery
|
||||||
|
=========================
|
||||||
|
|
||||||
|
KiCad uses cadquery to build high quality models of electrictronic components. ( https://github.com/KiCad/packages3D )
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://forum.freecadweb.org/download/file.php?id=33797&sid=b8584f80928497722e9ee9d582a3fa43" width="350"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
This Prusa i3 extruder support uses cadquery to build the model (https://github.com/adam-urbanczyk/cadquery-models) :
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/adam-urbanczyk/cadquery-models/raw/master/extruder_support.png" width="350"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
The mach30 project used cadquery to develop a tool that will create a rocket thruster directly from the appropriate equations (https://opendesignengine.net/projects/yavin-thruster/wiki):
|
||||||
|
<p align="center">
|
||||||
|
<img src="http://opendesignengine.net/dmsf_files/480?download=" width="700"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
This example uses Jupyter notebook to produce a really cool web-based scripting environment ( https://github.com/RustyVermeer/avnb/blob/master/readme.md ) :
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/RustyVermeer/avnb/raw/master/example.gif" width="700"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
We would love to link to your cadquery based project. Just let us know and we'll add it here.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Why CadQuery instead of OpenSCAD?
|
Why CadQuery instead of OpenSCAD?
|
||||||
========================================
|
========================================
|
||||||
|
|
|
@ -1047,7 +1047,7 @@ class Workplane(CQ):
|
||||||
false, the lower left corner will be at the center of the work plane
|
false, the lower left corner will be at the center of the work plane
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if xSpacing < 1 or ySpacing < 1 or xCount < 1 or yCount < 1:
|
if xSpacing <= 0 or ySpacing <= 0 or xCount < 1 or yCount < 1:
|
||||||
raise ValueError("Spacing and count must be > 0 ")
|
raise ValueError("Spacing and count must be > 0 ")
|
||||||
|
|
||||||
lpoints = [] # coordinates relative to bottom left point
|
lpoints = [] # coordinates relative to bottom left point
|
||||||
|
|
|
@ -115,11 +115,11 @@ class CQModel(object):
|
||||||
else:
|
else:
|
||||||
raise NoOutputError("Script did not call build_object-- no output available.")
|
raise NoOutputError("Script did not call build_object-- no output available.")
|
||||||
except Exception, ex:
|
except Exception, ex:
|
||||||
print "Error Executing Script:"
|
#print "Error Executing Script:"
|
||||||
result.set_failure_result(ex)
|
result.set_failure_result(ex)
|
||||||
traceback.print_exc()
|
#traceback.print_exc()
|
||||||
print "Full Text of Script:"
|
#print "Full Text of Script:"
|
||||||
print self.script_source
|
#print self.script_source
|
||||||
|
|
||||||
end = time.clock()
|
end = time.clock()
|
||||||
result.buildTime = end - start
|
result.buildTime = end - start
|
||||||
|
@ -180,7 +180,7 @@ class ScriptMetadata(object):
|
||||||
self.parameters[p.name] = p
|
self.parameters[p.name] = p
|
||||||
|
|
||||||
def add_parameter_description(self,name,description):
|
def add_parameter_description(self,name,description):
|
||||||
print 'Adding Parameter name=%s, desc=%s' % ( name, description )
|
#print 'Adding Parameter name=%s, desc=%s' % ( name, description )
|
||||||
p = self.parameters[name]
|
p = self.parameters[name]
|
||||||
p.desc = description
|
p.desc = description
|
||||||
|
|
||||||
|
@ -418,7 +418,7 @@ class ParameterDescriptionFinder(ast.NodeTransformer):
|
||||||
self.cqModel.add_parameter_description(varname,desc)
|
self.cqModel.add_parameter_description(varname,desc)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
print "Unable to handle function call"
|
#print "Unable to handle function call"
|
||||||
pass
|
pass
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ def _fc_path():
|
||||||
"/usr/lib/freecad/lib",
|
"/usr/lib/freecad/lib",
|
||||||
"/opt/freecad/lib/",
|
"/opt/freecad/lib/",
|
||||||
"/usr/bin/freecad/lib",
|
"/usr/bin/freecad/lib",
|
||||||
|
"/usr/lib/freecad-daily/lib",
|
||||||
"/usr/lib/freecad",
|
"/usr/lib/freecad",
|
||||||
"/usr/lib64/freecad/lib",
|
"/usr/lib64/freecad/lib",
|
||||||
]:
|
]:
|
||||||
|
|
|
@ -420,11 +420,17 @@ class Edge(Shape):
|
||||||
# self.endPoint = None
|
# self.endPoint = None
|
||||||
|
|
||||||
self.edgetypes = {
|
self.edgetypes = {
|
||||||
FreeCADPart.Line: 'LINE',
|
|
||||||
FreeCADPart.ArcOfCircle: 'ARC',
|
FreeCADPart.ArcOfCircle: 'ARC',
|
||||||
FreeCADPart.Circle: 'CIRCLE'
|
FreeCADPart.Circle: 'CIRCLE'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasattr(FreeCADPart,"LineSegment"):
|
||||||
|
#FreeCAD <= 0.16
|
||||||
|
self.edgetypes[FreeCADPart.LineSegment] = 'LINE'
|
||||||
|
else:
|
||||||
|
#FreeCAD >= 0.17
|
||||||
|
self.edgetypes[FreeCADPart.Line] = 'LINE'
|
||||||
|
|
||||||
# Helps identify this solid through the use of an ID
|
# Helps identify this solid through the use of an ID
|
||||||
self.label = ""
|
self.label = ""
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
from cadquery import Vector,Edge,Vertex,Face,Solid,Shell,Compound
|
from cadquery import Vector,Edge,Vertex,Face,Solid,Shell,Compound
|
||||||
|
from collections import defaultdict
|
||||||
from pyparsing import Literal,Word,nums,Optional,Combine,oneOf,upcaseTokens,\
|
from pyparsing import Literal,Word,nums,Optional,Combine,oneOf,upcaseTokens,\
|
||||||
CaselessLiteral,Group,infixNotation,opAssoc,Forward,\
|
CaselessLiteral,Group,infixNotation,opAssoc,Forward,\
|
||||||
ZeroOrMore,Keyword
|
ZeroOrMore,Keyword
|
||||||
|
@ -299,11 +300,6 @@ class DirectionMinMaxSelector(Selector):
|
||||||
|
|
||||||
def distance(tShape):
|
def distance(tShape):
|
||||||
return tShape.Center().dot(self.vector)
|
return tShape.Center().dot(self.vector)
|
||||||
#if tShape.ShapeType == 'Vertex':
|
|
||||||
# pnt = tShape.Point
|
|
||||||
#else:
|
|
||||||
# pnt = tShape.Center()
|
|
||||||
#return pnt.dot(self.vector)
|
|
||||||
|
|
||||||
# import OrderedDict
|
# import OrderedDict
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
@ -336,10 +332,7 @@ class DirectionNthSelector(ParallelDirSelector):
|
||||||
self.max = max
|
self.max = max
|
||||||
self.directionMax = directionMax
|
self.directionMax = directionMax
|
||||||
self.TOLERANCE = tolerance
|
self.TOLERANCE = tolerance
|
||||||
if directionMax:
|
self.N = n
|
||||||
self.N = n #do we want indexing from 0 or from 1?
|
|
||||||
else:
|
|
||||||
self.N = -n
|
|
||||||
|
|
||||||
def filter(self,objectList):
|
def filter(self,objectList):
|
||||||
#select first the objects that are normal/parallel to a given dir
|
#select first the objects that are normal/parallel to a given dir
|
||||||
|
@ -347,23 +340,22 @@ class DirectionNthSelector(ParallelDirSelector):
|
||||||
|
|
||||||
def distance(tShape):
|
def distance(tShape):
|
||||||
return tShape.Center().dot(self.direction)
|
return tShape.Center().dot(self.direction)
|
||||||
#if tShape.ShapeType == 'Vertex':
|
|
||||||
# pnt = tShape.Point
|
|
||||||
#else:
|
|
||||||
# pnt = tShape.Center()
|
|
||||||
#return pnt.dot(self.vector)
|
|
||||||
|
|
||||||
#make and distance to object dict
|
|
||||||
objectDict = {distance(el) : el for el in objectList}
|
|
||||||
#calculate how many digits of precision do we need
|
#calculate how many digits of precision do we need
|
||||||
digits = int(1/self.TOLERANCE)
|
digits = int(1/self.TOLERANCE)
|
||||||
# create a rounded distance to original distance mapping (implicitly perfroms unique operation)
|
|
||||||
dist_round_dist = {round(d,digits) : d for d in objectDict.keys()}
|
#make a distance to object dict
|
||||||
|
#this is one to many mapping so I am using a default dict with list
|
||||||
|
objectDict = defaultdict(list)
|
||||||
|
for el in objectList:
|
||||||
|
objectDict[round(distance(el),digits)].append(el)
|
||||||
|
|
||||||
# choose the Nth unique rounded distance
|
# choose the Nth unique rounded distance
|
||||||
nth_d = dist_round_dist[sorted(dist_round_dist.keys())[self.N]]
|
nth_distance = sorted(objectDict.keys(),
|
||||||
|
reverse=not self.directionMax)[self.N]
|
||||||
|
|
||||||
# map back to original objects and return
|
# map back to original objects and return
|
||||||
return [objectDict[d] for d in objectDict.keys() if abs(d-nth_d) < self.TOLERANCE]
|
return objectDict[nth_distance]
|
||||||
|
|
||||||
class BinarySelector(Selector):
|
class BinarySelector(Selector):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -157,6 +157,7 @@ as a basis for futher operations.
|
||||||
BaseDirSelector
|
BaseDirSelector
|
||||||
ParallelDirSelector
|
ParallelDirSelector
|
||||||
DirectionSelector
|
DirectionSelector
|
||||||
|
DirectionNthSelector
|
||||||
PerpendicularDirSelector
|
PerpendicularDirSelector
|
||||||
TypeSelector
|
TypeSelector
|
||||||
DirectionMinMaxSelector
|
DirectionMinMaxSelector
|
||||||
|
|
|
@ -52,6 +52,7 @@ Selector Classes
|
||||||
BaseDirSelector
|
BaseDirSelector
|
||||||
ParallelDirSelector
|
ParallelDirSelector
|
||||||
DirectionSelector
|
DirectionSelector
|
||||||
|
DirectionNthSelector
|
||||||
PerpendicularDirSelector
|
PerpendicularDirSelector
|
||||||
TypeSelector
|
TypeSelector
|
||||||
DirectionMinMaxSelector
|
DirectionMinMaxSelector
|
||||||
|
|
|
@ -47,11 +47,6 @@ If you prefer to have a GUI available, your best option is to use
|
||||||
Simply extract cadquery-freecad-module into your FreeCAD installation. You'll end up
|
Simply extract cadquery-freecad-module into your FreeCAD installation. You'll end up
|
||||||
with a cadquery workbench that allows you to interactively run scripts, and then see the results in the FreeCAD GUI
|
with a cadquery workbench that allows you to interactively run scripts, and then see the results in the FreeCAD GUI
|
||||||
|
|
||||||
If you are using Ubuntu, you can also install it via this ppa:
|
|
||||||
|
|
||||||
https://code.launchpad.net/~freecad-community/+archive/ubuntu/ppa/+packages
|
|
||||||
|
|
||||||
|
|
||||||
Zero Step Install
|
Zero Step Install
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -29,18 +29,6 @@ face.outerWire
|
||||||
Selectors
|
Selectors
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Chained Selectors
|
|
||||||
Space delimited selectors should be unioned to allow multiple selections. For example ">Z >X"
|
|
||||||
|
|
||||||
Ad-hoc axes
|
|
||||||
for example, >(1,2,1) would select a face with normal in the 1,2,1 direction
|
|
||||||
|
|
||||||
logic inversion
|
|
||||||
! or not to invert logic, such as "!(>Z)" to select faces _other_ than the most z facing
|
|
||||||
|
|
||||||
closest to point
|
|
||||||
support faces, points, or edges closest to a provided point
|
|
||||||
|
|
||||||
tagged entities
|
tagged entities
|
||||||
support tagging entities when they are created, so they can be selected later on using that tag.
|
support tagging entities when they are created, so they can be selected later on using that tag.
|
||||||
ideally, tags are propagated to features that are created from these features ( ie, an edge tagged with 'foo'
|
ideally, tags are propagated to features that are created from these features ( ie, an edge tagged with 'foo'
|
||||||
|
|
|
@ -28,26 +28,28 @@ string patterns in, CadQuery will automatically use the associated selector obje
|
||||||
Combining Selectors
|
Combining Selectors
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Selectors can be combined arithmetically and logically, so that it is possible to do intersection, union, and other
|
Selectors can be combined logically, currently defined operators include **and**, **or**, **not** and **exc[ept]** (set difference). For example:
|
||||||
combinations. For example::
|
|
||||||
|
|
||||||
box = cadquery.Workplane("XY").box(10,10,10)
|
.. cq_plot::
|
||||||
|
|
||||||
s = selectors.StringSyntaxSelector
|
result = cq.Workplane("XY").box(2, 2, 2) \
|
||||||
|
.edges("|Z and >Y") \
|
||||||
|
.chamfer(0.2)
|
||||||
|
|
||||||
### select all edges on right and left faces
|
build_object(result)
|
||||||
#box = box.edges((s("|Z") + s("|Y"))).fillet(1)
|
|
||||||
|
|
||||||
### select all edges on top and bottom
|
Much more complex expressions are possible as well:
|
||||||
#box = box.edges(-s("|Z")).fillet(1)
|
|
||||||
#box = box.edges(s('|X')+s('Y')).fillet(1)
|
|
||||||
box = box.faces(s('>Z')+s('<Z')).fillet(1)
|
|
||||||
|
|
||||||
|
.. cq_plot::
|
||||||
|
|
||||||
or for another example::
|
result = cq.Workplane("XY").box(2, 2, 2) \
|
||||||
|
.faces(">Z") \
|
||||||
|
.shell(-0.2) \
|
||||||
|
.faces(">Z") \
|
||||||
|
.edges("not(<X or >X or <Y or >Y)") \
|
||||||
|
.chamfer(0.1)
|
||||||
|
|
||||||
# select diagonal edges
|
build_object(result)
|
||||||
box = box.faces(s('>Z')+s('<Z')).edges(-s('|X')-s('Y')).fillet(1)
|
|
||||||
|
|
||||||
.. _filteringfaces:
|
.. _filteringfaces:
|
||||||
|
|
||||||
|
@ -64,17 +66,19 @@ of the face.
|
||||||
|
|
||||||
The axis used in the listing below are for illustration: any axis would work similarly in each case.
|
The axis used in the listing below are for illustration: any axis would work similarly in each case.
|
||||||
|
|
||||||
========= ====================================== ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
Selector Selects Selector Class # objects returned
|
Selector Selects Selector Class # objects returned
|
||||||
========= ====================================== ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
+Z Faces with normal in +z direction :py:class:`cadquery.DirectionSelector` 0 or 1
|
+Z Faces with normal in +z direction :py:class:`cadquery.DirectionSelector` 0..many
|
||||||
\|Z Faces parallel to xy plane :py:class:`cadquery.ParallelDirSelector` 0..many
|
\|Z Faces parallel to xy plane :py:class:`cadquery.ParallelDirSelector` 0..many
|
||||||
-X Faces with normal in neg x direction :py:class:`cadquery.DirectionSelector` 0..many
|
-X Faces with normal in neg x direction :py:class:`cadquery.DirectionSelector` 0..many
|
||||||
#Z Faces perpendicular to z direction :py:class:`cadquery.PerpendicularDirSelector` 0..many
|
#Z Faces perpendicular to z direction :py:class:`cadquery.PerpendicularDirSelector` 0..many
|
||||||
%Plane Faces of type plane :py:class:`cadquery.TypeSelector` 0..many
|
%Plane Faces of type plane :py:class:`cadquery.TypeSelector` 0..many
|
||||||
>Y Face farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
>Y Face farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
<Y Face farthest in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
<Y Face farthest in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
========= ====================================== ======================================================= ==========================
|
>Y[-2] 2nd Face farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
|
<Y[0] 1st closest Fase in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
|
========= ======================================= ======================================================= ==========================
|
||||||
|
|
||||||
|
|
||||||
.. _filteringedges:
|
.. _filteringedges:
|
||||||
|
@ -92,17 +96,19 @@ Some filter types are not supported for edges. The selector usually refers to t
|
||||||
The axis used in the listing below are for illustration: any axis would work similarly in each case.
|
The axis used in the listing below are for illustration: any axis would work similarly in each case.
|
||||||
|
|
||||||
|
|
||||||
========= ==================================== ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
Selector Selects Selector Class # objects returned
|
Selector Selects Selector Class # objects returned
|
||||||
========= ==================================== ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
+Z Edges aligned in the Z direction :py:class:`cadquery.DirectionSelector` 0..many
|
+Z Edges aligned in the Z direction :py:class:`cadquery.DirectionSelector` 0..many
|
||||||
\|Z Edges parallel to z direction :py:class:`cadquery.ParallelDirSelector` 0..many
|
\|Z Edges parallel to z direction :py:class:`cadquery.ParallelDirSelector` 0..many
|
||||||
-X Edges aligned in neg x direction :py:class:`cadquery.DirectionSelector` 0..many
|
-X Edges aligned in neg x direction :py:class:`cadquery.DirectionSelector` 0..many
|
||||||
#Z Edges perpendicular to z direction :py:class:`cadquery.PerpendicularDirSelector` 0..many
|
#Z Edges perpendicular to z direction :py:class:`cadquery.PerpendicularDirSelector` 0..many
|
||||||
%Line Edges of type line :py:class:`cadquery.TypeSelector` 0..many
|
%Line Edges of type line :py:class:`cadquery.TypeSelector` 0..many
|
||||||
>Y Edges farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
>Y Edges farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
<Y Edges farthest in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
<Y Edges farthest in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
========= ==================================== ======================================================= ==========================
|
>Y[1] 2nd closest edge in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
|
<Y[-2] 2nd farthest edge in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
|
========= ======================================= ======================================================= ==========================
|
||||||
|
|
||||||
|
|
||||||
.. _filteringvertices:
|
.. _filteringvertices:
|
||||||
|
@ -115,12 +121,20 @@ Only a few of the filter types apply to vertices. The location of the vertex is
|
||||||
========= ======================================= ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
Selector Selects Selector Class # objects returned
|
Selector Selects Selector Class # objects returned
|
||||||
========= ======================================= ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
>Y Vertices farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
>Y Vertices farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
<Y Vertices farthest in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
<Y Vertices farthest in the negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0..many
|
||||||
========= ======================================= ======================================================= ==========================
|
========= ======================================= ======================================================= ==========================
|
||||||
|
|
||||||
Future Enhancements
|
User-defined Directions
|
||||||
--------------------
|
-----------------------
|
||||||
|
|
||||||
* Support direct vectors inline, such as \|(x,y,z)
|
It is possible to use user defined vectors as a basis for the selectors. For example:
|
||||||
* Support multiple selectors separated by spaces, which unions the results, such as "+Z +Y to select both z and y-most faces
|
|
||||||
|
.. cq_plot::
|
||||||
|
|
||||||
|
result = cq.Workplane("XY").box(10,10,10)
|
||||||
|
|
||||||
|
# chamfer only one edge
|
||||||
|
result = result.edges('>(-1,1,0)').chamfer(1)
|
||||||
|
|
||||||
|
build_object(result)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
sphinx-rtd-theme==0.1.9
|
sphinx-rtd-theme==0.1.9
|
||||||
travis-sphinx==1.1.0
|
travis-sphinx
|
||||||
Sphinx==1.3.1
|
Sphinx==1.3.2
|
||||||
|
coverage
|
||||||
|
coveralls
|
||||||
|
pyparsing
|
||||||
|
|
|
@ -194,10 +194,14 @@ class TestCQSelectors(BaseTest):
|
||||||
#2nd face
|
#2nd face
|
||||||
val = c.faces('>(1,0,0)[1]').val()
|
val = c.faces('>(1,0,0)[1]').val()
|
||||||
self.assertAlmostEqual(val.Center().x,-1.5)
|
self.assertAlmostEqual(val.Center().x,-1.5)
|
||||||
|
val = c.faces('>X[1]').val()
|
||||||
|
self.assertAlmostEqual(val.Center().x,-1.5)
|
||||||
|
|
||||||
#2nd face with inversed selection vector
|
#2nd face with inversed selection vector
|
||||||
val = c.faces('>(-1,0,0)[1]').val()
|
val = c.faces('>(-1,0,0)[1]').val()
|
||||||
self.assertAlmostEqual(val.Center().x,1.5)
|
self.assertAlmostEqual(val.Center().x,1.5)
|
||||||
|
val = c.faces('<X[1]').val()
|
||||||
|
self.assertAlmostEqual(val.Center().x,1.5)
|
||||||
|
|
||||||
#2nd last face
|
#2nd last face
|
||||||
val = c.faces('>X[-2]').val()
|
val = c.faces('>X[-2]').val()
|
||||||
|
@ -210,6 +214,46 @@ class TestCQSelectors(BaseTest):
|
||||||
#check if the selected face if normal to the specified Vector
|
#check if the selected face if normal to the specified Vector
|
||||||
self.assertAlmostEqual(val.normalAt().cross(Vector(1,0,0)).Length,0.0)
|
self.assertAlmostEqual(val.normalAt().cross(Vector(1,0,0)).Length,0.0)
|
||||||
|
|
||||||
|
#test selection of multiple faces with the same distance
|
||||||
|
c = Workplane('XY')\
|
||||||
|
.box(1,4,1,centered=(False,True,False)).faces('<Z')\
|
||||||
|
.box(2,2,2,centered=(True,True,False)).faces('>Z')\
|
||||||
|
.box(1,1,1,centered=(True,True,False))
|
||||||
|
|
||||||
|
#select 2nd from the bottom (NB python indexing is 0-based)
|
||||||
|
vals = c.faces('>Z[1]').vals()
|
||||||
|
self.assertEqual(len(vals),2)
|
||||||
|
|
||||||
|
val = c.faces('>Z[1]').val()
|
||||||
|
self.assertAlmostEqual(val.Center().z,1)
|
||||||
|
|
||||||
|
#do the same but by selecting 3rd from the top
|
||||||
|
vals = c.faces('<Z[2]').vals()
|
||||||
|
self.assertEqual(len(vals),2)
|
||||||
|
|
||||||
|
val = c.faces('<Z[2]').val()
|
||||||
|
self.assertAlmostEqual(val.Center().z,1)
|
||||||
|
|
||||||
|
#do the same but by selecting 2nd last from the bottom
|
||||||
|
vals = c.faces('<Z[-2]').vals()
|
||||||
|
self.assertEqual(len(vals),2)
|
||||||
|
|
||||||
|
val = c.faces('<Z[-2]').val()
|
||||||
|
self.assertAlmostEqual(val.Center().z,1)
|
||||||
|
|
||||||
|
#verify that <Z[-1] is equivalent to <Z
|
||||||
|
val1 = c.faces('<Z[-1]').val()
|
||||||
|
val2 = c.faces('<Z').val()
|
||||||
|
self.assertTupleAlmostEquals(val1.Center().toTuple(),
|
||||||
|
val2.Center().toTuple(),
|
||||||
|
3)
|
||||||
|
|
||||||
|
#verify that >Z[-1] is equivalent to >Z
|
||||||
|
val1 = c.faces('>Z[-1]').val()
|
||||||
|
val2 = c.faces('>Z').val()
|
||||||
|
self.assertTupleAlmostEquals(val1.Center().toTuple(),
|
||||||
|
val2.Center().toTuple(),
|
||||||
|
3)
|
||||||
|
|
||||||
def testNearestTo(self):
|
def testNearestTo(self):
|
||||||
c = CQ(makeUnitCube())
|
c = CQ(makeUnitCube())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user