topologicpy 0.7.11__py3-none-any.whl → 0.7.14__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
@@ -855,6 +855,34 @@ class Topology():
855
855
  See Topology.Boolean()
856
856
 
857
857
  """
858
+ from topologicpy.Vertex import Vertex
859
+ from topologicpy.Face import Face
860
+ from topologicpy.Shell import Shell
861
+
862
+ if Topology.IsInstance(topologyA, "Face") and Topology.IsInstance(topologyB, "Face"):
863
+ if Face.IsCoplanar(topologyA, topologyB):
864
+ topologyC = Topology.Boolean(topologyA, topologyB, operation="merge", tranDict=tranDict, tolerance=tolerance)
865
+ if Topology.IsInstance(topologyC, "Cluster"):
866
+ return topologyC
867
+ elif Topology.IsInstance(topologyC, "Shell"):
868
+ eb_list = Shell.ExternalBoundary(topologyC)
869
+ if Topology.IsInstance(eb_list, "Cluster"):
870
+ eb_list = Topology.Wires(eb_list)
871
+ else:
872
+ eb_list = [eb_list]
873
+ topologyA_wire = Face.ExternalBoundary(topologyA)
874
+ topologyB_wire = Face.ExternalBoundary(topologyB)
875
+ internal_boundaries = []
876
+ found = False
877
+ for i, eb in enumerate(eb_list):
878
+ v = Topology.Vertices(eb)[0]
879
+ if found == False:
880
+ if Vertex.IsInternal(v, topologyA_wire) or Vertex.IsInternal(v, topologyB_wire):
881
+ external_boundary = eb
882
+ found = True
883
+ else:
884
+ internal_boundaries.append(eb)
885
+ return Face.ByWires(external_boundary, internal_boundaries)
858
886
  return Topology.Boolean(topologyA, topologyB, operation="union", tranDict=tranDict, tolerance=tolerance)
859
887
 
860
888
  @staticmethod
@@ -865,6 +893,59 @@ class Topology():
865
893
  """
866
894
  return Topology.Boolean(topologyA=topologyA, topologyB=topologyB, operation="difference", tranDict=tranDict, tolerance=tolerance)
867
895
 
896
+ @staticmethod
897
+ def ExternalBoundary(topology):
898
+ """
899
+ Returns the external boundary of the input topology.
900
+
901
+ Parameters
902
+ ----------
903
+ topology : topologic_core.Topology
904
+ The input topology.
905
+
906
+ Returns
907
+ -------
908
+ topologic_core.Topology
909
+ The external boundary of the input topology.
910
+
911
+ """
912
+ from topologicpy.Topology import Topology
913
+ from topologicpy.Vertex import Vertex
914
+ from topologicpy.Edge import Edge
915
+ from topologicpy.Wire import Wire
916
+ from topologicpy.Face import Face
917
+ from topologicpy.Shell import Shell
918
+ from topologicpy.Cell import Cell
919
+ from topologicpy.CellComplex import CellComplex
920
+ from topologicpy.Cluster import Cluster
921
+
922
+ if not Topology.IsInstance(topology, "Topology"):
923
+ print("Topology.ExternalBoundary - Error: The input topology parameter is not a valid topology. Returning None.")
924
+ return None
925
+
926
+ if Topology.IsInstance(topology, "Vertex"):
927
+ return Vertex.ExternalBoundary(topology)
928
+ elif Topology.IsInstance(topology, "Edge"):
929
+ return Edge.ExternalBoundary(topology)
930
+ elif Topology.IsInstance(topology, "Wire"):
931
+ return Wire.ExternalBoundary(topology)
932
+ elif Topology.IsInstance(topology, "Face"):
933
+ return Face.ExternalBoundary(topology)
934
+ elif Topology.IsInstance(topology, "Shell"):
935
+ return Shell.ExternalBoundary(topology)
936
+ elif Topology.IsInstance(topology, "Cell"):
937
+ return Cell.ExternalBoundary(topology)
938
+ elif Topology.IsInstance(topology, "CellComplex"):
939
+ return CellComplex.ExternalBoundary(topology)
940
+ elif Topology.IsInstance(topology, "Cluster"):
941
+ eb_list = Cluster.CellComplexes(topology) + Cluster.FreeCells(topology) + Cluster.FreeShells(topology) + Cluster.FreeFaces(topology) + Cluster.FreeWires(topology) + Cluster.FreeEdges(topology) + Cluster.FreeVertices(topology)
942
+ return_list = []
943
+ for item in eb_list:
944
+ return_list.append(Topology.ExternalBoundary(item))
945
+ return Cluster.ByTopologies(return_list)
946
+ else:
947
+ return None
948
+
868
949
  @staticmethod
869
950
  def Intersect(topologyA, topologyB, tranDict=False, tolerance=0.0001):
