topologicpy 0.7.24__py3-none-any.whl → 0.7.27__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
@@ -24,6 +24,9 @@ import math
24
24
  from collections import namedtuple
25
25
  from multiprocessing import Process, Queue
26
26
 
27
+ # This is for View3D as not to open new browser windows
28
+ opened_urls = set()
29
+
27
30
  try:
28
31
  import numpy as np
29
32
  from numpy import arctan, pi, signbit
@@ -968,18 +971,42 @@ class Topology():
968
971
  results = []
969
972
  if Topology.IsInstance(topologyA, "CellComplex"):
970
973
  cellsA = Topology.Cells(topologyA)
974
+ elif Topology.IsInstance(topologyA, "Cluster"):
975
+ cellsA = Cluster.FreeTopologies(topologyA)
971
976
  else:
972
977
  cellsA = [topologyA]
973
- for cellA in cellsA:
974
- if Topology.IsInstance(topologyB, "CellComplex"):
978
+ if Topology.IsInstance(topologyB, "CellComplex"):
975
979
  cellsB = Topology.Cells(topologyB)
980
+ elif Topology.IsInstance(topologyB, "Cluster"):
981
+ cellsB = Cluster.FreeTopologies(topologyB)
982
+ else:
983
+ cellsB = [topologyB]
984
+ cellsA_2 = []
985
+ cellsB_2 = []
986
+ for cellA in cellsA:
987
+ if Topology.IsInstance(cellA, "CellComplex"):
988
+ cellsA_2 += Topology.Cells(cellA)
989
+ elif Topology.IsInstance(cellA, "Shell"):
990
+ cellsA_2 += Topology.Faces(cellA)
991
+ else:
992
+ cellsA_2.append(cellA)
993
+
994
+ for cellB in cellsB:
995
+ if Topology.IsInstance(cellB, "CellComplex"):
996
+ cellsB_2 += Topology.Cells(cellB)
997
+ elif Topology.IsInstance(cellB, "Shell"):
998
+ cellsB_2 += Topology.Faces(cellB)
976
999
  else:
977
- cellsB = [topologyB]
978
- for cellB in cellsB:
1000
+ cellsB_2.append(cellB)
1001
+
1002
+ for cellA in cellsA_2:
1003
+ for cellB in cellsB_2:
979
1004
  cellC = cellA.Intersect(cellB)
980
1005
  results.append(cellC)
981
- results = [x for x in results if results is not None]
982
- if len(results) == 1:
1006
+ results = [x for x in results if x is not None]
1007
+ if len(results) == 0:
1008
+ return None
1009
+ elif len(results) == 1:
983
1010
  return results[0]
984
1011
  else:
985
1012
  return Topology.SelfMerge(Topology.SelfMerge(Cluster.ByTopologies(results)))
@@ -2007,7 +2034,7 @@ class Topology():
2007
2034
  try:
2008
2035
  import ezdxf
2009
2036
  except:
2010
- print("Topology.ExportToDXF - Information: Installing required ezdxf library.")
2037
+ print("Topology.ByDXFPath - Information: Installing required ezdxf library.")
2011
2038
  try:
2012
2039
  os.system("pip install ezdxf")
2013
2040
  except:
@@ -4686,7 +4713,7 @@ class Topology():
4686
4713
  return json_string
4687
4714
 
4688
4715
  @staticmethod
4689
- def OBJString(topology, color, vertexIndex, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001):
4716
+ def _OBJString(topology, color, vertexIndex, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001):
4690
4717
  """
4691
4718
  Returns the Wavefront string of the input topology. This is very experimental and outputs a simple solid topology.
4692
4719
 
@@ -4757,6 +4784,7 @@ class Topology():
4757
4784
  finalLines = finalLines + "\n" + lines[i]
4758
4785
  return finalLines, len(vertices)
4759
4786
 
4787
+
4760
4788
  @staticmethod
4761
4789
  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):
4762
4790
  """
@@ -4809,7 +4837,6 @@ class Topology():
4809
4837
 
