Merge pull request #694 from CadQuery/splineApprox

Implement makeSplineApprox for edges and faces
This commit is contained in:
Jeremy Wright
2021-03-31 15:06:41 -04:00
committed by GitHub
5 changed files with 536 additions and 38 deletions

83
tests/naca.py Executable file
View File

@ -0,0 +1,83 @@
naca5305 = [
[1.000074, 0.000520],
[0.998545, 0.000829],
[0.993968, 0.001750],
[0.986369, 0.003267],
[0.975792, 0.005351],
[0.962301, 0.007964],
[0.945974, 0.011059],
[0.926909, 0.014580],
[0.905218, 0.018465],
[0.881032, 0.022648],
[0.854494, 0.027058],
[0.825764, 0.031621],
[0.795017, 0.036263],
[0.762437, 0.040911],
[0.728224, 0.045493],
[0.692585, 0.049938],
[0.655739, 0.054180],
[0.617912, 0.058158],
[0.579336, 0.061813],
[0.540252, 0.065095],
[0.500900, 0.067958],
[0.461525, 0.070367],
[0.422374, 0.072291],
[0.383692, 0.073709],
[0.345722, 0.074611],
[0.308702, 0.074992],
[0.272257, 0.074521],
[0.237079, 0.072481],
[0.203612, 0.069020],
[0.172090, 0.064350],
[0.142727, 0.058704],
[0.115719, 0.052328],
[0.091240, 0.045475],
[0.069442, 0.038397],
[0.050454, 0.031335],
[0.034383, 0.024516],
[0.021313, 0.018141],
[0.011308, 0.012382],
[0.004410, 0.007379],
[0.000639, 0.003232],
[0.000000, 0.000000],
[0.002443, -0.002207],
[0.007902, -0.003318],
[0.016322, -0.003385],
[0.027630, -0.002492],
[0.041737, -0.000752],
[0.058539, 0.001696],
[0.077918, 0.004691],
[0.099743, 0.008055],
[0.123875, 0.011591],
[0.150167, 0.015098],
[0.178462, 0.018365],
[0.208603, 0.021185],
[0.240422, 0.023351],
[0.273752, 0.024670],
[0.308614, 0.024992],
[0.345261, 0.024967],
[0.382862, 0.024875],
[0.421191, 0.024682],
[0.460016, 0.024358],
[0.499100, 0.023878],
[0.538207, 0.023226],
[0.577098, 0.022390],
[0.615534, 0.021370],
[0.653278, 0.020171],
[0.690099, 0.018807],
[0.725767, 0.017298],
[0.760061, 0.015670],
[0.792768, 0.013955],
[0.823684, 0.012188],
[0.852613, 0.010407],
[0.879374, 0.008651],
[0.903799, 0.006957],
[0.925731, 0.005364],
[0.945032, 0.003906],
[0.961578, 0.002615],
[0.975264, 0.001519],
[0.986001, 0.000641],
[0.993720, 0.000001],
[0.998372, -0.000389],
[0.999926, -0.000520],
]

View File

