topologicpy 0.7.21__py3-none-any.whl → 0.7.23__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/Topology.py CHANGED
@@ -330,8 +330,8 @@ class Topology():
330
330
  The input topology with the input dictionary added to it.
331
331
 
332
332
  """
333
-
334
333
  from topologicpy.Dictionary import Dictionary
334
+
335
335
  if not Topology.IsInstance(topology, "Topology"):
336
336
  print("Topology.AddDictionary - Error: the input topology parameter is not a valid topology. Returning None.")
337
337
  return None
@@ -820,7 +820,6 @@ class Topology():
820
820
  apertures += Topology.Apertures(subTopology, subTopologyType=None)
821
821
  return apertures
822
822
 
823
-
824
823
  @staticmethod
825
824
  def ApertureTopologies(topology, subTopologyType=None):
826
825
  """
@@ -2139,6 +2138,7 @@ class Topology():
2139
2138
  The list of IFC object types to include. It is case insensitive. If set to an empty list, all types are included. The default is [].
2140
2139
  excludeTypes : list , optional
2141
2140
  The list of IFC object types to exclude. It is case insensitive. If set to an empty list, no types are excluded. The default is [].
2141
+
2142
2142
  Returns
2143
2143
  -------
2144
2144
  list
@@ -2706,141 +2706,303 @@ class Topology():
2706
2706
  return data
2707
2707
 
2708
2708
  @staticmethod
2709
- def ByOBJString(string, transposeAxes = True, progressBar=False, tolerance=0.0001):
2709
+ def ByOBJFile(objFile, mtlFile = None,
2710
+ defaultColor: list = [255,255,255],
2711
+ defaultOpacity: float = 1.0,
2712
+ transposeAxes: bool = True,
2713
+ removeCoplanarFaces: bool = True,
2714
+
2715
+ mantissa : int = 6,
2716
+ tolerance: float = 0.0001):
2710
2717
  """
2711
- Creates a topology from the input Wavefront OBJ string. This is a very experimental method and only works with simple planar solids. Materials and Colors are ignored.
2718
+ Imports a topology from an OBJ file and an associated materials file.
2719
+ This method is basic and does not support textures and vertex normals.
2712
2720
 
2713
2721
  Parameters
2714
2722
  ----------
2715
- string : str
2716
- The input OBJ string.
2723
+ objFile : file object
2724
+ The OBJ file.
2725
+ mtlFile : file object , optional
2726
+ The MTL file. The default is None.
2727
+ defaultColor : list , optional
2728
+ The default color to use if none is specified in the file. The default is [255, 255, 255] (white).
2729
+ defaultOpacity : float , optional
2730
+ The default opacity to use if none is specified in the file. The default is 1.0 (fully opaque).
2717
2731
  transposeAxes : bool , optional
2718
- If set to True the Z and Y coordinates are transposed so that Y points "up"
2719
- progressBar : bool , optional
2720
- If set to True a tqdm progress bar is shown. If not, it will not be shown. The default is False.
2732
+ If set to True the Z and Y axes are transposed. Otherwise, they are not. The default is True.
2733
+ removeCoplanarFaces : bool , optional
2734
+ If set to True, coplanar faces are merged. The default is True.
2735
+ mantissa : int , optional
2736
+ The desired length of the mantissa. The default is 6.
2721
2737
  tolerance : float , optional
2722
- The desired tolerance. The default is 0.0001.
2738
+ The desired tolerance. The default is 0.0001
2723
2739
 
2724
2740
  Returns
2725
2741
  -------
2726
- topology
2727
- The created topology.
2742
+ list
2743
+ The imported topologies.
2728
2744
 
2729
2745
  """
2730
- from topologicpy.Vertex import Vertex
2731
- from tqdm.auto import tqdm
2746
+ from os.path import dirname, join, exists
2732
2747
 
2733
- def parse(lines):
2734
- vertices = []
2735
- faces = []
2736
- for i in range(len(lines)):
2737
- l = lines[i].replace(",", " ")
2738
- s = l.split()
2739
- if isinstance(s, list):
2740
- if len(s) > 3:
2741
- if s[0].lower() == "v":
2742
- vertices.append([float(s[1]), float(s[2]), float(s[3])])
2743
- elif s[0].lower() == "f":
2744
- temp_faces = []
2745
- for j in range(1,len(s)):
2746
- f = s[j].split("/")[0]
2747
- temp_faces.append(int(f)-1)
2748
- faces.append(temp_faces)
2749
- return [vertices, faces]
2750
-
2751
- def parsetqdm(lines):
2752
- vertices = []
2753
- faces = []
2754
- for i in tqdm(range(len(lines))):
2755
- s = lines[i].split()
2756
- if isinstance(s, list):
2757
- if len(s) > 3:
2758
- if s[0].lower() == "v":
2759
- vertices.append([float(s[1]), float(s[2]), float(s[3])])
2760
- elif s[0].lower() == "f":
2761
- temp_faces = []
2762
- for j in range(1,len(s)):
2763
- f = s[j].split("/")[0]
2764
- temp_faces.append(int(f)-1)
2765
- faces.append(temp_faces)
2766
- return [vertices, faces]
2767
-
2768
-
2769
- lines = string.split("\n")
2770
- if lines:
2771
- if progressBar:
2772
- vertices, faces = parsetqdm(lines)
2773
- else:
2774
- vertices, faces = parse(lines)
2775
- if vertices or faces:
2776
- topology = Topology.ByGeometry(vertices = vertices, faces = faces, outputMode="default", tolerance=tolerance)
2777
- if transposeAxes == True:
2778
- topology = Topology.Rotate(topology, origin=Vertex.Origin(), axis=[1, 0, 0], angle=90)
2779
- return Topology.SelfMerge(topology)
2780
- print("Topology.ByOBJString - Error: Could not find vertices or faces. Returning None.")
2781
- return None
2748
+
2749
+ def find_next_word_after_mtllib(text):
2750
+ words = text.split()
2751
+ for i, word in enumerate(words):
2752
+ if word == 'mtllib' and i + 1 < len(words):
2753
+ return words[i + 1]
2754
+ return None
2755
+
2756
+ obj_string = objFile.read()
2757
+ mtl_filename = find_next_word_after_mtllib(obj_string)
2758
+ mtl_string = None
2759
+ if mtlFile:
2760
+ mtl_string = mtlFile.read()
2761
+ return Topology.ByOBJString(obj_string, mtl_string,
2762
+ defaultColor=defaultColor, defaultOpacity=defaultOpacity,
2763
+ transposeAxes=transposeAxes, removeCoplanarFaces=removeCoplanarFaces,
2764
+ mantissa=mantissa, tolerance=tolerance)
2782
2765
 