870
951
  """
@@ -1475,7 +1556,265 @@ class Topology():
1475
1556
  topDict = Dictionary.ByKeysValues(keys, values)
1476
1557
  Topology.SetDictionary(returnTopology, topDict)
1477
1558
  return returnTopology
1559
+
1560
+ @staticmethod
1561
+ def ByBIMPath(path, guidKey: str = "guid", colorKey: str = "color", typeKey: str = "type",
1562
+ defaultColor: list = [255,255,255,1], defaultType: str = "Structure",
1563
+ authorKey="author", dateKey="date", mantissa: int = 6, angTolerance: float = 0.001, tolerance: float = 0.0001):
1564
+ """
1565
+ Imports topologies from the input BIM file. See https://dotbim.net/
1566
+
1567
+ Parameters
1568
+ ----------
1569
+ path :str
1570
+ The path to the .bim file.
1571
+ path : str
1572
+ The input file path.
1573
+ overwrite : bool , optional
1574
+ If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
1575
+ version : str , optional
1576
+ The desired version number for the BIM file. The default is "1.0.0".
1577
+ guidKey : str , optional
1578
+ The key to use to store the the guid of the topology. The default is "guid".
1579
+ colorKey : str , optional
1580
+ The key to use to find the the color of the topology. The default is "color". If no color is found, the defaultColor parameter is used.
1581
+ typeKey : str , optional
1582
+ The key to use to find the the type of the topology. The default is "type". If no type is found, the defaultType parameter is used.
1583
+ defaultColor : list , optional
1584
+ The default color to use for the topology. The default is [255,255,255,1] which is opaque white.
1585
+ defaultType : str , optional
1586
+ The default type to use for the topology. The default is "Structure".
1587
+ authorKey : str , optional
1588
+ The key to use to store the author of the topology. The default is "author".
1589
+ dateKey : str , optional
1590
+ The key to use to store the creation date of the topology. This should be in the formate "DD.MM.YYYY". If no date is found the date of import is used.
1591
+ mantissa : int , optional
1592
+ The desired length of the mantissa. The default is 6.
1593
+ angTolerance : float , optional
1594
+ The angle tolerance in degrees under which no rotation is carried out. The default is 0.001 degrees.
1595
+ tolerance : float , optional
1596
+ The desired tolerance. The default is 0.0001.
1597
+
1598
+ Returns
1599
+ -------
1600
+ list
1601
+ The list of imported topologies
1602
+
1603
+ """
1604
+ import json
1605
+ if not path:
1606
+ print("Topology.ByBIMPath - Error: the input path parameter is not a valid path. Returning None.")
1607
+ return None
1608
+ topologies = None
1609
+ with open(path, "r") as bim_file:
1610
+ json_string = str(bim_file.read())
1611
+ topologies = Topology.ByBIMString(string=json_string, guidKey=guidKey, colorKey=colorKey, typeKey=typeKey,
1612
+ defaultColor=defaultColor, defaultType=defaultType,
1613
+ authorKey=authorKey, dateKey=dateKey, mantissa=mantissa, angTolerance=angTolerance, tolerance=tolerance)
1614
+ try:
1615
+ with open(path, "r") as bim_file:
1616
+ json_string = str(bim_file.read())
1617
+ topologies = Topology.ByBIMString(string=json_string, guidKey=guidKey, colorKey=colorKey, typeKey=typeKey,
1618
+ defaultColor=defaultColor, defaultType=defaultType,
1619
+ authorKey=authorKey, dateKey=dateKey, mantissa=mantissa, angTolerance=angTolerance, tolerance=tolerance)
1620
+ except:
1621
+ print("Topology.ByBIMPath - Error: the BIM file is not a valid file. Returning None.")
1622
+ return topologies
1623
+
1624
+ @staticmethod
1625
+ def ByBIMString(string, guidKey: str = "guid", colorKey: str = "color", typeKey: str = "type",
1626
+ defaultColor: list = [255,255,255,1], defaultType: str = "Structure",
1627
+ authorKey: str = "author", dateKey: str = "date", mantissa: int = 6, angTolerance: float = 0.001, tolerance: float = 0.0001):
1628
+ """
1629
+ Imports topologies from the input BIM file. See https://dotbim.net/
1630
+
1631
+ Parameters
1632
+ ----------
1633
+ string :str
1634
+ The input dotbim str (in JSON format).
1635
+ path : str
1636
+ The input file path.
1637
+ overwrite : bool , optional
1638
+ If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
1639
+ version : str , optional
1640
+ The desired version number for the BIM file. The default is "1.0.0".
1641
+ guidKey : str , optional
1642
+ The key to use to store the the guid of the topology. The default is "guid".
1643
+ colorKey : str , optional
1644
+ The key to use to find the the color of the topology. The default is "color". If no color is found, the defaultColor parameter is used.
1645
+ typeKey : str , optional
1646
+ The key to use to find the the type of the topology. The default is "type". If no type is found, the defaultType parameter is used.
1647
+ defaultColor : list , optional
1648
+ The default color to use for the topology. The default is [255,255,255,1] which is opaque white.
1649
+ defaultType : str , optional
1650
+ The default type to use for the topology. The default is "Structure".
1651
+ authorKey : str , optional
1652
+ The key to use to store the author of the topology. The default is "author".
1653
+ dateKey : str , optional
1654
+ The key to use to store the creation date of the topology. This should be in the formate "DD.MM.YYYY". If no date is found the date of import is used.
1655
+ mantissa : int , optional
1656
+ The desired length of the mantissa. The default is 6.
1657
+ angTolerance : float , optional
1658
+ The angle tolerance in degrees under which no rotation is carried out. The default is 0.001 degrees.
1659
+ tolerance : float , optional
1660
+ The desired tolerance. The default is 0.0001.
1661
+
1662
+ Returns
1663
+ -------
1664
+ list
1665
+ The list of imported topologies
1666
+
1667
+ """
1668
+ @staticmethod
1669
+ def convert_JSON_to_file(json_dictionary):
1670
+ import dotbimpy
1671
+ schema_version = json_dictionary["schema_version"]
1672
+ elements = json_dictionary["elements"]
1673
+ meshes = json_dictionary["meshes"]
1674
+ created_info = json_dictionary["info"]
1675
+
1676
+ created_meshes = []
1677
+ for i in meshes:
1678
+ created_meshes.append(dotbimpy.Mesh(
1679
+ mesh_id=i["mesh_id"],
1680
+ coordinates=i["coordinates"],
1681
+ indices=i["indices"]
1682
+ ))
1683
+
1684
+ created_elements = []
1685
+ for i in elements:
1686
+ new_element = dotbimpy.Element(
1687
+ mesh_id=i["mesh_id"],
1688
+ vector=dotbimpy.Vector(x=i["vector"]["x"],
1689
+ y=i["vector"]["y"],
1690
+ z=i["vector"]["z"]),
1691
+ rotation=dotbimpy.Rotation(qx=i["rotation"]["qx"],
1692
+ qy=i["rotation"]["qy"],
1693
+ qz=i["rotation"]["qz"],
1694
+ qw=i["rotation"]["qw"]),
1695
+ info=i["info"],
1696
+ color=dotbimpy.Color(r=i["color"]["r"],
1697
+ g=i["color"]["g"],
1698
+ b=i["color"]["b"],
1699
+ a=i["color"]["a"]),
1700
+ type=i["type"],
1701
+ guid=i["guid"]
1702
+ )
1703
+ try:
1704
+ new_element.face_colors = i["face_colors"]
1705
+ except KeyError as e:
1706
+ if str(e) == "'face_colors'":
1707
+ pass
1708
+ else:
1709
+ raise
1710
+ created_elements.append(new_element)
1711
+
1712
+ file = dotbimpy.File(schema_version=schema_version, meshes=created_meshes, elements=created_elements, info=created_info)
1713
+ return file
1714
+ json_dictionary = json.loads(string)
1715
+ file = convert_JSON_to_file(json_dictionary)
1716
+ return Topology.ByBIMFile(file, guidKey=guidKey, colorKey=colorKey, typeKey=typeKey,
1717
+ defaultColor=defaultColor, defaultType=defaultType,
1718
+ authorKey=authorKey, dateKey=dateKey,
1719
+ mantissa=mantissa, angTolerance=angTolerance, tolerance=tolerance)
1720
+ @staticmethod
1721
+ def ByBIMFile(file, guidKey: str = "guid", colorKey: str = "color", typeKey: str = "type",
1722
+ defaultColor: list = [255,255,255,1], defaultType: str = "Structure",
1723
+ authorKey="author", dateKey="date", mantissa: int = 6, angTolerance: float = 0.001, tolerance: float = 0.0001):
1724
+ """
1725
+ Imports topologies from the input BIM (dotbimpy.file.File) file object. See https://dotbim.net/
1726
+
1727
+ Parameters
1728
+ ----------
1729
+ file : dotbimpy.file.File
1730
+ The input dotbim file.
1731
+ path : str
1732
+ The input file path.
1733
+ overwrite : bool , optional
1734
+ If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
1735
+ version : str , optional
1736
+ The desired version number for the BIM file. The default is "1.0.0".
1737
+ guidKey : str , optional
1738
+ The key to use to store the the guid of the topology. The default is "guid".
1739
+ colorKey : str , optional
1740
+ The key to use to find the the color of the topology. The default is "color". If no color is found, the defaultColor parameter is used.
1741
+ typeKey : str , optional
1742
+ The key to use to find the the type of the topology. The default is "type". If no type is found, the defaultType parameter is used.
1743
+ defaultColor : list , optional
1744
+ The default color to use for the topology. The default is [255,255,255,1] which is opaque white.
1745
+ defaultType : str , optional
1746
+ The default type to use for the topology. The default is "Structure".
1747
+ authorKey : str , optional
1748
+ The key to use to store the author of the topology. The default is "author".
1749
+ dateKey : str , optional
1750
+ The key to use to store the creation date of the topology. This should be in the formate "DD.MM.YYYY". If no date is found the date of import is used.
1751
+ mantissa : int , optional
1752
+ The desired length of the mantissa. The default is 6.
1753
+ angTolerance : float , optional
1754
+ The angle tolerance in degrees under which no rotation is carried out. The default is 0.001 degrees.
1755
+ tolerance : float , optional
1756
+ The desired tolerance. The default is 0.0001.
1757
+
1758
+ Returns
1759
+ -------
1760
+ list
1761
+ The list of imported topologies
1762
+
1763
+ """
1764
+ from topologicpy.Vertex import Vertex
1765
+ from topologicpy.Topology import Topology
1766
+ from topologicpy.Dictionary import Dictionary
1767
+ import datetime
1478
1768
 
1769
+ file_info = file.info
1770
+ elements = file.elements
1771
+ meshes = file.meshes
1772
+ final_topologies = []
1773
+ topologies = []
1774
+ id_list = []
1775
+ for mesh in meshes:
1776
+ id_list.append(mesh.mesh_id)
1777
+ coordinates = mesh.coordinates
1778
+ indices = mesh.indices
1779
+ coordinates = [coordinates[i:i + 3] for i in range(0, len(coordinates),3)]
1780
+ indices = [indices[i:i + 3] for i in range(0, len(indices),3)]
1781
+ topology = Topology.ByGeometry(vertices=coordinates, faces=indices, tolerance=tolerance)
1782
+ topologies.append(topology)
1783
+
1784
+ for element in elements:
1785
+ element_info = element.info
1786
+ element_info[typeKey] = element.type
1787
+ element_info[colorKey] = [element.color.r, element.color.g, element.color.b, float(element.color.a)/float(255)]
1788
+ try:
1789
+ element_info[guidKey] = element.guid
1790
+ except:
1791
+ element_info[guidKey] = str(uuid.uuid4())
1792
+ try:
1793
+ element_info[authorKey] = file_info['author']
1794
+ except:
1795
+ element_info[authorKey] = "topologicpy"
1796
+ # Get the current date
1797
+ current_date = datetime.datetime.now()
1798
+ # Format the date as a string in DD.MM.YYYY format
1799
+ formatted_date = current_date.strftime("%d.%m.%Y")
1800
+ try:
1801
+ element_info[dateKey] = file_info['date']
1802
+ except:
1803
+ element_info[dateKey] = formatted_date
1804
+ d = Dictionary.ByPythonDictionary(element_info)
1805
+ mesh_id = element.mesh_id
1806
+ quat = element.rotation
1807
+ quaternion = [quat.qx, quat.qy, quat.qz, quat.qw]
1808
+ #roll, pitch, yaw = quaternion_to_euler([rot.qx, rot.qy, rot.qz, rot.qw])
1809
+ vector = element.vector
1810
+ topology = topologies[mesh_id]
1811
+ if Topology.IsInstance(topology, "Topology"):
1812
+ topology = Topology.RotateByQuaternion(topology=topology, origin=Vertex.Origin(), quaternion=quaternion, angTolerance=angTolerance, tolerance=tolerance)
1813
+ topology = Topology.Translate(topology, vector.x, vector.y, vector.z)
1814
+ topology = Topology.SetDictionary(topology, d)
1815
+ final_topologies.append(topology)
1816
+ return final_topologies
1817
+
1479
1818
  @staticmethod
1480
1819
  def ByBREPFile(file):
1481
1820
  """