@ -2189,13 +2189,15 @@ class TestCadQuery(BaseTest):
self.assertEqual(8, result.solids().item(0).faces().size())
def testSplitError(self):
"""
Test split produces the correct error when called with no solid to split.
"""
# Test split produces the correct error when called with no solid to split.
w = Workplane().hLine(1).vLine(1).close()
with raises(ValueError):
w.split(keepTop=True)
# Split should raise ValueError when called with no side kept
with raises(ValueError):
w.split(keepTop=False, keepBottom=False)
def testBoxDefaults(self):
"""
Tests creating a single box
@ -3291,6 +3293,11 @@ class TestCadQuery(BaseTest):
self.assertTrue(res_closed.solids().val().isValid())
self.assertEqual(len(res_closed.faces().vals()), 3)
res_edge = Workplane("XY").parametricCurve(func, makeWire=False)
self.assertEqual(len(res_edge.ctx.pendingEdges), 1)
self.assertEqual(len(res_edge.ctx.pendingWires), 0)
def testMakeShellSolid(self):
c0 = math.sqrt(2) / 4
@ -4387,3 +4394,125 @@ class TestCadQuery(BaseTest):
with raises(ValueError):
r.chamfer2D(0.25, [vs[0]])
def testSplineApprox(self):
from .naca import naca5305
from math import pi, cos
pts = [Vector(e[0], e[1], 0) for e in naca5305]
# spline
e1 = Edge.makeSplineApprox(pts, 1e-6, maxDeg=6, smoothing=(1, 1, 1))
e2 = Edge.makeSplineApprox(pts, 1e-6, minDeg=2, maxDeg=6)
self.assertTrue(e1.isValid())
self.assertTrue(e2.isValid())
self.assertTrue(e1.Length() > e2.Length())
with raises(ValueError):
e4 = Edge.makeSplineApprox(pts, 1e-6, maxDeg=3, smoothing=(1, 1, 1.0))
pts_closed = pts + [pts[0]]
e3 = Edge.makeSplineApprox(pts_closed)
w = Edge.makeSplineApprox(pts).close()
self.assertTrue(e3.IsClosed())
self.assertTrue(w.IsClosed())
# Workplane method
w1 = Workplane().splineApprox(pts)
w2 = Workplane().splineApprox(pts, forConstruction=True)
w3 = Workplane().splineApprox(pts, makeWire=True)
w4 = Workplane().splineApprox(pts, makeWire=True, forConstruction=True)
self.assertEqual(w1.edges().size(), 1)
self.assertEqual(len(w1.ctx.pendingEdges), 1)
self.assertEqual(w2.edges().size(), 1)
self.assertEqual(len(w2.ctx.pendingEdges), 0)
self.assertEqual(w3.wires().size(), 1)
self.assertEqual(len(w3.ctx.pendingWires), 1)
self.assertEqual(w4.wires().size(), 1)
self.assertEqual(len(w4.ctx.pendingWires), 0)
# spline surface
N = 40
T = 20
A = 5
pts = [
[
Vector(i, j, A * cos(2 * pi * i / T) * cos(2 * pi * j / T))
for i in range(N + 1)
]
for j in range(N + 1)
]
f1 = Face.makeSplineApprox(pts, smoothing=(1, 1, 1), maxDeg=6)
f2 = Face.makeSplineApprox(pts)
self.assertTrue(f1.isValid())
self.assertTrue(f2.isValid())
with raises(ValueError):
f3 = Face.makeSplineApprox(pts, smoothing=(1, 1, 1), maxDeg=3)
def testParametricSurface(self):
from math import pi, cos
r1 = Workplane().parametricSurface(
lambda u, v: (u, v, cos(pi * u) * cos(pi * v)), start=-1, stop=1
)
self.assertTrue(r1.faces().val().isValid())
r2 = Workplane().box(1, 1, 3).split(r1)
self.assertTrue(r2.solids().val().isValid())
self.assertEqual(r2.solids().size(), 2)
def testEdgeWireClose(self):
# test with edge
e0 = Edge.makeThreePointArc(Vector(0, 0, 0), Vector(1, 1, 0), Vector(0, 2, 0))
self.assertFalse(e0.IsClosed())
w0 = e0.close()
self.assertTrue(w0.IsClosed())
# test with already closed edge
e1 = Edge.makeCircle(1)
self.assertTrue(e1.IsClosed())
e2 = e1.close()
self.assertTrue(e2.IsClosed())
self.assertEqual(type(e1), type(e2))
# test with already closed WIRE
w1 = Wire.makeCircle(1, Vector(), Vector(0, 0, 1))
self.assertTrue(w1.IsClosed())
w2 = w1.close()
self.assertTrue(w1 is w2)
def testSplitShape(self):
"""
Testing the Shape.split method.
"""
# split an edge with a vertex
e0 = Edge.makeCircle(1, (0, 0, 0), (0, 0, 1))
v0 = Vertex.makeVertex(0, 1, 0)
list_of_edges = e0.split(v0).Edges()
self.assertEqual(len(list_of_edges), 2)
self.assertTrue(Vector(0, 1, 0) in [e.endPoint() for e in list_of_edges])
# split a circle with multiple vertices
angles = [2 * math.pi * idx / 10 for idx in range(10)]
vecs = [Vector(math.sin(a), math.cos(a), 0) for a in angles]
vertices = [Vertex.makeVertex(*v.toTuple()) for v in vecs]
edges = e0.split(*vertices).Edges()
self.assertEqual(len(edges), len(vertices) + 1)
endpoints = [e.endPoint() for e in edges]
self.assertTrue(all([v in endpoints for v in vecs]))