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 Settings
|
||||
import Shared
|
||||
from cadquery import cqgi
|
||||
from Helpers import show
|
||||
|
||||
# Distinguish python built-in open function from the one declared here
|
||||
if open.__module__ == '__builtin__':
|
||||
|
@ -122,17 +124,42 @@ class CadQueryExecuteScript:
|
|||
# Clear the old render before re-rendering
|
||||
Shared.clearActiveDocument()
|
||||
|
||||
# Save our code to a tempfile and render it
|
||||
tempFile = tempfile.NamedTemporaryFile(delete=False)
|
||||
tempFile.write(cqCodePane.toPlainText().encode('utf-8'))
|
||||
tempFile.close()
|
||||
# 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")
|
||||
|
||||
# Set some environment variables that may help the user
|
||||
os.environ["MYSCRIPT_FULL_PATH"] = cqCodePane.file.path
|
||||
os.environ["MYSCRIPT_DIR"] = os.path.dirname(os.path.abspath(cqCodePane.file.path))
|
||||
# A repreentation of the CQ script with all the metadata attached
|
||||
cqModel = cqgi.parse(scriptText)
|
||||
|
||||
# We import this way because using execfile() causes non-standard script execution in some situations
|
||||
imp.load_source('temp_module', tempFile.name)
|
||||
# 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
|
||||
tempFile = tempfile.NamedTemporaryFile(delete=False)
|
||||
tempFile.write(scriptText)
|
||||
tempFile.close()
|
||||
|
||||
# Set some environment variables that may help the user
|
||||
os.environ["MYSCRIPT_FULL_PATH"] = cqCodePane.file.path
|
||||
os.environ["MYSCRIPT_DIR"] = os.path.dirname(os.path.abspath(cqCodePane.file.path))
|
||||
|
||||
# We import this way because using execfile() causes non-standard script execution in some situations
|
||||
imp.load_source('temp_module', tempFile.name)
|
||||
|
||||
msg = QtGui.QApplication.translate(
|
||||
"cqCodeWidget",
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,22 +7,23 @@ install:
|
|||
- gcc --version
|
||||
- g++ --version
|
||||
- python ./setup.py install
|
||||
- pip install coverage
|
||||
- pip install coveralls
|
||||
- pip install Sphinx==1.3.2
|
||||
- pip install -r requirements-dev.txt
|
||||
- pip install travis-sphinx
|
||||
- pip install pyparsing
|
||||
|
||||
script:
|
||||
- coverage run --source=cadquery ./runtests.py
|
||||
- travis-sphinx --nowarn --source=doc build
|
||||
|
||||
after_success:
|
||||
- coveralls
|
||||
- travis-sphinx deploy
|
||||
|
||||
branches:
|
||||
except:
|
||||
- pythonocc
|
||||
- 2_0_branch
|
||||
deploy:
|
||||
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: dcowden
|
||||
password:
|
||||
|
|
|
@ -10,9 +10,9 @@ CadQuery is an intuitive, easy-to-use python based language for building paramet
|
|||
|
||||
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
|
||||
* 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
|
||||
|
||||
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
|
||||
|
||||
|
||||
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!
|
||||
|
||||
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?
|
||||
========================================
|
||||
|
|
|
@ -1047,7 +1047,7 @@ class Workplane(CQ):
|
|||
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 ")
|
||||
|
||||
lpoints = [] # coordinates relative to bottom left point
|
||||
|
|
|
@ -115,11 +115,11 @@ class CQModel(object):
|
|||
else:
|
||||
raise NoOutputError("Script did not call build_object-- no output available.")
|
||||
except Exception, ex:
|
||||
print "Error Executing Script:"
|
||||
#print "Error Executing Script:"
|
||||
result.set_failure_result(ex)
|
||||
traceback.print_exc()
|
||||
print "Full Text of Script:"
|
||||
print self.script_source
|
||||
#traceback.print_exc()
|
||||
#print "Full Text of Script:"
|
||||
#print self.script_source
|
||||
|
||||
end = time.clock()
|
||||
result.buildTime = end - start
|
||||
|
@ -180,7 +180,7 @@ class ScriptMetadata(object):
|
|||
self.parameters[p.name] = p
|
||||
|
||||
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.desc = description
|
||||
|
||||
|
@ -418,7 +418,7 @@ class ParameterDescriptionFinder(ast.NodeTransformer):
|
|||
self.cqModel.add_parameter_description(varname,desc)
|
||||
|
||||
except:
|
||||
print "Unable to handle function call"
|
||||
#print "Unable to handle function call"
|
||||
pass
|
||||
return node
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ def _fc_path():
|
|||
"/usr/lib/freecad/lib",
|
||||
"/opt/freecad/lib/",
|
||||
"/usr/bin/freecad/lib",
|
||||
"/usr/lib/freecad-daily/lib",
|
||||
"/usr/lib/freecad",
|
||||
"/usr/lib64/freecad/lib",
|
||||
]:
|
||||
|
|
|
@ -420,11 +420,17 @@ class Edge(Shape):
|
|||
# self.endPoint = None
|
||||
|
||||
self.edgetypes = {
|
||||
FreeCADPart.Line: 'LINE',
|
||||
FreeCADPart.ArcOfCircle: 'ARC',
|
||||
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
|
||||
self.label = ""
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import re
|
||||
import math
|
||||
from cadquery import Vector,Edge,Vertex,Face,Solid,Shell,Compound
|
||||
from collections import defaultdict
|
||||
from pyparsing import Literal,Word,nums,Optional,Combine,oneOf,upcaseTokens,\
|
||||
CaselessLiteral,Group,infixNotation,opAssoc,Forward,\
|
||||
ZeroOrMore,Keyword
|
||||
|
@ -299,11 +300,6 @@ class DirectionMinMaxSelector(Selector):
|
|||
|
||||
def distance(tShape):
|
||||
return tShape.Center().dot(self.vector)
|
||||
#if tShape.ShapeType == 'Vertex':
|
||||
# pnt = tShape.Point
|
||||
#else:
|
||||
# pnt = tShape.Center()
|
||||
#return pnt.dot(self.vector)
|
||||
|
||||
# import OrderedDict
|
||||
from collections import OrderedDict
|
||||
|
@ -336,34 +332,30 @@ class DirectionNthSelector(ParallelDirSelector):
|
|||
self.max = max
|
||||
self.directionMax = directionMax
|
||||
self.TOLERANCE = tolerance
|
||||
if directionMax:
|
||||
self.N = n #do we want indexing from 0 or from 1?
|
||||
else:
|
||||
self.N = -n
|
||||
|
||||
self.N = n
|
||||
|
||||
def filter(self,objectList):
|
||||
#select first the objects that are normal/parallel to a given dir
|
||||
objectList = super(DirectionNthSelector,self).filter(objectList)
|
||||
|
||||
def distance(tShape):
|
||||
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
|
||||
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
|
||||
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
|
||||
return [objectDict[d] for d in objectDict.keys() if abs(d-nth_d) < self.TOLERANCE]
|
||||
return objectDict[nth_distance]
|
||||
|
||||
class BinarySelector(Selector):
|
||||
"""
|
||||
|
|
|
@ -157,6 +157,7 @@ as a basis for futher operations.
|
|||
BaseDirSelector
|
||||
ParallelDirSelector
|
||||
DirectionSelector
|
||||
DirectionNthSelector
|
||||
PerpendicularDirSelector
|
||||
TypeSelector
|
||||
DirectionMinMaxSelector
|
||||
|
|
|
@ -52,6 +52,7 @@ Selector Classes
|
|||
BaseDirSelector
|
||||
ParallelDirSelector
|
||||
DirectionSelector
|
||||
DirectionNthSelector
|
||||
PerpendicularDirSelector
|
||||
TypeSelector
|
||||
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
|
||||
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
|
||||
-------------------------------------------------
|
||||
|
||||
|
|
|
@ -29,18 +29,6 @@ face.outerWire
|
|||
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
|
||||
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'
|
||||
|
|
|
@ -28,26 +28,28 @@ string patterns in, CadQuery will automatically use the associated selector obje
|
|||
Combining Selectors
|
||||
==========================
|
||||
|
||||
Selectors can be combined arithmetically and logically, so that it is possible to do intersection, union, and other
|
||||
combinations. For example::
|
||||
Selectors can be combined logically, currently defined operators include **and**, **or**, **not** and **exc[ept]** (set difference). 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)
|
||||
|
||||
build_object(result)
|
||||
|
||||
### select all edges on right and left faces
|
||||
#box = box.edges((s("|Z") + s("|Y"))).fillet(1)
|
||||
Much more complex expressions are possible as well:
|
||||
|
||||
### select all edges on top and bottom
|
||||
#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::
|
||||
|
||||
# select diagonal edges
|
||||
box = box.faces(s('>Z')+s('<Z')).edges(-s('|X')-s('Y')).fillet(1)
|
||||
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)
|
||||
|
||||
build_object(result)
|
||||
|
||||
.. _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.
|
||||
|
||||
========= ====================================== ======================================================= ==========================
|
||||
Selector Selects Selector Class # objects returned
|
||||
========= ====================================== ======================================================= ==========================
|
||||
+Z Faces with normal in +z direction :py:class:`cadquery.DirectionSelector` 0 or 1
|
||||
\|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
|
||||
#Z Faces perpendicular to z direction :py:class:`cadquery.PerpendicularDirSelector` 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 negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
||||
========= ====================================== ======================================================= ==========================
|
||||
========= ======================================= ======================================================= ==========================
|
||||
Selector Selects Selector Class # objects returned
|
||||
========= ======================================= ======================================================= ==========================
|
||||
+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
|
||||
-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
|
||||
%Plane Faces of type plane :py:class:`cadquery.TypeSelector` 0..many
|
||||
>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..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:
|
||||
|
@ -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.
|
||||
|
||||
|
||||
========= ==================================== ======================================================= ==========================
|
||||
Selector Selects Selector Class # objects returned
|
||||
========= ==================================== ======================================================= ==========================
|
||||
+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
|
||||
-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
|
||||
%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 negative y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
||||
========= ==================================== ======================================================= ==========================
|
||||
========= ======================================= ======================================================= ==========================
|
||||
Selector Selects Selector Class # objects returned
|
||||
========= ======================================= ======================================================= ==========================
|
||||
+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
|
||||
-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
|
||||
%Line Edges of type line :py:class:`cadquery.TypeSelector` 0..many
|
||||
>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..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:
|
||||
|
@ -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
|
||||
========= ======================================= ======================================================= ==========================
|
||||
>Y Vertices farthest in the positive y dir :py:class:`cadquery.DirectionMinMaxSelector` 0 or 1
|
||||
<Y Vertices farthest in the negative 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..many
|
||||
========= ======================================= ======================================================= ==========================
|
||||
|
||||
Future Enhancements
|
||||
--------------------
|
||||
User-defined Directions
|
||||
-----------------------
|
||||
|
||||
* Support direct vectors inline, such as \|(x,y,z)
|
||||
* Support multiple selectors separated by spaces, which unions the results, such as "+Z +Y to select both z and y-most faces
|
||||
It is possible to use user defined vectors as a basis for the selectors. For example:
|
||||
|
||||
.. 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
|
||||
travis-sphinx==1.1.0
|
||||
Sphinx==1.3.1
|
||||
travis-sphinx
|
||||
Sphinx==1.3.2
|
||||
coverage
|
||||
coveralls
|
||||
pyparsing
|
||||
|
|
|
@ -194,10 +194,14 @@ class TestCQSelectors(BaseTest):
|
|||
#2nd face
|
||||
val = c.faces('>(1,0,0)[1]').val()
|
||||
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
|
||||
val = c.faces('>(-1,0,0)[1]').val()
|
||||
self.assertAlmostEqual(val.Center().x,1.5)
|
||||
val = c.faces('<X[1]').val()
|
||||
self.assertAlmostEqual(val.Center().x,1.5)
|
||||
|
||||
#2nd last face
|
||||
val = c.faces('>X[-2]').val()
|
||||
|
@ -210,6 +214,46 @@ class TestCQSelectors(BaseTest):
|
|||
#check if the selected face if normal to the specified Vector
|
||||
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):
|
||||
c = CQ(makeUnitCube())
|
||||
|
|
Loading…
Reference in New Issue
Block a user