@@ -3317,6 +3656,157 @@ class Topology():
3317
3656
  newTopologies.append(newTopology)
3318
3657
  return Cluster.ByTopologies(newTopologies)
3319
3658
 
3659
+ @staticmethod
3660
+ def ExportToBIM(topologies, path : str, overwrite: bool = False, version: str = "1.0.0",
3661
+ guidKey: str = "guid", colorKey: str = "color", typeKey: str = "type",
3662
+ defaultColor: list = [255,255,255,1], defaultType: str = "Structure",
3663
+ author: str = "topologicpy", date: str = None, mantissa: int = 6, tolerance: float = 0.0001):
3664
+ """
3665
+ Exports the input topology to a BIM file. See https://dotbim.net/
3666
+
3667
+ Parameters
3668
+ ----------
3669
+ topologies : list or topologic_core.Topology
3670
+ The input list of topologies or a single topology. The .bim format is restricted to triangulated meshes. No wires, edges, or vertices are supported.
3671
+ path : str
3672
+ The input file path.
3673
+ overwrite : bool , optional
3674
+ If set to True the ouptut file will overwrite any pre-existing file. Otherwise, it won't. The default is False.
3675
+ version : str , optional
3676
+ The desired version number for the BIM file. The default is "1.0.0".
3677
+ guidKey : str , optional
3678
+ The key to use to find the the guid of the topology. It is case insensitive. The default is "guid". If no guid is found, one is generated automatically.
3679
+ colorKey : str , optional
3680
+ The key to use to find the the color of the topology. It is case insensitive. The default is "color". If no color is found, the defaultColor parameter is used.
3681
+ typeKey : str , optional
3682
+ The key to use to find the the type of the topology. It is case insensitive. The default is "type". If no type is found, the defaultType parameter is used.
3683
+ defaultColor : list , optional
3684
+ The default color to use for the topology. The default is [255,255,255,1] which is opaque white.
3685
+ defaultType : str , optional
3686
+ The default type to use for the topology. The default is "Structure".
3687
+ author : str , optional
3688
+ The author of the topology. The default is "topologicpy".
3689
+ date : str , optional
3690
+ The creation date of the topology. This should be in the formate "DD.MM.YYYY". The default is None which uses the date of export.
3691
+ mantissa : int , optional
3692
+ The desired length of the mantissa. The default is 6.
3693
+ tolerance : float , optional
3694
+ The desired tolerance. The default is 0.0001.
3695
+
3696
+ Returns
3697
+ -------
3698
+ bool
3699
+ True if the export operation is successful. False otherwise.
3700
+
3701
+ """
3702
+ from topologicpy.Topology import Topology
3703
+ from topologicpy.Helper import Helper
3704
+ from topologicpy.Dictionary import Dictionary
3705
+ from topologicpy.Cluster import Cluster
3706
+ import datetime
3707
+ from os.path import exists
3708
+
3709
+ try:
3710
+ import dotbimpy
3711
+ except:
3712
+ print("Topology - Installing required dotbimpy library.")
3713
+ try:
3714
+ os.system("pip install dotbimpy")
3715
+ except:
3716
+ os.system("pip install dotbimpy --user")
3717
+ try:
3718
+ import dotbimpy
3719
+ print("Topology - dotbimpy library installed successfully.")
3720
+ except:
3721
+ warnings.warn("Topology - Error: Could not import dotbimpy. Please install the dotbimpy library manually. Returning None.")
3722
+ return None
3723
+ # Make sure the file extension is .brep
3724
+ ext = path[len(path)-4:len(path)]
3725
+ if ext.lower() != ".bim":
3726
+ path = path+".bim"
3727
+ if not overwrite and exists(path):
3728
+ print("Topology.ExportToBIM - Error: a file already exists at the specified path and overwrite is set to False. Returning None.")
3729
+ return None
3730
+ # Get the current date
3731
+ current_date = datetime.datetime.now()
3732
+
3733
+ # Format the date as a string in DD.MM.YYYY format
3734
+ formatted_date = current_date.strftime("%d.%m.%Y")
3735
+
3736
+ if Topology.IsInstance(topologies, "Topology"):
3737
+ topologies = [topologies]
3738
+ # Prepare input data
3739
+ final_topologies = []
3740
+ for topology in topologies:
3741
+ d = Topology.Dictionary(topology)
3742
+ topology = Topology.SelfMerge(Topology.Triangulate(topology, tolerance=tolerance), tolerance=tolerance)
3743
+ topology = Topology.SetDictionary(topology, d)
3744
+ if Topology.IsInstance(topology, "Cluster"):
3745
+ final_topologies += Cluster.CellComplexes(topology) + Cluster.FreeCells(topology, tolerance=tolerance) + Cluster.FreeShells(topology, tolerance=tolerance) + Cluster.FreeFaces(topology, tolerance=tolerance)
3746
+ elif Topology.IsInstance(topology, "CellComplex") or Topology.IsInstance(topology, "Cell") or Topology.IsInstance(topology, "Shell") or Topology.IsInstance(topology, "Face"):
3747
+ final_topologies.append(topology)
3748
+ elements = []
3749
+ meshes = []
3750
+ for i, topology in enumerate(final_topologies):
3751
+ geo_dict = Topology.Geometry(topology, mantissa=mantissa)
3752
+ coordinates = Helper.Flatten(geo_dict['vertices'])
3753
+ indices = Helper.Flatten(geo_dict['faces'])
3754
+ d = Topology.Dictionary(topology)
3755
+ color = Dictionary.ValueAtKey(d, colorKey)
3756
+ r, g, b, a = defaultColor
3757
+ if isinstance(color, list):
3758
+ if len(color) > 2:
3759
+ r = color[0]
3760
+ g = color[1]
3761
+ b = color[2]
3762
+ if len(color) == 3:
3763
+ a = color[3]
3764
+ color = dotbimpy.Color(r=r, g=g, b=b, a=int(a*255))
3765
+ guid = Dictionary.ValueAtKey(d, guidKey)
3766
+ if not guid:
3767
+ guid = str(uuid.uuid4())
3768
+
3769
+ type = Dictionary.ValueAtKey(d, typeKey)
3770
+ if not type:
3771
+ type = defaultType
3772
+ info = Dictionary.PythonDictionary(d)
3773
+ info['color'] = str([r,g,b,a])
3774
+ info['guid'] = guid
3775
+ info['type'] = type
3776
+ print("info", info)
3777
+ rotation = dotbimpy.Rotation(qx=0, qy=0, qz=0, qw=1.0)
3778
+ vector = dotbimpy.Vector(x=0, y=0, z=0)
3779
+
3780
+ # Instantiate Mesh object
3781
+ mesh = dotbimpy.Mesh(mesh_id=i, coordinates=coordinates, indices=indices)
3782
+ meshes.append(mesh)
3783
+ # Instantiate Element object
3784
+ element = dotbimpy.Element(mesh_id=i,
3785
+ vector=vector,
3786
+ guid=guid,
3787
+ info=info,
3788
+ rotation=rotation,
3789
+ type=type,
3790
+ color=color)
3791
+ elements.append(element)
3792
+
3793
+ # File meta data
3794
+ file_info = {
3795
+ "Author": author,
3796
+ "Date": formatted_date
3797
+ }
3798
+
3799
+ print("Meshes:", meshes)
3800
+ print("Elements:", elements)
3801
+
3802
+ # Instantiate and save File object
3803
+ file = dotbimpy.File(version, meshes=meshes, elements=elements, info=file_info)
3804
+ try:
3805
+ file.save(path)
3806
+ return True
3807
+ except:
3808
+ return False
3809
+
3320
3810
  @staticmethod