2783
2766
  @staticmethod
2784
- def ByOBJFile(file, transposeAxes=True, progressBar=False, tolerance=0.0001):
2767
+ def ByOBJPath(objPath,
2768
+ defaultColor: list = [255,255,255], defaultOpacity: float = 1.0,
2769
+ transposeAxes: bool = True, removeCoplanarFaces: bool = True,
2770
+ mantissa : int = 6, tolerance: float = 0.0001):
2785
2771
  """
2786
- Imports the topology from a Wevefront OBJ file. This is a very experimental method and only works with simple planar solids. Materials and Colors are ignored.
2772
+ Imports a topology from an OBJ file path and an associated materials file.
2773
+ This method is basic and does not support textures and vertex normals.
2787
2774
 
2788
2775
  Parameters
2789
2776
  ----------
2790
- file : file object
2791
- The input OBJ file.
2777
+ objPath : str
2778
+ The path to the OBJ file.
2779
+ defaultColor : list , optional
2780
+ The default color to use if none is specified in the file. The default is [255, 255, 255] (white).
2781
+ defaultOpacity : float , optional
2782
+ The default opacity to use if none is specified in the file. The default is 1.0 (fully opaque).
2792
2783
  transposeAxes : bool , optional
2793
- If set to True the Z and Y coordinates are transposed so that Y points "up"
2794
- progressBar : bool , optional
2795
- If set to True a tqdm progress bar is shown. If not, it will not be shown. The default is False.
2784
+ If set to True the Z and Y axes are transposed. Otherwise, they are not. The default is True.
2785
+ removeCoplanarFaces : bool , optional
2786
+ If set to True, coplanar faces are merged. The default is True.
2787
+ mantissa : int , optional
2788
+ The desired length of the mantissa. The default is 6.
2796
2789
  tolerance : float , optional
2797
- The desired tolerance. The default is 0.0001.
2790
+ The desired tolerance. The default is 0.0001
2798
2791
 
2799
2792
  Returns
2800
2793
  -------
2801
- topology
2802
- The imported topology.
2794
+ list
2795
+ The imported topologies.
2803
2796
 
2804
2797
  """
2805
- if not file:
2806
- print("Topology.ByOBJFile - Error: the input file parameter is not a valid file. Returning None.")
2798
+ from os.path import dirname, join, exists
2799
+
2800
+ if not objPath:
2801
+ print("Topology.ByOBJPath - Error: the input OBJ path parameter is not a valid path. Returning None.")
2807
2802
  return None
2808
- obj_string = file.read()
2809
- topology = Topology.ByOBJString(obj_string, transposeAxes=transposeAxes, progressBar=progressBar, tolerance=tolerance)
2810
- file.close()
2811
- return topology
2812
-
2803
+ if not exists(objPath):
2804
+ print("Topology.ByOBJPath - Error: the input OBJ path does not exist. Returning None.")
2805
+ return None
2806
+
2807
+ def find_next_word_after_mtllib(text):
2808
+ words = text.split()
2809
+ for i, word in enumerate(words):
2810
+ if word == 'mtllib' and i + 1 < len(words):
2811
+ return words[i + 1]
2812
+ return None
2813
+
2814
+ with open(objPath, 'r') as obj_file:
2815
+ obj_string = obj_file.read()
2816
+ mtl_filename = find_next_word_after_mtllib(obj_string)
2817
+ mtl_string = None
2818
+ if mtl_filename:
2819
+ parent_folder = dirname(objPath)
2820
+ mtl_path = join(parent_folder, mtl_filename)
2821
+ if exists(mtl_path):
2822
+ with open(mtl_path, 'r') as mtl_file:
2823
+ mtl_string = mtl_file.read()
2824
+ return Topology.ByOBJString(obj_string, mtl_string,
2825
+ defaultColor=defaultColor, defaultOpacity=defaultOpacity,
2826
+ transposeAxes=transposeAxes, removeCoplanarFaces=removeCoplanarFaces,
2827
+ mantissa=mantissa, tolerance=tolerance)
2828
+
2813
2829
  @staticmethod
