topologicpy 0.8.55__py3-none-any.whl → 0.8.58__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/Vertex.py CHANGED
@@ -1818,6 +1818,7 @@ class Vertex():
1818
1818
  pt = project_point_onto_plane(Vertex.Coordinates(vertex), [eq["a"], eq["b"], eq["c"], eq["d"]], direction)
1819
1819
  return Vertex.ByCoordinates(pt[0], pt[1], pt[2])
1820
1820
 
1821
+
1821
1822
  @staticmethod
1822
1823
  def Separate(*vertices, minDistance: float = 0.0001, iterations: int = 100, strength: float = 0.1, tolerance: float = 0.0001, silent: bool = False):
1823
1824
  """
@@ -1846,68 +1847,97 @@ class Vertex():
1846
1847
  """
1847
1848
  from topologicpy.Topology import Topology
1848
1849
  from topologicpy.Helper import Helper
1850
+ from topologicpy.Vertex import Vertex
1849
1851
  import math
1852
+ from collections import defaultdict
1850
1853
 
1854
+ # --- Gather & validate inputs ---
1851
1855
  if len(vertices) == 0:
1852
1856
  if not silent:
1853
1857
  print("Vertex.Separate - Error: The input vertices parameter is an empty list. Returning None.")
1854
1858
  return None
1855
- if len(vertices) == 1:
1856
- vertices = vertices[0]
1857
- if isinstance(vertices, list):
1858
- if len(vertices) == 0:
1859
- if not silent:
1860
- print("Vertex.Separate - Error: The input vertices parameter is an empty list. Returning None.")
1861
- return None
1862
- else:
1863
- vertexList = [x for x in vertices if Topology.IsInstance(x, "Vertex")]
1864
- if len(vertexList) == 0:
1865
- if not silent:
1866
- print("Vertex.Separate - Error: The input vertices parameter does not contain any valid vertices. Returning None.")
1867
- return None
1868
- else:
1869
- if not silent:
1870
- print("Vertex.Separate - Warning: The input vertices parameter contains only one vertex. Returning the same vertex.")
1871
- return vertices
1859
+
1860
+ # Allow either a single list or varargs
1861
+ if len(vertices) == 1 and isinstance(vertices[0], list):
1862
+ raw_list = vertices[0]
1872
1863
  else:
1873
- vertexList = Helper.Flatten(list(vertices))
1874
- vertexList = [x for x in vertexList if Topology.IsInstance(x, "Vertex")]
1864
+ raw_list = Helper.Flatten(list(vertices))
1865
+
1866
+ vertexList = [v for v in raw_list if Topology.IsInstance(v, "Vertex")]
1875
1867
  if len(vertexList) == 0:
1876
1868
  if not silent:
1877
1869
  print("Vertex.Separate - Error: The input parameters do not contain any valid vertices. Returning None.")
1878
1870
  return None