3321
3811
  def ExportToBREP(topology, path, overwrite=False, version=3):
3322
3812
  """
@@ -4261,11 +4751,11 @@ class Topology():
4261
4751
  """
4262
4752
  from topologicpy.Vertex import Vertex
4263
4753
  from topologicpy.Vector import Vector
4264
-
4754
+
4265
4755
  if not Topology.IsInstance(topology, "Topology"):
4266
4756
  print("Topology.Flatten - Error: the input topology parameter is not a valid topology. Returning None.")
4267
4757
  return None
4268
- if origin == None:
4758
+ if not Topology.IsInstance(origin, "Vertex"):
4269
4759
  origin = Topology.Centroid(topology)
4270
4760
  up = Vector.Up()
4271
4761
  flat_topology = Topology.Translate(topology, -Vertex.X(origin, mantissa=mantissa), -Vertex.Y(origin, mantissa=mantissa), -Vertex.Z(origin, mantissa=mantissa))
@@ -5417,12 +5907,13 @@ class Topology():
5417
5907
  if not Topology.IsInstance(topology, "Topology"):
5418
5908
  print("Topology.Rotate - Error: The input topology parameter is not a valid topologic topology. Returning None.")
5419
5909
  return None
5420
- if not origin:
5910
+ if not Topology.IsInstance(origin, "Vertex"):
5421
5911
  origin = Vertex.ByCoordinates(0, 0, 0)