4810
4838
  """
4811
4839
  from topologicpy.Helper import Helper
4812
- from topologicpy.Dictionary import Dictionary
4813
4840
  from os.path import exists
4814
4841
 
4815
4842
  if isinstance(topologies, tuple):
@@ -4820,7 +4847,7 @@ class Topology():
4820
4847
  print("Topology.ExportToOBJ - Error: the input topologies parameter does not contain any valid topologies. Returning None.")
4821
4848
  return None
4822
4849
  if not isinstance(new_topologies, list):
4823
- print("Dictionary.ByMergedDictionaries - Error: The input dictionaries parameter is not a valid list. Returning None.")
4850
+ print("Topology.ExportToOBJ - Error: The input topologies parameter is not a valid list. Returning None.")
4824
4851
  return None
4825
4852
 
4826
4853
  if not overwrite and exists(path):
@@ -4834,39 +4861,126 @@ class Topology():
4834
4861
  status = False
4835
4862
 
4836
4863
  mtl_path = path[:-4] + ".mtl"
4837
-
4864
+
4865
+ obj_string, mtl_string = Topology.OBJString(new_topologies,
4866
+ nameKey=nameKey,
4867
+ colorKey=colorKey,
4868
+ opacityKey=opacityKey,
4869
+ defaultColor=defaultColor,
4870
+ defaultOpacity=defaultOpacity,
4871
+ transposeAxes=transposeAxes,
4872
+ mode=mode,
4873
+ meshSize=meshSize,
4874
+ mantissa=mantissa,
4875
+ tolerance=tolerance)
4838
4876
  # Write out the material file
4839
- n = max(len(str(len(topologies))), 3)
4840
4877
  with open(mtl_path, "w") as mtl_file:
4841
- for i in range(len(new_topologies)):
4842
- d = Topology.Dictionary(new_topologies[i])
4843
- name = Dictionary.ValueAtKey(d, nameKey) or "Untitled_"+str(i).zfill(n)
4844
- color = Dictionary.ValueAtKey(d, colorKey) or defaultColor
4845
- color = [c/255 for c in color]
4846
- opacity = Dictionary.ValueAtKey(d, opacityKey) or defaultOpacity
4847
- mtl_file.write("newmtl color_" + str(i).zfill(n) + "\n")
4848
- mtl_file.write("Kd " + ' '.join(map(str, color)) + "\n")
4849
- mtl_file.write("d " + str(opacity) + "\n")
4850
-
4878
+ mtl_file.write(mtl_string)
4851
4879
  # Write out the obj file
4852
4880
  with open(path, "w") as obj_file:
4853
- vertex_index = 1 # global vertex index counter
4854
- obj_file.writelines("# topologicpy "+Helper.Version()+"\n")
4855
- obj_file.writelines("mtllib " + mtl_path.split('/')[-1]) # reference the MTL file
4856
- for i in range(len(topologies)):
4857
- d = Topology.Dictionary(topologies[i])
4858
- name = Dictionary.ValueAtKey(d, nameKey) or "Untitled_"+str(i).zfill(n)
4859
- name = name.replace(" ", "_")
4860
- obj_file.writelines("\ng "+name+"\n")
4861
- result = Topology.OBJString(topologies[i], "color_" + str(i).zfill(n), vertex_index, transposeAxes=transposeAxes, mode=mode,
4862
- meshSize=meshSize,
4863
- mantissa=mantissa, tolerance=tolerance)
4864
-
4865
- obj_file.writelines(result[0])
4866
- vertex_index += result[1]
4867
- obj_file.close()
4868
- status = True
4869
- return status
4881
+ obj_file.write(obj_string)
4882
+ return True
4883
+
4884
+ @staticmethod
4885
+ def OBJString(*topologies, nameKey="name", colorKey="color", opacityKey="opacity", defaultColor=[256,256,256], defaultOpacity=0.5, transposeAxes: bool = True, mode: int = 0, meshSize: float = None, mantissa: int = 6, tolerance: float = 0.0001):
4886
+ """
4887
+ Exports the input topology to a Wavefront OBJ file. This is very experimental and outputs a simple solid topology.
4888
+
4889
+ Parameters
4890
+ ----------
4891
+ topologies : list or comma separated topologies
4892
+ The input list of topologies.
4893
+ path : str
4894
+ The input file path.
4895
+ nameKey : str , optional
4896
+ The topology dictionary key under which to find the name of the topology. The default is "name".
4897
+ colorKey : str, optional
4898
+ The topology dictionary key under which to find the color of the topology. The default is "color".
4899
+ opacityKey : str , optional
4900
+ The topology dictionary key under which to find the opacity of the topology. The default is "opacity".
4901
+ defaultColor : list , optional
4902
+ The default color to use if no color is stored in the topology dictionary. The default is [255,255, 255] (white).
4903
+ defaultOpacity : float , optional
4904
+ 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).
4905
+ transposeAxes : bool , optional
4906
+ If set to True the Z and Y coordinates are transposed so that Y points "up"
4907
+ mode : int , optional
4908
+ The desired mode of meshing algorithm (for triangulation). Several options are available:
4909
+ 0: Classic
4910
+ 1: MeshAdapt
4911
+ 3: Initial Mesh Only
4912
+ 5: Delaunay
4913
+ 6: Frontal-Delaunay
4914
+ 7: BAMG
4915
+ 8: Fontal-Delaunay for Quads
4916
+ 9: Packing of Parallelograms
4917
+ All options other than 0 (Classic) use the gmsh library. See https://gmsh.info/doc/texinfo/gmsh.html#Mesh-options
4918
+ WARNING: The options that use gmsh can be very time consuming and can create very heavy geometry.
4919
+ meshSize : float , optional
4920
+ The desired size of the mesh when using the "mesh" option. If set to None, it will be
4921
+ calculated automatically and set to 10% of the overall size of the face.
4922
+ mantissa : int , optional
4923
+ The desired length of the mantissa. The default is 6.
4924
+ tolerance : float , optional
4925
+ The desired tolerance. The default is 0.0001.
4926
+ overwrite : bool , optional
4927
+ If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
4928
+
4929
+ Returns
4930
+ -------
4931
+ list
4932
+ Return the OBJ and MTL strings as a list.
4933
+
4934
+ """
4935
+ from topologicpy.Helper import Helper
4936
+ from topologicpy.Dictionary import Dictionary
4937
+ import io
4938
+
4939
+ obj_file = io.StringIO()
4940
+ mtl_file = io.StringIO()
4941
+
4942
+ if isinstance(topologies, tuple):
4943
+ topologies = Helper.Flatten(list(topologies))
4944
+ if isinstance(topologies, list):
4945
+ new_topologies = [d for d in topologies if Topology.IsInstance(d, "Topology")]
4946
+ if len(new_topologies) == 0:
4947
+ print("Topology.OBJString - Error: the input topologies parameter does not contain any valid topologies. Returning None.")
4948
+ return None
4949
+ if not isinstance(new_topologies, list):
4950
+ print("Topology.OBJString - Error: The input dictionaries parameter is not a valid list. Returning None.")
4951
+ return None
4952
+
4953
+ # Write out the material file
4954
+ n = max(len(str(len(topologies))), 3)
4955
+ for i in range(len(new_topologies)):
4956
+ d = Topology.Dictionary(new_topologies[i])
4957
+ name = Dictionary.ValueAtKey(d, nameKey) or "Untitled_"+str(i).zfill(n)
4958
+ color = Dictionary.ValueAtKey(d, colorKey) or defaultColor
4959
+ color = [c/255 for c in color]
4960
+ opacity = Dictionary.ValueAtKey(d, opacityKey) or defaultOpacity
4961
+ mtl_file.write("newmtl color_" + str(i).zfill(n) + "\n")
4962
+ mtl_file.write("Kd " + ' '.join(map(str, color)) + "\n")
4963
+ mtl_file.write("d " + str(opacity) + "\n")
4964
+
4965
+ vertex_index = 1 # global vertex index counter
4966
+ obj_file.writelines("# topologicpy "+Helper.Version()+"\n")
4967
+ obj_file.writelines("mtllib example.mtl")
4968
+ for i in range(len(topologies)):
4969
+ d = Topology.Dictionary(topologies[i])
4970
+ name = Dictionary.ValueAtKey(d, nameKey) or "Untitled_"+str(i).zfill(n)
4971
+ name = name.replace(" ", "_")
4972
+ obj_file.writelines("\ng "+name+"\n")
4973
+ result = Topology._OBJString(topologies[i], "color_" + str(i).zfill(n), vertex_index, transposeAxes=transposeAxes, mode=mode,
4974
+ meshSize=meshSize,
4975
+ mantissa=mantissa, tolerance=tolerance)
4976
+
4977
+ obj_file.writelines(result[0])
4978
+ vertex_index += result[1]
4979
+ obj_string = obj_file.getvalue()
4980
+ mtl_string = mtl_file.getvalue()
4981
+ obj_file.close()
4982
+ mtl_file.close()
4983
+ return obj_string, mtl_string
4870
4984
 
4871
4985
  @staticmethod
4872
4986
  def Filter(topologies, topologyType="any", searchType="any", key=None, value=None):
@@ -6409,7 +6523,7 @@ class Topology():
6409
6523
  return return_topology
6410
6524
 
6411
6525
  @staticmethod
6412
- def SetDictionary(topology, dictionary):
6526
+ def SetDictionary(topology, dictionary, silent=False):
6413
6527
  """