1871
+ if len(vertexList) == 1:
1872
+ if not silent:
1873
+ print("Vertex.Separate - Warning: Only one vertex supplied. Returning it unchanged.")
1874
+ return vertexList
1875
+
1876
+ minDistance = float(minDistance) + float(tolerance) # safety margin
1877
+ n = len(vertexList)
1878
+
1879
+ # Mutable coordinates
1880
+ coords = [[vertexList[i].X(), vertexList[i].Y(), vertexList[i].Z()] for i in range(n)]
1881
+ dicts = [Topology.Dictionary(v) for v in vertexList]
1882
+
1883
+ # --- Pre-seed coincident vertices so they can start moving ---
1884
+ # Cluster indices by quantized coordinate to catch exact (or near-exact) duplicates
1885
+ key_scale = max(tolerance, 1e-12)
1886
+ clusters = defaultdict(list)
1887
+ for idx, (x, y, z) in enumerate(coords):
1888
+ key = (round(x / key_scale), round(y / key_scale), round(z / key_scale))
1889
+ clusters[key].append(idx)
1890
+
1891
+ # For any cluster with >1 vertex, spread them on a small circle in XY
1892
+ for idxs in clusters.values():
1893
+ k = len(idxs)
1894
+ if k > 1:
1895
+ r = minDistance * 0.5 # small initial spread; repulsion will take it from here
1896
+ for m, idx in enumerate(idxs):
1897
+ ang = (2.0 * math.pi * m) / k
1898
+ coords[idx][0] += r * math.cos(ang)
1899
+ coords[idx][1] += r * math.sin(ang)
1900
+ # leave Z unchanged to avoid unintended vertical drift
1901
+
1902
+ # --- Repulsion simulation ---
1903
+ eps = 1e-12
1904
+ for _ in range(int(iterations)):
1905
+ all_ok = True
1906
+ for i in range(n):
1907
+ xi, yi, zi = coords[i]
1908
+ for j in range(i + 1, n):
1909
+ xj, yj, zj = coords[j]
1910
+ dx = xj - xi
1911
+ dy = yj - yi
1912
+ dz = zj - zi
1913
+ dist_sq = dx*dx + dy*dy + dz*dz
1914
+ if dist_sq <= 0.0:
1915
+ # still coincident: nudge with a tiny deterministic push along x
1916
+ dx, dy, dz = (eps, 0.0, 0.0)
1917
+ dist_sq = eps*eps
1918
+ dist = math.sqrt(dist_sq)
1919
+
1920
+ if dist < minDistance:
1921
+ all_ok = False
1922
+ # Repulsion magnitude; clamp denominator to avoid blow-ups
1923
+ repel = (minDistance - dist) / max(dist, eps) * float(strength)
1924
+ # Split the move equally
1925
+ sx = 0.5 * dx * repel
1926
+ sy = 0.5 * dy * repel
1927
+ sz = 0.5 * dz * repel
1928
+ coords[i][0] -= sx; coords[i][1] -= sy; coords[i][2] -= sz
1929
+ coords[j][0] += sx; coords[j][1] += sy; coords[j][2] += sz
1930
+ if all_ok:
1931
+ break # everything already at least minDistance apart
1932
+
1933
+ # --- Rebuild vertices & restore dictionaries ---
1934
+ new_vertices = [Vertex.ByCoordinates(x, y, z) for (x, y, z) in coords]
1935
+ for i in range(n):
1936
+ new_vertices[i] = Topology.SetDictionary(new_vertices[i], dicts[i])
1879
1937
 
1880
- minDistance = minDistance + tolerance # Add a bit of a safety factor
1881
-
1882
- # Convert to mutable coordinates
1883
- coords = [[v.X(), v.Y(), v.Z()] for v in vertices]
1884
-
1885
- for _ in range(iterations):
1886
- for i in range(len(coords)):
1887
- for j in range(i + 1, len(coords)):
1888
- dx = coords[j][0] - coords[i][0]
1889
- dy = coords[j][1] - coords[i][1]
1890
- dz = coords[j][2] - coords[i][2]
1891
- dist = math.sqrt(dx*dx + dy*dy + dz*dz)
1892
-
1893
- if dist < minDistance and dist > 1e-9:
1894
- # Calculate repulsion vector
1895
- repel = (minDistance - dist) / dist * strength
1896
- shift = [dx * repel * 0.5, dy * repel * 0.5, dz * repel * 0.5]
1897
- coords[i][0] -= shift[0]
1898
- coords[i][1] -= shift[1]
1899
- coords[i][2] -= shift[2]
1900
- coords[j][0] += shift[0]
1901
- coords[j][1] += shift[1]
1902
- coords[j][2] += shift[2]
1903
-
1904
- # Reconstruct TopologicPy Vertex objects
1905
- new_vertices = [Vertex.ByCoordinates(x, y, z) for x, y, z in coords]
1906
- # Transfer the dictionaries
1907
- for i, v in enumerate(new_vertices):
1908
- v = Topology.SetDictionary(v, Topology.Dictionary(vertices[i]))
1909
1938
  return new_vertices
1910
1939
 
1940
+
1911
1941
  @staticmethod
1912
1942
  def Transform(vertex, matrix, mantissa: int = 6, silent: bool = False):
1913
1943
  """
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.55'
1
+ __version__ = '0.8.58'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.55
3
+ Version: 0.8.58
4
4
  Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
5
5
  Author-email: Wassim Jabi <wassim.jabi@gmail.com>
6
6
  License: AGPL v3 License
@@ -12,10 +12,11 @@ topologicpy/Dictionary.py,sha256=Z4YQ88tONWd-0X0dENQ8IZqIOa9mbBqhJkTBsHmft2g,446
12
12
  topologicpy/Edge.py,sha256=DifItuyabFDUFC7CVMlt2DeMFMNaGOqCg43iU9CPP0A,74029
13
13
  topologicpy/EnergyModel.py,sha256=hB1aiJe45gdDMFm1AhkBr-1djjtXSzn24iRpQMk43-4,57749
14
14
  topologicpy/Face.py,sha256=aX9EcR3JGbLITElhd25J0Z8m9U8KkmbYivGg3oZN-Uw,202296
15
- topologicpy/Graph.py,sha256=Oa0oOrPoOSUGL5fvJYHBH_r6kRZ944wk-P828GyAjk4,705757
15
+ topologicpy/Graph.py,sha256=eUVSdZ_1kJ6a9Bu6fgOcFWUWPeIulVnxdWbVphucbiQ,728898
16
16
  topologicpy/Grid.py,sha256=3OsBMyHh4w8gpFOTMKHMNTpo62V0CwRNu5cwm87yDUA,18421
17
17
  topologicpy/Helper.py,sha256=Nr6pyzl0sZm4Cu11wOqoYKu6yYal5N6A9jErXnaZBJc,31765
18
18
  topologicpy/Honeybee.py,sha256=DzaG9wpkJdcDWcjOGXhuN5X0gCqypmZGBa1y5E2MkjU,48964
19
+ topologicpy/Kuzu.py,sha256=THmXfImCVTZhvt0fH0M2-PJeFNyTnmBCf_sjXDxOraE,36305
19
20
  topologicpy/Matrix.py,sha256=bOofT34G3YHu9aMIWx60YHAJga4R0GbDjsZBUD4Hu_k,22706
20
21
  topologicpy/Neo4j.py,sha256=J8jU_mr5-mWC0Lg_D2dMjMlx1rY_eh8ks_aubUuTdWw,22319
21
22
  topologicpy/Plotly.py,sha256=kF7JwBMWJQAuGezaJYI6Cq7ErNwEtcKzaExOfdGPIMc,123003
@@ -27,12 +28,12 @@ topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
27
28
  topologicpy/Sun.py,sha256=8S6dhCKfOhUGVny-jEk87Q08anLYMB1JEBKRGCklvbQ,36670
28
29
  topologicpy/Topology.py,sha256=R5Ac3V_ADDRDZjhpcNvhM3AvDOLU6ORvB3yILyEkxnI,472559
29
30
  topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
30
- topologicpy/Vertex.py,sha256=0f6HouARKaCuxhdxsUEYi8T9giJycnWhQ8Cn70YILBA,84885
31
+ topologicpy/Vertex.py,sha256=r_3cicgpino96ymm1ANptfOuqE59b99YWwksxyPOYK4,85914
31
32
  topologicpy/Wire.py,sha256=gjgQUGHdBdXUIijgZc_VIW0E39w-smaVhhdl0jF63fQ,230466
32
33
  topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
33
- topologicpy/version.py,sha256=EKCMooHLmkTpTBR1XTlkHxj-YkXZosf7ysuCeCcjiR8,23
34
- topologicpy-0.8.55.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
35
- topologicpy-0.8.55.dist-info/METADATA,sha256=BIWmEWd275UZuMnl423fCxI3Bv6-vZpot8niPNApQAE,10535
36
- topologicpy-0.8.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- topologicpy-0.8.55.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
38
- topologicpy-0.8.55.dist-info/RECORD,,
34
+ topologicpy/version.py,sha256=DA9jSKsyKliNEMkA9DgxgAJe2EnQ3lzfjgUUq8L62sQ,23
35
+ topologicpy-0.8.58.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
36
+ topologicpy-0.8.58.dist-info/METADATA,sha256=n4u-5ftRHfgKJ_qzyHToMsD2AAl7LAl8gOTOovRtDEA,10535
37
+ topologicpy-0.8.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ topologicpy-0.8.58.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
39
+ topologicpy-0.8.58.dist-info/RECORD,,