2814
- def ByOBJPath(path, transposeAxes=True, progressBar=False, tolerance=0.0001):
2830
+ def ByOBJString(objString: str, mtlString: str = None,
2831
+ defaultColor: list = [255,255,255], defaultOpacity: float = 1.0,
2832
+ transposeAxes: bool = True, removeCoplanarFaces: bool = False,
2833
+ mantissa = 6, tolerance = 0.0001):
2815
2834
  """
2816
- Imports the topology from a Wevefront OBJ file path. This is a very experimental method and only works with simple planar solids. Materials and Colors are ignored.
2835
+ Imports a topology from OBJ and MTL strings.
2817
2836
 
2818
2837
  Parameters
2819
2838
  ----------
2820
- path : str
2821
- The file path to the OBJ file.
2839
+ objString : str
2840
+ The string of the OBJ file.
2841
+ mtlString : str , optional
2842
+ The string of the MTL file. The default is None.
2843
+ defaultColor : list , optional
2844
+ The default color to use if none is specified in the string. The default is [255, 255, 255] (white).
2845
+ defaultOpacity : float , optional
2846
+ The default opacity to use if none is specified in the string. The default is 1.0 (fully opaque).
2822
2847
  transposeAxes : bool , optional
2823
- If set to True the Z and Y coordinates are transposed so that Y points "up".
2824
- progressBar : bool , optional
2825
- If set to True a tqdm progress bar is shown. If not, it will not be shown. The default is False.
2848
+ If set to True the Z and Y axes are transposed. Otherwise, they are not. The default is True.
2849
+ removeCoplanarFaces : bool , optional
2850
+ If set to True, coplanar faces are merged. The default is True.
2851
+ mantissa : int , optional
2852
+ The desired length of the mantissa. The default is 6.
2826
2853
  tolerance : float , optional
2827
- The desired tolerance. The default is 0.0001.
2854
+ The desired tolerance. The default is 0.0001
2828
2855
 
2829
2856
  Returns
2830
2857
  -------
2831
- topology
2832
- The imported topology.
2858
+ list
2859
+ The imported topologies.
2833
2860
 
2834
2861
  """