5422
5912
  if not Topology.IsInstance(origin, "Vertex"):
5423
5913
  print("Topology.Rotate - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
5424
5914
  return None
5425
5915
  returnTopology = topology
5916
+ d = Topology.Dictionary(topology)
5426
5917
  if abs(angle) >= angTolerance:
5427
5918
  try:
5428
5919
  x, y, z = axis
@@ -5437,9 +5928,141 @@ class Topology():
5437
5928
  rot_vertices = [Vertex.ByCoordinates(rot_v) for rot_v in rot_vertices]
5438
5929
  new_topology = Topology.ReplaceVertices(topology, verticesA=Topology.Vertices(topology), verticesB=rot_vertices)
5439
5930
  new_topology = Topology.SelfMerge(new_topology, tolerance=tolerance)
5931
+ if len(Dictionary.Keys(d) > 0):
5932
+ new_topology = Topology.SetDictionary(new_topology, d)
5440
5933
  return new_topology
5934
+ if len(Dictionary.Keys(d)) > 0:
5935
+ returnTopology = Topology.SetDictionary(returnTopology, d)
5441
5936
  return returnTopology
5442
5937
 
5938
+ @staticmethod
5939
+ def RotateByEulerAngles(topology, origin = None, roll: float = 0, pitch: float = 0, yaw: float = 0, angTolerance: float = 0.001, tolerance: float = 0.0001):
5940
+ """
5941
+ Rotates the input topology using Euler angles (roll, pitch, yaw). See https://en.wikipedia.org/wiki/Aircraft_principal_axes
5942
+
5943
+ Parameters
5944
+ ----------
5945
+ topology : topologic_core.Topology
5946
+ The input topology.
5947
+ origin : topologic_core.Vertex , optional
5948
+ The origin (center) of the rotation. If set to None, the world origin (0, 0, 0) is used. The default is None.
5949
+ roll : float , optional
5950
+ The rotation angle in degrees around the X-axis. The default is 0.
5951
+ pitch = float , optional
5952
+ The rotation angle in degrees around the Y-axis. The default is 0.
5953
+ yaw = float , optional
5954
+ The rotation angle in degrees around the Z-axis. The default is 0.
5955
+ angTolerance : float , optional
5956
+ The angle tolerance in degrees under which no rotation is carried out. The default is 0.001 degrees.
5957
+ tolerance : float , optional
5958
+ The desired tolerance. The default is 0.0001.
5959
+
5960
+ Returns
5961
+ -------
5962
+ topologic_core.Topology
5963
+ The rotated topology.
5964
+
5965
+ """
5966
+ from topologicpy.Vertex import Vertex
5967
+ from topologicpy.Dictionary import Dictionary
5968
+
5969
+ if not Topology.IsInstance(topology, "Topology"):
5970
+ print("Topology.RotateByEulerAngles - Error: The input topology parameter is not a valid topologic topology. Returning None.")
5971
+ return None
5972
+ if not Topology.IsInstance(origin, "Vertex"):
5973
+ origin = Vertex.ByCoordinates(0, 0, 0)
5974
+ if not Topology.IsInstance(origin, "Vertex"):
5975
+ print("Topology.RotateByEulerAngles - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
5976
+ return None
5977
+ d = Topology.Dictionary(topology)
5978
+ return_topology = Topology.Copy(topology)
5979
+ return_topology = Topology.Rotate(return_topology, origin=origin, axis=[1,0,0], angle=roll, angTolerance=angTolerance, tolerance=tolerance)
5980
+ return_topology = Topology.Rotate(return_topology, origin=origin, axis=[0,1,0], angle=pitch, angTolerance=angTolerance, tolerance=tolerance)
5981
+ return_topology = Topology.Rotate(return_topology, origin=origin, axis=[0,0,1], angle=yaw, angTolerance=angTolerance, tolerance=tolerance)
5982
+ if len(Dictionary.Keys(d)) > 0:
5983
+ return_topology = Topology.SetDictionary(return_topology, d)
5984
+ return return_topology
5985
+
5986
+ @staticmethod
5987
+ def RotateByQuaternion(topology, origin=None, quaternion: list = [0,0,0,1], angTolerance: float = 0.001, tolerance: float = 0.0001):
5988
+ """
5989
+ Rotates the input topology using Quaternion rotations. See https://en.wikipedia.org/wiki/Quaternion
5990
+
5991
+ Parameters
5992
+ ----------
5993
+ topology : topologic_core.Topology
5994
+ The input topology.
5995
+ origin : topologic_core.Vertex , optional
5996
+ The origin (center) of the rotation. If set to None, the world origin (0, 0, 0) is used. The default is None.
5997
+ quaternion : list or numpy array of size 4
5998
+ The input Quaternion list. It should be in the form [x, y, z, w].
5999
+ angTolerance : float , optional
6000
+ The angle tolerance in degrees under which no rotation is carried out. The default is 0.001 degrees.
6001
+ tolerance : float , optional
6002
+ The desired tolerance. The default is 0.0001.
6003
+
6004
+ Returns
6005
+ -------
6006
+ topologic_core.Topology
6007
+ The rotated topology.
6008
+
6009
+ """
6010
+ from topologicpy.Vertex import Vertex
6011
+ from topologicpy.Dictionary import Dictionary
6012
+
6013
+ def quaternion_to_euler(quaternion):
6014
+ """
6015
+ Convert a quaternion into Euler angles (roll, pitch, yaw)
6016
+ Roll is rotation around x-axis, Pitch is rotation around y-axis, and Yaw is rotation around z-axis.
6017
+ Quaternion should be in the form [x, y, z, w]
6018
+
6019
+ Args:
6020
+ quaternion: list or numpy array of size 4
6021
+
6022
+ Returns:
6023
+ A list of Euler angles in degrees [roll, pitch, yaw]
6024
+ """
6025
+ import numpy as np
6026
+ x, y, z, w = quaternion
6027
+
6028
+ # Roll (x-axis rotation)
6029
+ sinr_cosp = 2 * (w * x + y * z)
6030
+ cosr_cosp = 1 - 2 * (x * x + y * y)
6031
+ roll = np.arctan2(sinr_cosp, cosr_cosp)
6032
+
6033
+ # Pitch (y-axis rotation)
6034
+ sinp = 2 * (w * y - z * x)
6035
+ if abs(sinp) >= 1:
6036
+ pitch = np.sign(sinp) * np.pi / 2 # use 90 degrees if out of range
6037
+ else:
6038
+ pitch = np.arcsin(sinp)
6039
+
6040
+ # Yaw (z-axis rotation)
6041
+ siny_cosp = 2 * (w * z + x * y)
6042
+ cosy_cosp = 1 - 2 * (y * y + z * z)
6043
+ yaw = np.arctan2(siny_cosp, cosy_cosp)
6044
+
6045
+ # Convert radians to degrees
6046
+ roll = np.degrees(roll)
6047
+ pitch = np.degrees(pitch)
6048
+ yaw = np.degrees(yaw)
6049
+ return [roll, pitch, yaw]
6050
+
6051
+ if not Topology.IsInstance(topology, "Topology"):
6052
+ print("Topology.RotateByQuaternion - Error: The input topology parameter is not a valid topologic topology. Returning None.", topology)
6053
+ return None
6054
+ if not Topology.IsInstance(origin, "Vertex"):
6055
+ origin = Vertex.ByCoordinates(0, 0, 0)
6056
+ if not Topology.IsInstance(origin, "Vertex"):
6057
+ print("Topology.RotateByQuaternion - Error: The input origin parameter is not a valid topologic vertex. Returning None.")
6058
+ return None
6059
+ roll, pitch, yaw = quaternion_to_euler(quaternion)
6060
+ d = Topology.Dictionary(topology)
6061
+ return_topology = Topology.RotateByEulerAngles(topology=topology, origin=origin, roll=roll, pitch=pitch, yaw=yaw, angTolerance=angTolerance, tolerance=tolerance)
6062
+ if len(Dictionary.Keys(d)) > 0:
6063
+ return_topology = Topology.SetDictionary(return_topology, d)
6064
+ return return_topology
6065
+
5443
6066
  @staticmethod
5444
6067
  def Scale(topology, origin=None, x=1, y=1, z=1):
5445
6068
  """
@@ -5467,7 +6090,7 @@ class Topology():
5467
6090
  from topologicpy.Vertex import Vertex
5468
6091
  if not Topology.IsInstance(topology, "Topology"):
5469
6092
  return None
5470
- if not origin:
6093
+ if not Topology.IsInstance(origin, "Vertex"):
5471
6094
  origin = Vertex.ByCoordinates(0, 0, 0)
5472
6095
  if not Topology.IsInstance(origin, "Vertex"):
5473
6096
  return None
@@ -6216,7 +6839,7 @@ class Topology():
6216
6839
  from topologicpy.CellComplex import CellComplex
6217
6840
  from topologicpy.Cluster import Cluster
6218
6841
 
6219
- if not origin:
6842
+ if not Topology.IsInstance(origin, "Vertex"):
6220
6843
  origin = Vertex.ByCoordinates(0, 0, 0)
6221
6844
  if not Topology.IsInstance(topology, "Topology"):
6222
6845
  if not silent:
@@ -6336,7 +6959,7 @@ class Topology():
6336
6959
  return topology
6337
6960
  if triangulate == True:
6338
6961
  topology = Topology.Triangulate(topology)
6339
- if origin == None:
6962
+ if not Topology.IsInstance(origin, "Vertex"):
6340
6963
  origin = Topology.Centroid(topology)
6341
6964
  vertices = Topology.Vertices(topology)
6342
6965
  zList = [Vertex.Z(v, mantissa=mantissa) for v in vertices]
@@ -6389,7 +7012,7 @@ class Topology():
6389
7012
  return topology
6390
7013
  if triangulate == True:
6391
7014
  topology = Topology.Triangulate(topology)
6392
- if origin == None:
7015
+ if not Topology.IsInstance(origin, "Vertex"):
6393
7016
  origin = Topology.Centroid(topology)
6394
7017
 
6395
7018
  vertices = Topology.Vertices(topology)
@@ -6433,7 +7056,7 @@ class Topology():
6433
7056
  if not Topology.IsInstance(topology, "Topology"):
6434
7057
  print("Topology.Unflatten - Error: the input topology parameter is not a valid topology. Returning None.")
6435
7058
  return None
6436
- if origin == None:
7059
+ if not Topology.IsInstance(origin, "Vertex"):
6437
7060
  origin = Vertex.Origin()
6438
7061
  up = Vector.Up()
6439
7062
  tran_mat = Vector.TransformationMatrix(up, direction)
@@ -7029,6 +7652,7 @@ class Topology():
7029
7652
  if transferDictionaries:
7030
7653
  selectors.append(Topology.SetDictionary(Face.Centroid(triFace), Topology.Dictionary(aFace)))
7031
7654
  faceTriangles.append(triFace)
7655
+ return_topology = None
7032
7656
  if t == Topology.TypeID("Face") or t == Topology.TypeID("Shell"): # Face or Shell
7033
7657
  return_topology = Shell.ByFaces(faceTriangles, tolerance=tolerance)
7034
7658
  if transferDictionaries and not return_topology == None: