From 7f0ef9386817930ff096a0e08c17c6e6b6d898ab Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Mon, 15 Jun 2015 15:59:28 -0400 Subject: [PATCH 1/2] Next small step in the CQ.py code cleanup. --- .coverage | Bin 7370 -> 7458 bytes cadquery/CQ.py | 22 +++++++-------- cadquery/freecad_impl/exporters.py | 1 + tests/TestCadQuery.py | 42 ++++++++++++++++++++++++++++- 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/.coverage b/.coverage index 612f7c075cb8c48b43e9ff8d2bf1c82431b18957..c551dc97b25709e683584dd60a2ebbe7303dbf42 100644 GIT binary patch delta 1977 zcmY+_i`&oj9mny#-tP}{OyxAXnIhy+G|V9;q}IEq+nr^aV=aeUBqdj&eksQ}q;6-T zh(u1ivusON!&*{FTSsbIb*<|{mnK%JeeYkb%l?4R_xgSw&)0Q*UhJ@{x4?X3UD&F*oML{8$hRV{t5rWw9YP#-`XDTVq>19oyrN@l5Q9o$;r5K3(^qaxLJ%Aw~IT(1Tm>(gbmfS(!JJQO$!r6iT;TJi9v}GiQ>el#Kgp; zL@qHm@jzl;Y5g>@T({GhNcrEYBr1z4qN+GsoFl4>TB5f2k*FgI#E-=VqM>Lc8jB{P zY006A4GY@luaVv13eod_HyJOMishwsE0s&R!Er;}lt~ZIgvZ9XxHBfkJ&}uhV{+y_ zHLv_oUU^Y0&6JnN!?7~f#=2Oai9eY)-x9x#r@mMJ)_hm&jy>_$cro^t>BlSaY8K!? zmf&ykM!Xqs#XDJyL%FQS`*Aovh=0b1@lkvnpM2NmM4ZgpoX+ZG5?P&!B9q7r&Jxw~ z5*b4cQS<*X>WX@zex9U(IR6ZoG8J)=XeOGA7NVtcWz}1YOT|w_89Zo)tUAbK-fiOY9bV#0z4dcv-wE_KO4J zP4Sj^Tf8FtNWPEJ=%Q#{j zHI5md8pn;#jg!VH;|t?!;|Gp8?j)zYQ^|8wcB(j4ooY@^r?ykqInOC@>N|~`3!SD; zbEk#V(z(QGFHeUgmaBk==64qoNJvvPG9Gj&aa&Q&Om37 zGuX)uac*>mIU}56XQVUAxy2ddjCF2v#yNL5? zI@6r#&J5=_&MarPGsl_h%yS-e9&$>Y`OX4ok+axYl6_*Sv&>oUtZ-I3k2sGytDM!& z8fUHZxU=5b;B0g@Ih&m=&Q_~{7ze{o)L z_Bt;)`~Y;;clq#5sv` z6V($n5;YUG5_J*;O7_xqcQhFascl`d0iVw)A+hTOJ7@1EV5UD+VJ$sJ7Kj(bjuk(1lmKVO;d2OjO z=SGug8qK14w2bp3KMLZa=oFo!FfNNOaYghDqF3~eKG81*#o!no#W5mEqBP2)JVwQ| zxI1RV%$OB(Vs6Zf`B51U#DlRg7RBOtCf39sf;Q&*fKP8IdU>7u^)g*a23CC(P-h=$@k(M&WKd7`B_U$heWmEYCPYn+Bp zlXn%}PUIgi9u|+NxTtq!vz#tfx1X}bGec70lGJu|jE&o3T#SziF)8kf>8bPVtn$67 z@`AW8m0lc6VreXk$Kvr=9xLL>csf?a>Qwzt@my4;>g!`es{TsW{PlPv-i$5rc5IDz z;?HRV+tLnp#;(|%*6`QZ6CcEfnfQBr6d$LZ?2pglKztboQMFF%%NK3L1>!={R$L<5XE)I4#0Dj zPBBeP7x##nVwRXK=7_mso|rEdi2FsQctAWP7K%mLP8W+M;!&|wEEA85<>_}%h!tX` zcuG7iR*5xYt$0p6FRH|gVx3qoHi(VlWwBYjA-0IO#8$CQY!^GkPO(eu5qrg_VxQPA z4v2%|kT@c~7DvUm;ydvlQLPL$;v_>xVw`N$HtHCs8ac-4Mt!4!k!zf7G%^|+O^l{S zp3%Z+X|y)-jW))GMqA_8#zjUu<6`3yqrK6==xB5@Iva&X7vl<}tI^HqZe*@CdKh8! zGWr_*j3T4I@q1%{G0+%f3^r~yh8V+);YP9X2jdo_+!$$$GR7F=jPb?)8^)c+G-J9k!@)TopBbMUUl@mtBgQwzx5hE!xKVA? zSca1vcM_+TQ`@QMErZu`Z?D-H#om_e&^ii^mlG@ z1~>zqLC#?3W@o4~%o*+!J0qMDr_?EPZgI+;kL%1n$VoUYp+Q1!!kG!@Bs5HD ll#rLuBB516eo|c2qo8E;$l`*siDOGEH|2Dwnv=Vz&X4n>AA|q^ diff --git a/cadquery/CQ.py b/cadquery/CQ.py index e0d8630..62fdbc9 100644 --- a/cadquery/CQ.py +++ b/cadquery/CQ.py @@ -20,6 +20,7 @@ import time, math from cadquery import * from cadquery import selectors +from cadquery import exporters class CQContext(object): @@ -389,7 +390,6 @@ class CQ(object): will return the same as:: CQ(obj).faces("+Z") - """ if self.parent: return self.parent @@ -643,7 +643,7 @@ class CQ(object): :type opts: dictionary, width and height :return: a string that contains SVG that represents this item. """ - return SVGexporter.getSVG(self.val().wrapped, opts) + return exporters.getSVG(self.val().wrapped, opts) def exportSvg(self, fileName): """ @@ -1093,6 +1093,15 @@ class Workplane(CQ): """ return self.line(0, distance, forConstruction) + def hLine(self, distance, forConstruction=False): + """ + Make a horizontal line from the current point the provided distance + + :param float distance: (x) distance from current point + :return: the Workplane object with the current point at the end of the new line + """ + return self.line(distance, 0, forConstruction) + def vLineTo(self, yCoord, forConstruction=False): """ Make a vertical line from the current point to the provided y coordinate. @@ -1119,15 +1128,6 @@ class Workplane(CQ): p = self._findFromPoint(True) return self.lineTo(xCoord, p.y, forConstruction) - def hLine(self, distance, forConstruction=False): - """ - Make a horizontal line from the current point the provided distance - - :param float distance: (x) distance from current point - :return: the Workplane object with the current point at the end of the new line - """ - return self.line(distance, 0, forConstruction) - #absolute move in current plane, not drawing def moveTo(self, x=0, y=0): """ diff --git a/cadquery/freecad_impl/exporters.py b/cadquery/freecad_impl/exporters.py index 6119200..8ba681b 100644 --- a/cadquery/freecad_impl/exporters.py +++ b/cadquery/freecad_impl/exporters.py @@ -249,6 +249,7 @@ def getPaths(freeCadSVG): else: return ([],[]) + def getSVG(shape,opts=None): """ Export a shape to SVG diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 65c2195..ca93d38 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -88,6 +88,18 @@ class TestCadQuery(BaseTest): self.assertEqual(12, len(r.Edges)) + def testToSVG(self): + """ + Tests to make sure that a CadQuery object is converted correctly to SVG + """ + r = Workplane('XY').rect(5, 5).extrude(5) + + r_str = r.toSvg() + + # Make sure that a couple of sections from the SVG output make sense + self.assertTrue(r_str.index('path d=" M 2.35965 -2.27987 L 4.0114 -3.23936 "') > 0) + self.assertTrue(r_str.index('line x1="30" y1="-30" x2="58" y2="-15" stroke-width="3"') > 0) + def testCubePlugin(self): """ Tests a plugin that combines cubes together with a base @@ -556,7 +568,9 @@ class TestCadQuery(BaseTest): self.saveModel(r) def test2DDrawing(self): - """Draw things like 2D lines and arcs, should be expanded later to include all 2D constructs""" + """ + Draw things like 2D lines and arcs, should be expanded later to include all 2D constructs + """ s = Workplane(Plane.XY()) r = s.lineTo(1.0, 0.0) \ .lineTo(1.0, 1.0) \ @@ -572,6 +586,32 @@ class TestCadQuery(BaseTest): self.assertEqual(1, r.wires().size()) + # Test the *LineTo functions + s = Workplane(Plane.XY()) + r = s.hLineTo(1.0).vLineTo(1.0).hLineTo(0.0).close() + + self.assertEqual(1, r.wire().size()) + self.assertEqual(4, r.edges().size()) + + # Test the *Line functions + s = Workplane(Plane.XY()) + r = s.hLine(1.0).vLine(1.0).hLine(-1.0).close() + + self.assertEqual(1, r.wire().size()) + self.assertEqual(4, r.edges().size()) + + # Test the move function + s = Workplane(Plane.XY()) + r = s.move(1.0, 1.0).hLine(1.0).vLine(1.0).hLine(-1.0).close() + + self.assertEqual(1, r.wire().size()) + self.assertEqual(4, r.edges().size()) + self.assertEqual((1.0, 1.0), + (r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0)))\ + .first().val().X, + r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0)))\ + .first().val().Y)) + def testOccBottle(self): """ Make the OCC bottle example. From f8989928a8247d27cc2a8261f1ead2e628ff0e30 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Tue, 16 Jun 2015 17:03:36 -0400 Subject: [PATCH 2/2] Next phase of cleaning up CQ.py. --- .coverage | Bin 7458 -> 7643 bytes cadquery/CQ.py | 6 ++-- tests/TestCadQuery.py | 69 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/.coverage b/.coverage index c551dc97b25709e683584dd60a2ebbe7303dbf42..b9068da9e624b33f1ffb445df2dab0ddb461085c 100644 GIT binary patch delta 1671 zcmX}rX|N7t9ES1k|NZc)NQ*WUr6z^6jmc8ybSULGr@K*s`|D(Y1qQlq#yFUd>u+j&J^nb+jCd0k$g zEAxiDF>lJ7^9T9E{88STKhB@zPxI&bi@Ysw&tKiqk|B(Nr`Or;Fz0 zCAE((wiIo|dE$IgB1**tqD-_E7mAJo(Mfa`UBqRgtGHZTA+8qJh#ulv(NpvheZ`HU zpBNwpib3LLF+|)VhKgZgxVTM>5VwmvL{W^c+TG$_ai16~#)xSVy1XO%o4N3%c4Te74yV=@v2xL7K%mU4Y61(5lh7~@s?Pw zA}hp7u}Z8KYsEWaomej_#d~6d*eKo?Tf|oJvG_!MCO#M2#Ft{b_*(1`JH;-sTl^$` z6Tgc+Vz1aI6dV}T3~B|31cwDj1xE*UgJQj)esFxyC^#uNC1@Np37Q7YgEND(g0q7b z!MQ=JpmoqDC<)4f_QAzLhad-?f=h$WLD%5&pj&Wd&^_o8TpL^$^bC3hy@Tt6K0)8$ zhTz6x&_5Uu3=9SZgM(Xwp~0|VcrYTkJ-D+fGZ-0+3hoNV1Y?77!2`kgU_$UvFfn*E zcr2I{JP|w9*MbGX>%p7Bl3;1DELa|_ z3RVYef_1_ApfY$jcrVxxYz#I9n}ZL7Ey31cZ}3;}cW{7_u{qEjWDYhpO)YbXscjB5 zhnpiz9dnc^);0A^eRG^?U>cc|%*o~y)5J70&CMC+OmmKDVa_!zO>0wPO3ekP%(OEd zOh1?`~uBN-W%JeWjO)t~i^fCQRe>1=gGK0;Kq8Vm}n_JC@svI-Yj54Fm-DZrr z$J}einfuLnGr>G$%FRUcuzAExGEbN%%~R$X^Q@U-rkZKyc{9VzG%uQ$%q%n8%rO;a zu9;`%yT~i%HM77hG_RXQX0dtGEHO*XGPB&gZC03-W|diO)|z+BIIwwtfaH|9I@ebwO|W~ceV>@q)@pUp4kSM$5sWA>Uq z%s%s{IUu1#BvPV!qDG=|;?%@xi6)7riDrq@6U`H6C0Zp~C)y;=Pn0A|6J?3EiFS#L z673TgCoV~J=$6QdPKhpw%Mx7^-4je0u%c{sGT5-}e9j delta 1451 zcmWO3i`N(O0mt!uKA-Q?o+6K_JS*jJWTKMJDTkP)lJV^=x#XG}W-hs8bO`&+=t5=e zS1!{+Dfe)~%#pB)jZk5%<&xpdIpnlaOg8_)>zvp6gBA>VWcQY4@0RJ!B`v!**R~wD z^vDjAS{uvi^7?FDQCHSg^)L0W^`-i9eWkuyU#n~Dy83#3qyDYFRo|}b>xTMHeYd_> zH`Wj8KkJ9}qxy0Eq;9I8*Uj~dx}|6U5KOS>haVt~gIj6z7Wz#YJM0xI|nkCW|TJN^zCA zMobmc8e+PbA+8fM#Vj#f+$e4mO)*Ez6*r4p#BJhsF;C1FcZfU101PI*{mei!$P707n*+=cbBOtg8EOtS!_06q!W?Esn#0XcO*KcE z(PoS}#vE_PniI`Q=2SDzj5ia^&&=uOO!Esfv0=_P7nqC8#pai0vbo$$F;|(Z%{As) zGu2Ep)6EQXotbH7nc3#IrfKGwx#nhbtC?s1VCI{<%mTB}+-(+_#pWJ!uer}WXqK3V z&1yGTW7e8==5_O?`G?tH-ZAf*_ssidqxrymXg)F@n}3;4&1Ytl`P^(aUz#mutNGe| zYrYp85=!V0I)&}R_MvlV3*AC{=n-}aJws#H&^zoI`h>orU+5nOgh63&I3OGp4i1Ne zVPSX}5k`h1Lk*+C=x}rx6OIYThOyylQ@nJ$ZJ)9NJ4(Eh(!^ChwxG+p= zgp0!^;nFZUTox`5Q^FPD%J8f2fZ^BS>hPOzZI~LSh3R2NxIWAbv%>6fL%1>A6n+<) zVNRGEZV9)B+rsa|?O|S+AMOZuhP%RourRGG3X8)%%~4%?G#(F6gg=HS!=J)aVOe-4 zEDz6xKZg}zWmpwn2rq`0!pmWGcr~mE>%zaof5I2x%djPE4PS+?!?w^;=ur56p<|&_ zVY@=-!VZPD!Vd~N6}p^I=vwGj*tyWX(6i91uxp`rVb8*k3VRjyDfBPwTiCBKurR1F mxUhfWz{0_W9~Xueh8IQ@YGG7ibYaZWWnIr~ZJyEVvi}1{%#kzz diff --git a/cadquery/CQ.py b/cadquery/CQ.py index 62fdbc9..653a5ce 100644 --- a/cadquery/CQ.py +++ b/cadquery/CQ.py @@ -1652,11 +1652,11 @@ class Workplane(CQ): if s: return s.BoundingBox().DiagonalLength * 5.0 else: - return 1000000 + return -1 def cutEach(self, fcn, useLocalCoords=False): """ - Evaluates the provided function at each point on the stack ( ie, eachpoint ) + Evaluates the provided function at each point on the stack (ie, eachpoint) and then cuts the result from the context solid. :param fcn: a function suitable for use in the eachpoint method: ie, that accepts a vector @@ -1968,7 +1968,7 @@ class Workplane(CQ): def combine(self): """ - Attempts to combine all of the items on the items on the stack into a single item. + Attempts to combine all of the items on the stack into a single item. WARNING: all of the items must be of the same type! :raises: ValueError if there are no items on the stack, or if they cannot be combined diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index ca93d38..6a1f586 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -185,17 +185,27 @@ class TestCadQuery(BaseTest): def testPointList(self): - "Tests adding points and using them" + """ + Tests adding points and using them + """ c = CQ(makeUnitCube()) - s = c.faces(">Z").workplane().pushPoints([(-0.3,0.3),(0.3,0.3),(0,0)]) - self.assertEqual(3,s.size()) + s = c.faces(">Z").workplane().pushPoints([(-0.3, 0.3), (0.3, 0.3), (0, 0)]) + self.assertEqual(3, s.size()) #TODO: is the ability to iterate over points with circle really worth it? #maybe we should just require using all() and a loop for this. the semantics and #possible combinations got too hard ( ie, .circle().circle() ) was really odd body = s.circle(0.05).cutThruAll() self.saveModel(body) - self.assertEqual(9,body.faces().size()) + self.assertEqual(9, body.faces().size()) + + # Test the case when using eachpoint with only a blank workplane + def callback_fn(pnt): + self.assertEqual((0.0, 0.0), (pnt.x, pnt.y)) + + r = Workplane('XY') + r.objects = [] + r.eachpoint(callback_fn) def testWorkplaneFromFace(self): @@ -260,7 +270,8 @@ class TestCadQuery(BaseTest): Test creating a solid using the revolve operation. :return: """ - #The dimensions of the model. These can be modified rather than changing the shape's code directly. + # The dimensions of the model. These can be modified rather than changing the + # shape's code directly. rectangle_width = 10.0 rectangle_length = 10.0 angle_degrees = 360.0 @@ -316,12 +327,14 @@ class TestCadQuery(BaseTest): Test creating a solid donut shape with square walls :return: """ - #The dimensions of the model. These can be modified rather than changing the shape's code directly. + # The dimensions of the model. These can be modified rather than changing the + # shape's code directly. rectangle_width = 10.0 rectangle_length = 10.0 angle_degrees = 360.0 - result = Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10)) + result = Workplane("XY").rect(rectangle_width, rectangle_length, True)\ + .revolve(angle_degrees, (20, 0), (20, 10)) self.assertEqual(4, result.faces().size()) self.assertEqual(4, result.vertices().size()) self.assertEqual(6, result.edges().size()) @@ -336,6 +349,24 @@ class TestCadQuery(BaseTest): self.assertEqual(2, result.vertices().size()) self.assertEqual(3, result.edges().size()) + def testTwistExtrude(self): + """ + Tests extrusion while twisting through an angle. + """ + profile = Workplane('XY').rect(10, 10) + r = profile.twistExtrude(10, 45, False) + + self.assertEqual(6, r.faces().size()) + + def testTwistExtrudeCombine(self): + """ + Tests extrusion while twisting through an angle, combining with other solids. + """ + profile = Workplane('XY').rect(10, 10) + r = profile.twistExtrude(10, 45) + + self.assertEqual(6, r.faces().size()) + def testRectArray(self): NUMX=3 NUMY=3 @@ -612,6 +643,20 @@ class TestCadQuery(BaseTest): r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0)))\ .first().val().Y)) + def testLargestDimension(self): + """ + Tests the largestDimension function when no solids are on the stack and when there are + """ + r = Workplane('XY').box(1, 1, 1) + dim = r.largestDimension() + + self.assertAlmostEqual(8.66025403784, dim) + + r = Workplane('XY') + dim = r.largestDimension() + + self.assertEqual(-1, dim) + def testOccBottle(self): """ Make the OCC bottle example. @@ -768,9 +813,17 @@ class TestCadQuery(BaseTest): (-1.0, -1.0), (0.0, 0.0), (1.0, 1.0) ] c.faces(">Z").workplane().pushPoints(pnts).cboreHole(0.1, 0.25, 0.25, 0.75) - self.assertEquals(18, c.faces().size() ) + self.assertEquals(18, c.faces().size()) self.saveModel(c) + # Tests the case where the depth of the cboreHole is not specified + c2 = CQ(makeCube(3.0)) + pnts = [ + (-1.0, -1.0), (0.0, 0.0), (1.0, 1.0) + ] + c2.faces(">Z").workplane().pushPoints(pnts).cboreHole(0.1, 0.25, 0.25) + self.assertEquals(15, c2.faces().size()) + def testCounterSinks(self): """ Tests countersinks