Merge commit '4a105d12413b11bbd29cd50249867d86b41e3965'
This commit is contained in:
commit
4a21c9e665
|
@ -1,6 +1,4 @@
|
||||||
---
|
|
||||||
language: python
|
language: python
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- sudo add-apt-repository -y ppa:freecad-maintainers/freecad-daily
|
- sudo add-apt-repository -y ppa:freecad-maintainers/freecad-daily
|
||||||
- sudo apt-get update -qq
|
- sudo apt-get update -qq
|
||||||
|
@ -13,15 +11,20 @@ install:
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
- pip install Sphinx==1.3.2
|
- pip install Sphinx==1.3.2
|
||||||
- pip install travis-sphinx
|
- pip install travis-sphinx
|
||||||
|
|
||||||
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
|
||||||
|
deploy:
|
||||||
|
provider: pypi
|
||||||
|
user: dcowden
|
||||||
|
password:
|
||||||
|
secure: aP02wBbry1j3hYG/w++siF1lk26teuRQlPAx1c+ec8fxUw+bECa2HbPQHcIvSXB5N6nc6P3L9LjHt9ktm+Dn6FLJu3qWYNGAZx9PTn24ug0iAmB+JyNrsET3nK6WUKR1XpBqvjKgdpukd1Hknh2FSzYoyUvFWH9/CovITCFN3jo=
|
||||||
|
on:
|
||||||
|
tags: true
|
|
@ -1,4 +1,5 @@
|
||||||
README.txt
|
README.txt
|
||||||
|
README.md
|
||||||
setup.cfg
|
setup.cfg
|
||||||
setup.py
|
setup.py
|
||||||
cadquery\cq.py
|
cadquery\cq.py
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
include README.md
|
|
@ -742,6 +742,19 @@ class CQ(object):
|
||||||
return self.newObject([o.rotate(axisStartPoint, axisEndPoint, angleDegrees)
|
return self.newObject([o.rotate(axisStartPoint, axisEndPoint, angleDegrees)
|
||||||
for o in self.objects])
|
for o in self.objects])
|
||||||
|
|
||||||
|
def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)):
|
||||||
|
"""
|
||||||
|
Mirror a single CQ object. This operation is the same as in the FreeCAD PartWB's mirroring
|
||||||
|
|
||||||
|
:param mirrorPlane: the plane to mirror about
|
||||||
|
:type mirrorPlane: string, one of "XY", "YX", "XZ", "ZX", "YZ", "ZY" the planes
|
||||||
|
:param basePointVector: the base point to mirror about
|
||||||
|
:type basePointVector: tuple
|
||||||
|
"""
|
||||||
|
newS = self.newObject([self.objects[0].mirror(mirrorPlane, basePointVector)])
|
||||||
|
return newS.first()
|
||||||
|
|
||||||
|
|
||||||
def translate(self, vec):
|
def translate(self, vec):
|
||||||
"""
|
"""
|
||||||
Returns a copy of all of the items on the stack moved by the specified translation vector.
|
Returns a copy of all of the items on the stack moved by the specified translation vector.
|
||||||
|
|
|
@ -44,9 +44,11 @@ class CQModel(object):
|
||||||
self.ast_tree = ast.parse(script_source, CQSCRIPT)
|
self.ast_tree = ast.parse(script_source, CQSCRIPT)
|
||||||
self.script_source = script_source
|
self.script_source = script_source
|
||||||
self._find_vars()
|
self._find_vars()
|
||||||
|
|
||||||
# TODO: pick up other scirpt metadata:
|
# TODO: pick up other scirpt metadata:
|
||||||
# describe
|
# describe
|
||||||
# pick up validation methods
|
# pick up validation methods
|
||||||
|
self._find_descriptions()
|
||||||
|
|
||||||
def _find_vars(self):
|
def _find_vars(self):
|
||||||
"""
|
"""
|
||||||
|
@ -65,6 +67,9 @@ class CQModel(object):
|
||||||
if isinstance(node, ast.Assign):
|
if isinstance(node, ast.Assign):
|
||||||
assignment_finder.visit_Assign(node)
|
assignment_finder.visit_Assign(node)
|
||||||
|
|
||||||
|
def _find_descriptions(self):
|
||||||
|
description_finder = ParameterDescriptionFinder(self.metadata)
|
||||||
|
description_finder.visit(self.ast_tree)
|
||||||
|
|
||||||
def validate(self, params):
|
def validate(self, params):
|
||||||
"""
|
"""
|
||||||
|
@ -75,11 +80,13 @@ class CQModel(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("not yet implemented")
|
raise NotImplementedError("not yet implemented")
|
||||||
|
|
||||||
def build(self, build_parameters=None):
|
def build(self, build_parameters=None, build_options=None):
|
||||||
"""
|
"""
|
||||||
Executes the script, using the optional parameters to override those in the model
|
Executes the script, using the optional parameters to override those in the model
|
||||||
:param build_parameters: a dictionary of variables. The variables must be
|
:param build_parameters: a dictionary of variables. The variables must be
|
||||||
assignable to the underlying variable type.
|
assignable to the underlying variable type. These variables override default values in the script
|
||||||
|
:param build_options: build options for how to build the model. Build options include things like
|
||||||
|
timeouts, tesselation tolerances, etc
|
||||||
:raises: Nothing. If there is an exception, it will be on the exception property of the result.
|
:raises: Nothing. If there is an exception, it will be on the exception property of the result.
|
||||||
This is the interface so that we can return other information on the result, such as the build time
|
This is the interface so that we can return other information on the result, such as the build time
|
||||||
:return: a BuildResult object, which includes the status of the result, and either
|
:return: a BuildResult object, which includes the status of the result, and either
|
||||||
|
@ -95,10 +102,14 @@ class CQModel(object):
|
||||||
self.set_param_values(build_parameters)
|
self.set_param_values(build_parameters)
|
||||||
collector = ScriptCallback()
|
collector = ScriptCallback()
|
||||||
env = EnvironmentBuilder().with_real_builtins().with_cadquery_objects() \
|
env = EnvironmentBuilder().with_real_builtins().with_cadquery_objects() \
|
||||||
.add_entry("build_object", collector.build_object).build()
|
.add_entry("build_object", collector.build_object) \
|
||||||
|
.add_entry("debug", collector.debug) \
|
||||||
|
.add_entry("describe_parameter",collector.describe_parameter) \
|
||||||
|
.build()
|
||||||
|
|
||||||
c = compile(self.ast_tree, CQSCRIPT, 'exec')
|
c = compile(self.ast_tree, CQSCRIPT, 'exec')
|
||||||
exec (c, env)
|
exec (c, env)
|
||||||
|
result.set_debug(collector.debugObjects )
|
||||||
if collector.has_results():
|
if collector.has_results():
|
||||||
result.set_success_result(collector.outputObjects)
|
result.set_success_result(collector.outputObjects)
|
||||||
else:
|
else:
|
||||||
|
@ -139,6 +150,7 @@ class BuildResult(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.buildTime = None
|
self.buildTime = None
|
||||||
self.results = []
|
self.results = []
|
||||||
|
self.debugObjects = []
|
||||||
self.first_result = None
|
self.first_result = None
|
||||||
self.success = False
|
self.success = False
|
||||||
self.exception = None
|
self.exception = None
|
||||||
|
@ -147,6 +159,9 @@ class BuildResult(object):
|
||||||
self.exception = ex
|
self.exception = ex
|
||||||
self.success = False
|
self.success = False
|
||||||
|
|
||||||
|
def set_debug(self, debugObjects):
|
||||||
|
self.debugObjects = debugObjects
|
||||||
|
|
||||||
def set_success_result(self, results):
|
def set_success_result(self, results):
|
||||||
self.results = results
|
self.results = results
|
||||||
self.first_result = self.results[0]
|
self.first_result = self.results[0]
|
||||||
|
@ -164,6 +179,11 @@ class ScriptMetadata(object):
|
||||||
def add_script_parameter(self, p):
|
def add_script_parameter(self, p):
|
||||||
self.parameters[p.name] = p
|
self.parameters[p.name] = p
|
||||||
|
|
||||||
|
def add_parameter_description(self,name,description):
|
||||||
|
print 'Adding Parameter name=%s, desc=%s' % ( name, description )
|
||||||
|
p = self.parameters[name]
|
||||||
|
p.desc = description
|
||||||
|
|
||||||
|
|
||||||
class ParameterType(object):
|
class ParameterType(object):
|
||||||
pass
|
pass
|
||||||
|
@ -204,19 +224,15 @@ class InputParameter:
|
||||||
self.varType = None
|
self.varType = None
|
||||||
|
|
||||||
#: help text describing the variable. Only available if the script used describe_parameter()
|
#: help text describing the variable. Only available if the script used describe_parameter()
|
||||||
self.shortDesc = None
|
self.desc = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#: valid values for the variable. Only available if the script used describe_parameter()
|
#: valid values for the variable. Only available if the script used describe_parameter()
|
||||||
self.valid_values = []
|
self.valid_values = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
self.ast_node = None
|
self.ast_node = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(ast_node, var_name, var_type, default_value, valid_values=None, short_desc=None):
|
def create(ast_node, var_name, var_type, default_value, valid_values=None, desc=None):
|
||||||
|
|
||||||
if valid_values is None:
|
if valid_values is None:
|
||||||
valid_values = []
|
valid_values = []
|
||||||
|
@ -225,10 +241,7 @@ class InputParameter:
|
||||||
p.ast_node = ast_node
|
p.ast_node = ast_node
|
||||||
p.default_value = default_value
|
p.default_value = default_value
|
||||||
p.name = var_name
|
p.name = var_name
|
||||||
if short_desc is None:
|
p.desc = desc
|
||||||
p.shortDesc = var_name
|
|
||||||
else:
|
|
||||||
p.shortDesc = short_desc
|
|
||||||
p.varType = var_type
|
p.varType = var_type
|
||||||
p.valid_values = valid_values
|
p.valid_values = valid_values
|
||||||
return p
|
return p
|
||||||
|
@ -270,9 +283,9 @@ class ScriptCallback(object):
|
||||||
the build_object() method is exposed to CQ scripts, to allow them
|
the build_object() method is exposed to CQ scripts, to allow them
|
||||||
to return objects to the execution environment
|
to return objects to the execution environment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.outputObjects = []
|
self.outputObjects = []
|
||||||
|
self.debugObjects = []
|
||||||
|
|
||||||
def build_object(self, shape):
|
def build_object(self, shape):
|
||||||
"""
|
"""
|
||||||
|
@ -281,10 +294,15 @@ class ScriptCallback(object):
|
||||||
"""
|
"""
|
||||||
self.outputObjects.append(shape)
|
self.outputObjects.append(shape)
|
||||||
|
|
||||||
def describe_parameter(self,var, valid_values, short_desc):
|
def debug(self,obj,args={}):
|
||||||
"""
|
"""
|
||||||
Not yet implemented: allows a script to document
|
Debug print/output an object, with optional arguments.
|
||||||
extra metadata about the parameters
|
"""
|
||||||
|
self.debugObjects.append(DebugObject(obj,args))
|
||||||
|
|
||||||
|
def describe_parameter(self,var_data ):
|
||||||
|
"""
|
||||||
|
Do Nothing-- we parsed the ast ahead of exection to get what we need.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -297,7 +315,15 @@ class ScriptCallback(object):
|
||||||
def has_results(self):
|
def has_results(self):
|
||||||
return len(self.outputObjects) > 0
|
return len(self.outputObjects) > 0
|
||||||
|
|
||||||
|
class DebugObject(object):
|
||||||
|
"""
|
||||||
|
Represents a request to debug an object
|
||||||
|
Object is the type of object we want to debug
|
||||||
|
args are parameters for use during debuging ( for example, color, tranparency )
|
||||||
|
"""
|
||||||
|
def __init__(self,object,args):
|
||||||
|
self.args = args
|
||||||
|
self.object = object
|
||||||
|
|
||||||
class InvalidParameterError(Exception):
|
class InvalidParameterError(Exception):
|
||||||
"""
|
"""
|
||||||
|
@ -371,6 +397,30 @@ class EnvironmentBuilder(object):
|
||||||
def build(self):
|
def build(self):
|
||||||
return self.env
|
return self.env
|
||||||
|
|
||||||
|
class ParameterDescriptionFinder(ast.NodeTransformer):
|
||||||
|
"""
|
||||||
|
Visits a parse tree, looking for function calls to describe_parameter(var, description )
|
||||||
|
"""
|
||||||
|
def __init__(self, cq_model):
|
||||||
|
self.cqModel = cq_model
|
||||||
|
|
||||||
|
def visit_Call(self,node):
|
||||||
|
"""
|
||||||
|
Called when we see a function call. Is it describe_parameter?
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if node.func.id == 'describe_parameter':
|
||||||
|
#looks like we have a call to our function.
|
||||||
|
#first parameter is the variable,
|
||||||
|
#second is the description
|
||||||
|
varname = node.args[0].id
|
||||||
|
desc = node.args[1].s
|
||||||
|
self.cqModel.add_parameter_description(varname,desc)
|
||||||
|
|
||||||
|
except:
|
||||||
|
print "Unable to handle function call"
|
||||||
|
pass
|
||||||
|
return node
|
||||||
|
|
||||||
class ConstantAssignmentFinder(ast.NodeTransformer):
|
class ConstantAssignmentFinder(ast.NodeTransformer):
|
||||||
"""
|
"""
|
||||||
|
@ -381,9 +431,6 @@ class ConstantAssignmentFinder(ast.NodeTransformer):
|
||||||
self.cqModel = cq_model
|
self.cqModel = cq_model
|
||||||
|
|
||||||
def handle_assignment(self, var_name, value_node):
|
def handle_assignment(self, var_name, value_node):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
if type(value_node) == ast.Num:
|
if type(value_node) == ast.Num:
|
||||||
|
|
|
@ -184,9 +184,23 @@ class Shape(object):
|
||||||
def isValid(self):
|
def isValid(self):
|
||||||
return self.wrapped.isValid()
|
return self.wrapped.isValid()
|
||||||
|
|
||||||
def BoundingBox(self):
|
def BoundingBox(self, tolerance=0.1):
|
||||||
|
self.wrapped.tessellate(tolerance)
|
||||||
return BoundBox(self.wrapped.BoundBox)
|
return BoundBox(self.wrapped.BoundBox)
|
||||||
|
|
||||||
|
def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)):
|
||||||
|
if mirrorPlane == "XY" or mirrorPlane== "YX":
|
||||||
|
mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 0, 1)
|
||||||
|
elif mirrorPlane == "XZ" or mirrorPlane == "ZX":
|
||||||
|
mirrorPlaneNormalVector = FreeCAD.Base.Vector(0, 1, 0)
|
||||||
|
elif mirrorPlane == "YZ" or mirrorPlane == "ZY":
|
||||||
|
mirrorPlaneNormalVector = FreeCAD.Base.Vector(1, 0, 0)
|
||||||
|
|
||||||
|
if type(basePointVector) == tuple:
|
||||||
|
basePointVector = Vector(basePointVector)
|
||||||
|
|
||||||
|
return Shape.cast(self.wrapped.mirror(basePointVector.wrapped, mirrorPlaneNormalVector))
|
||||||
|
|
||||||
def Center(self):
|
def Center(self):
|
||||||
# A Part.Shape object doesn't have the CenterOfMass function, but it's wrapped Solid(s) does
|
# A Part.Shape object doesn't have the CenterOfMass function, but it's wrapped Solid(s) does
|
||||||
if isinstance(self.wrapped, FreeCADPart.Shape):
|
if isinstance(self.wrapped, FreeCADPart.Shape):
|
||||||
|
@ -223,8 +237,8 @@ class Shape(object):
|
||||||
|
|
||||||
:param objects: a list of objects with mass
|
:param objects: a list of objects with mass
|
||||||
"""
|
"""
|
||||||
total_mass = sum(o.wrapped.Mass for o in objects)
|
total_mass = sum(Shape.computeMass(o) for o in objects)
|
||||||
weighted_centers = [o.wrapped.CenterOfMass.multiply(o.wrapped.Mass) for o in objects]
|
weighted_centers = [o.wrapped.CenterOfMass.multiply(Shape.computeMass(o)) for o in objects]
|
||||||
|
|
||||||
sum_wc = weighted_centers[0]
|
sum_wc = weighted_centers[0]
|
||||||
for wc in weighted_centers[1:] :
|
for wc in weighted_centers[1:] :
|
||||||
|
@ -232,6 +246,17 @@ class Shape(object):
|
||||||
|
|
||||||
return Vector(sum_wc.multiply(1./total_mass))
|
return Vector(sum_wc.multiply(1./total_mass))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def computeMass(object):
|
||||||
|
"""
|
||||||
|
Calculates the 'mass' of an object. in FreeCAD < 15, all objects had a mass.
|
||||||
|
in FreeCAD >=15, faces no longer have mass, but instead have area.
|
||||||
|
"""
|
||||||
|
if object.wrapped.ShapeType == 'Face':
|
||||||
|
return object.wrapped.Area
|
||||||
|
else:
|
||||||
|
return object.wrapped.Mass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def CombinedCenterOfBoundBox(objects):
|
def CombinedCenterOfBoundBox(objects):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -32,10 +32,26 @@ CQGI compliant containers provide an execution environment for scripts. The envi
|
||||||
|
|
||||||
* the cadquery library is automatically imported as 'cq'.
|
* the cadquery library is automatically imported as 'cq'.
|
||||||
* the :py:meth:`cadquery.cqgi.ScriptCallback.build_object()` method is defined that should be used to export a shape to the execution environment
|
* the :py:meth:`cadquery.cqgi.ScriptCallback.build_object()` method is defined that should be used to export a shape to the execution environment
|
||||||
|
* the :py:meth:`cadquery.cqgi.ScriptCallBack.debug()` method is defined, which can be used by scripts to debug model output during execution.
|
||||||
|
|
||||||
Scripts must call build_output at least once. Invoking build_object more than once will send multiple objects to
|
Scripts must call build_output at least once. Invoking build_object more than once will send multiple objects to
|
||||||
the container. An error will occur if the script does not return an object using the build_object() method.
|
the container. An error will occur if the script does not return an object using the build_object() method.
|
||||||
|
|
||||||
|
This CQGI compliant script produces a cube with a circle on top, and displays a workplane as well as an intermediate circle as debug output::
|
||||||
|
|
||||||
|
base_cube = cq.Workplane('XY').rect(1.0,1.0).extrude(1.0)
|
||||||
|
top_of_cube_plane = base_cube.faces(">Z").workplane()
|
||||||
|
debug(top_of_cube_plane, { 'color': 'yellow', } )
|
||||||
|
debug(top_of_cube_plane.center, { 'color' : 'blue' } )
|
||||||
|
|
||||||
|
circle=top_of_cube_plane.circle(0.5)
|
||||||
|
debug(circle, { 'color': 'red' } )
|
||||||
|
|
||||||
|
build_object( circle.extrude(1.0) )
|
||||||
|
|
||||||
|
Note that importing cadquery is not required.
|
||||||
|
At the end of this script, one object will be displayed, in addition to a workplane, a point, and a circle
|
||||||
|
|
||||||
Future enhancements will include several other methods, used to provide more metadata for the execution environment:
|
Future enhancements will include several other methods, used to provide more metadata for the execution environment:
|
||||||
* :py:meth:`cadquery.cqgi.ScriptCallback.add_error()`, indicates an error with an input parameter
|
* :py:meth:`cadquery.cqgi.ScriptCallback.add_error()`, indicates an error with an input parameter
|
||||||
* :py:meth:`cadquery.cqgi.ScriptCallback.describe_parameter()`, provides extra information about a parameter in the script,
|
* :py:meth:`cadquery.cqgi.ScriptCallback.describe_parameter()`, provides extra information about a parameter in the script,
|
||||||
|
@ -54,10 +70,29 @@ run code like this::
|
||||||
|
|
||||||
The :py:meth:`cadquery.cqgi.parse()` method returns a :py:class:`cadquery.cqgi.CQModel` object.
|
The :py:meth:`cadquery.cqgi.parse()` method returns a :py:class:`cadquery.cqgi.CQModel` object.
|
||||||
|
|
||||||
|
The `metadata`p property of the object contains a `cadquery.cqgi.ScriptMetaData` object, which can be used to discover the
|
||||||
|
user parameters available. This is useful if the execution environment would like to present a GUI to allow the user to change the
|
||||||
|
model parameters. Typically, after collecting new values, the environment will supply them in the build() method.
|
||||||
|
|
||||||
|
This code will return a dictionary of parameter values in the model text SCRIPT::
|
||||||
|
|
||||||
|
parameters = cqgi.parse(SCRIPT).metadata.parameters
|
||||||
|
|
||||||
|
The dictionary you get back is a map where key is the parameter name, and value is an InputParameter object,
|
||||||
|
which has a name, type, and default value.
|
||||||
|
|
||||||
|
The type is an object which extends ParameterType-- you can use this to determine what kind of widget to render ( checkbox for boolean, for example ).
|
||||||
|
|
||||||
|
The parameter object also has a description, valid values, minimum, and maximum values, if the user has provided them using the
|
||||||
|
describe_parameter() method.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Calling :py:meth:`cadquery.cqgi.CQModel.build()` returns a :py:class:`cadquery.cqgi.BuildResult` object,
|
Calling :py:meth:`cadquery.cqgi.CQModel.build()` returns a :py:class:`cadquery.cqgi.BuildResult` object,
|
||||||
,which includes the script execution time, and a success flag.
|
,which includes the script execution time, and a success flag.
|
||||||
|
|
||||||
If the script was successful, the results property will include a list of results returned by the script.
|
If the script was successful, the results property will include a list of results returned by the script,
|
||||||
|
as well as any debug the script produced
|
||||||
|
|
||||||
If the script failed, the exception property contains the exception object.
|
If the script failed, the exception property contains the exception object.
|
||||||
|
|
||||||
|
@ -67,12 +102,16 @@ with new values::
|
||||||
from cadquery import cqgi
|
from cadquery import cqgi
|
||||||
|
|
||||||
user_script = ...
|
user_script = ...
|
||||||
build_result = cqgi.parse(user_script).build({ 'param': 2 } )
|
build_result = cqgi.parse(user_script).build(build_parameters={ 'param': 2 }, build_options={} )
|
||||||
|
|
||||||
If a parameter called 'param' is defined in the model, it will be assigned the value 2 before the script runs.
|
If a parameter called 'param' is defined in the model, it will be assigned the value 2 before the script runs.
|
||||||
An error will occur if a value is provided that is not defined in the model, or if the value provided cannot
|
An error will occur if a value is provided that is not defined in the model, or if the value provided cannot
|
||||||
be assigned to a variable with the given name.
|
be assigned to a variable with the given name.
|
||||||
|
|
||||||
|
build_options is used to set server-side settings like timeouts, tesselation tolerances, and other details about
|
||||||
|
how the model should be built.
|
||||||
|
|
||||||
|
|
||||||
More about script variables
|
More about script variables
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|
|
@ -312,6 +312,64 @@ introduce horizontal and vertical lines, which make for slightly easier coding.
|
||||||
* :py:meth:`Workplane`
|
* :py:meth:`Workplane`
|
||||||
* :py:meth:`Workplane.extrude`
|
* :py:meth:`Workplane.extrude`
|
||||||
|
|
||||||
|
Mirroring 3D Objects
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. cq_plot::
|
||||||
|
|
||||||
|
result0 = (cadquery.Workplane("XY")
|
||||||
|
.moveTo(10,0)
|
||||||
|
.lineTo(5,0)
|
||||||
|
.threePointArc((3.9393,0.4393),(3.5,1.5))
|
||||||
|
.threePointArc((3.0607,2.5607),(2,3))
|
||||||
|
.lineTo(1.5,3)
|
||||||
|
.threePointArc((0.4393,3.4393),(0,4.5))
|
||||||
|
.lineTo(0,13.5)
|
||||||
|
.threePointArc((0.4393,14.5607),(1.5,15))
|
||||||
|
.lineTo(28,15)
|
||||||
|
.lineTo(28,13.5)
|
||||||
|
.lineTo(24,13.5)
|
||||||
|
.lineTo(24,11.5)
|
||||||
|
.lineTo(27,11.5)
|
||||||
|
.lineTo(27,10)
|
||||||
|
.lineTo(22,10)
|
||||||
|
.lineTo(22,13.2)
|
||||||
|
.lineTo(14.5,13.2)
|
||||||
|
.lineTo(14.5,10)
|
||||||
|
.lineTo(12.5,10 )
|
||||||
|
.lineTo(12.5,13.2)
|
||||||
|
.lineTo(5.5,13.2)
|
||||||
|
.lineTo(5.5,2)
|
||||||
|
.threePointArc((5.793,1.293),(6.5,1))
|
||||||
|
.lineTo(10,1)
|
||||||
|
.close())
|
||||||
|
result = result0.extrude(100)
|
||||||
|
|
||||||
|
result = result.rotate((0, 0, 0),(1, 0, 0), 90)
|
||||||
|
|
||||||
|
result = result.translate(result.val().BoundingBox().center.multiply(-1))
|
||||||
|
|
||||||
|
mirXY_neg = result.mirror(mirrorPlane="XY", basePointVector=(0, 0, -30))
|
||||||
|
mirXY_pos = result.mirror(mirrorPlane="XY", basePointVector=(0, 0, 30))
|
||||||
|
mirZY_neg = result.mirror(mirrorPlane="ZY", basePointVector=(-30,0,0))
|
||||||
|
mirZY_pos = result.mirror(mirrorPlane="ZY", basePointVector=(30,0,0))
|
||||||
|
|
||||||
|
result = result.union(mirXY_neg).union(mirXY_pos).union(mirZY_neg).union(mirZY_pos)
|
||||||
|
|
||||||
|
build_object(result)
|
||||||
|
|
||||||
|
.. topic:: Api References
|
||||||
|
|
||||||
|
.. hlist::
|
||||||
|
:columns: 2
|
||||||
|
|
||||||
|
* :py:meth:`Workplane.moveTo`
|
||||||
|
* :py:meth:`Workplane.lineTo`
|
||||||
|
* :py:meth:`Workplane.threePointArc`
|
||||||
|
* :py:meth:`Workplane.extrude`
|
||||||
|
* :py:meth:`Workplane.mirror`
|
||||||
|
* :py:meth:`Workplane.union`
|
||||||
|
* :py:meth:`CQ.rotate`
|
||||||
|
|
||||||
Creating Workplanes on Faces
|
Creating Workplanes on Faces
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
|
@ -8,11 +8,11 @@ import unittest
|
||||||
#on py 2.7.x on win
|
#on py 2.7.x on win
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
|
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCadObjects))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCadObjects))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadQuery.TestCadQuery))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadQuery.TestCadQuery))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestExporters.TestExporters))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestExporters.TestExporters))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestImporters.TestImporters))
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestImporters.TestImporters))
|
||||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQGI.TestCQGI))
|
|
||||||
unittest.TextTestRunner().run(suite)
|
unittest.TextTestRunner().run(suite)
|
||||||
|
|
|
@ -11,12 +11,19 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
import os
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
|
#if we are building in travis, use the build number as the sub-minor version
|
||||||
|
version = '0.5-SNAPSHOT'
|
||||||
|
if 'TRAVIS_TAG' in os.environ.keys():
|
||||||
|
version= os.environ['TRAVIS_TAG']
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='cadquery',
|
name='cadquery',
|
||||||
version='0.4.0',
|
version=version,
|
||||||
url='https://github.com/dcowden/cadquery',
|
url='https://github.com/dcowden/cadquery',
|
||||||
license='Apache Public License 2.0',
|
license='Apache Public License 2.0',
|
||||||
author='David Cowden',
|
author='David Cowden',
|
||||||
|
|
|
@ -23,6 +23,18 @@ TESTSCRIPT = textwrap.dedent(
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TEST_DEBUG_SCRIPT = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
height=2.0
|
||||||
|
width=3.0
|
||||||
|
(a,b) = (1.0,1.0)
|
||||||
|
foo="bar"
|
||||||
|
debug(foo, { "color": 'yellow' } )
|
||||||
|
result = "%s|%s|%s|%s" % ( str(height) , str(width) , foo , str(a) )
|
||||||
|
build_object(result)
|
||||||
|
debug(height )
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
class TestCQGI(BaseTest):
|
class TestCQGI(BaseTest):
|
||||||
def test_parser(self):
|
def test_parser(self):
|
||||||
|
@ -31,6 +43,16 @@ class TestCQGI(BaseTest):
|
||||||
|
|
||||||
self.assertEquals(set(metadata.parameters.keys()), {'height', 'width', 'a', 'b', 'foo'})
|
self.assertEquals(set(metadata.parameters.keys()), {'height', 'width', 'a', 'b', 'foo'})
|
||||||
|
|
||||||
|
def test_build_with_debug(self):
|
||||||
|
model = cqgi.CQModel(TEST_DEBUG_SCRIPT)
|
||||||
|
result = model.build()
|
||||||
|
debugItems = result.debugObjects
|
||||||
|
self.assertTrue(len(debugItems) == 2)
|
||||||
|
self.assertTrue( debugItems[0].object == "bar" )
|
||||||
|
self.assertTrue( debugItems[0].args == { "color":'yellow' } )
|
||||||
|
self.assertTrue( debugItems[1].object == 2.0 )
|
||||||
|
self.assertTrue( debugItems[1].args == {} )
|
||||||
|
|
||||||
def test_build_with_empty_params(self):
|
def test_build_with_empty_params(self):
|
||||||
model = cqgi.CQModel(TESTSCRIPT)
|
model = cqgi.CQModel(TESTSCRIPT)
|
||||||
result = model.build()
|
result = model.build()
|
||||||
|
@ -44,6 +66,30 @@ class TestCQGI(BaseTest):
|
||||||
result = model.build({'height': 3.0})
|
result = model.build({'height': 3.0})
|
||||||
self.assertTrue(result.results[0] == "3.0|3.0|bar|1.0")
|
self.assertTrue(result.results[0] == "3.0|3.0|bar|1.0")
|
||||||
|
|
||||||
|
def test_describe_parameters(self):
|
||||||
|
script = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
a = 2.0
|
||||||
|
describe_parameter(a,'FirstLetter')
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
model = cqgi.CQModel(script)
|
||||||
|
a_param = model.metadata.parameters['a']
|
||||||
|
self.assertTrue(a_param.default_value == 2.0)
|
||||||
|
self.assertTrue(a_param.desc == 'FirstLetter')
|
||||||
|
self.assertTrue(a_param.varType == cqgi.NumberParameterType )
|
||||||
|
|
||||||
|
def test_describe_parameter_invalid_doesnt_fail_script(self):
|
||||||
|
script = textwrap.dedent(
|
||||||
|
"""
|
||||||
|
a = 2.0
|
||||||
|
describe_parameter(a, 2 - 1 )
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
model = cqgi.CQModel(script)
|
||||||
|
a_param = model.metadata.parameters['a']
|
||||||
|
self.assertTrue(a_param.name == 'a' )
|
||||||
|
|
||||||
def test_build_with_exception(self):
|
def test_build_with_exception(self):
|
||||||
badscript = textwrap.dedent(
|
badscript = textwrap.dedent(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -550,6 +550,43 @@ class TestCadQuery(BaseTest):
|
||||||
|
|
||||||
self.assertEqual(10,currentS.faces().size())
|
self.assertEqual(10,currentS.faces().size())
|
||||||
|
|
||||||
|
def testBoundingBox(self):
|
||||||
|
"""
|
||||||
|
Tests the boudingbox center of a model
|
||||||
|
"""
|
||||||
|
result0 = (Workplane("XY")
|
||||||
|
.moveTo(10,0)
|
||||||
|
.lineTo(5,0)
|
||||||
|
.threePointArc((3.9393,0.4393),(3.5,1.5))
|
||||||
|
.threePointArc((3.0607,2.5607),(2,3))
|
||||||
|
.lineTo(1.5,3)
|
||||||
|
.threePointArc((0.4393,3.4393),(0,4.5))
|
||||||
|
.lineTo(0,13.5)
|
||||||
|
.threePointArc((0.4393,14.5607),(1.5,15))
|
||||||
|
.lineTo(28,15)
|
||||||
|
.lineTo(28,13.5)
|
||||||
|
.lineTo(24,13.5)
|
||||||
|
.lineTo(24,11.5)
|
||||||
|
.lineTo(27,11.5)
|
||||||
|
.lineTo(27,10)
|
||||||
|
.lineTo(22,10)
|
||||||
|
.lineTo(22,13.2)
|
||||||
|
.lineTo(14.5,13.2)
|
||||||
|
.lineTo(14.5,10)
|
||||||
|
.lineTo(12.5,10 )
|
||||||
|
.lineTo(12.5,13.2)
|
||||||
|
.lineTo(5.5,13.2)
|
||||||
|
.lineTo(5.5,2)
|
||||||
|
.threePointArc((5.793,1.293),(6.5,1))
|
||||||
|
.lineTo(10,1)
|
||||||
|
.close())
|
||||||
|
result = result0.extrude(100)
|
||||||
|
bb_center = result.val().BoundingBox().center
|
||||||
|
self.saveModel(result)
|
||||||
|
self.assertAlmostEqual(14.0, bb_center.x, 3)
|
||||||
|
self.assertAlmostEqual(7.5, bb_center.y, 3)
|
||||||
|
self.assertAlmostEqual(50.0, bb_center.z, 3)
|
||||||
|
|
||||||
def testCutThroughAll(self):
|
def testCutThroughAll(self):
|
||||||
"""
|
"""
|
||||||
Tests a model that uses more than one workplane
|
Tests a model that uses more than one workplane
|
||||||
|
|
Loading…
Reference in New Issue
Block a user