Updated the CadQuery library for the addition of multi-sweep.

This commit is contained in:
Jeremy Mack Wright 2018-04-10 09:53:56 -04:00
parent 00e6fb8d5c
commit 1194b9ee42
11 changed files with 180 additions and 23 deletions

View File

@ -0,0 +1,7 @@
[run]
include = cadquery/*
omit = cadquery/cq_directive.py
[xml]
output = coverage.xml

View File

@ -4,7 +4,6 @@ matrix:
include: include:
- python: 2.7 - python: 2.7
- python: 3.6 - python: 3.6
env: FREECAD_LIB="$HOME/miniconda/envs/freecad_cq3/lib/"
before_install: before_install:
- if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then - if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then
@ -15,7 +14,7 @@ before_install:
conda config --set always_yes yes --set changeps1 no; conda config --set always_yes yes --set changeps1 no;
conda update -q conda; conda update -q conda;
conda info -a; 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; source ~/miniconda/bin/activate freecad_cq3;
else else
sudo add-apt-repository -y ppa:freecad-maintainers/freecad-stable; sudo add-apt-repository -y ppa:freecad-maintainers/freecad-stable;
@ -30,11 +29,14 @@ install:
- python setup.py install; - python setup.py install;
script: script:
- coverage run runtests.py
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then - if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
coverage run --source=cadquery --omit=cadquery/cq_directive.py ./runtests.py;
travis-sphinx build --nowarn --source=doc; travis-sphinx build --nowarn --source=doc;
fi fi
- python runtests.py
after_success:
- coverage xml
- codecov -X gcov --file coverage.xml
after_script: after_script:
- coveralls - coveralls
@ -44,6 +46,7 @@ branches:
except: except:
- pythonocc - pythonocc
- 2_0_branch - 2_0_branch
deploy: deploy:
provider: pypi provider: pypi
user: dcowden user: dcowden

View File

@ -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) [![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) [![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) [![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) [![License](https://img.shields.io/badge/license-Apache2-blue.svg)](https://github.com/dcowden/cadquery/blob/master/LICENSE)

View File

@ -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

View File

@ -2073,17 +2073,20 @@ class Workplane(CQ):
if clean: newS = newS.clean() if clean: newS = newS.clean()
return newS 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. 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 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 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 :param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
:return: a CQ object with the resulting solid selected. :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: if combine:
newS = self._combineWithBase(r) newS = self._combineWithBase(r)
else: else:
@ -2397,11 +2400,14 @@ class Workplane(CQ):
return Compound.makeCompound(toFuse) 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. Makes a swept solid from an existing set of pending wires.
:param path: A wire along which the pending wires will be swept :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 :return:a FreeCAD solid, suitable for boolean operations
""" """
@ -2413,9 +2419,21 @@ class Workplane(CQ):
self.ctx.pendingWires = [] # now all of the wires have been used to create an extrusion self.ctx.pendingWires = [] # now all of the wires have been used to create an extrusion
toFuse = [] toFuse = []
if not sweepAlongWires:
for ws in wireSets: for ws in wireSets:
thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid, isFrenet) thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid, isFrenet)
toFuse.append(thisObj) 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) return Compound.makeCompound(toFuse)

View File

@ -18,6 +18,7 @@
""" """
import os import os
import sys import sys
import glob
#FreeCAD has crudified the stdout stream with a bunch of STEP output #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): if _PATH and os.path.exists(_PATH):
return _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'): if sys.platform.startswith('linux'):
# Make some dangerous assumptions... # Make some dangerous assumptions...
for _PATH in [ for _PATH in [

View File

@ -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)))

View File

@ -5,3 +5,4 @@ coverage
coveralls coveralls
pyparsing pyparsing
mock mock
codecov

View File

@ -12,6 +12,7 @@
# 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 import os
import codecs
from setuptools import setup from setuptools import setup
@ -29,7 +30,7 @@ setup(
author='David Cowden', author='David Cowden',
author_email='dave.cowden@gmail.com', author_email='dave.cowden@gmail.com',
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=codecs.open('README.md', 'rb', 'UTF-8').read(),
packages=['cadquery','cadquery.contrib','cadquery.freecad_impl','cadquery.plugins','tests'], packages=['cadquery','cadquery.contrib','cadquery.freecad_impl','cadquery.plugins','tests'],
install_requires=['pyparsing'], install_requires=['pyparsing'],
include_package_data=True, include_package_data=True,

View File

@ -3,7 +3,7 @@
""" """
#system modules #system modules
import math,sys,os.path,time import math,os.path,time,tempfile
#my modules #my modules
from cadquery import * from cadquery import *
@ -19,11 +19,7 @@ from tests import (
from cadquery.freecad_impl import suppress_stdout_stderr from cadquery.freecad_impl import suppress_stdout_stderr
#where unit test output will be saved #where unit test output will be saved
import sys OUTDIR = tempfile.gettempdir()
if sys.platform.startswith("win"):
OUTDIR = "c:/temp"
else:
OUTDIR = "/tmp"
SUMMARY_FILE = os.path.join(OUTDIR,"testSummary.html") SUMMARY_FILE = os.path.join(OUTDIR,"testSummary.html")
SUMMARY_TEMPLATE="""<html> SUMMARY_TEMPLATE="""<html>
@ -449,6 +445,54 @@ class TestCadQuery(BaseTest):
self.assertEqual(3, result.faces().size()) self.assertEqual(3, result.faces().size())
self.assertEqual(3, result.edges().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): def testTwistExtrude(self):
""" """
Tests extrusion while twisting through an angle. Tests extrusion while twisting through an angle.

View File

@ -2,7 +2,7 @@
Tests file importers such as STEP Tests file importers such as STEP
""" """
#core modules #core modules
import io import tempfile
from cadquery import * from cadquery import *
from cadquery import exporters from cadquery import exporters
@ -11,12 +11,7 @@ from cadquery.freecad_impl import suppress_stdout_stderr
from tests import BaseTest from tests import BaseTest
#where unit test output will be saved #where unit test output will be saved
import sys OUTDIR = tempfile.gettempdir()
if sys.platform.startswith("win"):
OUTDIR = "c:/temp"
else:
OUTDIR = "/tmp"
class TestImporters(BaseTest): class TestImporters(BaseTest):
def importBox(self, importType, fileName): def importBox(self, importType, fileName):