6414
6528
  Sets the input topology's dictionary to the input dictionary
6415
6529
 
@@ -6419,6 +6533,8 @@ class Topology():
6419
6533
  The input topology.
6420
6534
  dictionary : topologic_core.Dictionary
6421
6535
  The input dictionary.
6536
+ silent : bool , optional
6537
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
6422
6538
 
6423
6539
  Returns
6424
6540
  -------
@@ -6429,15 +6545,18 @@ class Topology():
6429
6545
  from topologicpy.Dictionary import Dictionary
6430
6546
 
6431
6547
  if not Topology.IsInstance(topology, "Topology") and not Topology.IsInstance(topology, "Graph"):
6432
- print("Topology.SetDictionary - Error: the input topology parameter is not a valid topology or graph. Returning None.")
6548
+ if not silent:
6549
+ print("Topology.SetDictionary - Error: the input topology parameter is not a valid topology or graph. Returning None.")
6433
6550
  return None
6434
6551
  if isinstance(dictionary, dict):
6435
6552
  dictionary = Dictionary.ByPythonDictionary(dictionary)
6436
6553
  if not Topology.IsInstance(dictionary, "Dictionary"):
6437
- print("Topology.SetDictionary - Warning: the input dictionary parameter is not a valid dictionary. Returning original input.")
6554
+ if not silent:
6555
+ print("Topology.SetDictionary - Warning: the input dictionary parameter is not a valid dictionary. Returning original input.")
6438
6556
  return topology
