1 """
2 Triangle Mesh holds the faces and edges of a triangular mesh.
3
4 """
5
6 from fabmetheus_utilities.geometry.geometry_tools import face
7 from fabmetheus_utilities.geometry.geometry_tools import vertex
8 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
9 from fabmetheus_utilities.geometry.geometry_utilities import matrix
10 from fabmetheus_utilities.geometry.solids import group
11 from fabmetheus_utilities import xml_simple_writer
12 from fabmetheus_utilities.vector3 import Vector3
13 from fabmetheus_utilities.vector3index import Vector3Index
14 from fabmetheus_utilities import euclidean
15 from fabmetheus_utilities import intercircle
16 import cmath
17 import math
18
19
20 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
21 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
22 __date__ = '$Date: 2008/02/05 $'
23 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
24
25
26 -def addEdgePair( edgePairTable, edges, faceEdgeIndex, remainingEdgeIndex, remainingEdgeTable ):
27 'Add edge pair to the edge pair table.'
28 if faceEdgeIndex == remainingEdgeIndex:
29 return
30 if not faceEdgeIndex in remainingEdgeTable:
31 return
32 edgePair = EdgePair().getFromIndexesEdges( [ remainingEdgeIndex, faceEdgeIndex ], edges )
33 edgePairTable[ str( edgePair ) ] = edgePair
34
36 'Add faces from a polygon which is concave.'
37 if len(indexedLoop) < 3:
38 return
39 remainingLoop = indexedLoop[:]
40 while len(remainingLoop) > 2:
41 remainingLoop = getRemainingLoopAddFace(faces, remainingLoop)
42
44 'Add faces from a convex polygon.'
45 if len(indexedLoop) < 3:
46 return
47 indexBegin = indexedLoop[0].index
48 for indexedPointIndex in xrange(1, len(indexedLoop) - 1):
49 indexCenter = indexedLoop[indexedPointIndex].index
50 indexEnd = indexedLoop[(indexedPointIndex + 1) % len(indexedLoop) ].index
51 if indexBegin != indexCenter and indexCenter != indexEnd and indexEnd != indexBegin:
52 faceFromConvex = face.Face()
53 faceFromConvex.index = len(faces)
54 faceFromConvex.vertexIndexes.append(indexBegin)
55 faceFromConvex.vertexIndexes.append(indexCenter)
56 faceFromConvex.vertexIndexes.append(indexEnd)
57 faces.append(faceFromConvex)
58
60 'Add faces from loops.'
61 for indexedLoopIndex in xrange(max(len(indexedLoopBottom), len(indexedLoopTop))):
62 indexedLoopIndexEnd = (indexedLoopIndex + 1) % len(indexedLoopBottom)
63 indexedConvex = []
64 if len(indexedLoopBottom) > 1:
65 indexedConvex.append(indexedLoopBottom[indexedLoopIndex])
66 indexedConvex.append(indexedLoopBottom[(indexedLoopIndex + 1) % len(indexedLoopBottom)])
67 else:
68 indexedConvex.append(indexedLoopBottom[0])
69 if len(indexedLoopTop) > 1:
70 indexedConvex.append(indexedLoopTop[(indexedLoopIndex + 1) % len(indexedLoopTop)])
71 indexedConvex.append(indexedLoopTop[indexedLoopIndex])
72 else:
73 indexedConvex.append(indexedLoopTop[0])
74 addFacesByConvex(faces, indexedConvex)
75
77 'Add faces from loops.'
78 if len(indexedLoops) < 2:
79 return
80 for indexedLoopsIndex in xrange(len(indexedLoops) - 2):
81 addFacesByConvexBottomTopLoop(faces, indexedLoops[indexedLoopsIndex], indexedLoops[indexedLoopsIndex + 1])
82 indexedLoopBottom = indexedLoops[-2]
83 indexedLoopTop = indexedLoops[-1]
84 if len(indexedLoopTop) < 1:
85 indexedLoopTop = indexedLoops[0]
86 addFacesByConvexBottomTopLoop(faces, indexedLoopBottom, indexedLoopTop)
87
89 'Add faces from a reversed convex polygon.'
90 addFacesByConvex(faces, indexedLoop[: : -1])
91
97
99 'Add faces from a polygon which may be concave.'
100 if len(indexedLoop) < 3:
101 return
102 lastNormal = None
103 for pointIndex, point in enumerate(indexedLoop):
104 center = indexedLoop[(pointIndex + 1) % len(indexedLoop)]
105 end = indexedLoop[(pointIndex + 2) % len(indexedLoop)]
106 normal = euclidean.getNormalWeighted(point, center, end)
107 if abs(normal) > 0.0:
108 if lastNormal != None:
109 if lastNormal.dot(normal) < 0.0:
110 addFacesByConcaveLoop(faces, indexedLoop)
111 return
112 lastNormal = normal
113
114
115
116
117
118
119 addFacesByConvex(faces, indexedLoop)
120
122 'Add faces from a reversed convex polygon.'
123 addFacesByLoop(faces, indexedLoop[: : -1])
124
126 'Add the points in the loop to the point table.'
127 for point in loop:
128 pointTable[point] = None
129
140
148
154
161
167
172
178
183
185 'Insert a point into a loop, at the index at which the loop would be shortest.'
186 close = importRadius + importRadius
187 shortestAdditionalLength = close
188 shortestLoop = None
189 shortestPointIndex = None
190 for loop in loops:
191 if len(loop) > 3:
192 for pointIndex in xrange(len(loop)):
193 additionalLoopLength = getAdditionalLoopLength(loop, point, pointIndex)
194 if additionalLoopLength < shortestAdditionalLength:
195 if getIsPointCloseInline(close, loop, point, pointIndex):
196 shortestAdditionalLength = additionalLoopLength
197 shortestLoop = loop
198 shortestPointIndex = pointIndex
199 if shortestPointIndex != None:
200 shortestLoop.insert( shortestPointIndex, point )
201
209
211 'Get and add an indexed grid.'
212 indexedGrid = []
213 for row in grid:
214 indexedRow = []
215 indexedGrid.append( indexedRow )
216 for pointComplex in row:
217 vector3index = Vector3Index( len(vertexes), pointComplex.real, pointComplex.imag, z )
218 indexedRow.append(vector3index)
219 vertexes.append(vector3index)
220 return indexedGrid
221
231
233 'Get and add indexed loops.'
234 indexedLoops = []
235 for z in zList:
236 indexedLoop = getAddIndexedLoop( loop, vertexes, z )
237 indexedLoops.append(indexedLoop)
238 return indexedLoops
239
241 'Get the additional length added by inserting a point into a loop.'
242 afterPoint = loop[pointIndex]
243 beforePoint = loop[(pointIndex + len(loop) - 1) % len(loop)]
244 return abs(point - beforePoint) + abs(point - afterPoint) - abs(afterPoint - beforePoint)
245
247 'Get span direction for the majority of the overhanging extrusion perimeter, if any.'
248 if len( belowLoops ) < 1:
249 return None
250 belowOutsetLoops = []
251 overhangInset = 1.875 * layerThickness
252 slightlyGreaterThanOverhang = 1.1 * overhangInset
253 for loop in belowLoops:
254 centers = intercircle.getCentersFromLoopDirection( True, loop, slightlyGreaterThanOverhang )
255 for center in centers:
256 outset = intercircle.getSimplifiedInsetFromClockwiseLoop( center, overhangInset )
257 if intercircle.isLargeSameDirection( outset, center, overhangInset ):
258 belowOutsetLoops.append( outset )
259 bridgeRotation = complex()
260 for loop in layerLoops:
261 for pointIndex in xrange(len(loop)):
262 previousIndex = ( pointIndex + len(loop) - 1 ) % len(loop)
263 bridgeRotation += getOverhangDirection( belowOutsetLoops, loop[ previousIndex ], loop[pointIndex] )
264 if abs( bridgeRotation ) < 0.7853 * layerThickness:
265 return None
266 else:
267 bridgeRotation /= abs( bridgeRotation )
268 return cmath.sqrt( bridgeRotation )
269
283
285 'Get the complex where the carve intersects the edge.'
286 firstVertex = vertexes[ edge.vertexIndexes[0] ]
287 firstVertexComplex = firstVertex.dropAxis()
288 secondVertex = vertexes[ edge.vertexIndexes[1] ]
289 secondVertexComplex = secondVertex.dropAxis()
290 zMinusFirst = z - firstVertex.z
291 up = secondVertex.z - firstVertex.z
292 return zMinusFirst * ( secondVertexComplex - firstVertexComplex ) / up + firstVertexComplex
293
295 'Get descending area loops which include most of the points.'
296 loops = intercircle.getCentersFromPoints(allPoints, importRadius)
297 descendingAreaLoops = []
298 sortLoopsInOrderOfArea(True, loops)
299 pointDictionary = {}
300 for loop in loops:
301 if len(loop) > 2 and getOverlapRatio(loop, pointDictionary) < 0.2:
302 intercircle.directLoop(not euclidean.getIsInFilledRegion(descendingAreaLoops, loop[0]), loop)
303 descendingAreaLoops.append(loop)
304 addLoopToPointTable(loop, pointDictionary)
305 descendingAreaLoops = euclidean.getSimplifiedLoops(descendingAreaLoops, importRadius)
306 return getLoopsWithCorners(corners, importRadius, descendingAreaLoops, pointDictionary)
307
311
313 'Get doubled plane angle around z of the overhanging segment.'
314 endpoint = overhangingSegment[0]
315 roundZ = endpoint.point - endpoint.otherEndpoint.point
316 roundZ *= segmentRoundZ
317 if abs( roundZ ) == 0.0:
318 return complex()
319 if roundZ.real < 0.0:
320 roundZ *= - 1.0
321 roundZLength = abs( roundZ )
322 return roundZ * roundZ / roundZLength
323
325 'Get geometry output dictionary by faces and vertexes.'
326 return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}}
327
329 'Get the geometry output copy.'
330 objectClass = object.__class__
331 if objectClass == dict:
332 objectCopy = {}
333 for key in object:
334 objectCopy[key] = getGeometryOutputCopy(object[key])
335 return objectCopy
336 if objectClass == list:
337 objectCopy = []
338 for value in object:
339 objectCopy.append(getGeometryOutputCopy(value))
340 return objectCopy
341 if objectClass == face.Face or objectClass == Vector3 or objectClass == Vector3Index:
342 return object.copy()
343 return object
344
346 'Get indexed cell loops from an indexed grid.'
347 indexedCellLoops = []
348 for rowIndex in xrange( len( grid ) - 1 ):
349 rowBottom = grid[ rowIndex ]
350 rowTop = grid[ rowIndex + 1 ]
351 for columnIndex in xrange( len( rowBottom ) - 1 ):
352 columnIndexEnd = columnIndex + 1
353 indexedConvex = []
354 indexedConvex.append( rowBottom[ columnIndex ] )
355 indexedConvex.append( rowBottom[ columnIndex + 1 ] )
356 indexedConvex.append( rowTop[ columnIndex + 1 ] )
357 indexedConvex.append( rowTop[ columnIndex ] )
358 indexedCellLoops.append( indexedConvex )
359 return indexedCellLoops
360
362 'Get indexed loop from around the indexed grid.'
363 indexedLoop = indexedGrid[0][:]
364 for row in indexedGrid[1 : -1]:
365 indexedLoop.append( row[-1] )
366 indexedLoop += indexedGrid[-1][: : -1]
367 for row in indexedGrid[ len( indexedGrid ) - 2 : 0 : - 1 ]:
368 indexedLoop.append( row[0] )
369 return indexedLoop
370
372 'Get the inset vertex.'
373 pointIndex = getWideAnglePointIndex(loop)
374 point = loop[ pointIndex % len(loop) ]
375 afterPoint = loop[(pointIndex + 1) % len(loop)]
376 beforePoint = loop[ ( pointIndex - 1 ) % len(loop) ]
377 afterSegmentNormalized = euclidean.getNormalized( afterPoint - point )
378 beforeSegmentNormalized = euclidean.getNormalized( beforePoint - point )
379 afterClockwise = complex( afterSegmentNormalized.imag, - afterSegmentNormalized.real )
380 beforeWiddershins = complex( - beforeSegmentNormalized.imag, beforeSegmentNormalized.real )
381 midpoint = afterClockwise + beforeWiddershins
382 midpointNormalized = midpoint / abs( midpoint )
383 return point + midpointNormalized * tinyRadius
384
386 'Insert a point into a loop, at the index at which the loop would be shortest.'
387 afterCenterComplex = loop[pointIndex]
388 if abs(afterCenterComplex - point) > close:
389 return False
390 afterEndComplex = loop[(pointIndex + 1) % len(loop)]
391 if not isInline( point, afterCenterComplex, afterEndComplex ):
392 return False
393 beforeCenterComplex = loop[(pointIndex + len(loop) - 1) % len(loop)]
394 if abs(beforeCenterComplex - point) > close:
395 return False
396 beforeEndComplex = loop[(pointIndex + len(loop) - 2) % len(loop)]
397 return isInline(point, beforeCenterComplex, beforeEndComplex)
398
407
409 'Get loops from a carve of a correct mesh.'
410 remainingEdgeTable = getRemainingEdgeTable(edges, vertexes, z)
411 remainingValues = remainingEdgeTable.values()
412 for edge in remainingValues:
413 if len( edge.faceIndexes ) < 2:
414 print('This should never happen, there is a hole in the triangle mesh, each edge should have two faces.')
415 print(edge)
416 print('Something will still be printed, but there is no guarantee that it will be the correct shape.' )
417 print('Once the gcode is saved, you should check over the layer with a z of:')
418 print(z)
419 return []
420 loops = []
421 while isPathAdded( edges, faces, loops, remainingEdgeTable, vertexes, z ):
422 pass
423 if euclidean.isLoopListIntersecting(loops):
424 print('Warning, the triangle mesh slice intersects itself in getLoopsFromCorrectMesh in triangle_mesh.')
425 print('Something will still be printed, but there is no guarantee that it will be the correct shape.')
426 print('Once the gcode is saved, you should check over the layer with a z of:')
427 print(z)
428 return []
429 return loops
430
431
432
433
434
435
436
437
438
439
440
441
442
444 'Get loops from a carve of an unproven mesh.'
445 edgePairTable = {}
446 corners = []
447 remainingEdgeTable = getRemainingEdgeTable(edges, vertexes, z)
448 remainingEdgeTableKeys = remainingEdgeTable.keys()
449 for remainingEdgeIndexKey in remainingEdgeTable:
450 edge = remainingEdgeTable[remainingEdgeIndexKey]
451 carveIntersection = getCarveIntersectionFromEdge(edge, vertexes, z)
452 corners.append(carveIntersection)
453 for edgeFaceIndex in edge.faceIndexes:
454 face = faces[edgeFaceIndex]
455 for edgeIndex in face.edgeIndexes:
456 addEdgePair(edgePairTable, edges, edgeIndex, remainingEdgeIndexKey, remainingEdgeTable)
457 allPoints = corners[:]
458 for edgePairValue in edgePairTable.values():
459 addPointsAtZ(edgePairValue, allPoints, importRadius, vertexes, z)
460 pointTable = {}
461 return getDescendingAreaLoops(allPoints, corners, importRadius)
462
464 'Add corners to the loops.'
465 for corner in corners:
466 if corner not in pointTable:
467 addWithLeastLength(importRadius, loops, corner)
468 pointTable[corner] = None
469 return euclidean.getSimplifiedLoops(loops, importRadius)
470
472 'Get the next edge index in the mesh carve.'
473 for faceIndex in edge.faceIndexes:
474 face = faces[ faceIndex ]
475 for edgeIndex in face.edgeIndexes:
476 if edgeIndex in remainingEdgeTable:
477 return edgeIndex
478 return - 1
479
488
490 'Add to span direction from the endpoint segments which overhang the layer below.'
491 segment = segmentEnd - segmentBegin
492 normalizedSegment = euclidean.getNormalized( complex( segment.real, segment.imag ) )
493 segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
494 segmentBegin = segmentYMirror * segmentBegin
495 segmentEnd = segmentYMirror * segmentEnd
496 solidXIntersectionList = []
497 y = segmentBegin.imag
498 solidXIntersectionList.append( euclidean.XIntersectionIndex( - 1.0, segmentBegin.real ) )
499 solidXIntersectionList.append( euclidean.XIntersectionIndex( - 1.0, segmentEnd.real ) )
500 for belowLoopIndex in xrange( len( belowOutsetLoops ) ):
501 belowLoop = belowOutsetLoops[ belowLoopIndex ]
502 rotatedOutset = euclidean.getPointsRoundZAxis( segmentYMirror, belowLoop )
503 euclidean.addXIntersectionIndexesFromLoopY( rotatedOutset, belowLoopIndex, solidXIntersectionList, y )
504 overhangingSegments = euclidean.getSegmentsFromXIntersectionIndexes( solidXIntersectionList, y )
505 overhangDirection = complex()
506 for overhangingSegment in overhangingSegments:
507 overhangDirection += getDoubledRoundZ( overhangingSegment, normalizedSegment )
508 return overhangDirection
509
511 'Get the overlap ratio between the loop and the point table.'
512 numberOfOverlaps = 0
513 for point in loop:
514 if point in pointTable:
515 numberOfOverlaps += 1
516 return float( numberOfOverlaps ) / float(len(loop))
517
518 -def getPath( edges, pathIndexes, loop, z ):
519 'Get the path from the edge intersections.'
520 path = []
521 for pathIndexIndex in xrange( len( pathIndexes ) ):
522 pathIndex = pathIndexes[ pathIndexIndex ]
523 edge = edges[ pathIndex ]
524 carveIntersection = getCarveIntersectionFromEdge( edge, loop, z )
525 path.append( carveIntersection )
526 return path
527
534
541
542 -def getRemainingEdgeTable(edges, vertexes, z):
543 'Get the remaining edge hashtable.'
544 remainingEdgeTable = {}
545 if len(edges) > 0:
546 if edges[0].zMinimum == None:
547 for edge in edges:
548 setEdgeMaximumMinimum(edge, vertexes)
549 for edgeIndex in xrange(len(edges)):
550 edge = edges[edgeIndex]
551 if (edge.zMinimum < z) and (edge.zMaximum > z):
552 remainingEdgeTable[edgeIndex] = edge
553 return remainingEdgeTable
554
555 -def getRemainingLoopAddFace(faces, remainingLoop):
556 'Get the remaining loop and add face.'
557 for indexedVertexIndex, indexedVertex in enumerate(remainingLoop):
558 nextIndex = (indexedVertexIndex + 1) % len(remainingLoop)
559 previousIndex = (indexedVertexIndex + len(remainingLoop) - 1) % len(remainingLoop)
560 nextVertex = remainingLoop[nextIndex]
561 previousVertex = remainingLoop[previousIndex]
562 remainingPath = euclidean.getAroundLoop((indexedVertexIndex + 2) % len(remainingLoop), previousIndex, remainingLoop)
563 if len(remainingLoop) < 4 or getIsPathEntirelyOutsideTriangle(previousVertex, indexedVertex, nextVertex, remainingPath):
564 faceConvex = face.Face()
565 faceConvex.index = len(faces)
566 faceConvex.vertexIndexes.append(indexedVertex.index)
567 faceConvex.vertexIndexes.append(nextVertex.index)
568 faceConvex.vertexIndexes.append(previousVertex.index)
569 faces.append(faceConvex)
570 return euclidean.getAroundLoop(nextIndex, indexedVertexIndex, remainingLoop)
571 print('Warning, could not decompose polygon in getRemainingLoopAddFace in trianglemesh for:')
572 print(remainingLoop)
573 return []
574
576 'Get the face which is shared by two edges.'
577 for firstEdgeFaceIndex in firstEdge.faceIndexes:
578 for secondEdgeFaceIndex in secondEdge.faceIndexes:
579 if firstEdgeFaceIndex == secondEdgeFaceIndex:
580 return faces[ firstEdgeFaceIndex ]
581 return None
582
584 'Get symmetrix x loop.'
585 loop = []
586 for point in path:
587 vector3Index = Vector3Index(len(vertexes), x, point.real, point.imag)
588 loop.append(vector3Index)
589 vertexes.append(vector3Index)
590 return loop
591
593 'Get symmetrix y loop.'
594 loop = []
595 for point in path:
596 vector3Index = Vector3Index(len(vertexes), point.real, y, point.imag)
597 loop.append(vector3Index)
598 vertexes.append(vector3Index)
599 return loop
600
602 'Get unified output.'
603 if len(outputs) < 1:
604 return {}
605 if len(outputs) == 1:
606 return outputs[0]
607 return {'union' : {'shapes' : outputs}}
608
610 'Get unique vertexes.'
611 vertexDictionary = {}
612 uniqueVertexes = []
613 for loop in loops:
614 for vertexIndex, vertex in enumerate(loop):
615 vertexTuple = (vertex.x, vertex.y, vertex.z)
616 if vertexTuple in vertexDictionary:
617 loop[vertexIndex] = vertexDictionary[vertexTuple]
618 else:
619 if vertex.__class__ == Vector3Index:
620 loop[vertexIndex].index = len(vertexDictionary)
621 else:
622 loop[vertexIndex] = Vector3Index(len(vertexDictionary), vertex.x, vertex.y, vertex.z)
623 vertexDictionary[vertexTuple] = loop[vertexIndex]
624 uniqueVertexes.append(loop[vertexIndex])
625 return uniqueVertexes
626
628 'Get a point index which has a wide enough angle, most point indexes have a wide enough angle, this is just to make sure.'
629 dotProductMinimum = 9999999.9
630 widestPointIndex = 0
631 for pointIndex in xrange(len(loop)):
632 point = loop[ pointIndex % len(loop) ]
633 afterPoint = loop[(pointIndex + 1) % len(loop)]
634 beforePoint = loop[ ( pointIndex - 1 ) % len(loop) ]
635 afterSegmentNormalized = euclidean.getNormalized( afterPoint - point )
636 beforeSegmentNormalized = euclidean.getNormalized( beforePoint - point )
637 dotProduct = euclidean.getDotProduct( afterSegmentNormalized, beforeSegmentNormalized )
638 if dotProduct < .99:
639 return pointIndex
640 if dotProduct < dotProductMinimum:
641 dotProductMinimum = dotProduct
642 widestPointIndex = pointIndex
643 return widestPointIndex
644
646 'Get next z and add extruder loops by solid carving.'
647 solidCarving.rotatedLoopLayers.append(rotatedLoopLayer)
648 nextZ = z + solidCarving.layerThickness
649 if not solidCarving.infillInDirectionOfBridge:
650 return nextZ
651 allExtrudateLoops = []
652 for loop in rotatedLoopLayer.loops:
653 allExtrudateLoops += getBridgeLoops(solidCarving.layerThickness, loop)
654 rotatedLoopLayer.rotation = getBridgeDirection(solidCarving.belowLoops, allExtrudateLoops, solidCarving.layerThickness)
655 solidCarving.belowLoops = allExtrudateLoops
656 return nextZ
657
658 -def isInline( beginComplex, centerComplex, endComplex ):
659 'Determine if the three complex points form a line.'
660 centerBeginComplex = beginComplex - centerComplex
661 centerEndComplex = endComplex - centerComplex
662 centerBeginLength = abs( centerBeginComplex )
663 centerEndLength = abs( centerEndComplex )
664 if centerBeginLength <= 0.0 or centerEndLength <= 0.0:
665 return False
666 centerBeginComplex /= centerBeginLength
667 centerEndComplex /= centerEndLength
668 return euclidean.getDotProduct( centerBeginComplex, centerEndComplex ) < -0.999
669
670 -def isPathAdded( edges, faces, loops, remainingEdgeTable, vertexes, z ):
671 'Get the path indexes around a triangle mesh carve and add the path to the flat loops.'
672 if len( remainingEdgeTable ) < 1:
673 return False
674 pathIndexes = []
675 remainingEdgeIndexKey = remainingEdgeTable.keys()[0]
676 pathIndexes.append( remainingEdgeIndexKey )
677 del remainingEdgeTable[remainingEdgeIndexKey]
678 nextEdgeIndexAroundZ = getNextEdgeIndexAroundZ( edges[remainingEdgeIndexKey], faces, remainingEdgeTable )
679 while nextEdgeIndexAroundZ != - 1:
680 pathIndexes.append( nextEdgeIndexAroundZ )
681 del remainingEdgeTable[ nextEdgeIndexAroundZ ]
682 nextEdgeIndexAroundZ = getNextEdgeIndexAroundZ( edges[ nextEdgeIndexAroundZ ], faces, remainingEdgeTable )
683 if len( pathIndexes ) < 3:
684 print('Dangling edges, will use intersecting circles to get import layer at height %s' % z)
685 del loops[:]
686 return False
687 loops.append( getPath( edges, pathIndexes, vertexes, z ) )
688 return True
689
693
695 'Set the edge maximum and minimum.'
696 beginIndex = edge.vertexIndexes[0]
697 endIndex = edge.vertexIndexes[1]
698 if beginIndex >= len(vertexes) or endIndex >= len(vertexes):
699 print('Warning, there are duplicate vertexes in setEdgeMaximumMinimum in triangle_mesh.')
700 print('Something might still be printed, but there is no guarantee that it will be the correct shape.' )
701 edge.zMaximum = -987654321.0
702 edge.zMinimum = -987654321.0
703 return
704 beginZ = vertexes[beginIndex].z
705 endZ = vertexes[endIndex].z
706 edge.zMinimum = min(beginZ, endZ)
707 edge.zMaximum = max(beginZ, endZ)
708
712
713
716 'Pair of edges on a face.'
717 self.edgeIndexes = []
718 self.edges = []
719
721 'Get the string representation of this EdgePair.'
722 return str( self.edgeIndexes )
723
725 'Initialize from edge indices.'
726 self.edgeIndexes = edgeIndexes[:]
727 self.edgeIndexes.sort()
728 for edgeIndex in self.edgeIndexes:
729 self.edges.append( edges[ edgeIndex ] )
730 return self
731
732
734 'A triangle mesh.'
736 'Add empty lists.'
737 group.Group.__init__(self)
738 self.belowLoops = []
739 self.infillInDirectionOfBridge = False
740 self.edges = []
741 self.faces = []
742 self.importCoarseness = 1.0
743 self.isCorrectMesh = True
744 self.oldChainTetragrid = None
745 self.rotatedLoopLayers = []
746 self.transformedVertexes = None
747 self.vertexes = []
748
753
755 'Get the corner maximum of the vertexes.'
756 return self.cornerMaximum
757
759 'Get the corner minimum of the vertexes.'
760 return self.cornerMinimum
761
763 'Get the layer thickness.'
764 return self.layerThickness
765
767 'Get the rotated boundary layers.'
768 if self.getMinimumZ() == None:
769 return []
770 halfHeight = 0.5 * self.layerThickness
771 self.zoneArrangement = ZoneArrangement(self.layerThickness, self.getTransformedVertexes())
772 layerTop = self.cornerMaximum.z - halfHeight * 0.5
773 z = self.cornerMinimum.z + halfHeight
774 while z < layerTop:
775 z = self.getZAddExtruderPaths(z)
776 return self.rotatedLoopLayers
777
779 'Return the fabmetheus XML.'
780 return None
781
785
787 'Return the suffix for a triangle mesh.'
788 return 'xml'
789
791 'Get loops sliced through shape.'
792 self.importRadius = importRadius
793 return self.getLoopsFromMesh(z)
794
806
808 'Get the minimum z.'
809 self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0)
810 self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0)
811 transformedVertexes = self.getTransformedVertexes()
812 if len(transformedVertexes) < 1:
813 return None
814 for point in transformedVertexes:
815 self.cornerMaximum.maximize(point)
816 self.cornerMinimum.minimize(point)
817 return self.cornerMinimum.z
818
832
834 'Get all triangleMeshes.'
835 return [self]
836
838 'Get all vertexes.'
839 self.transformedVertexes = None
840 return self.vertexes
841
849
851 'Lift the triangle mesh to the altitude.'
852 altitude = evaluate.getEvaluatedFloat(None, 'altitude', self.xmlElement)
853 if altitude == None:
854 return
855 lift = altitude - minimumZ
856 for vertex in self.vertexes:
857 vertex.z += lift
858
860 'Set the infill in direction of bridge.'
861 self.infillInDirectionOfBridge = infillInDirectionOfBridge
862
864 'Set the layer thickness.'
865 self.layerThickness = layerThickness
866
868 'Set the import radius.'
869 self.importRadius = importRadius
870
872 'Set the is correct mesh flag.'
873 self.isCorrectMesh = isCorrectMesh
874
880
881
883 'A zone arrangement.'
884 - def __init__(self, layerThickness, vertexes):
885 'Initialize the zone interval and the zZone table.'
886 self.zoneInterval = layerThickness / math.sqrt(len(vertexes)) / 1000.0
887 self.zZoneSet = set()
888 for point in vertexes:
889 zoneIndexFloat = point.z / self.zoneInterval
890 self.zZoneSet.add(math.floor(zoneIndexFloat))
891 self.zZoneSet.add(math.ceil(zoneIndexFloat ))
892
894 'Get the first z which is not in the zone table.'
895 zoneIndex = round(z / self.zoneInterval)
896 if zoneIndex not in self.zZoneSet:
897 return z
898 zoneAround = 1
899 while 1:
900 zoneDown = zoneIndex - zoneAround
901 if zoneDown not in self.zZoneSet:
902 return zoneDown * self.zoneInterval
903 zoneUp = zoneIndex + zoneAround
904 if zoneUp not in self.zZoneSet:
905 return zoneUp * self.zoneInterval
906 zoneAround += 1
907