2835
- if not path:
2836
- print("Topology.ByOBJPath - Error: the input path parameter is not a valid path. Returning None.")
2837
- return None
2838
- try:
2839
- file = open(path)
2840
- except:
2841
- print("Topology.ByOBJPath - Error: the OBJ file is not a valid file. Returning None.")
2842
- return None
2843
- return Topology.ByOBJFile(file, transposeAxes=transposeAxes, progressBar=progressBar, tolerance=tolerance)
2862
+ from topologicpy.Vertex import Vertex
2863
+ from topologicpy.Edge import Edge
2864
+ from topologicpy.Wire import Wire
2865
+ from topologicpy.Face import Face
2866
+ from topologicpy.Shell import Shell
2867
+ from topologicpy.Cell import Cell
2868
+ from topologicpy.Cluster import Cluster
2869
+ from topologicpy.Topology import Topology
2870
+ from topologicpy.Dictionary import Dictionary
2871
+ from topologicpy.Helper import Helper
2872
+
2873
+ def load_materials(mtl_string):
2874
+ materials = {}
2875
+ if not mtl_string:
2876
+ return materials
2877
+ current_material = None
2878
+ lines = mtlString.split('\n')
2879
+ for line in lines:
2880
+ line = line.strip()
2881
+ if line.startswith('#') or not line:
2882
+ continue
2883
+ parts = line.split()
2884
+ if not parts:
2885
+ continue
2886
+ if parts[0] == 'newmtl':
2887
+ current_material = parts[1]
2888
+ materials[current_material] = {}
2889
+ elif current_material:
2890
+ if parts[0] == 'Kd': # Diffuse color
2891
+ materials[current_material]['Kd'] = list(map(float, parts[1:4]))
2892
+ elif parts[0] == 'Ka': # Ambient color
2893
+ materials[current_material]['Ka'] = list(map(float, parts[1:4]))
2894
+ elif parts[0] == 'Ks': # Specular color
2895
+ materials[current_material]['Ks'] = list(map(float, parts[1:4]))
2896
+ elif parts[0] == 'Ns': # Specular exponent
2897
+ materials[current_material]['Ns'] = float(parts[1])
2898
+ elif parts[0] == 'd': # Transparency
2899
+ materials[current_material]['d'] = float(parts[1])
2900
+ elif parts[0] == 'map_Kd': # Diffuse texture map
2901
+ materials[current_material]['map_Kd'] = parts[1]
2902
+ # Add more properties as needed
2903
+ return materials
2904
+
2905
+ materials = load_materials(mtlString)
2906
+ vertices = []
2907
+ textures = []
2908
+ normals = []
2909
+ groups = {}
2910
+ current_group = None
2911
+ current_material = None
2912
+ lines = objString.split('\n')
2913
+ for line in lines:
2914
+ line = line.strip()
2915
+ if line.startswith('#'):
2916
+ continue
2917
+
2918
+ parts = line.split()
2919
+ if not parts:
2920
+ continue
2921
+
2922
+ if parts[0] == 'v':
2923
+ vertex = list(map(float, parts[1:4]))
2924
+ vertex = [round(coord, mantissa) for coord in vertex]
2925
+ if transposeAxes == True:
2926
+ vertex = [vertex[0], vertex[2], vertex[1]]
2927
+ vertices.append(vertex)
2928
+ elif parts[0] == 'vt':
2929
+ texture = list(map(float, parts[1:3]))
2930
+ textures.append(texture)
2931
+ elif parts[0] == 'vn':
2932
+ normal = list(map(float, parts[1:4]))
2933
+ normals.append(normal)
2934
+ elif parts[0] == 'f':
2935
+ face = []
2936
+ for part in parts[1:]:
2937
+ indices = part.split('/')
2938
+ vertex_index = int(indices[0]) - 1 if indices[0] else None
2939
+ texture_index = int(indices[1]) - 1 if len(indices) > 1 and indices[1] else None
2940
+ normal_index = int(indices[2]) - 1 if len(indices) > 2 and indices[2] else None
2941
+ face.append((vertex_index, texture_index, normal_index))
2942
+
2943
+ if current_group not in groups:
2944
+ groups[current_group] = []
2945
+ groups[current_group].append((face, current_material))
2946
+ elif parts[0] == 'usemtl':
2947
+ current_material = parts[1]
2948
+ elif parts[0] == 'g' or parts[0] == 'o':
2949
+ current_group = parts[1] if len(parts) > 1 else None
2950
+
2951
+ obj_data = {
2952
+ 'vertices': vertices,
2953
+ 'textures': textures,
2954
+ 'normals': normals,
2955
+ 'materials': materials,
2956
+ 'groups': groups
2957
+ }
2958
+ groups = obj_data['groups']
2959
+ vertices = obj_data['vertices']
2960
+ groups = obj_data['groups']
2961
+ materials = obj_data['materials']
2962
+ names = list(groups.keys())
2963
+ return_topologies = []
2964
+ for i in range(len(names)):
2965
+ object_faces = []
2966
+ face_selectors = []
2967
+ object_name = names[i]
2968
+ faces = groups[object_name]
2969
+ f = faces[0] # Get object material from first face. Assume it is the material of the group
2970
+ object_color = defaultColor
2971
+ object_opacity = defaultOpacity
2972
+ object_material = None
2973
+ if len(f) >= 2:
2974
+ object_material = f[1]
2975
+ if object_material in materials.keys():
2976
+ object_color = materials[object_material]['Kd']
2977
+ object_color = [int(round(c*255,0)) for c in object_color]
2978
+ object_opacity = materials[object_material]['d']
2979
+ for f in faces:
2980
+ indices = f[0]
2981
+ face_material = f[1]
2982
+ face_indices = []
2983
+ for coordinate in indices:
2984
+ face_indices.append(coordinate[0])
2985
+ face = Topology.ByGeometry(vertices=vertices, faces=[face_indices])
2986
+ object_faces.append(face)
2987
+ if not face_material == object_material:
2988
+ if face_material in materials.keys():
2989
+ face_color = materials[face_material]['Kd']
2990
+ face_color = [int(round(c*255,0)) for c in face_color]
2991
+ face_opacity = materials[face_material]['d']
2992
+ selector = Face.InternalVertex(face)
2993
+ d = Dictionary.ByKeysValues(['color', 'opacity'], [face_color, face_opacity])
2994
+ selector = Topology.SetDictionary(selector, d)
2995
+ face_selectors.append(selector)
2996
+
2997
+ topology = Topology.SelfMerge(Cluster.ByTopologies(object_faces), tolerance=tolerance)
2998
+ if removeCoplanarFaces:
2999
+ topology = Topology.RemoveCoplanarFaces(topology, tolerance=tolerance)
3000
+ d = Dictionary.ByKeysValues(['name', 'color', 'opacity'], [object_name, object_color, object_opacity])
3001
+ topology = Topology.SetDictionary(topology, d)
3002
+ if len(face_selectors) > 0:
3003
+ topology = Topology.TransferDictionariesBySelectors(topology, selectors=face_selectors, tranFaces=True, tolerance=tolerance)
3004
+ return_topologies.append(topology)
3005
+ return return_topologies
2844
3006
 
2845
3007
  @staticmethod
2846
3008
  def ByOCCTShape(occtShape):
@@ -3854,7 +4016,6 @@ class Topology():
3854
4016
  return True
3855
4017
  return False
3856
4018
 
3857
-
3858
4019
  def ExportToDXF(topologies, path: str, overwrite: bool = False, mantissa: int = 6):
3859
4020
  """
3860
4021
  Exports the input topology to a DXF file. See https://en.wikipedia.org/wiki/AutoCAD_DXF.
@@ -4373,7 +4534,6 @@ class Topology():
4373
4534
  returnDict['dictionary'] = Dictionary.PythonDictionary(Topology.Dictionary(topology))
4374
4535
  return returnDict
4375
4536
 
4376
-
4377
4537
  def getApertureData(topology, topLevel="False", uuidKey="uuid"):
4378
4538
  json_data = []
4379
4539
  if Topology.IsInstance(topology, "Vertex"):
@@ -4512,7 +4672,7 @@ class Topology():
4512
4672
  return json_string
4513
4673
 
4514
4674
  @staticmethod
4515
- def OBJString(topology, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001):
4675
+ def OBJString(topology, color, vertexIndex, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001):
4516
4676
  """
4517
4677
  Returns the Wavefront string of the input topology. This is very experimental and outputs a simple solid topology.
4518
4678
 
@@ -4520,6 +4680,10 @@ class Topology():
4520
4680
  ----------
4521
4681
  topology : topologic_core.Topology
4522
4682
  The input topology.
4683
+ color : list
4684
+ The desired color to assign to the topology
4685
+ vertexIndex : int
4686
+ The vertex index to use as the starting index.
4523
4687
  transposeAxes : bool , optional
4524
4688
  If set to True the Z and Y coordinates are transposed so that Y points "up"
4525
4689
  mode : int , optional
@@ -4548,17 +4712,16 @@ class Topology():
4548
4712
  The Wavefront OBJ string of the input topology
4549
4713
 
4550
4714
  """
4715
+
4551
4716
  from topologicpy.Helper import Helper
4552
- from topologicpy.Vertex import Vertex
4553
- from topologicpy.Face import Face
4554
4717
 
4555
4718
  if not Topology.IsInstance(topology, "Topology"):
4556
4719
  print("Topology.ExportToOBJ - Error: the input topology parameter is not a valid topology. Returning None.")
4557
4720
  return None
4558
-
4721
+
4559
4722
  lines = []
4560
- version = Helper.Version()
4561
- lines.append("# topologicpy "+version)
4723
+ #version = Helper.Version()
4724
+ #lines.append("# topologicpy " + version)
4562
4725
  topology = Topology.Triangulate(topology, mode=mode, meshSize=meshSize, tolerance=tolerance)
4563
4726
  d = Topology.Geometry(topology, mantissa=mantissa)
4564
4727
  vertices = d['vertices']
@@ -4569,32 +4732,42 @@ class Topology():
4569
4732
  tVertices.append([v[0], v[2], v[1]])
4570
4733
  vertices = tVertices
4571
4734
  for v in vertices:
4572
- lines.append("v "+str(v[0])+" "+str(v[1])+" "+str(v[2]))
4735
+ lines.append("v " + str(v[0]) + " " + str(v[1]) + " " + str(v[2]))
4573
4736
  for f in faces:
4574
- line = "f"
4737
+ line = "usemtl " + str(color) + "\nf" # reference the material name
4575
4738
  for j in f:
4576
- line = line+" "+str(j+1)
4739
+ line = line + " " + str(j + vertexIndex)
4577
4740
  lines.append(line)
4578
4741
  finalLines = lines[0]
4579
- for i in range(1,len(lines)):
4580
- finalLines = finalLines+"\n"+lines[i]
4581
- return finalLines
4742
+ for i in range(1, len(lines)):
4743
+ finalLines = finalLines + "\n" + lines[i]
4744
+ return finalLines, len(vertices)
4582
4745
 
4583
4746
  @staticmethod
4584
- def ExportToOBJ(topology, path, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, overwrite: bool = False, mantissa: int = 6, tolerance: float = 0.0001):
4747
+ def ExportToOBJ(*topologies, path, nameKey="name", colorKey="color", opacityKey="opacity", defaultColor=[256,256,256], defaultOpacity=0.5, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, overwrite: bool = False, mantissa: int = 6, tolerance: float = 0.0001):
4585
4748
  """
4586
4749
  Exports the input topology to a Wavefront OBJ file. This is very experimental and outputs a simple solid topology.
4587
4750
 
4588
4751
  Parameters
4589
4752
  ----------
4590
- topology : topologic_core.Topology
4591
- The input topology.
4753
+ topologies : list or comma separated topologies
4754
+ The input list of topologies.
4592
4755
  path : str
4593
4756
  The input file path.
4757
+ nameKey : str , optional
4758
+ The topology dictionary key under which to find the name of the topology. The default is "name".
4759
+ colorKey : str, optional
4760
+ The topology dictionary key under which to find the color of the topology. The default is "color".
4761
+ opacityKey : str , optional
4762
+ The topology dictionary key under which to find the opacity of the topology. The default is "opacity".
4763
+ defaultColor : list , optional
4764
+ The default color to use if no color is stored in the topology dictionary. The default is [255,255, 255] (white).
4765
+ defaultOpacity : float , optional
4766
+ The default opacity to use of no opacity is stored in the topology dictionary. This must be between 0 and 1. The default is 1 (fully opaque).
4594
4767
  transposeAxes : bool , optional
4595
4768
  If set to True the Z and Y coordinates are transposed so that Y points "up"
4596
4769
  mode : int , optional
4597
- The desired mode of meshing algorithm. Several options are available:
4770
+ The desired mode of meshing algorithm (for triangulation). Several options are available:
4598
4771
  0: Classic
4599
4772
  1: MeshAdapt
4600
4773
  3: Initial Mesh Only
@@ -4621,11 +4794,21 @@ class Topology():
4621
4794
  True if the export operation is successful. False otherwise.
4622
4795
 