6439
6557
  if len(dictionary.Keys()) < 1:
6440
- print("Topology.SetDictionary - Warning: the input dictionary parameter is empty. Returning original input.")
6558
+ if not silent:
6559
+ print("Topology.SetDictionary - Warning: the input dictionary parameter is empty. Returning original input.")
6441
6560
  return topology
6442
6561
  _ = topology.SetDictionary(dictionary)
6443
6562
  return topology
@@ -8003,4 +8122,105 @@ class Topology():
8003
8122
  predefined_namespace_dns = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
8004
8123
  namespace_uuid = uuid.uuid5(predefined_namespace_dns, namespace)
8005
8124
  brep_string = Topology.BREPString(topology)
8006
- return uuid.uuid5(namespace_uuid, brep_string)
8125
+ return uuid.uuid5(namespace_uuid, brep_string)
8126
+
8127
+ @staticmethod
8128
+ def View3D(*topologies, uuid = None, 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):
8129
+ """
8130
+ Sends the input topologies to 3dviewer.net. The topologies must be 3D meshes.
8131
+
8132
+ Parameters
8133
+ ----------
8134
+ topologies : list or comma separated topologies
8135
+ The input list of topologies.
8136
+ uuid : UUID , optional
8137
+ The UUID v5 to use to identify these topologies. The default is a UUID based on the topologies themselves.
8138
+ nameKey : str , optional
8139
+ The topology dictionary key under which to find the name of the topology. The default is "name".
8140
+ colorKey : str, optional
8141
+ The topology dictionary key under which to find the color of the topology. The default is "color".
8142
+ opacityKey : str , optional
8143
+ The topology dictionary key under which to find the opacity of the topology. The default is "opacity".
8144
+ defaultColor : list , optional
8145
+ The default color to use if no color is stored in the topology dictionary. The default is [255,255, 255] (white).
8146
+ defaultOpacity : float , optional
8147
+ 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).
8148
+ transposeAxes : bool , optional
8149
+ If set to True the Z and Y coordinates are transposed so that Y points "up"
8150
+ mode : int , optional
8151
+ The desired mode of meshing algorithm (for triangulation). Several options are available:
8152
+ 0: Classic
8153
+ 1: MeshAdapt
8154
+ 3: Initial Mesh Only
8155
+ 5: Delaunay
8156
+ 6: Frontal-Delaunay
8157
+ 7: BAMG
8158
+ 8: Fontal-Delaunay for Quads
8159
+ 9: Packing of Parallelograms
8160
+ All options other than 0 (Classic) use the gmsh library. See https://gmsh.info/doc/texinfo/gmsh.html#Mesh-options
8161
+ WARNING: The options that use gmsh can be very time consuming and can create very heavy geometry.
8162
+ meshSize : float , optional
8163
+ The desired size of the mesh when using the "mesh" option. If set to None, it will be
8164
+ calculated automatically and set to 10% of the overall size of the face.
8165
+ mantissa : int , optional
8166
+ The desired length of the mantissa. The default is 6.
8167
+ tolerance : float , optional
8168
+ The desired tolerance. The default is 0.0001.
8169
+ overwrite : bool , optional
8170
+ If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
8171
+
8172
+ Returns
8173
+ -------
8174
+ bool
8175
+ True if the export operation is successful. False otherwise.
8176
+
8177
+ """
8178
+ from topologicpy.Helper import Helper
8179
+ from topologicpy.Cluster import Cluster
8180
+ import requests
8181
+ import webbrowser
8182
+
8183
+ if isinstance(topologies, tuple):
8184
+ topologies = Helper.Flatten(list(topologies))
8185
+ if isinstance(topologies, list):
8186
+ new_topologies = [d for d in topologies if Topology.IsInstance(d, "Topology")]
8187
+ if len(new_topologies) == 0:
8188
+ print("Topology.View3D - Error: the input topologies parameter does not contain any valid topologies. Returning None.")
8189
+ return None
8190
+ if not isinstance(new_topologies, list):
8191
+ print("Topology.View3D - Error: The input topologies parameter is not a valid list. Returning None.")
8192
+ return None
8193
+
8194
+ if uuid == None:
8195
+ cluster = Cluster.ByTopologies(new_topologies)
8196
+ uuid = Topology.UUID(cluster)
8197
+ obj_string, mtl_string = Topology.OBJString(new_topologies,
8198
+ nameKey=nameKey,
8199
+ colorKey=colorKey,
8200
+ opacityKey=opacityKey,
8201
+ defaultColor=defaultColor,
8202
+ defaultOpacity=defaultOpacity,
8203
+ transposeAxes=transposeAxes,
8204
+ mode=mode,
8205
+ meshSize=meshSize,
8206
+ mantissa=mantissa,
8207
+ tolerance=tolerance)
8208
+
8209
+
8210
+ file_contents = {}
8211
+ file_contents['example.obj'] = obj_string
8212
+ file_contents['example.mtl'] = mtl_string
8213
+
8214
+ try:
8215
+ response = requests.post('https://3dviewer.deno.dev/upload/'+str(uuid), files=file_contents)
8216
+ if response.status_code != 200:
8217
+ print(f'Failed to upload file(s): {response.status_code} {response.reason}')
8218
+ # Open the web page in the default web browser
8219
+ # URL of the web page you want to open
8220
+ url = "https://3dviewer.deno.dev/#channel="+str(uuid)
8221
+ if not url in opened_urls:
8222
+ opened_urls.add(url)
8223
+ webbrowser.open(url)
8224
+ except requests.exceptions.RequestException as e:
8225
+ print(f'Error uploading file(s): {e}')
8226
+ return True
topologicpy/Vector.py CHANGED
@@ -77,6 +77,12 @@ class Vector(list):
77
77
  The angle in degrees between the two input vectors.
