Merge branch 'master' into marcus7070/tangentArc
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ dist/*
|
|||||||
.idea/*
|
.idea/*
|
||||||
cadquery.egg-info
|
cadquery.egg-info
|
||||||
target/*
|
target/*
|
||||||
|
.vscode
|
||||||
|
100
cadquery/cq.py
100
cadquery/cq.py
@ -1557,7 +1557,10 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
if tangents:
|
if tangents:
|
||||||
t1, t2 = tangents
|
t1, t2 = tangents
|
||||||
tangents = (self.plane.toWorldCoords(t1), self.plane.toWorldCoords(t2))
|
tangents = (
|
||||||
|
self.plane.toWorldCoords(t1) - self.plane.origin,
|
||||||
|
self.plane.toWorldCoords(t2) - self.plane.origin,
|
||||||
|
)
|
||||||
|
|
||||||
e = Edge.makeSpline(allPoints, tangents=tangents, periodic=periodic)
|
e = Edge.makeSpline(allPoints, tangents=tangents, periodic=periodic)
|
||||||
|
|
||||||
@ -1589,6 +1592,65 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.spline(allPoints, includeCurrent=False, makeWire=True)
|
return self.spline(allPoints, includeCurrent=False, makeWire=True)
|
||||||
|
|
||||||
|
def ellipseArc(
|
||||||
|
self,
|
||||||
|
x_radius,
|
||||||
|
y_radius,
|
||||||
|
angle1=360,
|
||||||
|
angle2=360,
|
||||||
|
rotation_angle=0.0,
|
||||||
|
sense=1,
|
||||||
|
forConstruction=False,
|
||||||
|
startAtCurrent=True,
|
||||||
|
makeWire=False,
|
||||||
|
):
|
||||||
|
"""Draw an elliptical arc with x and y radiuses either with start point at current point or
|
||||||
|
or current point being the center of the arc
|
||||||
|
|
||||||
|
:param x_radius: x radius of the ellipse (along the x-axis of plane the ellipse should lie in)
|
||||||
|
:param y_radius: y radius of the ellipse (along the y-axis of plane the ellipse should lie in)
|
||||||
|
:param angle1: start angle of arc
|
||||||
|
:param angle2: end angle of arc (angle2 == angle1 return closed ellipse = default)
|
||||||
|
:param rotation_angle: angle to rotate the created ellipse / arc
|
||||||
|
:param sense: clockwise (-1) or counter clockwise (1)
|
||||||
|
:param startAtCurrent: True: start point of arc is moved to current point; False: center of
|
||||||
|
arc is on current point
|
||||||
|
:param makeWire: convert the resulting arc edge to a wire
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Start building the ellipse with the current point as center
|
||||||
|
center = self._findFromPoint(useLocalCoords=False)
|
||||||
|
e = Edge.makeEllipse(
|
||||||
|
x_radius,
|
||||||
|
y_radius,
|
||||||
|
center,
|
||||||
|
self.plane.zDir,
|
||||||
|
self.plane.xDir,
|
||||||
|
angle1,
|
||||||
|
angle2,
|
||||||
|
sense == 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Rotate if necessary
|
||||||
|
if rotation_angle != 0.0:
|
||||||
|
e = e.rotate(center, center.add(self.plane.zDir), rotation_angle)
|
||||||
|
|
||||||
|
# Move the start point of the ellipse onto the last current point
|
||||||
|
if startAtCurrent:
|
||||||
|
startPoint = e.startPoint()
|
||||||
|
e = e.translate(center.sub(startPoint))
|
||||||
|
|
||||||
|
if makeWire:
|
||||||
|
rv = Wire.assembleEdges([e])
|
||||||
|
if not forConstruction:
|
||||||
|
self._addPendingWire(rv)
|
||||||
|
else:
|
||||||
|
rv = e
|
||||||
|
if not forConstruction:
|
||||||
|
self._addPendingEdge(e)
|
||||||
|
|
||||||
|
return self.newObject([rv])
|
||||||
|
|
||||||
def threePointArc(self, point1, point2, forConstruction=False):
|
def threePointArc(self, point1, point2, forConstruction=False):
|
||||||
"""
|
"""
|
||||||
Draw an arc from the current point, through point1, and ending at point2
|
Draw an arc from the current point, through point1, and ending at point2
|
||||||
@ -2073,6 +2135,42 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
return self.eachpoint(makeCircleWire, useLocalCoordinates=True)
|
return self.eachpoint(makeCircleWire, useLocalCoordinates=True)
|
||||||
|
|
||||||
|
# ellipse from current point
|
||||||
|
def ellipse(self, x_radius, y_radius, rotation_angle=0.0, forConstruction=False):
|
||||||
|
"""
|
||||||
|
Make an ellipse for each item on the stack.
|
||||||
|
:param x_radius: x radius of the ellipse (x-axis of plane the ellipse should lie in)
|
||||||
|
:type x_radius: float > 0
|
||||||
|
:param y_radius: y radius of the ellipse (y-axis of plane the ellipse should lie in)
|
||||||
|
:type y_radius: float > 0
|
||||||
|
:param rotation_angle: angle to rotate the ellipse (0 = no rotation = default)
|
||||||
|
:type rotation_angle: float
|
||||||
|
:param forConstruction: should the new wires be reference geometry only?
|
||||||
|
:type forConstruction: true if the wires are for reference, false if they are creating
|
||||||
|
part geometry
|
||||||
|
:return: a new CQ object with the created wires on the stack
|
||||||
|
|
||||||
|
*NOTE* Due to a bug in opencascade (https://tracker.dev.opencascade.org/view.php?id=31290)
|
||||||
|
the center of mass (equals center for next shape) is shifted. To create concentric ellipses
|
||||||
|
use Workplane("XY")
|
||||||
|
.center(10, 20).ellipse(100,10)
|
||||||
|
.center(0, 0).ellipse(50, 5)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def makeEllipseWire(obj):
|
||||||
|
elip = Wire.makeEllipse(
|
||||||
|
x_radius,
|
||||||
|
y_radius,
|
||||||
|
obj,
|
||||||
|
Vector(0, 0, 1),
|
||||||
|
Vector(1, 0, 0),
|
||||||
|
rotation_angle=rotation_angle,
|
||||||
|
)
|
||||||
|
elip.forConstruction = forConstruction
|
||||||
|
return elip
|
||||||
|
|
||||||
|
return self.eachpoint(makeEllipseWire, useLocalCoordinates=True)
|
||||||
|
|
||||||
def polygon(self, nSides, diameter, forConstruction=False):
|
def polygon(self, nSides, diameter, forConstruction=False):
|
||||||
"""
|
"""
|
||||||
Creates a polygon inscribed in a circle of the specified diameter for each point on
|
Creates a polygon inscribed in a circle of the specified diameter for each point on
|
||||||
|
@ -11,6 +11,7 @@ from OCC.Core.gp import (
|
|||||||
gp_Ax3,
|
gp_Ax3,
|
||||||
gp_Dir,
|
gp_Dir,
|
||||||
gp_Circ,
|
gp_Circ,
|
||||||
|
gp_Elips,
|
||||||
gp_Trsf,
|
gp_Trsf,
|
||||||
gp_Pln,
|
gp_Pln,
|
||||||
gp_GTrsf,
|
gp_GTrsf,
|
||||||
@ -76,7 +77,7 @@ from OCC.Core.TopoDS import (
|
|||||||
|
|
||||||
from OCC.Core.TopoDS import TopoDS_Compound, TopoDS_Builder
|
from OCC.Core.TopoDS import TopoDS_Compound, TopoDS_Builder
|
||||||
|
|
||||||
from OCC.Core.GC import GC_MakeArcOfCircle # geometry construction
|
from OCC.Core.GC import GC_MakeArcOfCircle, GC_MakeArcOfEllipse # geometry construction
|
||||||
from OCC.Core.GCE2d import GCE2d_MakeSegment
|
from OCC.Core.GCE2d import GCE2d_MakeSegment
|
||||||
from OCC.Core.GeomAPI import GeomAPI_Interpolate, GeomAPI_ProjectPointOnSurf
|
from OCC.Core.GeomAPI import GeomAPI_Interpolate, GeomAPI_ProjectPointOnSurf
|
||||||
|
|
||||||
@ -751,6 +752,62 @@ class Edge(Shape, Mixin1D):
|
|||||||
).Value()
|
).Value()
|
||||||
return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge())
|
return cls(BRepBuilderAPI_MakeEdge(circle_geom).Edge())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def makeEllipse(
|
||||||
|
cls,
|
||||||
|
x_radius,
|
||||||
|
y_radius,
|
||||||
|
pnt=Vector(0, 0, 0),
|
||||||
|
dir=Vector(0, 0, 1),
|
||||||
|
xdir=Vector(1, 0, 0),
|
||||||
|
angle1=360.0,
|
||||||
|
angle2=360.0,
|
||||||
|
sense=1,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Makes an Ellipse centered at the provided point, having normal in the provided direction
|
||||||
|
:param cls:
|
||||||
|
:param x_radius: x radius of the ellipse (along the x-axis of plane the ellipse should lie in)
|
||||||
|
:param y_radius: y radius of the ellipse (along the y-axis of plane the ellipse should lie in)
|
||||||
|
:param pnt: vector representing the center of the ellipse
|
||||||
|
:param dir: vector representing the direction of the plane the ellipse should lie in
|
||||||
|
:param angle1: start angle of arc
|
||||||
|
:param angle2: end angle of arc (angle2 == angle1 return closed ellipse = default)
|
||||||
|
:param sense: clockwise (-1) or counter clockwise (1)
|
||||||
|
:return: an Edge
|
||||||
|
"""
|
||||||
|
|
||||||
|
pnt = Vector(pnt).toPnt()
|
||||||
|
dir = Vector(dir).toDir()
|
||||||
|
xdir = Vector(xdir).toDir()
|
||||||
|
|
||||||
|
ax1 = gp_Ax1(pnt, dir)
|
||||||
|
ax2 = gp_Ax2(pnt, dir, xdir)
|
||||||
|
|
||||||
|
if y_radius > x_radius:
|
||||||
|
# swap x and y radius and rotate by 90° afterwards to create an ellipse with x_radius < y_radius
|
||||||
|
correction_angle = 90.0 * DEG2RAD
|
||||||
|
ellipse_gp = gp_Elips(ax2, y_radius, x_radius).Rotated(
|
||||||
|
ax1, correction_angle
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
correction_angle = 0.0
|
||||||
|
ellipse_gp = gp_Elips(ax2, x_radius, y_radius)
|
||||||
|
|
||||||
|
if angle1 == angle2: # full ellipse case
|
||||||
|
ellipse = cls(BRepBuilderAPI_MakeEdge(ellipse_gp).Edge())
|
||||||
|
else: # arc case
|
||||||
|
# take correction_angle into account
|
||||||
|
ellipse_geom = GC_MakeArcOfEllipse(
|
||||||
|
ellipse_gp,
|
||||||
|
angle1 * DEG2RAD - correction_angle,
|
||||||
|
angle2 * DEG2RAD - correction_angle,
|
||||||
|
sense == 1,
|
||||||
|
).Value()
|
||||||
|
ellipse = cls(BRepBuilderAPI_MakeEdge(ellipse_geom).Edge())
|
||||||
|
|
||||||
|
return ellipse
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeSpline(cls, listOfVector, tangents=None, periodic=False, tol=1e-6):
|
def makeSpline(cls, listOfVector, tangents=None, periodic=False, tol=1e-6):
|
||||||
"""
|
"""
|
||||||
@ -884,6 +941,46 @@ class Wire(Shape, Mixin1D):
|
|||||||
w = cls.assembleEdges([circle_edge])
|
w = cls.assembleEdges([circle_edge])
|
||||||
return w
|
return w
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def makeEllipse(
|
||||||
|
cls,
|
||||||
|
x_radius,
|
||||||
|
y_radius,
|
||||||
|
center,
|
||||||
|
normal,
|
||||||
|
xDir,
|
||||||
|
angle1=360.0,
|
||||||
|
angle2=360.0,
|
||||||
|
rotation_angle=0.0,
|
||||||
|
closed=True,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Makes an Ellipse centered at the provided point, having normal in the provided direction
|
||||||
|
:param x_radius: floating point major radius of the ellipse (x-axis), must be > 0
|
||||||
|
:param y_radius: floating point minor radius of the ellipse (y-axis), must be > 0
|
||||||
|
:param center: vector representing the center of the circle
|
||||||
|
:param normal: vector representing the direction of the plane the circle should lie in
|
||||||
|
:param angle1: start angle of arc
|
||||||
|
:param angle2: end angle of arc
|
||||||
|
:param rotation_angle: angle to rotate the created ellipse / arc
|
||||||
|
:return: Wire
|
||||||
|
"""
|
||||||
|
|
||||||
|
ellipse_edge = Edge.makeEllipse(
|
||||||
|
x_radius, y_radius, center, normal, xDir, angle1, angle2
|
||||||
|
)
|
||||||
|
|
||||||
|
if angle1 != angle2 and closed:
|
||||||
|
line = Edge.makeLine(ellipse_edge.endPoint(), ellipse_edge.startPoint())
|
||||||
|
w = cls.assembleEdges([ellipse_edge, line])
|
||||||
|
else:
|
||||||
|
w = cls.assembleEdges([ellipse_edge])
|
||||||
|
|
||||||
|
if rotation_angle != 0.0:
|
||||||
|
w = w.rotate(center, center + normal, rotation_angle)
|
||||||
|
|
||||||
|
return w
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makePolygon(cls, listOfVertices, forConstruction=False):
|
def makePolygon(cls, listOfVertices, forConstruction=False):
|
||||||
# convert list of tuples into Vectors.
|
# convert list of tuples into Vectors.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from cadquery import *
|
from cadquery import *
|
||||||
from OCC.gp import gp_Vec
|
from OCC.Core.gp import gp_Vec
|
||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
# system modules
|
# system modules
|
||||||
|
import math
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from tests import BaseTest
|
from tests import BaseTest
|
||||||
from OCC.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_DZ, gp_XYZ
|
from OCC.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_Elips, gp_DZ, gp_XYZ
|
||||||
from OCC.BRepBuilderAPI import (
|
from OCC.BRepBuilderAPI import (
|
||||||
BRepBuilderAPI_MakeVertex,
|
BRepBuilderAPI_MakeVertex,
|
||||||
BRepBuilderAPI_MakeEdge,
|
BRepBuilderAPI_MakeEdge,
|
||||||
BRepBuilderAPI_MakeFace,
|
BRepBuilderAPI_MakeFace,
|
||||||
)
|
)
|
||||||
|
|
||||||
from OCC.GC import GC_MakeCircle
|
from OCC.Core.GC import GC_MakeCircle
|
||||||
|
|
||||||
from cadquery import *
|
from cadquery import *
|
||||||
|
|
||||||
|
DEG2RAD = 2 * math.pi / 360
|
||||||
|
|
||||||
|
|
||||||
class TestCadObjects(BaseTest):
|
class TestCadObjects(BaseTest):
|
||||||
def _make_circle(self):
|
def _make_circle(self):
|
||||||
@ -20,6 +23,11 @@ class TestCadObjects(BaseTest):
|
|||||||
circle = gp_Circ(gp_Ax2(gp_Pnt(1, 2, 3), gp_DZ()), 2.0)
|
circle = gp_Circ(gp_Ax2(gp_Pnt(1, 2, 3), gp_DZ()), 2.0)
|
||||||
return Shape.cast(BRepBuilderAPI_MakeEdge(circle).Edge())
|
return Shape.cast(BRepBuilderAPI_MakeEdge(circle).Edge())
|
||||||
|
|
||||||
|
def _make_ellipse(self):
|
||||||
|
|
||||||
|
ellipse = gp_Elips(gp_Ax2(gp_Pnt(1, 2, 3), gp_DZ()), 4.0, 2.0)
|
||||||
|
return Shape.cast(BRepBuilderAPI_MakeEdge(ellipse).Edge())
|
||||||
|
|
||||||
def testVectorConstructors(self):
|
def testVectorConstructors(self):
|
||||||
v1 = Vector(1, 2, 3)
|
v1 = Vector(1, 2, 3)
|
||||||
v2 = Vector((1, 2, 3))
|
v2 = Vector((1, 2, 3))
|
||||||
@ -69,6 +77,13 @@ class TestCadObjects(BaseTest):
|
|||||||
|
|
||||||
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3)
|
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3)
|
||||||
|
|
||||||
|
def testEdgeWrapperEllipseCenter(self):
|
||||||
|
e = self._make_ellipse()
|
||||||
|
w = Wire.assembleEdges([e])
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(1.0, 2.0, 3.0), Face.makeFromWires(w).Center().toTuple(), 3
|
||||||
|
)
|
||||||
|
|
||||||
def testEdgeWrapperMakeCircle(self):
|
def testEdgeWrapperMakeCircle(self):
|
||||||
halfCircleEdge = Edge.makeCircle(
|
halfCircleEdge = Edge.makeCircle(
|
||||||
radius=10, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=0, angle2=180
|
radius=10, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=0, angle2=180
|
||||||
@ -100,6 +115,84 @@ class TestCadObjects(BaseTest):
|
|||||||
(0, -1, 0), tangent_arc.tangentAt(locationParam=1).toTuple(), 3
|
(0, -1, 0), tangent_arc.tangentAt(locationParam=1).toTuple(), 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def testEdgeWrapperMakeEllipse1(self):
|
||||||
|
# Check x_radius > y_radius
|
||||||
|
x_radius, y_radius = 20, 10
|
||||||
|
angle1, angle2 = -75.0, 90.0
|
||||||
|
arcEllipseEdge = Edge.makeEllipse(
|
||||||
|
x_radius=x_radius,
|
||||||
|
y_radius=y_radius,
|
||||||
|
pnt=(0, 0, 0),
|
||||||
|
dir=(0, 0, 1),
|
||||||
|
angle1=angle1,
|
||||||
|
angle2=angle2,
|
||||||
|
)
|
||||||
|
|
||||||
|
start = (
|
||||||
|
x_radius * math.cos(angle1 * DEG2RAD),
|
||||||
|
y_radius * math.sin(angle1 * DEG2RAD),
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
end = (
|
||||||
|
x_radius * math.cos(angle2 * DEG2RAD),
|
||||||
|
y_radius * math.sin(angle2 * DEG2RAD),
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(start, arcEllipseEdge.startPoint().toTuple(), 3)
|
||||||
|
self.assertTupleAlmostEquals(end, arcEllipseEdge.endPoint().toTuple(), 3)
|
||||||
|
|
||||||
|
def testEdgeWrapperMakeEllipse2(self):
|
||||||
|
# Check x_radius < y_radius
|
||||||
|
x_radius, y_radius = 10, 20
|
||||||
|
angle1, angle2 = 0.0, 45.0
|
||||||
|
arcEllipseEdge = Edge.makeEllipse(
|
||||||
|
x_radius=x_radius,
|
||||||
|
y_radius=y_radius,
|
||||||
|
pnt=(0, 0, 0),
|
||||||
|
dir=(0, 0, 1),
|
||||||
|
angle1=angle1,
|
||||||
|
angle2=angle2,
|
||||||
|
)
|
||||||
|
|
||||||
|
start = (
|
||||||
|
x_radius * math.cos(angle1 * DEG2RAD),
|
||||||
|
y_radius * math.sin(angle1 * DEG2RAD),
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
end = (
|
||||||
|
x_radius * math.cos(angle2 * DEG2RAD),
|
||||||
|
y_radius * math.sin(angle2 * DEG2RAD),
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(start, arcEllipseEdge.startPoint().toTuple(), 3)
|
||||||
|
self.assertTupleAlmostEquals(end, arcEllipseEdge.endPoint().toTuple(), 3)
|
||||||
|
|
||||||
|
def testEdgeWrapperMakeCircleWithEllipse(self):
|
||||||
|
# Check x_radius == y_radius
|
||||||
|
x_radius, y_radius = 20, 20
|
||||||
|
angle1, angle2 = 15.0, 60.0
|
||||||
|
arcEllipseEdge = Edge.makeEllipse(
|
||||||
|
x_radius=x_radius,
|
||||||
|
y_radius=y_radius,
|
||||||
|
pnt=(0, 0, 0),
|
||||||
|
dir=(0, 0, 1),
|
||||||
|
angle1=angle1,
|
||||||
|
angle2=angle2,
|
||||||
|
)
|
||||||
|
|
||||||
|
start = (
|
||||||
|
x_radius * math.cos(angle1 * DEG2RAD),
|
||||||
|
y_radius * math.sin(angle1 * DEG2RAD),
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
end = (
|
||||||
|
x_radius * math.cos(angle2 * DEG2RAD),
|
||||||
|
y_radius * math.sin(angle2 * DEG2RAD),
|
||||||
|
0.0,
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(start, arcEllipseEdge.startPoint().toTuple(), 3)
|
||||||
|
self.assertTupleAlmostEquals(end, arcEllipseEdge.endPoint().toTuple(), 3)
|
||||||
|
|
||||||
def testFaceWrapperMakePlane(self):
|
def testFaceWrapperMakePlane(self):
|
||||||
mplane = Face.makePlane(10, 10)
|
mplane = Face.makePlane(10, 10)
|
||||||
|
|
||||||
|
@ -545,6 +545,206 @@ class TestCadQuery(BaseTest):
|
|||||||
path1 = Workplane("XZ").spline(pts[1:], includeCurrent=True).val()
|
path1 = Workplane("XZ").spline(pts[1:], includeCurrent=True).val()
|
||||||
self.assertAlmostEqual(path.Length(), path1.Length())
|
self.assertAlmostEqual(path.Length(), path1.Length())
|
||||||
|
|
||||||
|
# test tangents and offset plane
|
||||||
|
pts = [(0, 0), (-1, 1), (-2, 0), (-1, 0)]
|
||||||
|
tangents = [(0, 1), (1, 0)]
|
||||||
|
|
||||||
|
path2 = Workplane("XY", (0, 0, 10)).spline(pts, tangents=tangents)
|
||||||
|
self.assertAlmostEqual(path2.val().tangentAt(0).z, 0)
|
||||||
|
|
||||||
|
def testRotatedEllipse(self):
|
||||||
|
def rotatePoint(x, y, alpha):
|
||||||
|
# rotation matrix
|
||||||
|
a = alpha * DEG2RAD
|
||||||
|
r = ((math.cos(a), math.sin(a)), (-math.sin(a), math.cos(a)))
|
||||||
|
return ((x * r[0][0] + y * r[1][0]), (x * r[0][1] + y * r[1][1]))
|
||||||
|
|
||||||
|
def ellipsePoints(r1, r2, a):
|
||||||
|
return (r1 * math.cos(a * DEG2RAD), r2 * math.sin(a * DEG2RAD))
|
||||||
|
|
||||||
|
DEG2RAD = math.pi / 180.0
|
||||||
|
p0 = (10, 20)
|
||||||
|
a1, a2 = 30, -60
|
||||||
|
r1, r2 = 20, 10
|
||||||
|
ra = 25
|
||||||
|
|
||||||
|
sx_rot, sy_rot = rotatePoint(*ellipsePoints(r1, r2, a1), ra)
|
||||||
|
ex_rot, ey_rot = rotatePoint(*ellipsePoints(r1, r2, a2), ra)
|
||||||
|
|
||||||
|
# startAtCurrent=False, sense = 1
|
||||||
|
ellipseArc1 = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(*p0)
|
||||||
|
.ellipseArc(
|
||||||
|
r1, r2, startAtCurrent=False, angle1=a1, angle2=a2, rotation_angle=ra
|
||||||
|
)
|
||||||
|
)
|
||||||
|
start = ellipseArc1.vertices().objects[0]
|
||||||
|
end = ellipseArc1.vertices().objects[1]
|
||||||
|
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(start.X, start.Y), (p0[0] + sx_rot, p0[1] + sy_rot), 3
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(end.X, end.Y), (p0[0] + ex_rot, p0[1] + ey_rot), 3
|
||||||
|
)
|
||||||
|
|
||||||
|
# startAtCurrent=True, sense = 1
|
||||||
|
ellipseArc2 = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(*p0)
|
||||||
|
.ellipseArc(
|
||||||
|
r1, r2, startAtCurrent=True, angle1=a1, angle2=a2, rotation_angle=ra
|
||||||
|
)
|
||||||
|
)
|
||||||
|
start = ellipseArc2.vertices().objects[0]
|
||||||
|
end = ellipseArc2.vertices().objects[1]
|
||||||
|
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(start.X, start.Y), (p0[0] + sx_rot - sx_rot, p0[1] + sy_rot - sy_rot), 3
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(end.X, end.Y), (p0[0] + ex_rot - sx_rot, p0[1] + ey_rot - sy_rot), 3
|
||||||
|
)
|
||||||
|
|
||||||
|
# startAtCurrent=False, sense = -1
|
||||||
|
ellipseArc3 = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(*p0)
|
||||||
|
.ellipseArc(
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
startAtCurrent=False,
|
||||||
|
angle1=a1,
|
||||||
|
angle2=a2,
|
||||||
|
rotation_angle=ra,
|
||||||
|
sense=-1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
start = ellipseArc3.vertices().objects[0]
|
||||||
|
end = ellipseArc3.vertices().objects[1]
|
||||||
|
|
||||||
|
# swap start and end points for coparison due to different sense
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(start.X, start.Y), (p0[0] + ex_rot, p0[1] + ey_rot), 3
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(end.X, end.Y), (p0[0] + sx_rot, p0[1] + sy_rot), 3
|
||||||
|
)
|
||||||
|
|
||||||
|
# startAtCurrent=True, sense = -1
|
||||||
|
ellipseArc4 = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(*p0)
|
||||||
|
.ellipseArc(
|
||||||
|
r1,
|
||||||
|
r2,
|
||||||
|
startAtCurrent=True,
|
||||||
|
angle1=a1,
|
||||||
|
angle2=a2,
|
||||||
|
rotation_angle=ra,
|
||||||
|
sense=-1,
|
||||||
|
makeWire=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(len(ellipseArc4.ctx.pendingWires), 1)
|
||||||
|
|
||||||
|
start = ellipseArc4.vertices().objects[0]
|
||||||
|
end = ellipseArc4.vertices().objects[1]
|
||||||
|
|
||||||
|
# swap start and end points for coparison due to different sense
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(start.X, start.Y), (p0[0] + ex_rot - ex_rot, p0[1] + ey_rot - ey_rot), 3
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(end.X, end.Y), (p0[0] + sx_rot - ex_rot, p0[1] + sy_rot - ey_rot), 3
|
||||||
|
)
|
||||||
|
|
||||||
|
def testEllipseArcsClockwise(self):
|
||||||
|
ellipseArc = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(10, 15)
|
||||||
|
.ellipseArc(5, 4, -10, 190, 45, sense=-1, startAtCurrent=False)
|
||||||
|
)
|
||||||
|
sp = ellipseArc.val().startPoint()
|
||||||
|
ep = ellipseArc.val().endPoint()
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(sp.x, sp.y), (7.009330014275797, 11.027027582524015), 3
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(ep.x, ep.y), (13.972972417475985, 17.990669985724203), 3
|
||||||
|
)
|
||||||
|
|
||||||
|
ellipseArc = (
|
||||||
|
ellipseArc.ellipseArc(5, 4, -10, 190, 315, sense=-1)
|
||||||
|
.ellipseArc(5, 4, -10, 190, 225, sense=-1)
|
||||||
|
.ellipseArc(5, 4, -10, 190, 135, sense=-1)
|
||||||
|
)
|
||||||
|
ep = ellipseArc.val().endPoint()
|
||||||
|
self.assertTupleAlmostEquals((sp.x, sp.y), (ep.x, ep.y), 3)
|
||||||
|
|
||||||
|
def testEllipseArcsCounterClockwise(self):
|
||||||
|
ellipseArc = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(10, 15)
|
||||||
|
.ellipseArc(5, 4, -10, 190, 45, startAtCurrent=False)
|
||||||
|
)
|
||||||
|
sp = ellipseArc.val().startPoint()
|
||||||
|
ep = ellipseArc.val().endPoint()
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(sp.x, sp.y), (13.972972417475985, 17.990669985724203), 3
|
||||||
|
)
|
||||||
|
self.assertTupleAlmostEquals(
|
||||||
|
(ep.x, ep.y), (7.009330014275797, 11.027027582524015), 3
|
||||||
|
)
|
||||||
|
|
||||||
|
ellipseArc = (
|
||||||
|
ellipseArc.ellipseArc(5, 4, -10, 190, 135)
|
||||||
|
.ellipseArc(5, 4, -10, 190, 225)
|
||||||
|
.ellipseArc(5, 4, -10, 190, 315)
|
||||||
|
)
|
||||||
|
ep = ellipseArc.val().endPoint()
|
||||||
|
self.assertTupleAlmostEquals((sp.x, sp.y), (ep.x, ep.y), 3)
|
||||||
|
|
||||||
|
def testEllipseCenterAndMoveTo(self):
|
||||||
|
# Whether we start from a center() call or a moveTo call, it should be the same ellipse Arc
|
||||||
|
p0 = (10, 20)
|
||||||
|
a1, a2 = 30, -60
|
||||||
|
r1, r2 = 20, 10
|
||||||
|
ra = 25
|
||||||
|
|
||||||
|
ellipseArc1 = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(*p0)
|
||||||
|
.ellipseArc(
|
||||||
|
r1, r2, startAtCurrent=False, angle1=a1, angle2=a2, rotation_angle=ra
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sp1 = ellipseArc1.val().startPoint()
|
||||||
|
ep1 = ellipseArc1.val().endPoint()
|
||||||
|
|
||||||
|
ellipseArc2 = (
|
||||||
|
Workplane("XY")
|
||||||
|
.moveTo(*p0)
|
||||||
|
.ellipseArc(
|
||||||
|
r1, r2, startAtCurrent=False, angle1=a1, angle2=a2, rotation_angle=ra
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sp2 = ellipseArc2.val().startPoint()
|
||||||
|
ep2 = ellipseArc2.val().endPoint()
|
||||||
|
|
||||||
|
self.assertTupleAlmostEquals(sp1.toTuple(), sp2.toTuple(), 3)
|
||||||
|
self.assertTupleAlmostEquals(ep1.toTuple(), ep2.toTuple(), 3)
|
||||||
|
|
||||||
|
def testMakeEllipse(self):
|
||||||
|
el = Wire.makeEllipse(
|
||||||
|
1, 2, Vector(0, 0, 0), Vector(0, 0, 1), Vector(1, 0, 0), 0, 90, 45, True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(el.IsClosed())
|
||||||
|
self.assertTrue(el.isValid())
|
||||||
|
|
||||||
def testSweep(self):
|
def testSweep(self):
|
||||||
"""
|
"""
|
||||||
Tests the operation of sweeping a wire(s) along a path
|
Tests the operation of sweeping a wire(s) along a path
|
||||||
@ -802,6 +1002,13 @@ class TestCadQuery(BaseTest):
|
|||||||
self.saveModel(s)
|
self.saveModel(s)
|
||||||
self.assertEqual(14, s.faces().size())
|
self.assertEqual(14, s.faces().size())
|
||||||
|
|
||||||
|
def testConcentricEllipses(self):
|
||||||
|
concentricEllipses = (
|
||||||
|
Workplane("XY").center(10, 20).ellipse(100, 10).center(0, 0).ellipse(50, 5)
|
||||||
|
)
|
||||||
|
v = concentricEllipses.vertices().objects[0]
|
||||||
|
self.assertTupleAlmostEquals((v.X, v.Y), (10 + 50, 20), 3)
|
||||||
|
|
||||||
def testLegoBrick(self):
|
def testLegoBrick(self):
|
||||||
# test making a simple lego brick
|
# test making a simple lego brick
|
||||||
# which of the below
|
# which of the below
|
||||||
|
Reference in New Issue
Block a user