topologicpy 0.8.97__py3-none-any.whl → 0.8.98__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/BVH.py +4 -3
- topologicpy/Face.py +12 -4
- topologicpy/Plotly.py +119 -10
- topologicpy/Topology.py +386 -172
- topologicpy/Vertex.py +34 -1
- topologicpy/version.py +1 -1
- {topologicpy-0.8.97.dist-info → topologicpy-0.8.98.dist-info}/METADATA +1 -1
- {topologicpy-0.8.97.dist-info → topologicpy-0.8.98.dist-info}/RECORD +11 -11
- {topologicpy-0.8.97.dist-info → topologicpy-0.8.98.dist-info}/WHEEL +1 -1
- {topologicpy-0.8.97.dist-info → topologicpy-0.8.98.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.97.dist-info → topologicpy-0.8.98.dist-info}/top_level.txt +0 -0
topologicpy/BVH.py
CHANGED
|
@@ -342,12 +342,13 @@ class BVH:
|
|
|
342
342
|
for topology in topologyList:
|
|
343
343
|
if Topology.IsInstance(topology, "vertex"):
|
|
344
344
|
x,y,z = Vertex.Coordinates(topology, mantissa=mantissa)
|
|
345
|
-
points = [[x-tolerance, y-tolerance, z-tolerance], [x+tolerance, y+tolerance, z+tolerance]]
|
|
345
|
+
# points = [[x-tolerance, y-tolerance, z-tolerance], [x+tolerance, y+tolerance, z+tolerance]]
|
|
346
|
+
aabb_box = AABB(x-tolerance, y-tolerance, z-tolerance, x+tolerance, y+tolerance, z+tolerance)
|
|
346
347
|
else:
|
|
347
348
|
points = [Vertex.Coordinates(v, mantissa=mantissa) for v in Topology.Vertices(topology)]
|
|
348
|
-
|
|
349
|
+
aabb_box = AABB.from_points(points, pad = tolerance)
|
|
349
350
|
return_topologies.extend([bvh.items[i] for i in BVH.QueryAABB(bvh, aabb_box)])
|
|
350
|
-
return return_topologies
|
|
351
|
+
return return_topologies
|
|
351
352
|
|
|
352
353
|
@staticmethod
|
|
353
354
|
def Raycast(bvh, origin, direction: Tuple[float, float, float], mantissa: int = 6, silent: bool = False) -> List[int]:
|
topologicpy/Face.py
CHANGED
|
@@ -1933,7 +1933,12 @@ class Face():
|
|
|
1933
1933
|
return centroid
|
|
1934
1934
|
|
|
1935
1935
|
shell = Topology.Triangulate(face)
|
|
1936
|
-
ib = Shell.
|
|
1936
|
+
# ib = Shell.InternalEdges(shell)
|
|
1937
|
+
# centroids = [Topology.Centroid(t) for t in ib]
|
|
1938
|
+
faces = Topology.Faces(shell)
|
|
1939
|
+
centroids = [Topology.Centroid(t) for t in faces]
|
|
1940
|
+
cluster = Cluster.ByTopologies(centroids)
|
|
1941
|
+
return Vertex.NearestVertex(centroid, cluster)
|
|
1937
1942
|
cluster = Cluster.ByTopologies(ib)
|
|
1938
1943
|
edges = Topology.Edges(cluster)
|
|
1939
1944
|
bvh = BVH.ByTopologies(edges, tolerance=tolerance, silent=True)
|
|
@@ -3356,7 +3361,7 @@ class Face():
|
|
|
3356
3361
|
return Face.Rectangle(origin=origin, width=width, length=length, direction = direction, placement=placement, tolerance=tolerance)
|
|
3357
3362
|
|
|
3358
3363
|
@staticmethod
|
|
3359
|
-
def Rectangle(origin= None, width: float = 1.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001):
|
|
3364
|
+
def Rectangle(origin= None, width: float = 1.0, length: float = 1.0, direction: list = [0, 0, 1], placement: str = "center", tolerance: float = 0.0001, silent: bool = True):
|
|
3360
3365
|
"""
|
|
3361
3366
|
Creates a rectangle.
|
|
3362
3367
|
|
|
@@ -3374,6 +3379,8 @@ class Face():
|
|
|
3374
3379
|
The description of the placement of the origin of the rectangle. This can be "center", "lowerleft", "upperleft", "lowerright", "upperright". It is case insensitive. Default is "center".
|
|
3375
3380
|
tolerance : float , optional
|
|
3376
3381
|
The desired tolerance. Default is 0.0001.
|
|
3382
|
+
silent : bool , optional
|
|
3383
|
+
If set to True, error and warning messages are suppressed. Default is False.
|
|
3377
3384
|
|
|
3378
3385
|
Returns
|
|
3379
3386
|
-------
|
|
@@ -3384,9 +3391,10 @@ class Face():
|
|
|
3384
3391
|
from topologicpy.Wire import Wire
|
|
3385
3392
|
from topologicpy.Topology import Topology
|
|
3386
3393
|
|
|
3387
|
-
wire = Wire.Rectangle(origin=origin, width=width, length=length, direction=direction, placement=placement, tolerance=tolerance)
|
|
3394
|
+
wire = Wire.Rectangle(origin=origin, width=width, length=length, direction=direction, placement=placement, tolerance=tolerance, silent=silent)
|
|
3388
3395
|
if not Topology.IsInstance(wire, "Wire"):
|
|
3389
|
-
|
|
3396
|
+
if not silent:
|
|
3397
|
+
print("Face.Rectangle - Error: Could not create the base wire for the rectangle. Returning None.")
|
|
3390
3398
|
return None
|
|
3391
3399
|
return Face.ByWire(wire, tolerance=tolerance)
|
|
3392
3400
|
|
topologicpy/Plotly.py
CHANGED
|
@@ -902,8 +902,22 @@ class Plotly:
|
|
|
902
902
|
faceLegendLabel="Topology Faces",
|
|
903
903
|
faceLegendRank=3,
|
|
904
904
|
faceLegendGroup=3,
|
|
905
|
-
intensityKey=None,
|
|
906
|
-
|
|
905
|
+
intensityKey=None,
|
|
906
|
+
intensities=[],
|
|
907
|
+
material = "plastic",
|
|
908
|
+
materialKey=None,
|
|
909
|
+
ambient = None,
|
|
910
|
+
ambientKey=None,
|
|
911
|
+
diffuse = None,
|
|
912
|
+
diffuseKey=None,
|
|
913
|
+
specular = None,
|
|
914
|
+
specularKey=None,
|
|
915
|
+
roughness = None,
|
|
916
|
+
roughnessKey=None,
|
|
917
|
+
colorScale="viridis",
|
|
918
|
+
mantissa=6,
|
|
919
|
+
tolerance=0.0001,
|
|
920
|
+
silent=False):
|
|
907
921
|
"""
|
|
908
922
|
Creates plotly face, edge, and vertex data.
|
|
909
923
|
|
|
@@ -1035,10 +1049,50 @@ class Plotly:
|
|
|
1035
1049
|
The legend rank order of the faces of this topology. Default is 3.
|
|
1036
1050
|
faceLegendGroup : int , optional
|
|
1037
1051
|
The number of the face legend group to which the faces of this topology belong. Default is 3.
|
|
1038
|
-
intensityKey: str, optional
|
|
1052
|
+
intensityKey : str, optional
|
|
1039
1053
|
If not None, the dictionary of each vertex is searched for the value associated with the intensity key. This value is then used to color-code the vertex based on the colorScale. Default is None.
|
|
1040
1054
|
intensities : list , optional
|
|
1041
1055
|
The list of intensities against which to index the intensity of the vertex. Default is [].
|
|
1056
|
+
material : str , optional
|
|
1057
|
+
The type of object material. Supported pre-built materials are:
|
|
1058
|
+
Preset Ambient Diffuse Specular Roughness Description
|
|
1059
|
+
--------------------------------------------------------------
|
|
1060
|
+
chalk 1.0 0.4 0.0 1.0 Very soft shading, low contrast
|
|
1061
|
+
concrete 0.85 0.75 0.05 0.9 Highly matte, micro-rough surface, minimal specular reflection
|
|
1062
|
+
eggshell 0.65 0.85 0.25 0.45 Slight sheen, soft highlights without gloss
|
|
1063
|
+
glossy 0.5 0.9 0.6 0.1 Highly polished appearance
|
|
1064
|
+
matte 0.9 0.7 0.0 1.0 Flat, non-reflective surfaces
|
|
1065
|
+
metallic 0.3 0.8 0.9 0.2 Strong, sharp reflections
|
|
1066
|
+
plastic 0.6 0.9 0.2 0.4 Soft highlights, good shape readability
|
|
1067
|
+
Default is plastic.
|
|
1068
|
+
materialKey : str , optional
|
|
1069
|
+
The dictionary key under which the material string is stored. Default is None.
|
|
1070
|
+
ambient : float , optional
|
|
1071
|
+
Controls the strength of ambient light applied uniformly to the surface.
|
|
1072
|
+
Higher values reduce shading contrast by increasing overall brightness.
|
|
1073
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is 0.6.
|
|
1074
|
+
ambientKey : str , optional
|
|
1075
|
+
The dictionary key under which the ambient value (float) is stored. Default is None.
|
|
1076
|
+
diffuse : float , optional
|
|
1077
|
+
Controls the strength of diffuse (Lambertian) lighting based on the angle
|
|
1078
|
+
between the light direction and the surface normal.
|
|
1079
|
+
Higher values enhance shape perception through shading.
|
|
1080
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is None.
|
|
1081
|
+
diffuseKey : str , optional
|
|
1082
|
+
The dictionary key under which the diffuse value (float) is stored. Default is None.
|
|
1083
|
+
specular : float , optional
|
|
1084
|
+
Controls the intensity of specular (mirror-like) highlights on the surface.
|
|
1085
|
+
Higher values produce sharper and brighter highlights, giving a glossy appearance.
|
|
1086
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is None.
|
|
1087
|
+
specularKey : str , optional
|
|
1088
|
+
The dictionary key under which the specular value (float) is stored. Default is None.
|
|
1089
|
+
roughness : float , optional
|
|
1090
|
+
Controls the spread of specular highlights on the surface.
|
|
1091
|
+
Lower values result in sharp, concentrated highlights (smooth surfaces),
|
|
1092
|
+
while higher values produce broader, softer highlights (rough surfaces).
|
|
1093
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is None.
|
|
1094
|
+
roughnessKey : str , optional
|
|
1095
|
+
The dictionary key under which the roughness value (float) is stored. Default is None.
|
|
1042
1096
|
colorScale : str , optional
|
|
1043
1097
|
The desired type of plotly color scales to use (e.g. "Viridis", "Plasma"). Default is "Viridis". For a full list of names, see https://plotly.com/python/builtin-colorscales/.
|
|
1044
1098
|
mantissa : int , optional
|
|
@@ -1061,12 +1115,29 @@ class Plotly:
|
|
|
1061
1115
|
from topologicpy.Helper import Helper
|
|
1062
1116
|
from time import time
|
|
1063
1117
|
|
|
1118
|
+
materials = {
|
|
1119
|
+
"chalk": {"ambient":1.0, "diffuse":0.4, "specular":0.0, "roughness":1.0},
|
|
1120
|
+
"concrete": {"ambient":0.85, "diffuse":0.75, "specular":0.05, "roughness":0.9},
|
|
1121
|
+
"eggshell": {"ambient":0.65, "diffuse":0.85, "specular":0.25, "roughness":0.45},
|
|
1122
|
+
"glossy": {"ambient":0.5, "diffuse":0.9, "specular":0.6, "roughness":0.1},
|
|
1123
|
+
"matte": {"ambient":0.9, "diffuse":0.7, "specular":0.0, "roughness":1.0},
|
|
1124
|
+
"metallic": {"ambient":0.3, "diffuse":0.8, "specular":0.9, "roughness":0.2},
|
|
1125
|
+
"plastic": {"ambient":0.6, "diffuse":0.9, "specular":0.2, "roughness":0.4}
|
|
1126
|
+
}
|
|
1064
1127
|
def closest_index(input_value, values):
|
|
1065
1128
|
return int(min(range(len(values)), key=lambda i: abs(values[i] - input_value)))
|
|
1066
1129
|
|
|
1067
1130
|
|
|
1068
|
-
def faceData(vertices, faces, dictionaries=None,
|
|
1069
|
-
|
|
1131
|
+
def faceData(vertices, faces, dictionaries=None,
|
|
1132
|
+
color="#FAFAFA",
|
|
1133
|
+
colorKey=None,
|
|
1134
|
+
opacity=0.5,
|
|
1135
|
+
opacityKey=None,
|
|
1136
|
+
ambient=0.6,
|
|
1137
|
+
diffuse=0.9,
|
|
1138
|
+
specular=0.2,
|
|
1139
|
+
roughness=0.4,
|
|
1140
|
+
labelKey=None, groupKey=None,
|
|
1070
1141
|
minGroup=None, maxGroup=None, groups=[], legendLabel="Topology Faces",
|
|
1071
1142
|
legendGroup=3, legendRank=3, showLegend=True, intensities=None, colorScale="viridis"):
|
|
1072
1143
|
x = []
|
|
@@ -1163,9 +1234,8 @@ class Plotly:
|
|
|
1163
1234
|
hoverinfo = 'text',
|
|
1164
1235
|
text = labels,
|
|
1165
1236
|
hovertext = labels,
|
|
1166
|
-
flatshading = True,
|
|
1167
1237
|
showscale = False,
|
|
1168
|
-
lighting =
|
|
1238
|
+
lighting=dict(ambient=ambient, diffuse=diffuse, specular=specular, roughness=roughness)
|
|
1169
1239
|
)
|
|
1170
1240
|
return fData
|
|
1171
1241
|
|
|
@@ -1274,15 +1344,52 @@ class Plotly:
|
|
|
1274
1344
|
data.extend(Plotly.edgeData(vertices, edges, dictionaries=e_dictionaries, color=edgeColor, colorKey=edgeColorKey, width=edgeWidth, widthKey=edgeWidthKey, directed=directed, arrowSize=arrowSize, arrowSizeKey=arrowSizeKey, labelKey=edgeLabelKey, showEdgeLabel=showEdgeLabel, groupKey=edgeGroupKey, minGroup=edgeMinGroup, maxGroup=edgeMaxGroup, groups=edgeGroups, legendLabel=edgeLegendLabel, legendGroup=edgeLegendGroup, legendRank=edgeLegendRank, showLegend=showEdgeLegend, colorScale=colorScale))
|
|
1275
1345
|
|
|
1276
1346
|
if showFaces and Topology.Type(topology) >= Topology.TypeID("Face"):
|
|
1347
|
+
d = Topology.Dictionary(topology)
|
|
1277
1348
|
if not faceColorKey == None:
|
|
1278
|
-
d = Topology.Dictionary(topology)
|
|
1279
1349
|
faceColor = Dictionary.ValueAtKey(d, faceColorKey, faceColor)
|
|
1280
1350
|
if not faceOpacityKey == None:
|
|
1281
|
-
d = Topology.Dictionary(topology)
|
|
1282
1351
|
d_opacity = Dictionary.ValueAtKey(d, key=faceOpacityKey)
|
|
1283
1352
|
if not d_opacity == None:
|
|
1284
1353
|
if 0 <= d_opacity <= 1:
|
|
1285
1354
|
faceOpacity = d_opacity
|
|
1355
|
+
|
|
1356
|
+
if not materialKey == None:
|
|
1357
|
+
d_material = Dictionary.ValueAtKey(d, key=materialKey)
|
|
1358
|
+
if not d_material == None and isinstance(d_material, str):
|
|
1359
|
+
if material in list(materials.keys()):
|
|
1360
|
+
material = d_material
|
|
1361
|
+
if not material == None and isinstance(material, str):
|
|
1362
|
+
material = material.lower()
|
|
1363
|
+
if not material in list(materials.keys()):
|
|
1364
|
+
material = "plastic"
|
|
1365
|
+
if not ambientKey == None:
|
|
1366
|
+
d_ambient = Dictionary.ValueAtKey(d, key=ambientKey)
|
|
1367
|
+
if not d_ambient == None:
|
|
1368
|
+
if 0 <= d_ambient <= 1:
|
|
1369
|
+
ambient = d_ambient
|
|
1370
|
+
if not diffuseKey == None:
|
|
1371
|
+
d_diffuse = Dictionary.ValueAtKey(d, key=diffuseKey)
|
|
1372
|
+
if not d_diffuse == None:
|
|
1373
|
+
if 0 <= d_diffuse <= 1:
|
|
1374
|
+
diffuse = d_diffuse
|
|
1375
|
+
if not specularKey == None:
|
|
1376
|
+
d_specular = Dictionary.ValueAtKey(d, key=specularKey)
|
|
1377
|
+
if not d_specular == None:
|
|
1378
|
+
if 0 <= d_specular <= 1:
|
|
1379
|
+
specular = d_specular
|
|
1380
|
+
if not roughnessKey == None:
|
|
1381
|
+
d_roughness = Dictionary.ValueAtKey(d, key=roughnessKey)
|
|
1382
|
+
if not d_roughness == None:
|
|
1383
|
+
if 0 <= d_roughness <= 1:
|
|
1384
|
+
roughness = d_roughness
|
|
1385
|
+
if ambient == None:
|
|
1386
|
+
ambient = materials[material]['ambient']
|
|
1387
|
+
if diffuse == None:
|
|
1388
|
+
diffuse = materials[material]['diffuse']
|
|
1389
|
+
if specular == None:
|
|
1390
|
+
specular = materials[material]['specular']
|
|
1391
|
+
if roughness == None:
|
|
1392
|
+
roughness = materials[material]['roughness']
|
|
1286
1393
|
if Topology.IsInstance(topology, "Face"):
|
|
1287
1394
|
tp_faces = [topology]
|
|
1288
1395
|
else:
|
|
@@ -1305,7 +1412,9 @@ class Plotly:
|
|
|
1305
1412
|
vertices = geo['vertices']
|
|
1306
1413
|
faces = geo['faces']
|
|
1307
1414
|
if len(faces) > 0:
|
|
1308
|
-
data.append(faceData(vertices, faces, dictionaries=f_dictionaries, color=faceColor, colorKey=faceColorKey, opacity=faceOpacity, opacityKey=faceOpacityKey,
|
|
1415
|
+
data.append(faceData(vertices, faces, dictionaries=f_dictionaries, color=faceColor, colorKey=faceColorKey, opacity=faceOpacity, opacityKey=faceOpacityKey,
|
|
1416
|
+
ambient=ambient, diffuse=diffuse, specular=specular, roughness=roughness,
|
|
1417
|
+
labelKey=faceLabelKey, groupKey=faceGroupKey, minGroup=faceMinGroup, maxGroup=faceMaxGroup, groups=faceGroups, legendLabel=faceLegendLabel, legendGroup=faceLegendGroup, legendRank=faceLegendRank, showLegend=showFaceLegend, intensities=intensityList, colorScale=colorScale))
|
|
1309
1418
|
return data
|
|
1310
1419
|
|
|
1311
1420
|
@staticmethod
|
topologicpy/Topology.py
CHANGED
|
@@ -1476,7 +1476,7 @@ class Topology():
|
|
|
1476
1476
|
if Topology.Type(topologyC) == Topology.TypeID("Vertex"):
|
|
1477
1477
|
sinkVertices = [topologyC]
|
|
1478
1478
|
elif hidimC >= Topology.TypeID("Vertex"):
|
|
1479
|
-
sinkVertices = Topology.Vertices(topologyC)
|
|
1479
|
+
sinkVertices = Topology.Vertices(topologyC, silent=True)
|
|
1480
1480
|
if len(sourceVertices) > 0 and len(sinkVertices) > 0:
|
|
1481
1481
|
_ = Topology.TransferDictionaries(sourceVertices, sinkVertices, tolerance=tolerance)
|
|
1482
1482
|
|
|
@@ -1561,7 +1561,7 @@ class Topology():
|
|
|
1561
1561
|
from topologicpy.Dictionary import Dictionary
|
|
1562
1562
|
|
|
1563
1563
|
def bb(topology):
|
|
1564
|
-
vertices = Topology.Vertices(topology)
|
|
1564
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
1565
1565
|
x = []
|
|
1566
1566
|
y = []
|
|
1567
1567
|
z = []
|
|
@@ -3207,191 +3207,326 @@ class Topology():
|
|
|
3207
3207
|
|
|
3208
3208
|
@staticmethod
|
|
3209
3209
|
def ByOBJString(objString: str, mtlString: str = None,
|
|
3210
|
-
defaultColor
|
|
3210
|
+
defaultColor=None, defaultOpacity: float = 1.0,
|
|
3211
3211
|
transposeAxes: bool = True, removeCoplanarFaces: bool = False,
|
|
3212
3212
|
selfMerge: bool = False,
|
|
3213
|
-
mantissa = 6, tolerance = 0.0001):
|
|
3213
|
+
mantissa: int = 6, tolerance: float = 0.0001):
|
|
3214
3214
|
"""
|
|
3215
|
-
Imports a
|
|
3215
|
+
Imports a TopologicPy hierarchy from OBJ and optional MTL strings.
|
|
3216
3216
|
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
If set to True, the faces of the imported topologies will each be self-merged to create higher-dimensional objects. Otherwise, they remain a cluster of faces. Default is False.
|
|
3233
|
-
mantissa : int , optional
|
|
3234
|
-
The number of decimal places to round the result to. Default is 6.
|
|
3235
|
-
tolerance : float , optional
|
|
3236
|
-
The desired tolerance. Default is 0.0001
|
|
3217
|
+
Supported OBJ primitives
|
|
3218
|
+
------------------------
|
|
3219
|
+
- v : vertices
|
|
3220
|
+
- l : polylines (edges-only / wire-only models)
|
|
3221
|
+
- f : faces (tri/quad/ngon; may be self-merged)
|
|
3222
|
+
|
|
3223
|
+
Grouping
|
|
3224
|
+
--------
|
|
3225
|
+
- g / o : groups/objects become separate returned topologies (one per group/object)
|
|
3226
|
+
|
|
3227
|
+
Materials
|
|
3228
|
+
---------
|
|
3229
|
+
- usemtl + MTL Kd/d/Tr are used to set:
|
|
3230
|
+
color : [R,G,B] in 0..255
|
|
3231
|
+
opacity : 0..1
|
|
3237
3232
|
|
|
3238
3233
|
Returns
|
|
3239
3234
|
-------
|
|
3240
3235
|
list
|
|
3241
|
-
|
|
3242
|
-
|
|
3236
|
+
One TopologicPy topology per OBJ group/object:
|
|
3237
|
+
- If a group has faces: returns (best effort) a merged topology (or a Cluster of faces).
|
|
3238
|
+
- Else if a group has polylines/edges: returns a Cluster of Wires/Edges.
|
|
3239
|
+
- Else if a group has only points: returns a Cluster of Vertices.
|
|
3240
|
+
- If mixed: returns a Cluster containing the appropriate mix.
|
|
3243
3241
|
"""
|
|
3244
3242
|
from topologicpy.Vertex import Vertex
|
|
3245
3243
|
from topologicpy.Edge import Edge
|
|
3246
3244
|
from topologicpy.Wire import Wire
|
|
3247
|
-
from topologicpy.Face import Face
|
|
3248
|
-
from topologicpy.Shell import Shell
|
|
3249
|
-
from topologicpy.Cell import Cell
|
|
3250
3245
|
from topologicpy.Cluster import Cluster
|
|
3251
|
-
from topologicpy.Topology import Topology
|
|
3252
3246
|
from topologicpy.Dictionary import Dictionary
|
|
3253
|
-
from topologicpy.
|
|
3247
|
+
from topologicpy.Topology import Topology
|
|
3248
|
+
|
|
3249
|
+
if defaultColor is None:
|
|
3250
|
+
defaultColor = [255, 255, 255]
|
|
3254
3251
|
|
|
3255
|
-
|
|
3252
|
+
# -----------------------------
|
|
3253
|
+
# MTL parsing (robust)
|
|
3254
|
+
# -----------------------------
|
|
3255
|
+
def load_materials(mtl_string: str):
|
|
3256
3256
|
materials = {}
|
|
3257
3257
|
if not mtl_string:
|
|
3258
3258
|
return materials
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
for
|
|
3262
|
-
line =
|
|
3263
|
-
if line.startswith(
|
|
3259
|
+
|
|
3260
|
+
current = None
|
|
3261
|
+
for raw in mtl_string.splitlines():
|
|
3262
|
+
line = raw.strip()
|
|
3263
|
+
if not line or line.startswith("#"):
|
|
3264
3264
|
continue
|
|
3265
3265
|
parts = line.split()
|
|
3266
3266
|
if not parts:
|
|
3267
3267
|
continue
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
materials[
|
|
3276
|
-
elif
|
|
3277
|
-
materials[
|
|
3278
|
-
elif parts
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
materials[
|
|
3284
|
-
|
|
3268
|
+
|
|
3269
|
+
tag = parts[0]
|
|
3270
|
+
if tag == "newmtl" and len(parts) > 1:
|
|
3271
|
+
current = parts[1]
|
|
3272
|
+
materials[current] = {}
|
|
3273
|
+
elif current:
|
|
3274
|
+
if tag in ("Kd", "Ka", "Ks") and len(parts) >= 4:
|
|
3275
|
+
materials[current][tag] = list(map(float, parts[1:4]))
|
|
3276
|
+
elif tag == "Ns" and len(parts) >= 2:
|
|
3277
|
+
materials[current]["Ns"] = float(parts[1])
|
|
3278
|
+
elif tag in ("d", "Tr") and len(parts) >= 2:
|
|
3279
|
+
# d is opacity; Tr sometimes is transparency (inverse)
|
|
3280
|
+
val = float(parts[1])
|
|
3281
|
+
if tag == "Tr":
|
|
3282
|
+
val = 1.0 - val
|
|
3283
|
+
materials[current]["d"] = val
|
|
3284
|
+
elif tag == "map_Kd" and len(parts) >= 2:
|
|
3285
|
+
materials[current]["map_Kd"] = " ".join(parts[1:]) # allow spaces
|
|
3285
3286
|
return materials
|
|
3286
3287
|
|
|
3287
3288
|
materials = load_materials(mtlString)
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3289
|
+
|
|
3290
|
+
def clamp01(x: float) -> float:
|
|
3291
|
+
return max(0.0, min(1.0, x))
|
|
3292
|
+
|
|
3293
|
+
def material_to_color_opacity(mat_name):
|
|
3294
|
+
color = defaultColor
|
|
3295
|
+
opacity = defaultOpacity
|
|
3296
|
+
if mat_name and mat_name in materials:
|
|
3297
|
+
m = materials[mat_name]
|
|
3298
|
+
if "Kd" in m and isinstance(m["Kd"], list) and len(m["Kd"]) >= 3:
|
|
3299
|
+
color = [int(round(clamp01(c) * 255.0, 0)) for c in m["Kd"][:3]]
|
|
3300
|
+
if "d" in m:
|
|
3301
|
+
try:
|
|
3302
|
+
opacity = float(m["d"])
|
|
3303
|
+
except Exception:
|
|
3304
|
+
opacity = defaultOpacity
|
|
3305
|
+
return color, opacity
|
|
3306
|
+
|
|
3307
|
+
# -----------------------------
|
|
3308
|
+
# OBJ parsing
|
|
3309
|
+
# -----------------------------
|
|
3310
|
+
verts_xyz = []
|
|
3311
|
+
groups = {} # name -> {"faces":[(triplets,mat)], "lines":[(indices,mat)], "points":[(idx,mat)]}
|
|
3312
|
+
current_group = "default"
|
|
3293
3313
|
current_material = None
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3314
|
+
|
|
3315
|
+
def ensure_group(name: str):
|
|
3316
|
+
if not name:
|
|
3317
|
+
name = "default"
|
|
3318
|
+
if name not in groups:
|
|
3319
|
+
groups[name] = {"faces": [], "lines": [], "points": []}
|
|
3320
|
+
return name
|
|
3321
|
+
|
|
3322
|
+
def resolve_index(idx: int, n: int):
|
|
3323
|
+
"""
|
|
3324
|
+
OBJ indices are 1-based; negative indices are relative to the end.
|
|
3325
|
+
Returns a 0-based index or None.
|
|
3326
|
+
"""
|
|
3327
|
+
if idx is None:
|
|
3328
|
+
return None
|
|
3329
|
+
if idx > 0:
|
|
3330
|
+
z = idx - 1
|
|
3331
|
+
else:
|
|
3332
|
+
z = n + idx # idx is negative
|
|
3333
|
+
if z < 0 or z >= n:
|
|
3334
|
+
return None
|
|
3335
|
+
return z
|
|
3336
|
+
|
|
3337
|
+
for raw in objString.splitlines():
|
|
3338
|
+
line = raw.strip()
|
|
3339
|
+
if not line or line.startswith("#"):
|
|
3298
3340
|
continue
|
|
3299
3341
|
|
|
3300
3342
|
parts = line.split()
|
|
3301
3343
|
if not parts:
|
|
3302
3344
|
continue
|
|
3303
3345
|
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
elif
|
|
3314
|
-
normal = list(map(float, parts[1:4]))
|
|
3315
|
-
normals.append(normal)
|
|
3316
|
-
elif parts[0] == 'f':
|
|
3317
|
-
face = []
|
|
3318
|
-
for part in parts[1:]:
|
|
3319
|
-
indices = part.split('/')
|
|
3320
|
-
vertex_index = int(indices[0]) - 1 if indices[0] else None
|
|
3321
|
-
texture_index = int(indices[1]) - 1 if len(indices) > 1 and indices[1] else None
|
|
3322
|
-
normal_index = int(indices[2]) - 1 if len(indices) > 2 and indices[2] else None
|
|
3323
|
-
face.append((vertex_index, texture_index, normal_index))
|
|
3324
|
-
|
|
3325
|
-
if current_group not in groups:
|
|
3326
|
-
groups[current_group] = []
|
|
3327
|
-
groups[current_group].append((face, current_material))
|
|
3328
|
-
elif parts[0] == 'usemtl':
|
|
3346
|
+
tag = parts[0]
|
|
3347
|
+
|
|
3348
|
+
if tag == "v" and len(parts) >= 4:
|
|
3349
|
+
v = list(map(float, parts[1:4]))
|
|
3350
|
+
v = [round(c, mantissa) for c in v]
|
|
3351
|
+
if transposeAxes:
|
|
3352
|
+
v = [v[0], -v[2], v[1]]
|
|
3353
|
+
verts_xyz.append(v)
|
|
3354
|
+
|
|
3355
|
+
elif tag == "usemtl" and len(parts) >= 2:
|
|
3329
3356
|
current_material = parts[1]
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
#
|
|
3393
|
-
|
|
3394
|
-
|
|
3357
|
+
|
|
3358
|
+
elif tag in ("g", "o"):
|
|
3359
|
+
name = " ".join(parts[1:]).strip() if len(parts) > 1 else "default"
|
|
3360
|
+
current_group = ensure_group(name)
|
|
3361
|
+
|
|
3362
|
+
elif tag == "f" and len(parts) >= 4:
|
|
3363
|
+
current_group = ensure_group(current_group)
|
|
3364
|
+
|
|
3365
|
+
triplets = []
|
|
3366
|
+
for p in parts[1:]:
|
|
3367
|
+
toks = p.split("/")
|
|
3368
|
+
vi = int(toks[0]) if toks[0] else None
|
|
3369
|
+
vti = int(toks[1]) if len(toks) > 1 and toks[1] else None
|
|
3370
|
+
vni = int(toks[2]) if len(toks) > 2 and toks[2] else None
|
|
3371
|
+
|
|
3372
|
+
vi = resolve_index(vi, len(verts_xyz))
|
|
3373
|
+
triplets.append((vi, vti, vni))
|
|
3374
|
+
|
|
3375
|
+
if any(t[0] is None for t in triplets):
|
|
3376
|
+
continue
|
|
3377
|
+
|
|
3378
|
+
groups[current_group]["faces"].append((triplets, current_material))
|
|
3379
|
+
|
|
3380
|
+
elif tag == "l" and len(parts) >= 3:
|
|
3381
|
+
# OBJ "l": polyline defined by vertex indices, optionally with texture indices.
|
|
3382
|
+
# We'll read only vertex indices (first number before any '/')
|
|
3383
|
+
current_group = ensure_group(current_group)
|
|
3384
|
+
|
|
3385
|
+
idxs = []
|
|
3386
|
+
ok = True
|
|
3387
|
+
for token in parts[1:]:
|
|
3388
|
+
# token can be "v" or "v/vt"
|
|
3389
|
+
subtoks = token.split("/")
|
|
3390
|
+
if not subtoks or not subtoks[0]:
|
|
3391
|
+
ok = False
|
|
3392
|
+
break
|
|
3393
|
+
vi = resolve_index(int(subtoks[0]), len(verts_xyz))
|
|
3394
|
+
if vi is None:
|
|
3395
|
+
ok = False
|
|
3396
|
+
break
|
|
3397
|
+
idxs.append(vi)
|
|
3398
|
+
|
|
3399
|
+
if not ok or len(idxs) < 2:
|
|
3400
|
+
continue
|
|
3401
|
+
|
|
3402
|
+
groups[current_group]["lines"].append((idxs, current_material))
|
|
3403
|
+
|
|
3404
|
+
elif tag == "p" and len(parts) >= 2:
|
|
3405
|
+
# OBJ "p": point list (rare). Keep as vertices.
|
|
3406
|
+
current_group = ensure_group(current_group)
|
|
3407
|
+
for token in parts[1:]:
|
|
3408
|
+
try:
|
|
3409
|
+
vi = resolve_index(int(token), len(verts_xyz))
|
|
3410
|
+
except Exception:
|
|
3411
|
+
vi = None
|
|
3412
|
+
if vi is not None:
|
|
3413
|
+
groups[current_group]["points"].append((vi, current_material))
|
|
3414
|
+
|
|
3415
|
+
else:
|
|
3416
|
+
# ignore: vt, vn, s, mtllib, etc. (vt/vn are not needed for Topologic hierarchy here)
|
|
3417
|
+
pass
|
|
3418
|
+
|
|
3419
|
+
# If OBJ never declared a group but had geometry, ensure "default" exists
|
|
3420
|
+
if not groups:
|
|
3421
|
+
ensure_group("default")
|
|
3422
|
+
|
|
3423
|
+
# -----------------------------
|
|
3424
|
+
# Build Topologic hierarchy
|
|
3425
|
+
# -----------------------------
|
|
3426
|
+
def set_dict(topo, group_name: str, mat_name: str):
|
|
3427
|
+
c, a = material_to_color_opacity(mat_name)
|
|
3428
|
+
d = Dictionary.ByKeysValues(
|
|
3429
|
+
["name", "group", "material", "color", "opacity"],
|
|
3430
|
+
[group_name, group_name, mat_name if mat_name else "", c, a]
|
|
3431
|
+
)
|
|
3432
|
+
return Topology.SetDictionary(topo, d)
|
|
3433
|
+
|
|
3434
|
+
imported = []
|
|
3435
|
+
|
|
3436
|
+
for group_name, rec in groups.items():
|
|
3437
|
+
faces_rec = rec.get("faces", [])
|
|
3438
|
+
lines_rec = rec.get("lines", [])
|
|
3439
|
+
points_rec = rec.get("points", [])
|
|
3440
|
+
|
|
3441
|
+
topologies = []
|
|
3442
|
+
|
|
3443
|
+
# --- Faces
|
|
3444
|
+
face_topos = []
|
|
3445
|
+
for triplets, mat in faces_rec:
|
|
3446
|
+
face_indices = [t[0] for t in triplets]
|
|
3447
|
+
# Create a face from raw coordinates (Topology.ByGeometry expects vertices as coordinate lists)
|
|
3448
|
+
topo_face = Topology.ByGeometry(vertices=verts_xyz, faces=[face_indices])
|
|
3449
|
+
if topo_face is None:
|
|
3450
|
+
continue
|
|
3451
|
+
topo_face = set_dict(topo_face, group_name, mat)
|
|
3452
|
+
face_topos.append(topo_face)
|
|
3453
|
+
|
|
3454
|
+
if face_topos:
|
|
3455
|
+
# If requested, attempt to merge coplanar faces / build higher-dimensional objects
|
|
3456
|
+
face_cluster = Cluster.ByTopologies(face_topos)
|
|
3457
|
+
face_cluster = set_dict(face_cluster, group_name, faces_rec[0][1] if faces_rec else None)
|
|
3458
|
+
|
|
3459
|
+
if selfMerge or removeCoplanarFaces:
|
|
3460
|
+
merged = Topology.SelfMerge(face_cluster, tolerance=tolerance)
|
|
3461
|
+
if merged is not None:
|
|
3462
|
+
face_cluster = merged
|
|
3463
|
+
# Keep group-level dict (SelfMerge may drop dictionaries)
|
|
3464
|
+
face_cluster = set_dict(face_cluster, group_name, faces_rec[0][1] if faces_rec else None)
|
|
3465
|
+
|
|
3466
|
+
topologies.append(face_cluster)
|
|
3467
|
+
|
|
3468
|
+
# --- Lines (Wires/Edges)
|
|
3469
|
+
line_topos = []
|
|
3470
|
+
for idxs, mat in lines_rec:
|
|
3471
|
+
v_objs = [Vertex.ByCoordinates(*verts_xyz[i]) for i in idxs]
|
|
3472
|
+
if len(v_objs) == 2:
|
|
3473
|
+
topo = Edge.ByVertices(v_objs[0], v_objs[1], tolerance=tolerance)
|
|
3474
|
+
else:
|
|
3475
|
+
topo = Wire.ByVertices(v_objs, close=False, tolerance=tolerance)
|
|
3476
|
+
if topo is None:
|
|
3477
|
+
continue
|
|
3478
|
+
topo = set_dict(topo, group_name, mat)
|
|
3479
|
+
line_topos.append(topo)
|
|
3480
|
+
|
|
3481
|
+
if line_topos:
|
|
3482
|
+
line_cluster = Cluster.ByTopologies(line_topos)
|
|
3483
|
+
line_cluster = set_dict(line_cluster, group_name, lines_rec[0][1] if lines_rec else None)
|
|
3484
|
+
topologies.append(line_cluster)
|
|
3485
|
+
|
|
3486
|
+
# --- Points (Vertices)
|
|
3487
|
+
point_topos = []
|
|
3488
|
+
# If there are explicit "p" points, use them.
|
|
3489
|
+
# If there is no geometry at all except vertices in the file and no p/l/f,
|
|
3490
|
+
# we’ll fall back to all vertices later.
|
|
3491
|
+
for vi, mat in points_rec:
|
|
3492
|
+
v = Vertex.ByCoordinates(*verts_xyz[vi])
|
|
3493
|
+
v = set_dict(v, group_name, mat)
|
|
3494
|
+
point_topos.append(v)
|
|
3495
|
+
|
|
3496
|
+
if point_topos:
|
|
3497
|
+
point_cluster = Cluster.ByTopologies(point_topos)
|
|
3498
|
+
point_cluster = set_dict(point_cluster, group_name, points_rec[0][1] if points_rec else None)
|
|
3499
|
+
topologies.append(point_cluster)
|
|
3500
|
+
|
|
3501
|
+
# --- If group had *no* faces/lines/points but there are vertices in the file, return all vertices
|
|
3502
|
+
if not topologies and verts_xyz:
|
|
3503
|
+
all_vs = [Vertex.ByCoordinates(*xyz) for xyz in verts_xyz]
|
|
3504
|
+
v_cluster = Cluster.ByTopologies(all_vs)
|
|
3505
|
+
v_cluster = set_dict(v_cluster, group_name, None)
|
|
3506
|
+
topologies.append(v_cluster)
|
|
3507
|
+
|
|
3508
|
+
# --- Decide "correct hierarchy" output for this group
|
|
3509
|
+
if not topologies:
|
|
3510
|
+
continue
|
|
3511
|
+
elif len(topologies) == 1:
|
|
3512
|
+
group_topo = topologies[0]
|
|
3513
|
+
else:
|
|
3514
|
+
# Mixed geometry in same group: return a cluster containing the mixed subclusters
|
|
3515
|
+
group_topo = Cluster.ByTopologies(topologies)
|
|
3516
|
+
# Use first available material as group hint
|
|
3517
|
+
hint_mat = None
|
|
3518
|
+
if faces_rec:
|
|
3519
|
+
hint_mat = faces_rec[0][1]
|
|
3520
|
+
elif lines_rec:
|
|
3521
|
+
hint_mat = lines_rec[0][1]
|
|
3522
|
+
elif points_rec:
|
|
3523
|
+
hint_mat = points_rec[0][1]
|
|
3524
|
+
group_topo = set_dict(group_topo, group_name, hint_mat)
|
|
3525
|
+
|
|
3526
|
+
imported.append(group_topo)
|
|
3527
|
+
|
|
3528
|
+
return imported
|
|
3529
|
+
|
|
3395
3530
|
|
|
3396
3531
|
@staticmethod
|
|
3397
3532
|
def ByOCCTShape(occtShape):
|
|
@@ -4537,7 +4672,7 @@ class Topology():
|
|
|
4537
4672
|
convex_hull = Topology.Unflatten(convex_hull_2, origin=centroid, direction=normal)
|
|
4538
4673
|
return convex_hull
|
|
4539
4674
|
|
|
4540
|
-
vertices = Topology.Vertices(topology)
|
|
4675
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
4541
4676
|
if len(vertices) < 3:
|
|
4542
4677
|
if not silent:
|
|
4543
4678
|
print("Topology.ConvexHull - Error: Need at least 3 points to compute a convex hull.")
|
|
@@ -5723,7 +5858,7 @@ class Topology():
|
|
|
5723
5858
|
topology = Topology.SelfMerge(topology, tolerance=tolerance)
|
|
5724
5859
|
if Topology.TypeAsString(topology).lower() == "edge":
|
|
5725
5860
|
return topology
|
|
5726
|
-
vertices = Topology.Vertices(topology)
|
|
5861
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
5727
5862
|
if len(vertices) < 2:
|
|
5728
5863
|
print("Topology.Fix - Error: Desired topologyType cannot be achieved. Returning original topology.")
|
|
5729
5864
|
return topology
|
|
@@ -5736,7 +5871,7 @@ class Topology():
|
|
|
5736
5871
|
topology = Topology.SelfMerge(topology, tolerance=tolerance)
|
|
5737
5872
|
if Topology.TypeAsString(topology).lower() == "vertex":
|
|
5738
5873
|
return topology
|
|
5739
|
-
vertices = Topology.Vertices(topology)
|
|
5874
|
+
vertices = Topology.Vertices(topology,silent=True)
|
|
5740
5875
|
if len(vertices) < 1:
|
|
5741
5876
|
print("Topology.Fix - Error: Desired topologyType cannot be achieved. Returning original topology.")
|
|
5742
5877
|
return topology
|
|
@@ -6673,7 +6808,7 @@ class Topology():
|
|
|
6673
6808
|
if not Topology.IsInstance(topology, "Topology"):
|
|
6674
6809
|
print("Topology.IsPlanar - Error: the input topology parameter is not a valid topology. Returning None.")
|
|
6675
6810
|
return None
|
|
6676
|
-
vertices = Topology.Vertices(topology)
|
|
6811
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
6677
6812
|
|
|
6678
6813
|
result = True
|
|
6679
6814
|
if len(vertices) <= 3:
|
|
@@ -8493,7 +8628,7 @@ class Topology():
|
|
|
8493
8628
|
vertices = [v for v in vertices if Topology.IsInstance(v, "Vertex")]
|
|
8494
8629
|
if len(vertices) < 1:
|
|
8495
8630
|
return topology
|
|
8496
|
-
t_vertices = Topology.Vertices(topology)
|
|
8631
|
+
t_vertices = Topology.Vertices(topology, silent=True)
|
|
8497
8632
|
t_edges = Topology.Edges(topology)
|
|
8498
8633
|
if len(t_vertices) < 1:
|
|
8499
8634
|
return topology
|
|
@@ -9011,7 +9146,7 @@ class Topology():
|
|
|
9011
9146
|
topFaces = Topology.Faces(topology)
|
|
9012
9147
|
topWires = Topology.Wires(topology)
|
|
9013
9148
|
topEdges = Topology.Edges(topology)
|
|
9014
|
-
topVertices = Topology.Vertices(topology)
|
|
9149
|
+
topVertices = Topology.Vertices(topology, silent=True)
|
|
9015
9150
|
if len(topCC) == 1:
|
|
9016
9151
|
cc = topCC[0]
|
|
9017
9152
|
ccVertices = Topology.Vertices(cc)
|
|
@@ -9937,6 +10072,17 @@ class Topology():
|
|
|
9937
10072
|
faceLegendLabel="Faces",
|
|
9938
10073
|
intensityKey=None,
|
|
9939
10074
|
intensities=[],
|
|
10075
|
+
|
|
10076
|
+
material = "plastic",
|
|
10077
|
+
materialKey=None,
|
|
10078
|
+
ambient = None,
|
|
10079
|
+
ambientKey=None,
|
|
10080
|
+
diffuse = None,
|
|
10081
|
+
diffuseKey=None,
|
|
10082
|
+
specular = None,
|
|
10083
|
+
specularKey=None,
|
|
10084
|
+
roughness = None,
|
|
10085
|
+
roughnessKey=None,
|
|
9940
10086
|
|
|
9941
10087
|
width=950,
|
|
9942
10088
|
height=500,
|
|
@@ -10139,6 +10285,46 @@ class Topology():
|
|
|
10139
10285
|
If not None, the dictionary of each vertex is searched for the value associated with the intensity key. This value is then used to color-code the vertex based on the colorScale. Default is None.
|
|
10140
10286
|
intensities : list , optional
|
|
10141
10287
|
The list of intensities against which to index the intensity of the vertex. Default is [].
|
|
10288
|
+
material : str , optional
|
|
10289
|
+
The type of object material. Case in-sensitive. Supported pre-built materials are:
|
|
10290
|
+
Preset Ambient Diffuse Specular Roughness Description
|
|
10291
|
+
--------------------------------------------------------------
|
|
10292
|
+
chalk 1.0 0.4 0.0 1.0 Very soft shading, low contrast
|
|
10293
|
+
concrete 0.85 0.75 0.05 0.9 Highly matte, micro-rough surface, minimal specular reflection
|
|
10294
|
+
eggshell 0.65 0.85 0.25 0.45 Slight sheen, soft highlights without gloss
|
|
10295
|
+
glossy 0.5 0.9 0.6 0.1 Highly polished appearance
|
|
10296
|
+
matte 0.9 0.7 0.0 1.0 Flat, non-reflective surfaces
|
|
10297
|
+
metallic 0.3 0.8 0.9 0.2 Strong, sharp reflections
|
|
10298
|
+
plastic 0.6 0.9 0.2 0.4 Soft highlights, good shape readability
|
|
10299
|
+
Default is "plastic".
|
|
10300
|
+
materialKey : str , optional
|
|
10301
|
+
The dictionary key under which the material string is stored. Default is None.
|
|
10302
|
+
ambient : float , optional
|
|
10303
|
+
Controls the strength of ambient light applied uniformly to the surface.
|
|
10304
|
+
Higher values reduce shading contrast by increasing overall brightness.
|
|
10305
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is 0.6.
|
|
10306
|
+
ambientKey : str , optional
|
|
10307
|
+
The dictionary key under which the ambient value (float) is stored. Default is None.
|
|
10308
|
+
diffuse : float , optional
|
|
10309
|
+
Controls the strength of diffuse (Lambertian) lighting based on the angle
|
|
10310
|
+
between the light direction and the surface normal.
|
|
10311
|
+
Higher values enhance shape perception through shading.
|
|
10312
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is None.
|
|
10313
|
+
diffuseKey : str , optional
|
|
10314
|
+
The dictionary key under which the diffuse value (float) is stored. Default is None.
|
|
10315
|
+
specular : float , optional
|
|
10316
|
+
Controls the intensity of specular (mirror-like) highlights on the surface.
|
|
10317
|
+
Higher values produce sharper and brighter highlights, giving a glossy appearance.
|
|
10318
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is None.
|
|
10319
|
+
specularKey : str , optional
|
|
10320
|
+
The dictionary key under which the specular value (float) is stored. Default is None.
|
|
10321
|
+
roughness : float , optional
|
|
10322
|
+
Controls the spread of specular highlights on the surface.
|
|
10323
|
+
Lower values result in sharp, concentrated highlights (smooth surfaces),
|
|
10324
|
+
while higher values produce broader, softer highlights (rough surfaces).
|
|
10325
|
+
Typical range is [0, 1]. This over-rides the material pre-sets. Default is None.
|
|
10326
|
+
roughnessKey : str , optional
|
|
10327
|
+
The dictionary key under which the roughness value (float) is stored. Default is None.
|
|
10142
10328
|
showScale : bool , optional
|
|
10143
10329
|
If set to True, the colorbar is shown. Default is False.
|
|
10144
10330
|
cbValues : list , optional
|
|
@@ -10333,6 +10519,16 @@ class Topology():
|
|
|
10333
10519
|
faceLegendGroup=topology_counter+3,
|
|
10334
10520
|
intensityKey=intensityKey,
|
|
10335
10521
|
intensities=intensities,
|
|
10522
|
+
material=material,
|
|
10523
|
+
materialKey=materialKey,
|
|
10524
|
+
ambient=ambient,
|
|
10525
|
+
ambientKey=ambientKey,
|
|
10526
|
+
diffuse=diffuse,
|
|
10527
|
+
diffuseKey=diffuseKey,
|
|
10528
|
+
specular=specular,
|
|
10529
|
+
specularKey=specularKey,
|
|
10530
|
+
roughness=roughness,
|
|
10531
|
+
roughnessKey=roughnessKey,
|
|
10336
10532
|
colorScale=colorScale,
|
|
10337
10533
|
mantissa=mantissa,
|
|
10338
10534
|
tolerance=tolerance,
|
|
@@ -11276,7 +11472,7 @@ class Topology():
|
|
|
11276
11472
|
topology = Topology.Triangulate(topology)
|
|
11277
11473
|
if not Topology.IsInstance(origin, "Vertex"):
|
|
11278
11474
|
origin = Topology.Centroid(topology)
|
|
11279
|
-
vertices = Topology.Vertices(topology)
|
|
11475
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
11280
11476
|
zList = [Vertex.Z(v, mantissa=mantissa) for v in vertices]
|
|
11281
11477
|
z_min = min(zList)
|
|
11282
11478
|
maxZ = max(zList)
|
|
@@ -11330,7 +11526,7 @@ class Topology():
|
|
|
11330
11526
|
if not Topology.IsInstance(origin, "Vertex"):
|
|
11331
11527
|
origin = Topology.Centroid(topology)
|
|
11332
11528
|
|
|
11333
|
-
vertices = Topology.Vertices(topology)
|
|
11529
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
11334
11530
|
zList = [Vertex.Z(v, mantissa=mantissa) for v in vertices]
|
|
11335
11531
|
z_min = min(zList)
|
|
11336
11532
|
maxZ = max(zList)
|
|
@@ -11392,7 +11588,7 @@ class Topology():
|
|
|
11392
11588
|
return unflat_topology
|
|
11393
11589
|
|
|
11394
11590
|
@staticmethod
|
|
11395
|
-
def Vertices(topology, silent: bool =
|
|
11591
|
+
def Vertices(topology, silent: bool = True):
|
|
11396
11592
|
"""
|
|
11397
11593
|
Returns the vertices of the input topology.
|
|
11398
11594
|
|
|
@@ -11694,6 +11890,8 @@ class Topology():
|
|
|
11694
11890
|
The input topology with the dictionaries transferred to its subtopologies.
|
|
11695
11891
|
|
|
11696
11892
|
"""
|
|
11893
|
+
import time
|
|
11894
|
+
# timePrep = time.time()
|
|
11697
11895
|
from topologicpy.Vertex import Vertex
|
|
11698
11896
|
from topologicpy.Cluster import Cluster
|
|
11699
11897
|
from topologicpy.Dictionary import Dictionary
|
|
@@ -11736,7 +11934,6 @@ class Topology():
|
|
|
11736
11934
|
return [topo]
|
|
11737
11935
|
else:
|
|
11738
11936
|
return Topology.Vertices(topo, silent=True)
|
|
11739
|
-
|
|
11740
11937
|
vertices = []
|
|
11741
11938
|
edges = []
|
|
11742
11939
|
faces = []
|
|
@@ -11754,14 +11951,31 @@ class Topology():
|
|
|
11754
11951
|
primitives.extend(faces)
|
|
11755
11952
|
primitives.extend(edges)
|
|
11756
11953
|
primitives.extend(vertices)
|
|
11757
|
-
|
|
11954
|
+
# print("bvh preparation", f"{time.time() - timePrep:.4f}s", len(primitives))
|
|
11955
|
+
|
|
11956
|
+
# timeStart = time.time()
|
|
11957
|
+
|
|
11958
|
+
bvh = BVH.ByTopologies(primitives, tolerance=tolerance, silent=True)
|
|
11959
|
+
# print("bvh created", f"{time.time() - timeStart:.4f}s", len(bvh.items))
|
|
11960
|
+
|
|
11758
11961
|
for s in selectors:
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11962
|
+
try:
|
|
11963
|
+
candidates = BVH.Clashes(bvh, s, tolerance=tolerance) or []
|
|
11964
|
+
except Exception as e:
|
|
11965
|
+
# print(f"BVH clash query failed for a selector. Trying fallback. {e}")
|
|
11966
|
+
# Fallback if your BVH needs a non-degenerate query
|
|
11967
|
+
candidates = primitives
|
|
11968
|
+
|
|
11969
|
+
if not candidates:
|
|
11970
|
+
continue
|
|
11971
|
+
for element in candidates:
|
|
11972
|
+
status = Vertex.IsInternal(s, element, tolerance=tolerance)
|
|
11973
|
+
if status:
|
|
11974
|
+
d1 = Topology.Dictionary(s)
|
|
11975
|
+
d2 = Topology.Dictionary(element)
|
|
11976
|
+
d3 = Dictionary.ByMergedDictionaries(d1, d2)
|
|
11977
|
+
element = Topology.SetDictionary(element, d3)
|
|
11978
|
+
# print("bvh query done", f"{time.time() - timeStart:.4f}s")
|
|
11765
11979
|
return topology
|
|
11766
11980
|
|
|
11767
11981
|
@staticmethod
|
|
@@ -11885,7 +12099,7 @@ class Topology():
|
|
|
11885
12099
|
silent=silent)
|
|
11886
12100
|
|
|
11887
12101
|
if transferDictionaries == True:
|
|
11888
|
-
vertices = Topology.Vertices(topology)
|
|
12102
|
+
vertices = Topology.Vertices(topology, silent=True)
|
|
11889
12103
|
edges = Topology.Edges(topology, silent=True)
|
|
11890
12104
|
wires = Topology.Wires(topology, silent=True)
|
|
11891
12105
|
faces = Topology.Faces(topology, silent=True)
|
topologicpy/Vertex.py
CHANGED
|
@@ -1293,7 +1293,13 @@ class Vertex():
|
|
|
1293
1293
|
# --------------------------
|
|
1294
1294
|
points = [Vertex.Coordinates(v) for v in Topology.Vertices(topology)]
|
|
1295
1295
|
aabb = AABB.from_points(points, pad=tolerance)
|
|
1296
|
+
if(Vertex.Coordinates(vertex) is None):
|
|
1297
|
+
if identify:
|
|
1298
|
+
return (False, None)
|
|
1299
|
+
return False
|
|
1296
1300
|
if not aabb.contains_point(Vertex.Coordinates(vertex)):
|
|
1301
|
+
if identify:
|
|
1302
|
+
return (False, None)
|
|
1297
1303
|
return False
|
|
1298
1304
|
|
|
1299
1305
|
# --------------------------
|
|
@@ -1333,6 +1339,8 @@ class Vertex():
|
|
|
1333
1339
|
edges = [] if faces or cells else collect_edges(topology)
|
|
1334
1340
|
vertices = [] if edges or faces or cells else collect_vertices(topology)
|
|
1335
1341
|
if not cells and not faces and not edges and not vertices:
|
|
1342
|
+
if identify:
|
|
1343
|
+
return (False, None)
|
|
1336
1344
|
return False
|
|
1337
1345
|
|
|
1338
1346
|
# --------------------------
|
|
@@ -1345,12 +1353,14 @@ class Vertex():
|
|
|
1345
1353
|
primitives.extend(cells)
|
|
1346
1354
|
bvh = BVH.ByTopologies(primitives, maxLeafSize=maxLeafSize, tolerance=tolerance, silent=True)
|
|
1347
1355
|
try:
|
|
1348
|
-
candidates = BVH.Clashes(bvh, vertex,
|
|
1356
|
+
candidates = BVH.Clashes(bvh, vertex, tolerance=tolerance) or []
|
|
1349
1357
|
except Exception:
|
|
1350
1358
|
# Fallback if your BVH needs a non-degenerate query
|
|
1351
1359
|
candidates = primitives
|
|
1352
1360
|
|
|
1353
1361
|
if not candidates:
|
|
1362
|
+
if identify:
|
|
1363
|
+
return (False, None)
|
|
1354
1364
|
return False
|
|
1355
1365
|
|
|
1356
1366
|
# sort by types so that priority is given to lower dimensional types (e.g. vertices, then edges, then faces, then cells)
|
|
@@ -2313,6 +2323,29 @@ class Vertex():
|
|
|
2313
2323
|
coords = [round(v, mantissa) for v in coords]
|
|
2314
2324
|
return Vertex.ByCoordinates(coords)
|
|
2315
2325
|
|
|
2326
|
+
@staticmethod
|
|
2327
|
+
def Weld(vertices: list, mantissa: int = 6, tolerance: float = 0.0001):
|
|
2328
|
+
"""
|
|
2329
|
+
Returns a list of vertices where vertices within a specified tolerance distance are fused while retaining duplicates, ensuring that vertices with nearly identical coordinates are replaced by a single shared coordinate.
|
|
2330
|
+
|
|
2331
|
+
Parameters
|
|
2332
|
+
----------
|
|
2333
|
+
vertices : list
|
|
2334
|
+
The input list of topologic vertices.
|
|
2335
|
+
mantissa : int , optional
|
|
2336
|
+
The desired length of the mantissa for retrieving vertex coordinates. Default is 6.
|
|
2337
|
+
tolerance : float , optional
|
|
2338
|
+
The desired tolerance for computing if vertices need to be fused. Any vertices that are closer to each other than this tolerance will be fused. Default is 0.0001.
|
|
2339
|
+
|
|
2340
|
+
Returns
|
|
2341
|
+
-------
|
|
2342
|
+
list
|
|
2343
|
+
The list of fused vertices. This list contains the same number of vertices and in the same order as the input list of vertices. However, the coordinates
|
|
2344
|
+
of these vertices have now been modified so that they are exactly the same with other vertices that are within the tolerance distance.
|
|
2345
|
+
|
|
2346
|
+
"""
|
|
2347
|
+
return Vertex.Fuse(vertices = vertices, mantissa = mantissa, tolerance = tolerance)
|
|
2348
|
+
|
|
2316
2349
|
@staticmethod
|
|
2317
2350
|
def X(vertex, mantissa: int = 6) -> float:
|
|
2318
2351
|
"""
|
topologicpy/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.8.
|
|
1
|
+
__version__ = '0.8.98'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: topologicpy
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.98
|
|
4
4
|
Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
|
|
5
5
|
Author-email: Wassim Jabi <wassim.jabi@gmail.com>
|
|
6
6
|
License: AGPL v3 License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
topologicpy/ANN.py,sha256=gpflv4lFypOW789vO7mSkMLaMF_ZftVOCqCvtGr6-JA,47873
|
|
2
2
|
topologicpy/Aperture.py,sha256=wNn5miB_IrGCBYuQ18HXQYRva20dUC3id4AJCulL7to,2723
|
|
3
|
-
topologicpy/BVH.py,sha256=
|
|
3
|
+
topologicpy/BVH.py,sha256=cdRld28xxZgKbY-k45WN4PKzLRGCf9XSrFOGYIvPG2o,22615
|
|
4
4
|
topologicpy/CSG.py,sha256=09la1-xzS9vr-WnV7tpJ0I-mkZ-XY0MRSd5iB50Nfgw,15556
|
|
5
5
|
topologicpy/Cell.py,sha256=CuJogHS7N7svEKbEazYrYyOhLBb38vlF2Xkwqg3mS3w,201692
|
|
6
6
|
topologicpy/CellComplex.py,sha256=B8bAW6M5fClfXb9nSLDhrgtNRlU888Z4EcUzBZtBqss,68558
|
|
@@ -11,7 +11,7 @@ topologicpy/DGL.py,sha256=O7r22ss0tgak4kWaACkyExhR_rZ46O_bqIBpxAcwJkw,138918
|
|
|
11
11
|
topologicpy/Dictionary.py,sha256=3QRxvobXxaCb7TVeiNgG3Ka3R_1Hmco-njZCVjKNQBM,78073
|
|
12
12
|
topologicpy/Edge.py,sha256=m_ThzU748H-vySQHx15VBIQCekWBbbISns29cnbEGfs,77593
|
|
13
13
|
topologicpy/EnergyModel.py,sha256=MEai1GF1hINeH5bhclJj_lpMU3asFTvW2RlPm40GNj4,57794
|
|
14
|
-
topologicpy/Face.py,sha256=
|
|
14
|
+
topologicpy/Face.py,sha256=XEs6cSDBLzhRQLtNydIOJVLCvetkR6E4HE7KVeqhngE,203677
|
|
15
15
|
topologicpy/Graph.py,sha256=7C9Cw3-0L9IqKsCTscHIo9luKoC-80s_-xXDxsBn95w,868150
|
|
16
16
|
topologicpy/Grid.py,sha256=3OsBMyHh4w8gpFOTMKHMNTpo62V0CwRNu5cwm87yDUA,18421
|
|
17
17
|
topologicpy/Helper.py,sha256=NsmMlbbKFPRX6jfoko-ZQVQ7MBsfVp9FD0ZvC2U7q-8,32002
|
|
@@ -19,21 +19,21 @@ topologicpy/Honeybee.py,sha256=dBk01jIvxjQMGHqSarM1Cukv16ot4Op7Dwlitn2OMoc,48990
|
|
|
19
19
|
topologicpy/Kuzu.py,sha256=fxaDPWM5Xam7Tot0olPZsazS_7xgE_Vo-jf0Sbv3VZE,36298
|
|
20
20
|
topologicpy/Matrix.py,sha256=bOofT34G3YHu9aMIWx60YHAJga4R0GbDjsZBUD4Hu_k,22706
|
|
21
21
|
topologicpy/Neo4j.py,sha256=J8jU_mr5-mWC0Lg_D2dMjMlx1rY_eh8ks_aubUuTdWw,22319
|
|
22
|
-
topologicpy/Plotly.py,sha256=
|
|
22
|
+
topologicpy/Plotly.py,sha256=NqTdQYOnc2WLx13-vIZnumPzPD5IReQmj43pmBhufx4,129402
|
|
23
23
|
topologicpy/Polyskel.py,sha256=oVfM4lqSMPTjnkHfsRU9VI8Blt6Vf0LVPkD9ebz7Wmw,27082
|
|
24
24
|
topologicpy/PyG.py,sha256=wOsoBFxMgwZYWjj86OMkz_PJuQ02locV_djhSDD6dVc,109644
|
|
25
25
|
topologicpy/ShapeGrammar.py,sha256=q_BvMKOBDW3GVSRjPLIGAZkHW2egw3mTOPzIyEpYOLg,23349
|
|
26
26
|
topologicpy/Shell.py,sha256=2EPzDT_t0IAjBRYPDuKNAz_Ax_HaEkvNpXBxDkPdcTg,101084
|
|
27
27
|
topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
|
|
28
28
|
topologicpy/Sun.py,sha256=ezisiHfc2nd7A_8w0Ykq2VgbS0A9WNSg-tBwvfTQAVM,36735
|
|
29
|
-
topologicpy/Topology.py,sha256=
|
|
29
|
+
topologicpy/Topology.py,sha256=DZ1C7lhaEq-gORVkFuttd117SsFGWbjGyAINH3Ityi4,574696
|
|
30
30
|
topologicpy/Vector.py,sha256=9COmLAEo3GSDntVFWligGFKJbEqUTSGmDWQDxpvfR-M,47485
|
|
31
|
-
topologicpy/Vertex.py,sha256=
|
|
31
|
+
topologicpy/Vertex.py,sha256=tO8A6du8AuqObSaKcdg6rf4096sfBQ3ZZ9yRNuOt_NU,99607
|
|
32
32
|
topologicpy/Wire.py,sha256=3TkcBmkfGIL8Heffwnc1DQtkKyFG3qvV-kJ-lvXXB0g,273765
|
|
33
33
|
topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
|
|
34
|
-
topologicpy/version.py,sha256=
|
|
35
|
-
topologicpy-0.8.
|
|
36
|
-
topologicpy-0.8.
|
|
37
|
-
topologicpy-0.8.
|
|
38
|
-
topologicpy-0.8.
|
|
39
|
-
topologicpy-0.8.
|
|
34
|
+
topologicpy/version.py,sha256=7BBu0vOw-A-OJaqnGWj1psRzl_NVgYN_GFKu2htJik4,23
|
|
35
|
+
topologicpy-0.8.98.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
|
36
|
+
topologicpy-0.8.98.dist-info/METADATA,sha256=eObpiO-eV-0F-eooxd1qylFZ5cW2S7l9vVyb6HX-Too,10535
|
|
37
|
+
topologicpy-0.8.98.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
38
|
+
topologicpy-0.8.98.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
|
39
|
+
topologicpy-0.8.98.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|