Updated CadQuery library and updated version number to 1.0.0

This commit is contained in:
Jeremy Mack Wright 2017-04-12 22:14:55 -04:00
parent 60f48dfb78
commit eae8efd9ee
14 changed files with 257 additions and 119 deletions

View File

@ -1,7 +1,7 @@
What is a CadQuery? What is a CadQuery?
======================================== ========================================
[![Travis Build Status](https://travis-ci.org/dcowden/cadquery.svg)](https://travis-ci.org/dcowden/cadquery) [![Travis Build Status](https://travis-ci.org/dcowden/cadquery.svg?branch=master)](https://travis-ci.org/dcowden/cadquery?branch=master)
[![Coverage Status](https://coveralls.io/repos/dcowden/cadquery/badge.svg)](https://coveralls.io/r/dcowden/cadquery) [![Coverage Status](https://coveralls.io/repos/dcowden/cadquery/badge.svg)](https://coveralls.io/r/dcowden/cadquery)
[![GitHub version](https://badge.fury.io/gh/dcowden%2Fcadquery.svg)](https://github.com/dcowden/cadquery/releases/tag/v0.3.0) [![GitHub version](https://badge.fury.io/gh/dcowden%2Fcadquery.svg)](https://github.com/dcowden/cadquery/releases/tag/v0.3.0)
[![License](https://img.shields.io/badge/license-Apache2-blue.svg)](https://github.com/dcowden/cadquery/blob/master/LICENSE) [![License](https://img.shields.io/badge/license-Apache2-blue.svg)](https://github.com/dcowden/cadquery/blob/master/LICENSE)
@ -46,6 +46,7 @@ This resin mold was modeled using cadquery and then created on a CNC machine:
The cadquery script is surprisingly short, and allows easily customizing any of the variables:: The cadquery script is surprisingly short, and allows easily customizing any of the variables::
```python
import cadquery as cq import cadquery as cq
from Helpers import show from Helpers import show
BS = cq.selectors.BoxSelector BS = cq.selectors.BoxSelector
@ -108,7 +109,7 @@ The cadquery script is surprisingly short, and allows easily customizing any of
]).hole(fhd, mw/2.) ]).hole(fhd, mw/2.)
show(r) show(r)
```
Thanks go to cadquery contributor hyOzd ( Altu Technology ) for the example! Thanks go to cadquery contributor hyOzd ( Altu Technology ) for the example!
@ -171,26 +172,30 @@ Use these steps if you would like to write CadQuery scripts as a python API. In
preferably one that has virtualenv available. To use FreeCAD from any python interpreter, just append the FreeCAD preferably one that has virtualenv available. To use FreeCAD from any python interpreter, just append the FreeCAD
lib directory to your path. On (*Nix):: lib directory to your path. On (*Nix)::
import sys ```python
import sys
sys.path.append('/usr/lib/freecad/lib') sys.path.append('/usr/lib/freecad/lib')
```
or on Windows:: or on Windows::
import sys ```python
import sys
sys.path.append('/c/apps/FreeCAD/bin') sys.path.append('/c/apps/FreeCAD/bin')
```
*NOTE* FreeCAD on Windows will not work with python 2.7-- you must use pthon 2.6.X!!!! *NOTE* FreeCAD on Windows will not work with python 2.7-- you must use pthon 2.6.X!!!!
3. install cadquery:: 3. install cadquery::
```bash
pip install cadquery pip install cadquery
```
3. test your installation:: 3. test your installation::
```python
from cadquery import * from cadquery import *
box = Workplane("XY").box(1,2,3) box = Workplane("XY").box(1,2,3)
exporters.toString(box,'STL') exporters.toString(box,'STL')
```
You're up and running! You're up and running!
Installing -- Using CadQuery from Inside FreeCAD Installing -- Using CadQuery from Inside FreeCAD
@ -204,17 +209,16 @@ It includes a distribution of the latest version of cadquery.
Roadmap/Future Work Roadmap/Future Work
======================= =======================
Work is underway on two other installation methods for cadquery: Work has begun on Cadquery 2.0, which will feature:
1. a conda package, which will install CQ and all of its depedencies, if you are using Anaconda
2. a Docker image, which comes ready-to-run after you have installed docker.
Work has also begun on Cadquery 2.0, which will feature:
1. Feature trees, for more powerful selection 1. Feature trees, for more powerful selection
2. Direct use of OpenCascade Community Edition(OCE), so that it is no longer required to install FreeCAD 2. Direct use of OpenCascade Community Edition(OCE), so that it is no longer required to install FreeCAD
3. https://github.com/jmwright/cadquery-gui, which will allow visualization of workplanes 3. https://github.com/jmwright/cadquery-gui, which will allow visualization of workplanes
The project page can be found here: https://github.com/dcowden/cadquery/projects/1
A more detailed description of the plan for CQ 2.0 is here: https://docs.google.com/document/d/1cXuxBkVeYmGOo34MGRdG7E3ILypQqkrJ26oVf3CUSPQ
Where does the name CadQuery come from? Where does the name CadQuery come from?
======================================== ========================================

View File

@ -1,6 +1,6 @@
Metadata-Version: 1.1 Metadata-Version: 1.1
Name: cadquery Name: cadquery
Version: 0.4.0 Version: 1.0.0
Summary: CadQuery is a parametric scripting language for creating and traversing CAD models Summary: CadQuery is a parametric scripting language for creating and traversing CAD models
Home-page: https://github.com/dcowden/cadquery Home-page: https://github.com/dcowden/cadquery
Author: David Cowden Author: David Cowden

View File

@ -13,9 +13,9 @@ from .cq import *
__all__ = [ __all__ = [
'CQ','Workplane','plugins','selectors','Plane','BoundBox','Matrix','Vector','sortWiresByBuildOrder', 'CQ','Workplane','plugins','selectors','Plane','BoundBox','Matrix','Vector','sortWiresByBuildOrder',
'Shape','Vertex','Edge','Wire','Solid','Shell','Compound','exporters', 'importers', 'Shape','Vertex','Edge','Wire','Face','Solid','Shell','Compound','exporters', 'importers',
'NearestToPointSelector','ParallelDirSelector','DirectionSelector','PerpendicularDirSelector', 'NearestToPointSelector','ParallelDirSelector','DirectionSelector','PerpendicularDirSelector',
'TypeSelector','DirectionMinMaxSelector','StringSyntaxSelector','Selector','plugins' 'TypeSelector','DirectionMinMaxSelector','StringSyntaxSelector','Selector','plugins'
] ]
__version__ = "0.5.1" __version__ = "1.0.0"

View File

@ -16,23 +16,19 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/> License along with this library; If not, see <http://www.gnu.org/licenses/>
""" """
import os, sys import os
import sys
def _fc_path(): def _fc_path():
"""Find FreeCAD""" """Find FreeCAD"""
_PATH = "" # Look for FREECAD_LIB env variable
if _PATH: _PATH = os.environ.get('FREECAD_LIB', '')
if _PATH and os.path.exists(_PATH):
return _PATH return _PATH
#look for FREECAD_LIB env variable
if os.environ.has_key('FREECAD_LIB'):
_PATH = os.environ.get('FREECAD_LIB')
if os.path.exists( _PATH):
return _PATH
if sys.platform.startswith('linux'): if sys.platform.startswith('linux'):
#Make some dangerous assumptions... # Make some dangerous assumptions...
for _PATH in [ for _PATH in [
os.path.join(os.path.expanduser("~"), "lib/freecad/lib"), os.path.join(os.path.expanduser("~"), "lib/freecad/lib"),
"/usr/local/lib/freecad/lib", "/usr/local/lib/freecad/lib",
@ -40,12 +36,13 @@ def _fc_path():
"/opt/freecad/lib/", "/opt/freecad/lib/",
"/usr/bin/freecad/lib", "/usr/bin/freecad/lib",
"/usr/lib/freecad", "/usr/lib/freecad",
"/usr/lib64/freecad/lib",
]: ]:
if os.path.exists(_PATH): if os.path.exists(_PATH):
return _PATH return _PATH
elif sys.platform.startswith('win'): elif sys.platform.startswith('win'):
#try all the usual suspects # Try all the usual suspects
for _PATH in [ for _PATH in [
"c:/Program Files/FreeCAD0.12/bin", "c:/Program Files/FreeCAD0.12/bin",
"c:/Program Files/FreeCAD0.13/bin", "c:/Program Files/FreeCAD0.13/bin",
@ -86,27 +83,24 @@ def _fc_path():
]: ]:
if os.path.exists(_PATH): if os.path.exists(_PATH):
return _PATH return _PATH
elif sys.platform.startswith('darwin'): elif sys.platform.startswith('darwin'):
#Assume we're dealing with a Mac # Assume we're dealing with a Mac
for _PATH in [ for _PATH in [
"/Applications/FreeCAD.app/Contents/lib", "/Applications/FreeCAD.app/Contents/lib",
os.path.join(os.path.expanduser("~"), "Library/Application Support/FreeCAD/lib"), os.path.join(os.path.expanduser("~"),
"Library/Application Support/FreeCAD/lib"),
]: ]:
if os.path.exists(_PATH): if os.path.exists(_PATH):
return _PATH return _PATH
raise ImportError('cadquery was unable to determine freecad library path')
#Make sure that the correct FreeCAD path shows up in Python's system path # Make sure that the correct FreeCAD path shows up in Python's system path
no_library_path = ImportError('cadquery was unable to determine freecads library path')
try: try:
import FreeCAD import FreeCAD
except ImportError: except ImportError:
path = _fc_path() path = _fc_path()
if path: sys.path.insert(0, path)
sys.path.insert(0, path) import FreeCAD
try:
import FreeCAD
except ImportError:
raise no_library_path
else: raise no_library_path

View File

@ -216,7 +216,8 @@ class Shape(object):
else: else:
raise ValueError("Cannot find the center of %s object type" % str(type(self.Solids()[0].wrapped))) raise ValueError("Cannot find the center of %s object type" % str(type(self.Solids()[0].wrapped)))
def CenterOfBoundBox(self): def CenterOfBoundBox(self, tolerance = 0.1):
self.wrapped.tessellate(tolerance)
if isinstance(self.wrapped, FreeCADPart.Shape): if isinstance(self.wrapped, FreeCADPart.Shape):
# If there are no Solids, we're probably dealing with a Face or something similar # If there are no Solids, we're probably dealing with a Face or something similar
if len(self.Solids()) == 0: if len(self.Solids()) == 0:
@ -258,14 +259,18 @@ class Shape(object):
return object.wrapped.Mass return object.wrapped.Mass
@staticmethod @staticmethod
def CombinedCenterOfBoundBox(objects): def CombinedCenterOfBoundBox(objects, tolerance = 0.1):
""" """
Calculates the center of BoundBox of multiple objects. Calculates the center of BoundBox of multiple objects.
:param objects: a list of objects with mass 1 :param objects: a list of objects with mass 1
""" """
total_mass = len(objects) total_mass = len(objects)
weighted_centers = [o.wrapped.BoundBox.Center.multiply(1.0) for o in objects]
weighted_centers = []
for o in objects:
o.wrapped.tessellate(tolerance)
weighted_centers.append(o.wrapped.BoundBox.Center.multiply(1.0))
sum_wc = weighted_centers[0] sum_wc = weighted_centers[0]
for wc in weighted_centers[1:] : for wc in weighted_centers[1:] :
@ -470,7 +475,9 @@ class Edge(Shape):
@classmethod @classmethod
def makeCircle(cls, radius, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=360.0, angle2=360): def makeCircle(cls, radius, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=360.0, angle2=360):
return Edge(FreeCADPart.makeCircle(radius, toVector(pnt), toVector(dir), angle1, angle2)) center = Vector(pnt)
normal = Vector(dir)
return Edge(FreeCADPart.makeCircle(radius, center.wrapped, normal.wrapped, angle1, angle2))
@classmethod @classmethod
def makeSpline(cls, listOfVector): def makeSpline(cls, listOfVector):
@ -621,8 +628,10 @@ class Face(Shape):
return Vector(self.wrapped.normalAt(u, v).normalize()) return Vector(self.wrapped.normalAt(u, v).normalize())
@classmethod @classmethod
def makePlane(cls, length, width, basePnt=None, dir=None): def makePlane(cls, length, width, basePnt=(0, 0, 0), dir=(0, 0, 1)):
return Face(FreeCADPart.makePlan(length, width, toVector(basePnt), toVector(dir))) basePnt = Vector(basePnt)
dir = Vector(dir)
return Face(FreeCADPart.makePlane(length, width, basePnt.wrapped, dir.wrapped))
@classmethod @classmethod
def makeRuledSurface(cls, edgeOrWire1, edgeOrWire2, dist=None): def makeRuledSurface(cls, edgeOrWire1, edgeOrWire2, dist=None):

View File

@ -20,8 +20,9 @@
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 pyparsing import Literal,Word,nums,Optional,Combine,oneOf,\ from pyparsing import Literal,Word,nums,Optional,Combine,oneOf,upcaseTokens,\
upcaseTokens,CaselessLiteral,Group CaselessLiteral,Group,infixNotation,opAssoc,Forward,\
ZeroOrMore,Keyword
class Selector(object): class Selector(object):
@ -288,10 +289,6 @@ class DirectionMinMaxSelector(Selector):
CQ(aCube).faces( ">Z" ) CQ(aCube).faces( ">Z" )
Future Enhancements:
provide a nicer way to select in arbitrary directions. IE, a bit more code could
allow '>(0,0,1)' to work.
""" """
def __init__(self, vector, directionMax=True, tolerance=0.0001): def __init__(self, vector, directionMax=True, tolerance=0.0001):
self.vector = vector self.vector = vector
@ -423,7 +420,7 @@ class InverseSelector(Selector):
def _makeGrammar(): def _makeGrammar():
""" """
Define the string selector grammar using PyParsing Define the simple string selector grammar using PyParsing
""" """
#float definition #float definition
@ -476,44 +473,14 @@ def _makeGrammar():
_grammar = _makeGrammar() #make a grammar instance _grammar = _makeGrammar() #make a grammar instance
class StringSyntaxSelector(Selector): class _SimpleStringSyntaxSelector(Selector):
""" """
Filter lists objects using a simple string syntax. All of the filters available in the string syntax This is a private class that converts a parseResults object into a simple
are also available ( usually with more functionality ) through the creation of full-fledged selector object
selector objects. see :py:class:`Selector` and its subclasses
Filtering works differently depending on the type of object list being filtered.
:param selectorString: A two-part selector string, [selector][axis]
:return: objects that match the specified selector
***Modfiers*** are ``('|','+','-','<','>','%')``
:\|:
parallel to ( same as :py:class:`ParallelDirSelector` ). Can return multiple objects.
:#:
perpendicular to (same as :py:class:`PerpendicularDirSelector` )
:+:
positive direction (same as :py:class:`DirectionSelector` )
:-:
negative direction (same as :py:class:`DirectionSelector` )
:>:
maximize (same as :py:class:`DirectionMinMaxSelector` with directionMax=True)
:<:
minimize (same as :py:class:`DirectionMinMaxSelector` with directionMax=False )
:%:
curve/surface type (same as :py:class:`TypeSelector`)
***axisStrings*** are: ``X,Y,Z,XY,YZ,XZ``
Selectors are a complex topic: see :ref:`selector_reference` for more information
""" """
def __init__(self,selectorString): def __init__(self,parseResults):
#define all token to object mappings
self.axes = { self.axes = {
'X': Vector(1,0,0), 'X': Vector(1,0,0),
'Y': Vector(0,1,0), 'Y': Vector(0,1,0),
@ -545,9 +512,8 @@ class StringSyntaxSelector(Selector):
'#' : PerpendicularDirSelector, '#' : PerpendicularDirSelector,
'|' : ParallelDirSelector} '|' : ParallelDirSelector}
self.selectorString = selectorString self.parseResults = parseResults
parsing_result = _grammar.parseString(selectorString) self.mySelector = self._chooseSelector(parseResults)
self.mySelector = self._chooseSelector(parsing_result)
def _chooseSelector(self,pr): def _chooseSelector(self,pr):
""" """
@ -593,3 +559,113 @@ class StringSyntaxSelector(Selector):
[+\|-\|<\|>\|] \<X\|Y\|Z> [+\|-\|<\|>\|] \<X\|Y\|Z>
""" """
return self.mySelector.filter(objectList) return self.mySelector.filter(objectList)
def _makeExpressionGrammar(atom):
"""
Define the complex string selector grammar using PyParsing (which supports
logical operations and nesting)
"""
#define operators
and_op = Literal('and')
or_op = Literal('or')
delta_op = oneOf(['exc','except'])
not_op = Literal('not')
def atom_callback(res):
return _SimpleStringSyntaxSelector(res)
atom.setParseAction(atom_callback) #construct a simple selector from every matched
#define callback functions for all operations
def and_callback(res):
items = res.asList()[0][::2] #take every secend items, i.e. all operands
return reduce(AndSelector,items)
def or_callback(res):
items = res.asList()[0][::2] #take every secend items, i.e. all operands
return reduce(SumSelector,items)
def exc_callback(res):
items = res.asList()[0][::2] #take every secend items, i.e. all operands
return reduce(SubtractSelector,items)
def not_callback(res):
right = res.asList()[0][1] #take second item, i.e. the operand
return InverseSelector(right)
#construct the final grammar and set all the callbacks
expr = infixNotation(atom,
[(and_op,2,opAssoc.LEFT,and_callback),
(or_op,2,opAssoc.LEFT,or_callback),
(delta_op,2,opAssoc.LEFT,exc_callback),
(not_op,1,opAssoc.RIGHT,not_callback)])
return expr
_expression_grammar = _makeExpressionGrammar(_grammar)
class StringSyntaxSelector(Selector):
"""
Filter lists objects using a simple string syntax. All of the filters available in the string syntax
are also available ( usually with more functionality ) through the creation of full-fledged
selector objects. see :py:class:`Selector` and its subclasses
Filtering works differently depending on the type of object list being filtered.
:param selectorString: A two-part selector string, [selector][axis]
:return: objects that match the specified selector
***Modfiers*** are ``('|','+','-','<','>','%')``
:\|:
parallel to ( same as :py:class:`ParallelDirSelector` ). Can return multiple objects.
:#:
perpendicular to (same as :py:class:`PerpendicularDirSelector` )
:+:
positive direction (same as :py:class:`DirectionSelector` )
:-:
negative direction (same as :py:class:`DirectionSelector` )
:>:
maximize (same as :py:class:`DirectionMinMaxSelector` with directionMax=True)
:<:
minimize (same as :py:class:`DirectionMinMaxSelector` with directionMax=False )
:%:
curve/surface type (same as :py:class:`TypeSelector`)
***axisStrings*** are: ``X,Y,Z,XY,YZ,XZ`` or ``(x,y,z)`` which defines an arbitrary direction
It is possible to combine simple selectors together using logical operations.
The following operations are suuported
:and:
Logical AND, e.g. >X and >Y
:or:
Logical OR, e.g. |X or |Y
:not:
Logical NOT, e.g. not #XY
:exc(ept):
Set difference (equivalent to AND NOT): |X exc >Z
Finally, it is also possible to use even more complex expressions with nesting
and arbitrary number of terms, e.g.
(not >X[0] and #XY) or >XY[0]
Selectors are a complex topic: see :ref:`selector_reference` for more information
"""
def __init__(self,selectorString):
"""
Feed the input string through the parser and construct an relevant complex selector object
"""
self.selectorString = selectorString
parse_result = _expression_grammar.parseString(selectorString,
parseAll=True)
self.mySelector = parse_result.asList()[0]
def filter(self,objectList):
"""
Filter give object list through th already constructed complex selector object
"""
return self.mySelector.filter(objectList)

View File

@ -4,32 +4,32 @@ Changes
v0.1 v0.1
----- -----
* Initial Version * Initial Version
v0.1.6 v0.1.6
----- -----
* Added STEP import and supporting tests * Added STEP import and supporting tests
v0.1.7 v0.1.7
----- -----
* Added revolve operation and supporting tests * Added revolve operation and supporting tests
* Fixed minor documentation errors * Fixed minor documentation errors
v0.1.8 v0.1.8
----- -----
* Added toFreecad() function as a convenience for val().wrapped * Added toFreecad() function as a convenience for val().wrapped
* Converted all examples to use toFreecad() * Converted all examples to use toFreecad()
* Updated all version numbers that were missed before * Updated all version numbers that were missed before
* Fixed import issues in Windows caused by fc_import * Fixed import issues in Windows caused by fc_import
* Added/fixed Mac OS support * Added/fixed Mac OS support
* Improved STEP import * Improved STEP import
* Fixed bug in rotateAboutCenter that negated its effect on solids * Fixed bug in rotateAboutCenter that negated its effect on solids
* Added Travis config (thanks @krasin) * Added Travis config (thanks @krasin)
* Removed redundant workplane.py file left over from the PParts.com migration * Removed redundant workplane.py file left over from the PParts.com migration
* Fixed toWorldCoordinates bug in moveTo (thanks @xix-xeaon) * Fixed toWorldCoordinates bug in moveTo (thanks @xix-xeaon)
* Added new tests for 2D drawing functions * Added new tests for 2D drawing functions
* Integrated Coveralls.io, with a badge in README.md * Integrated Coveralls.io, with a badge in README.md
* Integrated version badge in README.md * Integrated version badge in README.md
v0.2.0 v0.2.0
----- -----
@ -89,7 +89,12 @@ v0.5.2
------ ------
* Added the sweep operation #33 * Added the sweep operation #33
v1.0.0 (unreleased) v1.0.0
------ ------
* Added an option to do symmetric extrusion about the workplane (thanks @adam-urbanczyk) * Added an option to do symmetric extrusion about the workplane (thanks @adam-urbanczyk)
* Extended selector syntax to include Nth selector and re-implemented selectors using pyparsing (thanks @adam-urbanczyk) * Extended selector syntax to include Nth selector and re-implemented selectors using pyparsing (thanks @adam-urbanczyk)
* Added logical operations to string selectors (thanks @adam-urbanczyk)
* Cleanup of README.md and changes.md (thanks @baoboa)
* Fixed bugs with toVector and Face 'Not Defined' errors (thanks @huskier)
* Refactor of the initialization code for PEP8 compliance and Python 3 compatibility (thanks @Peque)
* Making sure that the new pyparsing library dependency is handled properly (thanks @Peque)

View File

@ -55,9 +55,9 @@ copyright = u'Parametric Products Intellectual Holdings LLC, All Rights Reserved
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.3' version = '1.0'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.3.0' release = '1.0.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -0,0 +1 @@
-e .

View File

@ -19,7 +19,7 @@ from setuptools import setup
version = '0.5-SNAPSHOT' version = '0.5-SNAPSHOT'
if 'TRAVIS_TAG' in os.environ.keys(): if 'TRAVIS_TAG' in os.environ.keys():
version= os.environ['TRAVIS_TAG'] version= os.environ['TRAVIS_TAG']
setup( setup(
name='cadquery', name='cadquery',
@ -31,6 +31,7 @@ setup(
description='CadQuery is a parametric scripting language for creating and traversing CAD models', description='CadQuery is a parametric scripting language for creating and traversing CAD models',
long_description=open('README.md').read(), long_description=open('README.md').read(),
packages=['cadquery','cadquery.contrib','cadquery.freecad_impl','cadquery.plugins','tests'], packages=['cadquery','cadquery.contrib','cadquery.freecad_impl','cadquery.plugins','tests'],
install_requires=['pyparsing'],
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,
platforms='any', platforms='any',

View File

@ -338,6 +338,10 @@ class TestCQSelectors(BaseTest):
# test 'and' (intersection) operator # test 'and' (intersection) operator
el = c.edges(S('|X') & BS((-2,-2,0.1), (2,2,2))).vals() el = c.edges(S('|X') & BS((-2,-2,0.1), (2,2,2))).vals()
self.assertEqual(2, len(el)) self.assertEqual(2, len(el))
# test using extended string syntax
v = c.vertices(">X and >Y").vals()
self.assertEqual(2, len(v))
def testSumSelector(self): def testSumSelector(self):
c = CQ(makeUnitCube()) c = CQ(makeUnitCube())
@ -354,6 +358,12 @@ class TestCQSelectors(BaseTest):
self.assertEqual(2, len(fl)) self.assertEqual(2, len(fl))
el = c.edges(S("|X") + S("|Y")).vals() el = c.edges(S("|X") + S("|Y")).vals()
self.assertEqual(8, len(el)) self.assertEqual(8, len(el))
# test using extended string syntax
fl = c.faces(">Z or <Z").vals()
self.assertEqual(2, len(fl))
el = c.edges("|X or |Y").vals()
self.assertEqual(8, len(el))
def testSubtractSelector(self): def testSubtractSelector(self):
c = CQ(makeUnitCube()) c = CQ(makeUnitCube())
@ -366,6 +376,10 @@ class TestCQSelectors(BaseTest):
# test the subtract operator # test the subtract operator
fl = c.faces(S("#Z") - S(">X")).vals() fl = c.faces(S("#Z") - S(">X")).vals()
self.assertEqual(3, len(fl)) self.assertEqual(3, len(fl))
# test using extended string syntax
fl = c.faces("#Z exc >X").vals()
self.assertEqual(3, len(fl))
def testInverseSelector(self): def testInverseSelector(self):
c = CQ(makeUnitCube()) c = CQ(makeUnitCube())
@ -382,6 +396,19 @@ class TestCQSelectors(BaseTest):
self.assertEqual(5, len(fl)) self.assertEqual(5, len(fl))
el = c.faces('>Z').edges(-S('>X')).vals() el = c.faces('>Z').edges(-S('>X')).vals()
self.assertEqual(3, len(el)) self.assertEqual(3, len(el))
# test using extended string syntax
fl = c.faces('not >Z').vals()
self.assertEqual(5, len(fl))
el = c.faces('>Z').edges('not >X').vals()
self.assertEqual(3, len(el))
def testComplexStringSelector(self):
c = CQ(makeUnitCube())
v = c.vertices('(>X and >Y) or (<X and <Y)').vals()
self.assertEqual(4, len(v))
def testFaceCount(self): def testFaceCount(self):
c = CQ(makeUnitCube()) c = CQ(makeUnitCube())
@ -406,7 +433,7 @@ class TestCQSelectors(BaseTest):
Test if reasonable string selector expressions parse without an error Test if reasonable string selector expressions parse without an error
""" """
gram = selectors._makeGrammar() gram = selectors._expression_grammar
expressions = ['+X ', expressions = ['+X ',
'-Y', '-Y',
@ -423,7 +450,10 @@ class TestCQSelectors(BaseTest):
'left', 'left',
'right', 'right',
'top', 'top',
'bottom'] 'bottom',
'not |(1,1,0) and >(0,0,1) or XY except >(1,1,1)[-1]',
for e in expressions: gram.parseString(e) '(not |(1,1,0) and >(0,0,1)) exc XY and (Z or X)',
'not ( <X or >X or <Y or >Y )']
for e in expressions: gram.parseString(e,parseAll=True)

View File

@ -41,6 +41,24 @@ class TestCadObjects(BaseTest):
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3) self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3)
def testEdgeWrapperMakeCircle(self):
halfCircleEdge = Edge.makeCircle(radius=10, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=0, angle2=180)
self.assertTupleAlmostEquals((0.0, 5.0, 0.0), halfCircleEdge.CenterOfBoundBox(0.0001).toTuple(),3)
self.assertTupleAlmostEquals((10.0, 0.0, 0.0), halfCircleEdge.startPoint().toTuple(), 3)
self.assertTupleAlmostEquals((-10.0, 0.0, 0.0), halfCircleEdge.endPoint().toTuple(), 3)
def testFaceWrapperMakePlane(self):
mplane = Face.makePlane(10,10)
self.assertTupleAlmostEquals((0.0, 0.0, 1.0), mplane.normalAt().toTuple(), 3)
def testCenterOfBoundBox(self):
pass
def testCombinedCenterOfBoundBox(self):
pass
def testCompoundCenter(self): def testCompoundCenter(self):
""" """
Tests whether or not a proper weighted center can be found for a compound Tests whether or not a proper weighted center can be found for a compound

View File

@ -1,6 +1,6 @@
__author__ = "Jeremy Wright (jmwright)" __author__ = "Jeremy Wright (jmwright)"
__copyright__ = "Copyright 2014-2016" __copyright__ = "Copyright 2014-2017"
__license__ = "LGPL v3" __license__ = "LGPL v3"
__version__ = "0.5.2" __version__ = "1.0.0"
__maintainer__ = "Jeremy Wright" __maintainer__ = "Jeremy Wright"
__status__ = "Production/Stable" __status__ = "Production/Stable"

View File

@ -28,7 +28,7 @@ v0.5.1
* Version updates for CadQuery v0.4.0, v0.4.1, v0.5.0-stable and v0.5.1 * Version updates for CadQuery v0.4.0, v0.4.1, v0.5.0-stable and v0.5.1
* Updated CadQuery license to Apache 2.0 * Updated CadQuery license to Apache 2.0
v1.0.0 (Unreleased) v1.0.0
----- -----
* Embedded pyparsing package as a supporting library for new selector syntax * Embedded pyparsing package as a supporting library for new selector syntax
* Added a check to remove any disallowed characters from the document name when executing a script * Added a check to remove any disallowed characters from the document name when executing a script