From 1194b9ee423aafa226ab556cd992bba9b599db8b Mon Sep 17 00:00:00 2001 From: Jeremy Mack Wright Date: Tue, 10 Apr 2018 09:53:56 -0400 Subject: [PATCH] Updated the CadQuery library for the addition of multi-sweep. --- Libs/cadquery/.coveragerc | 7 +++ Libs/cadquery/.travis.yml | 11 ++-- Libs/cadquery/README.md | 1 + Libs/cadquery/appveyor.yml | 26 +++++++++ Libs/cadquery/cadquery/cq.py | 28 ++++++++-- .../cadquery/freecad_impl/__init__.py | 14 +++++ .../Ex034_Sweep_Along_List_Of_Wires.py | 47 ++++++++++++++++ Libs/cadquery/requirements-dev.txt | 1 + Libs/cadquery/setup.py | 3 +- Libs/cadquery/tests/TestCadQuery.py | 56 +++++++++++++++++-- Libs/cadquery/tests/TestImporters.py | 9 +-- 11 files changed, 180 insertions(+), 23 deletions(-) create mode 100644 Libs/cadquery/.coveragerc create mode 100644 Libs/cadquery/appveyor.yml create mode 100644 Libs/cadquery/examples/FreeCAD/Ex034_Sweep_Along_List_Of_Wires.py diff --git a/Libs/cadquery/.coveragerc b/Libs/cadquery/.coveragerc new file mode 100644 index 0000000..9da08a7 --- /dev/null +++ b/Libs/cadquery/.coveragerc @@ -0,0 +1,7 @@ +[run] +include = cadquery/* +omit = cadquery/cq_directive.py + + +[xml] +output = coverage.xml \ No newline at end of file diff --git a/Libs/cadquery/.travis.yml b/Libs/cadquery/.travis.yml index 8942e1f..d61778e 100644 --- a/Libs/cadquery/.travis.yml +++ b/Libs/cadquery/.travis.yml @@ -4,7 +4,6 @@ matrix: include: - python: 2.7 - python: 3.6 - env: FREECAD_LIB="$HOME/miniconda/envs/freecad_cq3/lib/" before_install: - if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then @@ -15,7 +14,7 @@ before_install: conda config --set always_yes yes --set changeps1 no; conda update -q conda; conda info -a; - conda create -y -q -n freecad_cq3 -c freecad -c conda-forge freecad=0.17=py36_11 occt=7.2.0=occt7.2.0_0 python=3.6 pyparsing conda mock; + conda create -y -q -n freecad_cq3 -c freecad -c freecad/label/broken -c conda-forge freecad=0.17=py36_11 occt=7.2.0=occt7.2.0_0 python=3.6 pyparsing conda mock coverage codecov; source ~/miniconda/bin/activate freecad_cq3; else sudo add-apt-repository -y ppa:freecad-maintainers/freecad-stable; @@ -30,11 +29,14 @@ install: - python setup.py install; script: +- coverage run runtests.py - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then - coverage run --source=cadquery --omit=cadquery/cq_directive.py ./runtests.py; travis-sphinx build --nowarn --source=doc; fi -- python runtests.py + +after_success: +- coverage xml +- codecov -X gcov --file coverage.xml after_script: - coveralls @@ -44,6 +46,7 @@ branches: except: - pythonocc - 2_0_branch + deploy: provider: pypi user: dcowden diff --git a/Libs/cadquery/README.md b/Libs/cadquery/README.md index 3939307..af897e6 100644 --- a/Libs/cadquery/README.md +++ b/Libs/cadquery/README.md @@ -6,6 +6,7 @@ What is a CadQuery? ======================================== [![Travis Build Status](https://travis-ci.org/dcowden/cadquery.svg?branch=master)](https://travis-ci.org/dcowden/cadquery?branch=master) +[![Build status](https://ci.appveyor.com/api/projects/status/c7u4yjl8xxlokrw0/branch/master?svg=true)](https://ci.appveyor.com/project/jmwright/cadquery/branch/master) [![Coverage Status](https://coveralls.io/repos/github/dcowden/cadquery/badge.svg?branch=master)](https://coveralls.io/github/dcowden/cadquery?branch=master) [![GitHub version](https://d25lcipzij17d.cloudfront.net/badge.svg?id=gh&type=6&v=1.1.0&x2=0)](https://github.com/dcowden/cadquery/releases/tag/v1.1.0) [![License](https://img.shields.io/badge/license-Apache2-blue.svg)](https://github.com/dcowden/cadquery/blob/master/LICENSE) diff --git a/Libs/cadquery/appveyor.yml b/Libs/cadquery/appveyor.yml new file mode 100644 index 0000000..892835b --- /dev/null +++ b/Libs/cadquery/appveyor.yml @@ -0,0 +1,26 @@ +shallow_clone: true + +platform: + - x64 + +environment: + matrix: + - PYTHON_VERSION: 3.6 + MINICONDA_DIRNAME: C:\Miniconda36-x64 + +install: + - set "PATH=%MINICONDA_DIRNAME%;%MINICONDA_DIRNAME%\\Scripts;%PATH%" + - conda config --set always_yes yes + - conda update -q conda + - conda create --quiet --name cqtest -c freecad -c freecad/label/broken -c conda-forge python=%PYTHON_VERSION% freecad=0.17=py36_vc14_13 occt=7.2.0 python=3.6 pyparsing mock coverage codecov + - activate cqtest + - python setup.py install + +build: false + +test_script: + - coverage run runtests.py + +on_success: + - coverage xml + - codecov -X gcov --file coverage.xml diff --git a/Libs/cadquery/cadquery/cq.py b/Libs/cadquery/cadquery/cq.py index 94d155e..943f1fc 100644 --- a/Libs/cadquery/cadquery/cq.py +++ b/Libs/cadquery/cadquery/cq.py @@ -2073,17 +2073,20 @@ class Workplane(CQ): if clean: newS = newS.clean() return newS - def sweep(self, path, makeSolid=True, isFrenet=False, combine=True, clean=True): + def sweep(self, path, sweepAlongWires=False, makeSolid=True, isFrenet=False, combine=True, clean=True): """ Use all un-extruded wires in the parent chain to create a swept solid. :param path: A wire along which the pending wires will be swept + :param boolean sweepAlongWires: + False to create mutliple swept from wires on the chain along path + True to create only one solid swept along path with shape following the list of wires on the chain :param boolean combine: True to combine the resulting solid with parent solids if found. :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape :return: a CQ object with the resulting solid selected. """ - r = self._sweep(path.wire(), makeSolid, isFrenet) # returns a Solid (or a compound if there were multiple) + r = self._sweep(path.wire(), sweepAlongWires, makeSolid, isFrenet) # returns a Solid (or a compound if there were multiple) if combine: newS = self._combineWithBase(r) else: @@ -2397,11 +2400,14 @@ class Workplane(CQ): return Compound.makeCompound(toFuse) - def _sweep(self, path, makeSolid=True, isFrenet=False): + def _sweep(self, path, sweepAlongWires=False, makeSolid=True, isFrenet=False): """ Makes a swept solid from an existing set of pending wires. :param path: A wire along which the pending wires will be swept + :param boolean sweepAlongWires: + False to create mutliple swept from wires on the chain along path + True to create only one solid swept along path with shape following the list of wires on the chain :return:a FreeCAD solid, suitable for boolean operations """ @@ -2413,8 +2419,20 @@ class Workplane(CQ): self.ctx.pendingWires = [] # now all of the wires have been used to create an extrusion toFuse = [] - for ws in wireSets: - thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid, isFrenet) + if not sweepAlongWires: + for ws in wireSets: + thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid, isFrenet) + toFuse.append(thisObj) + else: + section = [] + for ws in wireSets: + for i in range(0, len(ws)): + section.append(ws[i]) + + # implementation + outW = Wire(section[0].wrapped) + inW = section[1:] + thisObj = Solid.sweep(outW, inW, path.val(), makeSolid, isFrenet) toFuse.append(thisObj) return Compound.makeCompound(toFuse) diff --git a/Libs/cadquery/cadquery/freecad_impl/__init__.py b/Libs/cadquery/cadquery/freecad_impl/__init__.py index 2d2cbe8..e356c21 100644 --- a/Libs/cadquery/cadquery/freecad_impl/__init__.py +++ b/Libs/cadquery/cadquery/freecad_impl/__init__.py @@ -18,6 +18,7 @@ """ import os import sys +import glob #FreeCAD has crudified the stdout stream with a bunch of STEP output @@ -60,6 +61,19 @@ def _fc_path(): if _PATH and os.path.exists(_PATH): return _PATH + # Try to guess if using Anaconda + if 'env' in sys.prefix: + if sys.platform.startswith('linux'): + _PATH = os.path.join(sys.prefix,'lib') + # return PATH if FreeCAD.[so,pyd] is present + if len(glob.glob(os.path.join(_PATH,'FreeCAD.so'))) > 0: + return _PATH + elif sys.platform.startswith('win'): + _PATH = os.path.join(sys.prefix,'Library','bin') + # return PATH if FreeCAD.[so,pyd] is present + if len(glob.glob(os.path.join(_PATH,'FreeCAD.pyd'))) > 0: + return _PATH + if sys.platform.startswith('linux'): # Make some dangerous assumptions... for _PATH in [ diff --git a/Libs/cadquery/examples/FreeCAD/Ex034_Sweep_Along_List_Of_Wires.py b/Libs/cadquery/examples/FreeCAD/Ex034_Sweep_Along_List_Of_Wires.py new file mode 100644 index 0000000..6ead2c8 --- /dev/null +++ b/Libs/cadquery/examples/FreeCAD/Ex034_Sweep_Along_List_Of_Wires.py @@ -0,0 +1,47 @@ +import cadquery as cq + +# X axis line length 20.0 +path = cq.Workplane("XZ").moveTo(-10, 0).lineTo(10, 0) + +# Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0 +defaultSweep = cq.Workplane("YZ").workplane(offset=-10.0).circle(2.0). \ + workplane(offset=10.0).circle(1.0). \ + workplane(offset=10.0).circle(2.0).sweep(path, sweepAlongWires=True) + +# We can sweep thrue different shapes +recttocircleSweep = cq.Workplane("YZ").workplane(offset=-10.0).rect(2.0, 2.0). \ + workplane(offset=8.0).circle(1.0).workplane(offset=4.0).circle(1.0). \ + workplane(offset=8.0).rect(2.0, 2.0).sweep(path, sweepAlongWires=True) + +circletorectSweep = cq.Workplane("YZ").workplane(offset=-10.0).circle(1.0). \ + workplane(offset=7.0).rect(2.0, 2.0).workplane(offset=6.0).rect(2.0, 2.0). \ + workplane(offset=7.0).circle(1.0).sweep(path, sweepAlongWires=True) + + +# Placement of the Shape is important otherwise could produce unexpected shape +specialSweep = cq.Workplane("YZ").circle(1.0).workplane(offset=10.0).rect(2.0, 2.0). \ + sweep(path, sweepAlongWires=True) + +# Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0 +path = cq.Workplane("XZ").moveTo(-5, 4).lineTo(0, 4). \ + threePointArc((4, 0), (0, -4)).lineTo(-5, -4) + +# Placement of different shapes should follow the path +# cylinder r=1.5 along first line +# then sweep allong arc from r=1.5 to r=1.0 +# then cylinder r=1.0 along last line +arcSweep = cq.Workplane("YZ").workplane(offset=-5).moveTo(0, 4).circle(1.5). \ + workplane(offset=5).circle(1.5). \ + moveTo(0, -8).circle(1.0). \ + workplane(offset=-5).circle(1.0). \ + sweep(path, sweepAlongWires=True) + + +# Translate the resulting solids so that they do not overlap and display them left to right +show_object(defaultSweep) +show_object(circletorectSweep.translate((0, 5, 0))) +show_object(recttocircleSweep.translate((0, 10, 0))) +show_object(specialSweep.translate((0, 15, 0))) +show_object(arcSweep.translate((0, -5, 0))) + + diff --git a/Libs/cadquery/requirements-dev.txt b/Libs/cadquery/requirements-dev.txt index ea86d57..e5b3ec9 100644 --- a/Libs/cadquery/requirements-dev.txt +++ b/Libs/cadquery/requirements-dev.txt @@ -5,3 +5,4 @@ coverage coveralls pyparsing mock +codecov diff --git a/Libs/cadquery/setup.py b/Libs/cadquery/setup.py index 790980e..93f75c5 100644 --- a/Libs/cadquery/setup.py +++ b/Libs/cadquery/setup.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +import codecs from setuptools import setup @@ -29,7 +30,7 @@ setup( author='David Cowden', author_email='dave.cowden@gmail.com', description='CadQuery is a parametric scripting language for creating and traversing CAD models', - long_description=open('README.md').read(), + long_description=codecs.open('README.md', 'rb', 'UTF-8').read(), packages=['cadquery','cadquery.contrib','cadquery.freecad_impl','cadquery.plugins','tests'], install_requires=['pyparsing'], include_package_data=True, diff --git a/Libs/cadquery/tests/TestCadQuery.py b/Libs/cadquery/tests/TestCadQuery.py index 743f8c4..db329dc 100644 --- a/Libs/cadquery/tests/TestCadQuery.py +++ b/Libs/cadquery/tests/TestCadQuery.py @@ -3,7 +3,7 @@ """ #system modules -import math,sys,os.path,time +import math,os.path,time,tempfile #my modules from cadquery import * @@ -19,11 +19,7 @@ from tests import ( from cadquery.freecad_impl import suppress_stdout_stderr #where unit test output will be saved -import sys -if sys.platform.startswith("win"): - OUTDIR = "c:/temp" -else: - OUTDIR = "/tmp" +OUTDIR = tempfile.gettempdir() SUMMARY_FILE = os.path.join(OUTDIR,"testSummary.html") SUMMARY_TEMPLATE=""" @@ -449,6 +445,54 @@ class TestCadQuery(BaseTest): self.assertEqual(3, result.faces().size()) self.assertEqual(3, result.edges().size()) + def testSweepAlongListOfWires(self): + """ + Tests the operation of sweeping along a list of wire(s) along a path + """ + + # X axis line length 20.0 + path = Workplane("XZ").moveTo(-10, 0).lineTo(10, 0) + + # Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0 + defaultSweep = Workplane("YZ").workplane(offset=-10.0).circle(2.0). \ + workplane(offset=10.0).circle(1.0). \ + workplane(offset=10.0).circle(2.0).sweep(path, sweepAlongWires=True) + + # We can sweep thrue different shapes + recttocircleSweep = Workplane("YZ").workplane(offset=-10.0).rect(2.0, 2.0). \ + workplane(offset=8.0).circle(1.0).workplane(offset=4.0).circle(1.0). \ + workplane(offset=8.0).rect(2.0, 2.0).sweep(path, sweepAlongWires=True) + + circletorectSweep = Workplane("YZ").workplane(offset=-10.0).circle(1.0). \ + workplane(offset=7.0).rect(2.0, 2.0).workplane(offset=6.0).rect(2.0, 2.0). \ + workplane(offset=7.0).circle(1.0).sweep(path, sweepAlongWires=True) + + # Placement of the Shape is important otherwise could produce unexpected shape + specialSweep = Workplane("YZ").circle(1.0).workplane(offset=10.0).rect(2.0, 2.0). \ + sweep(path, sweepAlongWires=True) + + # Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0 + path = Workplane("XZ").moveTo(-5, 4).lineTo(0, 4). \ + threePointArc((4, 0), (0, -4)).lineTo(-5, -4) + + # Placement of different shapes should follow the path + # cylinder r=1.5 along first line + # then sweep allong arc from r=1.5 to r=1.0 + # then cylinder r=1.0 along last line + arcSweep = Workplane("YZ").workplane(offset=-5).moveTo(0, 4).circle(1.5). \ + workplane(offset=5).circle(1.5). \ + moveTo(0, -8).circle(1.0). \ + workplane(offset=-5).circle(1.0). \ + sweep(path, sweepAlongWires=True) + + # Test and saveModel + self.assertEqual(1, defaultSweep.solids().size()) + self.assertEqual(1, circletorectSweep.solids().size()) + self.assertEqual(1, recttocircleSweep.solids().size()) + self.assertEqual(1, specialSweep.solids().size()) + self.assertEqual(1, arcSweep.solids().size()) + self.saveModel(defaultSweep) + def testTwistExtrude(self): """ Tests extrusion while twisting through an angle. diff --git a/Libs/cadquery/tests/TestImporters.py b/Libs/cadquery/tests/TestImporters.py index ffc878a..da11e75 100644 --- a/Libs/cadquery/tests/TestImporters.py +++ b/Libs/cadquery/tests/TestImporters.py @@ -2,7 +2,7 @@ Tests file importers such as STEP """ #core modules -import io +import tempfile from cadquery import * from cadquery import exporters @@ -11,12 +11,7 @@ from cadquery.freecad_impl import suppress_stdout_stderr from tests import BaseTest #where unit test output will be saved -import sys -if sys.platform.startswith("win"): - OUTDIR = "c:/temp" -else: - OUTDIR = "/tmp" - +OUTDIR = tempfile.gettempdir() class TestImporters(BaseTest): def importBox(self, importType, fileName):