78
78
 
79
79
  """
80
+ if vectorA == None:
81
+ print("Vector.Angle - Error: The input vectorA is None. Returning None.")
82
+ return None
83
+ if vectorB == None:
84
+ print("Vector.Angle - Error: The input vectorB is None. Returning None.")
85
+ return None
80
86
  n_v1=la.norm(vectorA)
81
87
  n_v2=la.norm(vectorB)
82
88
  if n_v1 == 0 or n_v2 == 0:
topologicpy/Vertex.py CHANGED
@@ -1008,7 +1008,7 @@ class Vertex():
1008
1008
  tolerance : float , optional
1009
1009
  The tolerance for computing if the input vertex is external to the input topology. The default is 0.0001.
1010
1010
  silent : bool , optional
1011
- If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
1011
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1012
1012
 
1013
1013
  Returns
1014
1014
  -------
@@ -1042,7 +1042,7 @@ class Vertex():
1042
1042
  tolerance : float , optional
1043
1043
  The tolerance for computing if the input vertex is internal to the input topology. The default is 0.0001.
1044
1044
  silent : bool , optional
1045
- If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
1045
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1046
1046
 
1047
1047
  Returns
1048
1048
  -------
@@ -1153,7 +1153,7 @@ class Vertex():
1153
1153
  tolerance : float , optional
1154
1154
  The tolerance for computing if the input vertex is peripheral to the input topology. The default is 0.0001.
1155
1155
  silent : bool , optional
1156
- If set to False, error and warning messages are printed. Otherwise, they are not. The default is False.
1156
+ If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
1157
1157
 
1158
1158
  Returns
1159
1159
  -------