4623
4796
  """
4797
+ from topologicpy.Helper import Helper
4798
+ from topologicpy.Dictionary import Dictionary
4624
4799
  from os.path import exists
4625
4800
 
4626
- if not Topology.IsInstance(topology, "Topology"):
4627
- print("Topology.ExportToOBJ - Error: the input topology parameter is not a valid topology. Returning None.")
4801
+ if isinstance(topologies, tuple):
4802
+ topologies = Helper.Flatten(list(topologies))
4803
+ if isinstance(topologies, list):
4804
+ new_topologies = [d for d in topologies if Topology.IsInstance(d, "Topology")]
4805
+ if len(new_topologies) == 0:
4806
+ print("Topology.ExportToOBJ - Error: the input topologies parameter does not contain any valid topologies. Returning None.")
4628
4807
  return None
4808
+ if not isinstance(new_topologies, list):
4809
+ print("Dictionary.ByMergedDictionaries - Error: The input dictionaries parameter is not a valid list. Returning None.")
4810
+ return None
4811
+
4629
4812
  if not overwrite and exists(path):
4630
4813
  print("Topology.ExportToOBJ - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
4631
4814
  return None
@@ -4635,10 +4818,39 @@ class Topology():
4635
4818
  if ext.lower() != ".obj":
4636
4819
  path = path+".obj"
4637
4820
  status = False
4638
- objString = Topology.OBJString(topology, transposeAxes=transposeAxes, mode=mode, meshSize=meshSize, mantissa=mantissa, tolerance=tolerance)
4639
- with open(path, "w") as f:
4640
- f.writelines(objString)
4641
- f.close()
4821
+
4822
+ mtl_path = path[:-4] + ".mtl"
4823
+
4824
+ # Write out the material file
4825
+ n = max(len(str(len(topologies))), 3)
4826
+ with open(mtl_path, "w") as mtl_file:
4827
+ for i in range(len(new_topologies)):
4828
+ d = Topology.Dictionary(new_topologies[i])
4829
+ name = Dictionary.ValueAtKey(d, nameKey) or "Untitled_"+str(i).zfill(n)
4830
+ color = Dictionary.ValueAtKey(d, colorKey) or defaultColor
4831
+ color = [c/255 for c in color]
4832
+ opacity = Dictionary.ValueAtKey(d, opacityKey) or defaultOpacity
4833
+ mtl_file.write("newmtl color_" + str(i).zfill(n) + "\n")
4834
+ mtl_file.write("Kd " + ' '.join(map(str, color)) + "\n")
4835
+ mtl_file.write("d " + str(opacity) + "\n")
4836
+
4837
+ # Write out the obj file
4838
+ with open(path, "w") as obj_file:
4839
+ vertex_index = 1 # global vertex index counter
4840
+ obj_file.writelines("# topologicpy "+Helper.Version()+"\n")
4841
+ obj_file.writelines("mtllib " + mtl_path.split('/')[-1]) # reference the MTL file
4842
+ for i in range(len(topologies)):
4843
+ d = Topology.Dictionary(topologies[i])
4844
+ name = Dictionary.ValueAtKey(d, nameKey) or "Untitled_"+str(i).zfill(n)
4845
+ name = name.replace(" ", "_")
4846
+ obj_file.writelines("\ng "+name+"\n")
4847
+ result = Topology.OBJString(topologies[i], "color_" + str(i).zfill(n), vertex_index, transposeAxes=transposeAxes, mode=mode,
4848
+ meshSize=meshSize,
4849
+ mantissa=mantissa, tolerance=tolerance)
4850
+
4851
+ obj_file.writelines(result[0])
4852
+ vertex_index += result[1]
4853
+ obj_file.close()
4642
4854
  status = True
4643
4855
  return status
4644
4856
 
@@ -4779,25 +4991,6 @@ class Topology():
4779
4991
  """
4780
4992
  from topologicpy.Vertex import Vertex
4781
4993
  from topologicpy.Face import Face
4782
- from topologicpy.Vector import Vector
4783
-
4784
- def getSubTopologies(topology, subTopologyClass):
4785
- topologies = []
4786
- if subTopologyClass == topologic.Vertex:
4787
- _ = topology.Vertices(None, topologies)
4788
- elif subTopologyClass == topologic.Edge:
4789
- _ = topology.Edges(None, topologies)
4790
- elif subTopologyClass == topologic.Wire:
4791
- _ = topology.Wires(None, topologies)
4792
- elif subTopologyClass == topologic.Face:
4793
- _ = topology.Faces(None, topologies)
4794
- elif subTopologyClass == topologic.Shell:
4795
- _ = topology.Shells(None, topologies)
4796
- elif subTopologyClass == topologic.Cell:
4797
- _ = topology.Cells(None, topologies)
4798
- elif subTopologyClass == topologic.CellComplex:
4799
- _ = topology.CellComplexes(None, topologies)
4800
- return topologies
4801
4994
 
4802
4995
  vertices = []
4803
4996
  edges = []
@@ -4850,7 +5043,7 @@ class Topology():
4850
5043
  triFaces = Face.Triangulate(aFace)
4851
5044
  for aTriFace in triFaces:
4852
5045
  wire = Face.ExternalBoundary(aTriFace)
4853
- faceVertices = getSubTopologies(wire, topologic.Vertex)
5046
+ faceVertices = Topology.Vertices(wire)
4854
5047
  f = []
4855
5048
  for aVertex in faceVertices:
4856
5049
  try:
@@ -4862,7 +5055,7 @@ class Topology():
4862
5055
  faces.append(f)
4863
5056
  else:
4864
5057
  wire = Face.ExternalBoundary(aFace)
