topologicpy 0.8.26__py3-none-any.whl → 0.8.29__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- topologicpy/Face.py +38 -6
- topologicpy/Graph.py +198 -0
- topologicpy/Grid.py +16 -12
- topologicpy/Plotly.py +12 -7
- topologicpy/ShapeGrammar.py +492 -0
- topologicpy/Shell.py +36 -0
- topologicpy/Topology.py +301 -251
- topologicpy/Vertex.py +95 -0
- topologicpy/version.py +1 -1
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/METADATA +1 -1
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/RECORD +14 -13
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/WHEEL +1 -1
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.26.dist-info → topologicpy-0.8.29.dist-info}/top_level.txt +0 -0
topologicpy/Face.py
CHANGED
@@ -1927,6 +1927,34 @@ class Face():
|
|
1927
1927
|
else:
|
1928
1928
|
inverted_face = Face.ByWires(inverted_wire, internal_boundaries, tolerance=tolerance)
|
1929
1929
|
return inverted_face
|
1930
|
+
@staticmethod
|
1931
|
+
def IsConvex(face, mantissa: int = 6, silent: bool = False) -> bool:
|
1932
|
+
"""
|
1933
|
+
Returns True if the input face is convex. Returns False otherwise.
|
1934
|
+
|
1935
|
+
Parameters
|
1936
|
+
----------
|
1937
|
+
face : topologic_core.Face
|
1938
|
+
The input face.
|
1939
|
+
mantissa : int , optional
|
1940
|
+
The length of the desired mantissa. The default is 6.
|
1941
|
+
silent : bool , optional
|
1942
|
+
If set to True no warnings or errors are printed. The default is False.
|
1943
|
+
Returns
|
1944
|
+
-------
|
1945
|
+
bool
|
1946
|
+
True if the nput face is convex. False otherwise.
|
1947
|
+
|
1948
|
+
"""
|
1949
|
+
from topologicpy.Topology import Topology
|
1950
|
+
|
1951
|
+
if not Topology.IsInstance(face, "face"):
|
1952
|
+
if not silent:
|
1953
|
+
print("Face.IsConvex - Error: The input face parameter is not a valid topologic face. Returning None.")
|
1954
|
+
return None
|
1955
|
+
eb = Face.ExternalBoundary(face)
|
1956
|
+
eb = Face.ByWire(eb)
|
1957
|
+
return all(Face.InteriorAngles(eb)) < 180
|
1930
1958
|
|
1931
1959
|
@staticmethod
|
1932
1960
|
def IsCoplanar(faceA, faceB, mantissa: int = 6, tolerance: float = 0.0001) -> bool:
|
@@ -2468,9 +2496,9 @@ class Face():
|
|
2468
2496
|
flat_face = Topology.Difference(flat_face, Face.ByWire(obs))
|
2469
2497
|
|
2470
2498
|
# Check that the viewpoint is inside the face
|
2471
|
-
if not Vertex.IsInternal(flat_vertex, flat_face):
|
2472
|
-
|
2473
|
-
|
2499
|
+
# if not Vertex.IsInternal(flat_vertex, flat_face):
|
2500
|
+
# print("Face.Isovist - Error: The viewpoint is not inside the face. Returning None.")
|
2501
|
+
# return None
|
2474
2502
|
targets = Topology.Vertices(flat_face)
|
2475
2503
|
distances = []
|
2476
2504
|
for target in targets:
|
@@ -3544,7 +3572,7 @@ class Face():
|
|
3544
3572
|
return Face.ByWire(wire, tolerance=tolerance)
|
3545
3573
|
|
3546
3574
|
@staticmethod
|
3547
|
-
def Triangulate(face, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001) -> list:
|
3575
|
+
def Triangulate(face, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001, silent: bool = False) -> list:
|
3548
3576
|
"""
|
3549
3577
|
Triangulates the input face and returns a list of faces.
|
3550
3578
|
|
@@ -3571,6 +3599,8 @@ class Face():
|
|
3571
3599
|
The desired length of the mantissa. The default is 6.
|
3572
3600
|
tolerance : float , optional
|
3573
3601
|
The desired tolerance. The default is 0.0001.
|
3602
|
+
silent : bool , optional
|
3603
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
3574
3604
|
|
3575
3605
|
Returns
|
3576
3606
|
-------
|
@@ -3638,7 +3668,8 @@ class Face():
|
|
3638
3668
|
from topologicpy.Topology import Topology
|
3639
3669
|
|
3640
3670
|
if not Topology.IsInstance(face, "Face"):
|
3641
|
-
|
3671
|
+
if not silent:
|
3672
|
+
print("Face.Triangulate - Error: The input face parameter is not a valid face. Returning None.")
|
3642
3673
|
return None
|
3643
3674
|
if not meshSize:
|
3644
3675
|
bounding_face = Face.BoundingRectangle(face)
|
@@ -3713,7 +3744,8 @@ class Face():
|
|
3713
3744
|
return faces
|
3714
3745
|
|
3715
3746
|
if not Topology.IsInstance(face, "Face"):
|
3716
|
-
|
3747
|
+
if not silent:
|
3748
|
+
print("Face.Triangulate - Error: The input face parameter is not a valid face. Returning None.")
|
3717
3749
|
return None
|
3718
3750
|
vertices = Topology.Vertices(face)
|
3719
3751
|
if len(vertices) == 3: # Already a triangle
|
topologicpy/Graph.py
CHANGED
@@ -2455,6 +2455,204 @@ class Graph:
|
|
2455
2455
|
|
2456
2456
|
@staticmethod
|
2457
2457
|
def ByIFCFile(file,
|
2458
|
+
includeTypes: list = [],
|
2459
|
+
excludeTypes: list = [],
|
2460
|
+
includeRels: list = [],
|
2461
|
+
excludeRels: list = [],
|
2462
|
+
transferDictionaries: bool = False,
|
2463
|
+
useInternalVertex: bool = False,
|
2464
|
+
storeBREP: bool = False,
|
2465
|
+
removeCoplanarFaces: bool = False,
|
2466
|
+
xMin: float = -0.5, yMin: float = -0.5, zMin: float = -0.5,
|
2467
|
+
xMax: float = 0.5, yMax: float = 0.5, zMax: float = 0.5,
|
2468
|
+
epsilon: float = 0.0001,
|
2469
|
+
tolerance: float = 0.0001,
|
2470
|
+
silent: bool = False):
|
2471
|
+
|
2472
|
+
"""
|
2473
|
+
Create a Graph from an IFC file. This code is partially based on code from Bruno Postle.
|
2474
|
+
|
2475
|
+
Parameters
|
2476
|
+
----------
|
2477
|
+
file : file
|
2478
|
+
The input IFC file
|
2479
|
+
includeTypes : list , optional
|
2480
|
+
A list of IFC object types to include in the graph. The default is [] which means all object types are included.
|
2481
|
+
excludeTypes : list , optional
|
2482
|
+
A list of IFC object types to exclude from the graph. The default is [] which mean no object type is excluded.
|
2483
|
+
includeRels : list , optional
|
2484
|
+
A list of IFC relationship types to include in the graph. The default is [] which means all relationship types are included.
|
2485
|
+
excludeRels : list , optional
|
2486
|
+
A list of IFC relationship types to exclude from the graph. The default is [] which mean no relationship type is excluded.
|
2487
|
+
transferDictionaries : bool , optional
|
2488
|
+
NOT USED. If set to True, the dictionaries from the IFC file will be transferred to the topology. Otherwise, they won't. The default is False.
|
2489
|
+
useInternalVertex : bool , optional
|
2490
|
+
If set to True, use an internal vertex to represent the subtopology. Otherwise, use its centroid. The default is False.
|
2491
|
+
storeBREP : bool , optional
|
2492
|
+
If set to True, store the BRep of the subtopology in its representative vertex. The default is False.
|
2493
|
+
removeCoplanarFaces : bool , optional
|
2494
|
+
If set to True, coplanar faces are removed. Otherwise they are not. The default is False.
|
2495
|
+
xMin : float, optional
|
2496
|
+
The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
|
2497
|
+
yMin : float, optional
|
2498
|
+
The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
|
2499
|
+
zMin : float, optional
|
2500
|
+
The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
|
2501
|
+
xMax : float, optional
|
2502
|
+
The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
|
2503
|
+
yMax : float, optional
|
2504
|
+
The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
|
2505
|
+
zMax : float, optional
|
2506
|
+
The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
|
2507
|
+
tolerance : float , optional
|
2508
|
+
The desired tolerance. The default is 0.0001.
|
2509
|
+
|
2510
|
+
Returns
|
2511
|
+
-------
|
2512
|
+
topologic_core.Graph
|
2513
|
+
The created graph.
|
2514
|
+
|
2515
|
+
"""
|
2516
|
+
|
2517
|
+
from topologicpy.Vertex import Vertex
|
2518
|
+
from topologicpy.Edge import Edge
|
2519
|
+
from topologicpy.Dictionary import Dictionary
|
2520
|
+
from topologicpy.Topology import Topology
|
2521
|
+
|
2522
|
+
def vertex_at_key_value(vertices, key, value):
|
2523
|
+
for v in vertices:
|
2524
|
+
d = Topology.Dictionary(v)
|
2525
|
+
d_value = Dictionary.ValueAtKey(d, key)
|
2526
|
+
if value == d_value:
|
2527
|
+
return v
|
2528
|
+
return None
|
2529
|
+
|
2530
|
+
def get_vertices(includeTypes=[], excludeTypes=[], removeCoplanarFaces=False, storeBREP=False, useInternalVertex=useInternalVertex, epsilon=0.0001, tolerance=0.0001):
|
2531
|
+
# Get the topologies
|
2532
|
+
topologies = Topology.ByIFCFile(file,
|
2533
|
+
includeTypes=includeTypes,
|
2534
|
+
excludeTypes=excludeTypes,
|
2535
|
+
transferDictionaries=True,
|
2536
|
+
removeCoplanarFaces=removeCoplanarFaces,
|
2537
|
+
epsilon=epsilon,
|
2538
|
+
tolerance=tolerance)
|
2539
|
+
vertices = []
|
2540
|
+
for topology in topologies:
|
2541
|
+
if Topology.IsInstance(topology, "Topology"):
|
2542
|
+
if useInternalVertex == True:
|
2543
|
+
v = Topology.InternalVertex(topology)
|
2544
|
+
else:
|
2545
|
+
v = Topology.Centroid(topology)
|
2546
|
+
d = Topology.Dictionary(topology)
|
2547
|
+
if storeBREP:
|
2548
|
+
d = Dictionary.SetValueAtKey(d, "BREP", Topology.BREPString(topology))
|
2549
|
+
if Topology.IsInstance(v, "vertex"):
|
2550
|
+
v = Topology.SetDictionary(v, Topology.Dictionary(topology))
|
2551
|
+
vertices.append(v)
|
2552
|
+
else:
|
2553
|
+
if not silent:
|
2554
|
+
ifc_id = Dictionary.ValueAtKey(Topology.Dictionary(topology), "IFC_global_id", 0)
|
2555
|
+
print(f"Graph.ByIFCFile - Warning: Could not create a vertex for entity {ifc_id}. Skipping")
|
2556
|
+
return vertices
|
2557
|
+
|
2558
|
+
# Get the relationships
|
2559
|
+
def get_relationships(ifc_file, includeRels=[], excludeRels=[]):
|
2560
|
+
include_set = set(s.lower() for s in includeRels)
|
2561
|
+
exclude_set = set(s.lower() for s in excludeRels)
|
2562
|
+
|
2563
|
+
relationships = [
|
2564
|
+
rel for rel in ifc_file.by_type("IfcRelationship")
|
2565
|
+
if (rel.is_a().lower() not in exclude_set) and
|
2566
|
+
(not include_set or rel.is_a().lower() in include_set)
|
2567
|
+
]
|
2568
|
+
|
2569
|
+
return relationships
|
2570
|
+
def get_edges(ifc_relationships, vertices):
|
2571
|
+
tuples = []
|
2572
|
+
edges = []
|
2573
|
+
|
2574
|
+
for ifc_rel in ifc_relationships:
|
2575
|
+
source = None
|
2576
|
+
destinations = []
|
2577
|
+
if ifc_rel.is_a("IfcRelConnectsPorts"):
|
2578
|
+
source = ifc_rel.RelatingPort
|
2579
|
+
destinations = ifc_rel.RelatedPorts
|
2580
|
+
elif ifc_rel.is_a("IfcRelConnectsPortToElement"):
|
2581
|
+
source = ifc_rel.RelatingPort
|
2582
|
+
destinations = [ifc_rel.RelatedElement]
|
2583
|
+
elif ifc_rel.is_a("IfcRelAggregates"):
|
2584
|
+
source = ifc_rel.RelatingObject
|
2585
|
+
destinations = ifc_rel.RelatedObjects
|
2586
|
+
elif ifc_rel.is_a("IfcRelNests"):
|
2587
|
+
source = ifc_rel.RelatingObject
|
2588
|
+
destinations = ifc_rel.RelatedObjects
|
2589
|
+
elif ifc_rel.is_a("IfcRelAssignsToGroup"):
|
2590
|
+
source = ifc_rel.RelatingGroup
|
2591
|
+
destinations = ifc_rel.RelatedObjects
|
2592
|
+
elif ifc_rel.is_a("IfcRelConnectsPathElements"):
|
2593
|
+
source = ifc_rel.RelatingElement
|
2594
|
+
destinations = [ifc_rel.RelatedElement]
|
2595
|
+
elif ifc_rel.is_a("IfcRelConnectsStructuralMember"):
|
2596
|
+
source = ifc_rel.RelatingStructuralMember
|
2597
|
+
destinations = [ifc_rel.RelatedStructuralConnection]
|
2598
|
+
elif ifc_rel.is_a("IfcRelContainedInSpatialStructure"):
|
2599
|
+
source = ifc_rel.RelatingStructure
|
2600
|
+
destinations = ifc_rel.RelatedElements
|
2601
|
+
elif ifc_rel.is_a("IfcRelFillsElement"):
|
2602
|
+
source = ifc_rel.RelatingOpeningElement
|
2603
|
+
destinations = [ifc_rel.RelatedBuildingElement]
|
2604
|
+
elif ifc_rel.is_a("IfcRelSpaceBoundary"):
|
2605
|
+
source = ifc_rel.RelatingSpace
|
2606
|
+
destinations = [ifc_rel.RelatedBuildingElement]
|
2607
|
+
elif ifc_rel.is_a("IfcRelVoidsElement"):
|
2608
|
+
source = ifc_rel.RelatingBuildingElement
|
2609
|
+
destinations = [ifc_rel.RelatedOpeningElement]
|
2610
|
+
elif ifc_rel.is_a("IfcRelDefinesByProperties") or ifc_rel.is_a("IfcRelAssociatesMaterial") or ifc_rel.is_a("IfcRelDefinesByType"):
|
2611
|
+
source = None
|
2612
|
+
destinations = None
|
2613
|
+
else:
|
2614
|
+
print("Graph.ByIFCFile - Warning: The relationship", ifc_rel, "is not supported. Skipping.")
|
2615
|
+
if source:
|
2616
|
+
sv = vertex_at_key_value(vertices, key="IFC_global_id", value=getattr(source, 'GlobalId', 0))
|
2617
|
+
if sv:
|
2618
|
+
si = Vertex.Index(sv, vertices, tolerance=tolerance)
|
2619
|
+
if not si == None:
|
2620
|
+
for destination in destinations:
|
2621
|
+
if destination == None:
|
2622
|
+
continue
|
2623
|
+
ev = vertex_at_key_value(vertices, key="IFC_global_id", value=getattr(destination, 'GlobalId', 0))
|
2624
|
+
if ev:
|
2625
|
+
ei = Vertex.Index(ev, vertices, tolerance=tolerance)
|
2626
|
+
if not ei == None:
|
2627
|
+
if not si == ei:
|
2628
|
+
if not [si,ei] in tuples:
|
2629
|
+
tuples.append([si,ei])
|
2630
|
+
tuples.append([ei,si])
|
2631
|
+
e = Edge.ByVertices([sv,ev])
|
2632
|
+
if Topology.IsInstance(e, "edge"):
|
2633
|
+
d = Dictionary.ByKeysValues(["IFC_global_id", "IFC_name", "IFC_type"], [ifc_rel.id(), ifc_rel.Name, ifc_rel.is_a()])
|
2634
|
+
e = Topology.SetDictionary(e, d)
|
2635
|
+
edges.append(e)
|
2636
|
+
else:
|
2637
|
+
if not silent:
|
2638
|
+
if not silent:
|
2639
|
+
print(f"Graph.ByIFCFile - Warning: Could not create an edge for relationship {ifc_rel.id()}. Skipping")
|
2640
|
+
|
2641
|
+
return edges
|
2642
|
+
|
2643
|
+
vertices = get_vertices(includeTypes=includeTypes,
|
2644
|
+
excludeTypes=excludeTypes,
|
2645
|
+
removeCoplanarFaces=removeCoplanarFaces,
|
2646
|
+
storeBREP=storeBREP,
|
2647
|
+
useInternalVertex=useInternalVertex,
|
2648
|
+
epsilon=epsilon,
|
2649
|
+
tolerance=0.0001)
|
2650
|
+
relationships = get_relationships(file, includeRels=includeRels, excludeRels=excludeRels)
|
2651
|
+
edges = get_edges(relationships, vertices)
|
2652
|
+
return Graph.ByVerticesEdges(vertices, edges)
|
2653
|
+
|
2654
|
+
@staticmethod
|
2655
|
+
def ByIFCFile_old(file,
|
2458
2656
|
includeTypes: list = [],
|
2459
2657
|
excludeTypes: list = [],
|
2460
2658
|
includeRels: list = [],
|
topologicpy/Grid.py
CHANGED
@@ -286,12 +286,11 @@ class Grid():
|
|
286
286
|
vTempVec = Vector.Multiply(uvVector, v, tolerance=tolerance)
|
287
287
|
gridVertex = Vertex.ByCoordinates(Vertex.X(origin, mantissa=mantissa)+uTempVec[0], Vertex.Y(origin, mantissa=mantissa)+vTempVec[1], Vertex.Z(origin, mantissa=mantissa)+uTempVec[2])
|
288
288
|
if clip and Topology.IsInstance(face, "Face"):
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
gridVertices.append(gridVertex)
|
289
|
+
if Vertex.IsInternal(gridVertex, face):
|
290
|
+
d = Dictionary.ByKeysValues(["u","v"],[u,v])
|
291
|
+
if d:
|
292
|
+
gridVertex.SetDictionary(d)
|
293
|
+
gridVertices.append(gridVertex)
|
295
294
|
grid = None
|
296
295
|
if len(gridVertices) > 0:
|
297
296
|
grid = Cluster.ByTopologies(gridVertices)
|
@@ -347,12 +346,17 @@ class Grid():
|
|
347
346
|
for v in vRange:
|
348
347
|
gridVertex = Face.VertexByParameters(face, u, v)
|
349
348
|
if clip and Topology.IsInstance(face, "Face"):
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
349
|
+
if Vertex.IsInternal(gridVertex, face):
|
350
|
+
d = Dictionary.ByKeysValues(["u","v"],[u,v])
|
351
|
+
if d:
|
352
|
+
gridVertex.SetDictionary(d)
|
353
|
+
gridVertices.append(gridVertex)
|
354
|
+
# gridVertex = gridVertex.Intersect(face, False)
|
355
|
+
# if Topology.IsInstance(gridVertex, "Vertex"):
|
356
|
+
# d = Dictionary.ByKeysValues(["u","v"],[u,v])
|
357
|
+
# if d:
|
358
|
+
# gridVertex.SetDictionary(d)
|
359
|
+
# gridVertices.append(gridVertex)
|
356
360
|
grid = None
|
357
361
|
if len(gridVertices) > 0:
|
358
362
|
grid = Cluster.ByTopologies(gridVertices)
|
topologicpy/Plotly.py
CHANGED
@@ -592,6 +592,7 @@ class Plotly:
|
|
592
592
|
mode = "markers+text"
|
593
593
|
else:
|
594
594
|
mode = "markers"
|
595
|
+
|
595
596
|
vData2 = go.Scatter3d(x=x,
|
596
597
|
y=y,
|
597
598
|
z=z,
|
@@ -603,11 +604,13 @@ class Plotly:
|
|
603
604
|
opacity=1,
|
604
605
|
sizemode="diameter"),
|
605
606
|
mode=mode,
|
607
|
+
customdata = labels,
|
606
608
|
legendgroup=legendGroup,
|
607
609
|
legendrank=legendRank,
|
608
610
|
text=labels,
|
609
611
|
hoverinfo='text',
|
610
|
-
hovertext=labels
|
612
|
+
hovertext=labels,
|
613
|
+
hovertemplate=["Click "+label for label in labels]
|
611
614
|
)
|
612
615
|
if borderWidth > 0 or borderWidthKey:
|
613
616
|
vData1 = go.Scatter3d(x=x,
|
@@ -676,14 +679,13 @@ class Plotly:
|
|
676
679
|
labels = []
|
677
680
|
for j, elements_cluster in enumerate(elements_clusters):
|
678
681
|
d_color = color
|
679
|
-
labels.append("Edge_"+str(j+1).zfill(n))
|
680
682
|
d = dict_clusters[j][0] # All dicitonaries have same values in dictionaries, so take first one.
|
681
683
|
if d:
|
682
684
|
if not colorKey == None:
|
683
685
|
d_color = Dictionary.ValueAtKey(d, key=colorKey) or color
|
684
686
|
d_color = Color.AnyToHex(d_color)
|
685
687
|
if not labelKey == None:
|
686
|
-
|
688
|
+
labels.append(str(Dictionary.ValueAtKey(d, labelKey, "")))
|
687
689
|
if not widthKey == None:
|
688
690
|
e_width = Dictionary.ValueAtKey(d, key=widthKey)
|
689
691
|
if not e_width == None:
|
@@ -727,8 +729,10 @@ class Plotly:
|
|
727
729
|
line=dict(color=d_color, width=width),
|
728
730
|
legendgroup=legendGroup,
|
729
731
|
legendrank=legendRank,
|
730
|
-
text=
|
731
|
-
|
732
|
+
text=labels,
|
733
|
+
customdata=labels,
|
734
|
+
hoverinfo='text',
|
735
|
+
hovertext=labels)
|
732
736
|
traces.append(trace)
|
733
737
|
else:
|
734
738
|
x = []
|
@@ -809,7 +813,8 @@ class Plotly:
|
|
809
813
|
faceLegendLabel="Topology Faces",
|
810
814
|
faceLegendRank=3,
|
811
815
|
faceLegendGroup=3,
|
812
|
-
intensityKey=None, intensities=[], colorScale="viridis",
|
816
|
+
intensityKey=None, intensities=[], colorScale="viridis",
|
817
|
+
mantissa=6, tolerance=0.0001, silent=False):
|
813
818
|
"""
|
814
819
|
Creates plotly face, edge, and vertex data.
|
815
820
|
|
@@ -1188,7 +1193,7 @@ class Plotly:
|
|
1188
1193
|
f_dictionaries = []
|
1189
1194
|
all_triangles = []
|
1190
1195
|
for tp_face in tp_faces:
|
1191
|
-
triangles = Face.Triangulate(tp_face, tolerance=tolerance)
|
1196
|
+
triangles = Face.Triangulate(tp_face, tolerance=tolerance, silent=silent)
|
1192
1197
|
if isinstance(triangles, list):
|
1193
1198
|
for tri in triangles:
|
1194
1199
|
if faceColorKey or faceOpacityKey or faceLabelKey or faceGroupKey:
|