726 lines
28 KiB
Python
726 lines
28 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2007 Jürgen Riegel <juergen.riegel@web.de>
|
|
# LGPL
|
|
|
|
import os
|
|
import sys
|
|
import io
|
|
import FreeCAD, unittest, Mesh
|
|
import MeshEnums
|
|
from FreeCAD import Base
|
|
import time, tempfile, math
|
|
# http://python-kurs.eu/threads.php
|
|
try:
|
|
import _thread as thread
|
|
except Exception:
|
|
import thread
|
|
|
|
from os.path import join
|
|
|
|
#---------------------------------------------------------------------------
|
|
# define the functions to test the FreeCAD mesh module
|
|
#---------------------------------------------------------------------------
|
|
|
|
|
|
class MeshTopoTestCases(unittest.TestCase):
|
|
def setUp(self):
|
|
# set up a planar face with 18 triangles
|
|
self.planarMesh = []
|
|
for x in range(3):
|
|
for y in range(3):
|
|
self.planarMesh.append( [0.0 + x, 0.0 + y,0.0000] )
|
|
self.planarMesh.append( [1.0 + x, 1.0 + y,0.0000] )
|
|
self.planarMesh.append( [0.0 + x, 1.0 + y,0.0000] )
|
|
self.planarMesh.append( [0.0 + x, 0.0 + y,0.0000] )
|
|
self.planarMesh.append( [1.0 + x, 0.0 + y,0.0000] )
|
|
self.planarMesh.append( [1.0 + x, 1.0 + y,0.0000] )
|
|
|
|
|
|
def testCollapseFacetsSingle(self):
|
|
for i in range(18):
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
planarMeshObject.collapseFacets([i])
|
|
|
|
def testCollapseFacetsMultible(self):
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
planarMeshObject.collapseFacets(range(7))
|
|
|
|
def testCollapseFacetsAll(self):
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
planarMeshObject.collapseFacets(range(18))
|
|
|
|
def testCorruptedFacet(self):
|
|
v = FreeCAD.Vector
|
|
mesh = Mesh.Mesh()
|
|
mesh.addFacet(
|
|
v(1.0e1, -1.0e1, 1.0e1),
|
|
v(1.0e1, +1.0e1, 1.0e1),
|
|
v(0.0e0, 0.0e0, 1.0e1))
|
|
|
|
mesh.addFacet(
|
|
v(-1.0e1, -1.0e1, 1.0e1),
|
|
v(-1.0e1, +1.0e1, 1.0e1),
|
|
v(0e0, 0.0e0, 1.0e1))
|
|
|
|
mesh.addFacet(
|
|
v(+1.0e1, +1.0e1, 1.0e1),
|
|
v(-1.0e1, +1.0e1, 1.0e1),
|
|
v(.0e0, 0.0e0, 1.0e1))
|
|
|
|
mesh.addFacet(
|
|
v(+1.0e1, -1.0e1, 1.0e1),
|
|
v(-1.0e1, -1.0e1, 1.0e1),
|
|
v(.0e0, 0.0e0, 1.0e1))
|
|
|
|
mesh.addFacet(
|
|
v(-1.0e1, +1.0e1, 1.0e1),
|
|
v(+1.0e1, +1.0e1, 1.0e1),
|
|
v(+1.0e1, +1.0e1, 1.0e1))
|
|
|
|
mesh.addFacet(
|
|
v(+1.0e1, +1.0e1, 1.0e1),
|
|
v(+1.0e1, 00.0e1, 1.0e1),
|
|
v(+1.0e1, -1.0e1, 1.0e1))
|
|
|
|
self.assertEqual(mesh.CountFacets, 6)
|
|
mesh.fixIndices()
|
|
self.assertEqual(mesh.CountFacets, 5)
|
|
|
|
|
|
class MeshSplitTestCases(unittest.TestCase):
|
|
def setUp(self):
|
|
self.mesh = Mesh.createBox(1.0, 1.0, 1.0)
|
|
|
|
def testSplitFacetOnOneEdge(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, p1, (p2 + p3) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_21(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, (p1 + p3) / 2, (p2 + p3) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_12(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, (p2 + p3) / 2, (p1 + p3) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_01(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, (p1 + p2) / 2, (p2 + p3) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_10(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, (p2 + p3) / 2, (p1 + p2) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_02(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, (p1 + p2) / 2, (p1 + p3) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_20(self):
|
|
p1 = self.mesh.Points[0].Vector
|
|
p2 = self.mesh.Points[1].Vector
|
|
p3 = self.mesh.Points[2].Vector
|
|
self.mesh.splitFacet(0, (p1 + p3) / 2, (p1 + p2) / 2)
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testSplitFacetOnTwoEdges_5teps(self):
|
|
Vec3d = FreeCAD.Vector
|
|
for i in range(5):
|
|
f = self.mesh.Facets[0]
|
|
p1 = Vec3d(f.Points[0])
|
|
p2 = Vec3d(f.Points[1])
|
|
p3 = Vec3d(f.Points[2])
|
|
self.mesh.splitFacet(0, (p1 + p3) / 2, (p2 + p3) / 2)
|
|
|
|
self.assertFalse(self.mesh.hasNonManifolds())
|
|
self.assertFalse(self.mesh.hasInvalidNeighbourhood())
|
|
self.assertFalse(self.mesh.hasPointsOutOfRange())
|
|
self.assertFalse(self.mesh.hasFacetsOutOfRange())
|
|
self.assertFalse(self.mesh.hasCorruptedFacets())
|
|
self.assertTrue(self.mesh.isSolid())
|
|
|
|
def testFindNearest(self):
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay((-2,2,-6),(0,0,1))), 0)
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay((0.5,0.5,0.5),(0,0,1))), 1)
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay((0.5,0.5,0.5),(0,0,1),-math.pi/2)), 0)
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay((0.2,0.1,0.2),(0,0, 1))),
|
|
len(self.mesh.nearestFacetOnRay((0.2,0.1,0.2),(0,0,-1))))
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay((0.2,0.1,0.2),(0,0, 1), math.pi/2)),
|
|
len(self.mesh.nearestFacetOnRay((0.2,0.1,0.2),(0,0,-1), math.pi/2)))
|
|
# Apply placement to mesh
|
|
plm = Base.Placement(Base.Vector(1,2,3), Base.Rotation(1,1,1,1))
|
|
pnt = Base.Vector(0.5, 0.5, 0.5)
|
|
vec = Base.Vector(0.0, 0.0, 1.0)
|
|
|
|
self.mesh.Placement = plm
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay(pnt,vec)), 0)
|
|
|
|
# Apply the placement on the ray as well
|
|
pnt = plm.multVec(pnt)
|
|
vec = plm.Rotation.multVec(vec)
|
|
self.assertEqual(len(self.mesh.nearestFacetOnRay(pnt,vec)), 1)
|
|
|
|
def testForaminate(self):
|
|
class FilterAngle:
|
|
def __init__(self, mesh, vec, limit):
|
|
self.myMesh = mesh
|
|
self.vec = vec
|
|
self.limit = limit
|
|
|
|
def check_angle(self, item):
|
|
angle = self.myMesh.Facets[item].Normal.getAngle(self.vec)
|
|
return angle < self.limit
|
|
|
|
results = self.mesh.foraminate((0.0, 0.0, 0.0), (0,1,1))
|
|
filtered_result = list(filter(FilterAngle(self.mesh, FreeCAD.Vector(0,1,1), math.pi/2).check_angle, results.keys()))
|
|
|
|
self.assertEqual(filtered_result, list(self.mesh.foraminate((0.0, 0.0, 0.0), (0,1,1), math.pi/2).keys()))
|
|
|
|
def testForaminatePlacement(self):
|
|
pnt = Base.Vector(0.0, 0.0, 0.0)
|
|
vec = Base.Vector(0.0, 1.0, 1.0)
|
|
results = self.mesh.foraminate(pnt, vec)
|
|
self.assertEqual(len(results), 4)
|
|
|
|
# Apply placement to mesh
|
|
plm = Base.Placement(Base.Vector(1,2,3), Base.Rotation(1,1,1,1))
|
|
self.mesh.Placement = plm
|
|
self.assertEqual(len(self.mesh.foraminate(pnt, vec)), 0)
|
|
|
|
# Apply the placement on the ray as well
|
|
pnt = plm.multVec(pnt)
|
|
vec = plm.Rotation.multVec(vec)
|
|
results2 = self.mesh.foraminate(pnt, vec)
|
|
self.assertEqual(len(results2), 4)
|
|
self.assertEqual(list(results.keys()), list(results2.keys()))
|
|
|
|
class MeshGeoTestCases(unittest.TestCase):
|
|
def setUp(self):
|
|
# set up a planar face with 2 triangles
|
|
self.planarMesh = []
|
|
|
|
|
|
def testIntersection(self):
|
|
self.planarMesh.append( [0.9961,1.5413,4.3943] )
|
|
self.planarMesh.append( [9.4796,10.024,-3.0937] )
|
|
self.planarMesh.append( [1.4308,11.3841,2.6829] )
|
|
self.planarMesh.append( [2.6493,2.2536,3.0679] )
|
|
self.planarMesh.append( [13.1126,0.4857,-4.4417] )
|
|
self.planarMesh.append( [10.2410,8.9040,-3.5002] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
res=f1.intersect(f2)
|
|
self.assertTrue(len(res) == 0)
|
|
|
|
|
|
def testIntersection2(self):
|
|
self.planarMesh.append( [-16.097176,-29.891157,15.987688] )
|
|
self.planarMesh.append( [-16.176304,-29.859991,15.947966] )
|
|
self.planarMesh.append( [-16.071451,-29.900553,15.912505] )
|
|
self.planarMesh.append( [-16.092241,-29.893408,16.020439] )
|
|
self.planarMesh.append( [-16.007210,-29.926180,15.967641] )
|
|
self.planarMesh.append( [-16.064457,-29.904951,16.090832] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
# does definitely NOT intersect
|
|
res=f1.intersect(f2)
|
|
self.assertTrue(len(res) == 0)
|
|
|
|
def testIntersectionOfTransformedMesh(self):
|
|
self.planarMesh.append( [0.0,10.0,10.0] )
|
|
self.planarMesh.append( [10.0,0.0,10.0] )
|
|
self.planarMesh.append( [10.0,10.0,10.0] )
|
|
self.planarMesh.append( [6.0,8.0,10.0] )
|
|
self.planarMesh.append( [16.0,8.0,10.0] )
|
|
self.planarMesh.append( [6.0,18.0,10.0] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
mat = Base.Matrix()
|
|
mat.rotateX(1.0)
|
|
mat.rotateY(1.0)
|
|
mat.rotateZ(1.0)
|
|
planarMeshObject.transformGeometry(mat)
|
|
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
res=f1.intersect(f2)
|
|
self.assertEqual(len(res), 2)
|
|
|
|
def testIntersectionOfParallelTriangles(self):
|
|
self.planarMesh.append( [0.0,10.0,10.0] )
|
|
self.planarMesh.append( [10.0,0.0,10.0] )
|
|
self.planarMesh.append( [10.0,10.0,10.0] )
|
|
self.planarMesh.append( [6.0,8.0,10.1] )
|
|
self.planarMesh.append( [16.0,8.0,10.1] )
|
|
self.planarMesh.append( [6.0,18.0,10.1] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
mat = Base.Matrix()
|
|
mat.rotateX(1.0)
|
|
mat.rotateY(1.0)
|
|
mat.rotateZ(1.0)
|
|
planarMeshObject.transformGeometry(mat)
|
|
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
res=f1.intersect(f2)
|
|
self.assertTrue(len(res) == 0)
|
|
|
|
def testIntersectionOnEdge(self):
|
|
self.planarMesh.append( [5.0, -1.9371663331985474, 0.49737977981567383] )
|
|
self.planarMesh.append( [4.0, -1.9371663331985474, 0.49737977981567383] )
|
|
self.planarMesh.append( [5.0, -1.9842294454574585, 0.25066646933555603] )
|
|
self.planarMesh.append( [4.6488823890686035, -1.7827962636947632, 0.4577442705631256] )
|
|
self.planarMesh.append( [4.524135112762451, -2.0620131492614746, 0.5294350385665894] )
|
|
self.planarMesh.append( [4.6488823890686035, -1.8261089324951172, 0.23069120943546295] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
res = f1.intersect(f2)
|
|
self.assertEqual(len(res), 2)
|
|
|
|
def testIntersectionCoplanar(self):
|
|
self.planarMesh.append( [0.,10.,10.] )
|
|
self.planarMesh.append( [10.,0.,10.] )
|
|
self.planarMesh.append( [10.,10.,10.] )
|
|
self.planarMesh.append( [6.,8.,10.] )
|
|
self.planarMesh.append( [16.,8.,10.] )
|
|
self.planarMesh.append( [6.,18.,10.] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
res = f1.intersect(f2)
|
|
self.assertTrue(len(res) == 2)
|
|
|
|
def testIntersectionOverlap(self):
|
|
self.planarMesh.append( [0.,0.,0.] )
|
|
self.planarMesh.append( [5.,0.,0.] )
|
|
self.planarMesh.append( [8.,5.,0.] )
|
|
self.planarMesh.append( [4.,0.,0.] )
|
|
self.planarMesh.append( [10.,0.,0.] )
|
|
self.planarMesh.append( [9.,5.,0.] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
f1 = planarMeshObject.Facets[0]
|
|
f2 = planarMeshObject.Facets[1]
|
|
res = f1.intersect(f2)
|
|
self.assertTrue(len(res) == 2)
|
|
|
|
def testIntersectionOfIntersectingEdges(self):
|
|
self.planarMesh.append( [0.,10.,10.] )
|
|
self.planarMesh.append( [10.,0.,10.] )
|
|
self.planarMesh.append( [10.,10.,10.] )
|
|
self.planarMesh.append( [6.,8.,10.] )
|
|
self.planarMesh.append( [16.,8.,10.] )
|
|
self.planarMesh.append( [6.,18.,10.] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
edge1 = planarMeshObject.Facets[0].getEdge(2)
|
|
edge2 = planarMeshObject.Facets[1].getEdge(2)
|
|
res = edge1.intersectWithEdge(edge2)
|
|
self.assertTrue(len(res) == 1)
|
|
self.assertEqual(res[0][0], 6.0)
|
|
self.assertEqual(res[0][1], 10.0)
|
|
self.assertEqual(res[0][2], 10.0)
|
|
|
|
def testIntersectionOfParallelEdges(self):
|
|
self.planarMesh.append( [0.,10.,10.] )
|
|
self.planarMesh.append( [10.,0.,10.] )
|
|
self.planarMesh.append( [10.,10.,10.] )
|
|
self.planarMesh.append( [6.,8.,10.] )
|
|
self.planarMesh.append( [16.,8.,10.] )
|
|
self.planarMesh.append( [6.,18.,10.] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
edge1 = planarMeshObject.Facets[0].getEdge(2)
|
|
edge2 = planarMeshObject.Facets[1].getEdge(0)
|
|
res = edge1.intersectWithEdge(edge2)
|
|
self.assertTrue(len(res) == 0)
|
|
|
|
def testIntersectionOfCollinearEdges(self):
|
|
self.planarMesh.append( [0.,0.,0.] )
|
|
self.planarMesh.append( [6.,0.,0.] )
|
|
self.planarMesh.append( [3.,4.,0.] )
|
|
self.planarMesh.append( [7.,0.,0.] )
|
|
self.planarMesh.append( [13.,0.,0.] )
|
|
self.planarMesh.append( [10.,4.,0.] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
edge1 = planarMeshObject.Facets[0].getEdge(0)
|
|
edge2 = planarMeshObject.Facets[1].getEdge(0)
|
|
res = edge1.intersectWithEdge(edge2)
|
|
self.assertTrue(len(res) == 0)
|
|
|
|
def testIntersectionOfWarpedEdges(self):
|
|
self.planarMesh.append( [0.,0.,0.] )
|
|
self.planarMesh.append( [6.,0.,0.] )
|
|
self.planarMesh.append( [3.,4.,0.] )
|
|
self.planarMesh.append( [2.,2.,1.] )
|
|
self.planarMesh.append( [8.,2.,1.] )
|
|
self.planarMesh.append( [5.,6.,1.] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
edge1 = planarMeshObject.Facets[0].getEdge(1)
|
|
edge2 = planarMeshObject.Facets[1].getEdge(0)
|
|
res = edge1.intersectWithEdge(edge2)
|
|
self.assertTrue(len(res) == 0)
|
|
|
|
def testSelfIntersection(self):
|
|
s = b"""solid Simple
|
|
facet normal 0.0e0 0.0e0 1.0e1
|
|
outer loop
|
|
vertex 0.0e1 0.0e1 1.0e1
|
|
vertex 0.0e1 +1.0e1 1.0e1
|
|
vertex +1.0e1 0.0e1 1.0e1
|
|
endloop
|
|
endfacet
|
|
facet normal 0.0e0 0.0e0 1.0e1
|
|
outer loop
|
|
vertex 0.0e1 +1.0e1 1.0e1
|
|
vertex +1.0e1 0.0e1 1.0e1
|
|
vertex 1.0e1 1.0e1 1.0e1
|
|
endloop
|
|
endfacet
|
|
facet normal 0.0e0 0.0e0 1.0e1
|
|
outer loop
|
|
vertex 0.0e1 0.0e1 1.0e1
|
|
vertex 0.0e1 +1.0e1 1.0e1
|
|
vertex -1.0e1 1.0e1 1.0e1
|
|
endloop
|
|
endfacet
|
|
facet normal 0.0e0 0.0e0 1.0e1
|
|
outer loop
|
|
vertex 0.0e1 0.0e1 1.0e1
|
|
vertex +1.0e1 0.0e1 1.0e1
|
|
vertex +1.0e1 -1.0e1 1.0e1
|
|
endloop
|
|
endfacet
|
|
facet normal 0.0e0 0.0e0 1.0e1
|
|
outer loop
|
|
vertex 0.6e1 0.8e1 1.0e1
|
|
vertex +1.6e1 0.8e1 1.0e1
|
|
vertex +0.6e1 1.8e1 1.0e1
|
|
endloop
|
|
endfacet
|
|
endsolid Simple"""
|
|
mesh = Mesh.Mesh()
|
|
data = io.BytesIO(s)
|
|
mesh.read(Stream=data, Format="AST")
|
|
self.assertTrue(mesh.hasSelfIntersections())
|
|
|
|
|
|
class PivyTestCases(unittest.TestCase):
|
|
def setUp(self):
|
|
# set up a planar face with 2 triangles
|
|
self.planarMesh = []
|
|
FreeCAD.newDocument("MeshTest")
|
|
|
|
def testRayPick(self):
|
|
if not FreeCAD.GuiUp:
|
|
return
|
|
self.planarMesh.append( [-16.097176,-29.891157,15.987688] )
|
|
self.planarMesh.append( [-16.176304,-29.859991,15.947966] )
|
|
self.planarMesh.append( [-16.071451,-29.900553,15.912505] )
|
|
self.planarMesh.append( [-16.092241,-29.893408,16.020439] )
|
|
self.planarMesh.append( [-16.007210,-29.926180,15.967641] )
|
|
self.planarMesh.append( [-16.064457,-29.904951,16.090832] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
from pivy import coin; import FreeCADGui
|
|
Mesh.show(planarMeshObject)
|
|
view=FreeCADGui.ActiveDocument.ActiveView.getViewer()
|
|
rp=coin.SoRayPickAction(view.getSoRenderManager().getViewportRegion())
|
|
rp.setRay(coin.SbVec3f(-16.05,16.0,16.0),coin.SbVec3f(0,-1,0))
|
|
rp.apply(view.getSoRenderManager().getSceneGraph())
|
|
pp=rp.getPickedPoint()
|
|
self.assertTrue(pp is not None)
|
|
det=pp.getDetail()
|
|
self.assertTrue(det.getTypeId() == coin.SoFaceDetail.getClassTypeId())
|
|
det=coin.cast(det, det.getTypeId().getName().getString())
|
|
self.assertTrue(det.getFaceIndex() == 1)
|
|
|
|
def testPrimitiveCount(self):
|
|
if not FreeCAD.GuiUp:
|
|
return
|
|
self.planarMesh.append( [-16.097176,-29.891157,15.987688] )
|
|
self.planarMesh.append( [-16.176304,-29.859991,15.947966] )
|
|
self.planarMesh.append( [-16.071451,-29.900553,15.912505] )
|
|
self.planarMesh.append( [-16.092241,-29.893408,16.020439] )
|
|
self.planarMesh.append( [-16.007210,-29.926180,15.967641] )
|
|
self.planarMesh.append( [-16.064457,-29.904951,16.090832] )
|
|
planarMeshObject = Mesh.Mesh(self.planarMesh)
|
|
|
|
from pivy import coin; import FreeCADGui
|
|
Mesh.show(planarMeshObject)
|
|
view=FreeCADGui.ActiveDocument.ActiveView
|
|
view.setAxisCross(False)
|
|
pc=coin.SoGetPrimitiveCountAction()
|
|
pc.apply(view.getSceneGraph())
|
|
self.assertTrue(pc.getTriangleCount() == 2)
|
|
#self.assertTrue(pc.getPointCount() == 6)
|
|
|
|
def tearDown(self):
|
|
#closing doc
|
|
FreeCAD.closeDocument("MeshTest")
|
|
|
|
# Threads
|
|
|
|
def loadFile(name):
|
|
#lock.acquire()
|
|
mesh=Mesh.Mesh()
|
|
#FreeCAD.Console.PrintMessage("Create mesh instance\n")
|
|
#lock.release()
|
|
mesh.read(name)
|
|
#FreeCAD.Console.PrintMessage("Mesh loaded successfully.\n")
|
|
|
|
def createMesh(r,s):
|
|
#FreeCAD.Console.PrintMessage("Create sphere (%s,%s)...\n"%(r,s))
|
|
mesh=Mesh.createSphere(r,s)
|
|
#FreeCAD.Console.PrintMessage("... destroy sphere\n")
|
|
|
|
class LoadMeshInThreadsCases(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
pass
|
|
|
|
def testSphereMesh(self):
|
|
for i in range(6,8):
|
|
thread.start_new(createMesh,(10.0,(i+1)*20))
|
|
time.sleep(10)
|
|
|
|
def testLoadMesh(self):
|
|
mesh=Mesh.createSphere(10.0,100) # a fine sphere
|
|
name=tempfile.gettempdir() + os.sep + "mesh.stl"
|
|
mesh.write(name)
|
|
#FreeCAD.Console.PrintMessage("Write mesh to %s\n"%(name))
|
|
#lock=thread.allocate_lock()
|
|
for i in range(2):
|
|
thread.start_new(loadFile,(name,))
|
|
time.sleep(1)
|
|
|
|
def tearDown(self):
|
|
pass
|
|
|
|
|
|
class PolynomialFitCases(unittest.TestCase):
|
|
def setUp(self):
|
|
pass
|
|
|
|
def testFitGood(self):
|
|
# symmetric
|
|
v=[]
|
|
v.append(FreeCAD.Vector(0,0,0.0))
|
|
v.append(FreeCAD.Vector(1,0,0.5))
|
|
v.append(FreeCAD.Vector(2,0,0.0))
|
|
v.append(FreeCAD.Vector(0,1,0.5))
|
|
v.append(FreeCAD.Vector(1,1,1.0))
|
|
v.append(FreeCAD.Vector(2,1,0.5))
|
|
v.append(FreeCAD.Vector(0,2,0.0))
|
|
v.append(FreeCAD.Vector(1,2,0.5))
|
|
v.append(FreeCAD.Vector(2,2,0.0))
|
|
d = Mesh.polynomialFit(v)
|
|
c = d["Coefficients"]
|
|
#print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5]))
|
|
for i in d["Residuals"]:
|
|
self.assertTrue(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i))
|
|
|
|
def testFitExact(self):
|
|
# symmetric
|
|
v=[]
|
|
v.append(FreeCAD.Vector(0,0,0.0))
|
|
v.append(FreeCAD.Vector(1,0,0.0))
|
|
v.append(FreeCAD.Vector(2,0,0.0))
|
|
v.append(FreeCAD.Vector(0,1,0.0))
|
|
v.append(FreeCAD.Vector(1,1,1.0))
|
|
v.append(FreeCAD.Vector(2,1,0.0))
|
|
d = Mesh.polynomialFit(v)
|
|
c = d["Coefficients"]
|
|
#print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5]))
|
|
for i in d["Residuals"]:
|
|
self.assertTrue(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i))
|
|
|
|
def testFitBad(self):
|
|
# symmetric
|
|
v=[]
|
|
v.append(FreeCAD.Vector(0,0,0.0))
|
|
v.append(FreeCAD.Vector(1,0,0.0))
|
|
v.append(FreeCAD.Vector(2,0,0.0))
|
|
v.append(FreeCAD.Vector(0,1,0.0))
|
|
v.append(FreeCAD.Vector(1,1,1.0))
|
|
v.append(FreeCAD.Vector(2,1,0.0))
|
|
v.append(FreeCAD.Vector(0,2,0.0))
|
|
v.append(FreeCAD.Vector(1,2,0.0))
|
|
v.append(FreeCAD.Vector(2,2,0.0))
|
|
d = Mesh.polynomialFit(v)
|
|
c = d["Coefficients"]
|
|
#print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5]))
|
|
for i in d["Residuals"]:
|
|
self.assertFalse(math.fabs(i) < 0.0001, "Residual %f must be higher" % math.fabs(i))
|
|
|
|
def tearDown(self):
|
|
pass
|
|
|
|
|
|
class NastranReader(unittest.TestCase):
|
|
def setUp(self):
|
|
self.test_dir = join(FreeCAD.getHomePath(), "Mod", "Mesh", "App", "TestData")
|
|
|
|
def testEightCharGRIDElement(self):
|
|
m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRID_CQUAD4.bdf")
|
|
self.assertEqual(m.CountPoints,10)
|
|
self.assertEqual(m.CountFacets,8) # Quads split into two triangles
|
|
|
|
def testDelimitedGRIDElement(self):
|
|
m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf")
|
|
self.assertEqual(m.CountPoints,10)
|
|
self.assertEqual(m.CountFacets,8) # Quads split into two triangles
|
|
|
|
def testSixteenCharGRIDElement(self):
|
|
m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf")
|
|
self.assertEqual(m.CountPoints,4)
|
|
self.assertEqual(m.CountFacets,2) # Quads split into two triangles
|
|
|
|
def testCTRIA3Element(self):
|
|
m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRID_CTRIA3.bdf")
|
|
self.assertEqual(m.CountPoints,3)
|
|
self.assertEqual(m.CountFacets,1)
|
|
|
|
def tearDown(self):
|
|
pass
|
|
|
|
class MeshSubElement(unittest.TestCase):
|
|
def setUp(self):
|
|
self.mesh = Mesh.createBox(1.0, 1.0, 1.0)
|
|
|
|
def testCenterOfGravity(self):
|
|
c = self.mesh.CenterOfGravity
|
|
self.assertEqual(c, Base.Vector(0.0, 0.0, 0.0))
|
|
|
|
def testSubElements(self):
|
|
types = self.mesh.getElementTypes()
|
|
self.assertIn("Mesh", types)
|
|
self.assertIn("Segment", types)
|
|
|
|
def testCountSubElements(self):
|
|
self.assertEqual(self.mesh.countSubElements("Mesh"), 1)
|
|
self.assertEqual(self.mesh.countSubElements("Segment"), 0)
|
|
|
|
def testFacesFromSubElement(self):
|
|
element = self.mesh.getFacesFromSubElement("Mesh", 0)
|
|
self.assertIsInstance(element, tuple)
|
|
self.assertEqual(len(element), 2)
|
|
self.assertEqual(len(element[0]), 8)
|
|
self.assertEqual(len(element[1]), 12)
|
|
|
|
def testSegmentSubElement(self):
|
|
self.mesh.addSegment([0, 2, 4, 6, 8])
|
|
self.assertEqual(self.mesh.countSegments(), 1)
|
|
self.assertEqual(self.mesh.countSubElements("Segment"), 1)
|
|
element = self.mesh.getFacesFromSubElement("Segment", 0)
|
|
self.assertIsInstance(element, tuple)
|
|
self.assertEqual(len(element), 2)
|
|
self.assertEqual(len(element[0]), 7)
|
|
self.assertEqual(len(element[1]), 5)
|
|
segment = self.mesh.meshFromSegment(self.mesh.getSegment(0))
|
|
self.assertEqual(segment.CountPoints, 7)
|
|
self.assertEqual(segment.CountFacets, 5)
|
|
|
|
def tearDown(self):
|
|
pass
|
|
|
|
class MeshProperty(unittest.TestCase):
|
|
def setUp(self):
|
|
self.doc = FreeCAD.newDocument("MeshTest")
|
|
|
|
def tearDown(self):
|
|
FreeCAD.closeDocument(self.doc.Name)
|
|
|
|
def testMaterial(self):
|
|
mesh = self.doc.addObject("Mesh::Feature", "Sphere")
|
|
mesh.Mesh = Mesh.createBox(1.0, 1.0, 1.0)
|
|
len1 = int(mesh.Mesh.CountFacets / 2)
|
|
len2 = int(mesh.Mesh.CountFacets - len1)
|
|
material = {"transparency" : [0.2] * len1 + [0.8] * len2}
|
|
material["binding"] = MeshEnums.Binding.PER_FACE
|
|
material["ambientColor"] = [(1,0,0)] * (len1 + len2)
|
|
material["diffuseColor"] = [(0,1,0)] * (len1 + len2)
|
|
material["specularColor"] = [(0,0,1)] * (len1 + len2)
|
|
material["emissiveColor"] = [(1,1,1)] * (len1 + len2)
|
|
material["shininess"] = [0.3] * (len1 + len2)
|
|
|
|
mesh.addProperty("Mesh::PropertyMaterial", "Material")
|
|
mesh.Material = material
|
|
|
|
TempPath = tempfile.gettempdir()
|
|
SaveName = TempPath + os.sep + "mesh_with_material.FCStd"
|
|
self.doc.saveAs(SaveName)
|
|
FreeCAD.closeDocument(self.doc.Name)
|
|
|
|
self.doc = FreeCAD.openDocument(SaveName)
|
|
mesh2 = self.doc.Sphere
|
|
material2 = mesh2.Material
|
|
|
|
self.assertEqual(int(material2["binding"]), int(MeshEnums.Binding.PER_FACE))
|
|
self.assertEqual(len(material2["ambientColor"]), len1 + len2)
|
|
self.assertEqual(len(material2["diffuseColor"]), len1 + len2)
|
|
self.assertEqual(len(material2["specularColor"]), len1 + len2)
|
|
self.assertEqual(len(material2["emissiveColor"]), len1 + len2)
|
|
self.assertEqual(len(material2["shininess"]), len1 + len2)
|
|
self.assertEqual(len(material2["transparency"]), len1 + len2)
|
|
|