4865
- faceVertices = getSubTopologies(wire, topologic.Vertex)
5058
+ faceVertices = Topology.Vertices(wire)
4866
5059
  f = []
4867
5060
  for aVertex in faceVertices:
4868
5061
  try:
@@ -5121,7 +5314,6 @@ class Topology():
5121
5314
  The resulting merged Topology
5122
5315
 
5123
5316
  """
5124
-
5125
5317
  from topologicpy.Cluster import Cluster
5126
5318
 
5127
5319
  if not isinstance(topologies, list):
@@ -5207,6 +5399,7 @@ class Topology():
5207
5399
  The input topology.
5208
5400
  tolerance : float , optional
5209
5401
  The desired tolerance. The default is 0.0001.
5402
+
5210
5403
  Returns
5211
5404
  -------
5212
5405
  list
@@ -6302,7 +6495,6 @@ class Topology():
6302
6495
  A dictionary with the list of vertices, edges, wires, and faces. The keys are "vertices", "edges", "wires", and "faces".
6303
6496
 
6304
6497
  """
6305
-
6306
6498
  if not Topology.IsInstance(topologyA, "Topology"):
6307
6499
  print("Topology.SharedTopologies - Error: the input topologyA parameter is not a valid topology. Returning None.")
6308
6500
  return None
@@ -6429,9 +6621,10 @@ class Topology():
6429
6621
  l = None
6430
6622
  return l
6431
6623
 
6432
-
6433
6624
  @staticmethod
