From 0118cac57afef559d9570341f335f57160a936bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20N=C3=A4slund?= Date: Wed, 18 Apr 2018 23:20:33 +0200 Subject: [PATCH] Add sagittaArc function and test --- cadquery/cq.py | 39 +++++++++++++++++++++++++++++++++++++-- tests/TestCadQuery.py | 4 ++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/cadquery/cq.py b/cadquery/cq.py index 943f1fc..38d9b41 100644 --- a/cadquery/cq.py +++ b/cadquery/cq.py @@ -1331,6 +1331,41 @@ def threePointArc(self, point1, point2, forConstruction=False): return self.newObject([arc]) + def sagittaArc(self, endPoint, sag, forConstruction=False): + """ + Draw an arc from the current point to endPoint with an arc defined by the sag (sagitta). + + :param endPoint: end point for the arc + :type endPoint: 2-tuple, in workplane coordinates + :param sag: the sagitta of the arc + :type sag: float, perpendicular distance from arc center to arc baseline. + :return: a workplane with the current point at the end of the arc + + The sagitta is the distance from the center of the arc to the arc base. + Given that a closed contour is drawn clockwise; + A positive sagitta means convex arc and negative sagitta means concave arc. + See "https://en.wikipedia.org/wiki/Sagitta_(geometry)" for more information. + """ + + startPoint = self._findFromPoint(False) + endPoint = self.plane.toWorldCoords(endPoint) + midPoint = endPoint.add(startPoint).multiply(0.5) + + sagVector = endPoint.sub(startPoint).normalized().multiply(abs(sag)) + if(sag > 0): + sagVector.x, sagVector.y = -sagVector.y, sagVector.x # Rotate sagVector +90 deg + else: + sagVector.x, sagVector.y = sagVector.y, -sagVector.x # Rotate sagVector -90 deg + + sagPoint = midPoint.add(sagVector) + + arc = Edge.makeThreePointArc(startPoint, sagPoint, endPoint) + + if not forConstruction: + self._addPendingEdge(arc) + + return self.newObject([arc]) + def rotateAndCopy(self, matrix): """ Makes a copy of all edges on the stack, rotates them according to the @@ -2080,7 +2115,7 @@ def sweep(self, path, sweepAlongWires=False, makeSolid=True, isFrenet=False, com :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 + 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. @@ -2407,7 +2442,7 @@ def _sweep(self, path, sweepAlongWires=False, makeSolid=True, isFrenet=False): :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 + 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 """ diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index db329dc..5b16a7f 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -867,6 +867,10 @@ def test2DDrawing(self): r.vertices(selectors.NearestToPointSelector((0.0, 0.0, 0.0)))\ .first().val().Y)) + # Test the sagittaArc functions + s = Workplane(Plane.YZ()) + r = s.sagittaArc((10, 8), 1).close() + def testLargestDimension(self): """ Tests the largestDimension function when no solids are on the stack and when there are