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

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)
[![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)

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

View File

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

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
pyparsing
mock
codecov

View File

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

View File

@ -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="""<html>
@ -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.

View File

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