6434
6625
  def Show(*topologies,
6626
+ colorKey = "color",
6627
+ opacityKey = "opacity",
6435
6628
  showVertices=True, vertexSize=1.1, vertexColor="black",
6436
6629
  vertexLabelKey=None, vertexGroupKey=None, vertexGroups=[],
6437
6630
  vertexMinGroup=None, vertexMaxGroup=None,
@@ -6466,7 +6659,10 @@ class Topology():
6466
6659
  ----------
6467
6660
  topologies : topologic_core.Topology or list
6468
6661
  The input topology. This must contain faces and or edges. If the input is a list, a cluster is first created
6469
-
6662
+ colorKey : str , optional
6663
+ The key under which to find the color of the topology. The default is "color".
6664
+ opacityKey : str , optional
6665
+ The key under which to find the opacity of the topology. The default is "opacity".
6470
6666
  showVertices : bool , optional
6471
6667
  If set to True the vertices will be drawn. Otherwise, they will not be drawn. The default is True.
6472
6668
  vertexSize : float , optional
@@ -6630,10 +6826,11 @@ class Topology():
6630
6826
 
6631
6827
  """
6632
6828
 
6633
- from topologicpy.Cluster import Cluster
6829
+ from topologicpy.Dictionary import Dictionary
6634
6830
  from topologicpy.Plotly import Plotly
6635
6831
  from topologicpy.Helper import Helper
6636
6832
  from topologicpy.Graph import Graph
6833
+ from topologicpy.Color import Color
6637
6834
 
6638
6835
  if isinstance(topologies, tuple):
6639
6836
  topologies = Helper.Flatten(list(topologies))
@@ -6644,30 +6841,38 @@ class Topology():
6644
6841
  if len(new_topologies) == 0:
6645
6842
  print("Topology.Show - Error: the input topologies parameter does not contain any valid topology. Returning None.")
6646
6843
  return None
6647
- if len(new_topologies) == 1:
6648
- topology = new_topologies[0]
6649
- else:
6650
- topology = Cluster.ByTopologies(new_topologies)
6651
- if not Topology.IsInstance(topology, "Topology"):
6652
- print("Topology.Show - Error: the input topology parameter is not a valid topology. Returning None.")
6653
- return None
6654
- data = Plotly.DataByTopology(topology=topology,
6655
- showVertices=showVertices, vertexSize=vertexSize, vertexColor=vertexColor,
6656
- vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups,
6657
- vertexMinGroup=vertexMinGroup, vertexMaxGroup=vertexMaxGroup,
6658
- showVertexLegend=showVertexLegend, vertexLegendLabel=vertexLegendLabel, vertexLegendRank=vertexLegendRank,
6659
- vertexLegendGroup=vertexLegendGroup,
6660
- showEdges=showEdges, edgeWidth=edgeWidth, edgeColor=edgeColor,
6661
- edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups,
6662
- edgeMinGroup=edgeMinGroup, edgeMaxGroup=edgeMaxGroup,
6663
- showEdgeLegend=showEdgeLegend, edgeLegendLabel=edgeLegendLabel, edgeLegendRank=edgeLegendRank,
6664
- edgeLegendGroup=edgeLegendGroup,
6665
- showFaces=showFaces, faceOpacity=faceOpacity, faceColor=faceColor,
6666
- faceLabelKey=faceLabelKey, faceGroupKey=faceGroupKey, faceGroups=faceGroups,
6667
- faceMinGroup=faceMinGroup, faceMaxGroup=faceMaxGroup,
6668
- showFaceLegend=showFaceLegend, faceLegendLabel=faceLegendLabel, faceLegendRank=faceLegendRank,
6669
- faceLegendGroup=faceLegendGroup,
6670
- intensityKey=intensityKey, intensities=intensities, colorScale=colorScale, mantissa=mantissa, tolerance=tolerance)
6844
+ # if len(new_topologies) == 1:
6845
+ # topology = new_topologies[0]
6846
+ # else:
6847
+ # topology = Cluster.ByTopologies(new_topologies)
6848
+ # if not Topology.IsInstance(topology, "Topology"):
6849
+ # print("Topology.Show - Error: the input topology parameter is not a valid topology. Returning None.")
6850
+ # return None
6851
+ data = []
6852
+ for topology in new_topologies:
6853
+ d = Topology.Dictionary(topology)
6854
+ if isinstance(colorKey, str):
6855
+ f_color = Dictionary.ValueAtKey(d, colorKey)
6856
+ if f_color:
6857
+ faceColor = Color.PlotlyColor(f_color, alpha=1.0, useAlpha=False)
6858
+ faceOpacity = Dictionary.ValueAtKey(d, opacityKey) or faceOpacity
6859
+ data += Plotly.DataByTopology(topology=topology,
6860
+ showVertices=showVertices, vertexSize=vertexSize, vertexColor=vertexColor,
6861
+ vertexLabelKey=vertexLabelKey, vertexGroupKey=vertexGroupKey, vertexGroups=vertexGroups,
6862
+ vertexMinGroup=vertexMinGroup, vertexMaxGroup=vertexMaxGroup,
6863
+ showVertexLegend=showVertexLegend, vertexLegendLabel=vertexLegendLabel, vertexLegendRank=vertexLegendRank,
6864
+ vertexLegendGroup=vertexLegendGroup,
6865
+ showEdges=showEdges, edgeWidth=edgeWidth, edgeColor=edgeColor,
6866
+ edgeLabelKey=edgeLabelKey, edgeGroupKey=edgeGroupKey, edgeGroups=edgeGroups,
6867
+ edgeMinGroup=edgeMinGroup, edgeMaxGroup=edgeMaxGroup,
6868
+ showEdgeLegend=showEdgeLegend, edgeLegendLabel=edgeLegendLabel, edgeLegendRank=edgeLegendRank,
6869
+ edgeLegendGroup=edgeLegendGroup,
6870
+ showFaces=showFaces, faceOpacity=faceOpacity, faceColor=faceColor,
6871
+ faceLabelKey=faceLabelKey, faceGroupKey=faceGroupKey, faceGroups=faceGroups,
6872
+ faceMinGroup=faceMinGroup, faceMaxGroup=faceMaxGroup,
6873
+ showFaceLegend=showFaceLegend, faceLegendLabel=faceLegendLabel, faceLegendRank=faceLegendRank,
6874
+ faceLegendGroup=faceLegendGroup,
6875
+ intensityKey=intensityKey, intensities=intensities, colorScale=colorScale, mantissa=mantissa, tolerance=tolerance)
6671
6876
  figure = Plotly.FigureByData(data=data, width=width, height=height,
6672
6877
  xAxis=xAxis, yAxis=yAxis, zAxis=zAxis, axisSize=axisSize,
6673
6878
  backgroundColor=backgroundColor,
@@ -6700,8 +6905,8 @@ class Topology():
6700
6905
  A dictionary containing the list of sorted and unsorted topologies. The keys are "sorted" and "unsorted".
6701
6906
 
6702
6907
  """
6703
-
6704
6908
  from topologicpy.Vertex import Vertex
6909
+
6705
6910
  usedTopologies = []
6706
6911
  sortedTopologies = []
6707
6912
  unsortedTopologies = []
@@ -6925,6 +7130,7 @@ class Topology():
6925
7130
 
6926
7131
  """
6927
7132
  from topologicpy.Vertex import Vertex
7133
+
6928
7134
  ratioRange = [min(1,ratioRange[0]), min(1,ratioRange[1])]
6929
7135
  if ratioRange == [0, 0]:
6930
7136
  return topology
@@ -7263,7 +7469,6 @@ class Topology():
7263
7469
  The list of supertopologies connected to the input topology.
7264
7470
 
7265
7471
  """
7266
-
7267
7472
  if not Topology.IsInstance(topology, "Topology"):
7268
7473
  print("Topology.SuperTopologies - Error: the input topology parameter is not a valid topology. Returning None.")
7269
7474
  return None
@@ -7394,7 +7599,6 @@ class Topology():
7394
7599
  The input topology with the dictionaries transferred to its subtopologies.
7395
7600
 
7396
7601
  """
7397
-
7398
7602
  if not Topology.IsInstance(topology, "Topology"):
7399
7603
  print("Topology.TransferDictionariesBySelectors - Error: The input topology parameter is not a valid topology. Returning None.")
7400
7604
  return None
@@ -7664,9 +7868,6 @@ class Topology():
7664
7868
  The type of the input topology.
7665
7869
 
7666
7870
  """
7667
- #if not Topology.IsInstance(topology, "Topology"):
7668
- #print("Topology.Type - Error: The input topology parameter is not a valid topology. Returning None.")
7669
- #return None
7670
7871
  return topology.Type()
7671
7872
 
7672
7873
  @staticmethod
@@ -7721,7 +7922,6 @@ class Topology():
7721
7922
  The type id of the input topologyType string.
7722
7923
 
7723
7924
  """
7724
-
7725
7925
  if not isinstance(name, str):
7726
7926
  print("Topology.TypeID - Error: The input topologyType parameter is not a valid string. Returning None.")
7727
7927
  return None