topologicpy 0.8.88__py3-none-any.whl → 0.8.89__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/Graph.py +252 -29
- topologicpy/version.py +1 -1
- {topologicpy-0.8.88.dist-info → topologicpy-0.8.89.dist-info}/METADATA +1 -1
- {topologicpy-0.8.88.dist-info → topologicpy-0.8.89.dist-info}/RECORD +7 -7
- {topologicpy-0.8.88.dist-info → topologicpy-0.8.89.dist-info}/WHEEL +0 -0
- {topologicpy-0.8.88.dist-info → topologicpy-0.8.89.dist-info}/licenses/LICENSE +0 -0
- {topologicpy-0.8.88.dist-info → topologicpy-0.8.89.dist-info}/top_level.txt +0 -0
topologicpy/Graph.py
CHANGED
|
@@ -4756,12 +4756,13 @@ class Graph:
|
|
|
4756
4756
|
include: list = ["contains", "coveredBy", "covers", "crosses", "disjoint", "equals", "overlaps", "touches","within", "proximity"],
|
|
4757
4757
|
proximityValues = [1, 5, 10],
|
|
4758
4758
|
proximityLabels = ["near", "intermediate", "far"],
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4759
|
+
useShortestDistance: bool = False,
|
|
4760
|
+
useInternalVertex: bool = False,
|
|
4761
|
+
vertexIDKey: str = "id",
|
|
4762
|
+
edgeKeyFwd: str = "relFwd",
|
|
4763
|
+
edgeKeyBwd: str = "relBwd",
|
|
4764
|
+
connectsKey:str = "connects",
|
|
4765
|
+
storeBREP: bool = False,
|
|
4765
4766
|
mantissa: int = 6,
|
|
4766
4767
|
tolerance: float = 0.0001,
|
|
4767
4768
|
silent: bool = False
|
|
@@ -4785,7 +4786,8 @@ class Graph:
|
|
|
4785
4786
|
proximityLabels: list , optional
|
|
4786
4787
|
The list of range labels (e.g. "near", "intermediate", "far") that correspond to the proximityValues list.
|
|
4787
4788
|
The list must have the same number of elements as the proximityValues list. Default is ["near", "intermediate", "far"]
|
|
4788
|
-
|
|
4789
|
+
useShortestDistance: bool , optional
|
|
4790
|
+
If set to True, the shortest distance between objects is used. Otherwise, the distance between their centroids is used. Default is False.
|
|
4789
4791
|
useInternalVertex: bool , optional
|
|
4790
4792
|
If set to True, an internal vertex of the represented topology will be used as a graph node.
|
|
4791
4793
|
Otherwise, its centroid will be used. Default is False.
|
|
@@ -4814,6 +4816,7 @@ class Graph:
|
|
|
4814
4816
|
"""
|
|
4815
4817
|
from topologicpy.Graph import Graph
|
|
4816
4818
|
from topologicpy.BVH import BVH
|
|
4819
|
+
from topologicpy.Cell import Cell
|
|
4817
4820
|
from topologicpy.Vertex import Vertex
|
|
4818
4821
|
from topologicpy.Topology import Topology
|
|
4819
4822
|
from topologicpy.Dictionary import Dictionary
|
|
@@ -4850,6 +4853,234 @@ class Graph:
|
|
|
4850
4853
|
v = Topology.SetDictionary(v, Topology.Dictionary(topologyList[0]))
|
|
4851
4854
|
return Graph.ByVerticesEdges([v], [])
|
|
4852
4855
|
|
|
4856
|
+
# ---------- Calculate Proximity ------------
|
|
4857
|
+
|
|
4858
|
+
def _calc_proximity(topologies: list,
|
|
4859
|
+
ranges: list,
|
|
4860
|
+
labels: list,
|
|
4861
|
+
useShortestDistance: bool = True,
|
|
4862
|
+
tolerance: float = 0.0001,
|
|
4863
|
+
silent: bool = False):
|
|
4864
|
+
"""
|
|
4865
|
+
Creates a proximity graph from a list of topologies using a BVH
|
|
4866
|
+
to prune distance checks for large input sets.
|
|
4867
|
+
|
|
4868
|
+
Each topology is represented by a vertex in the graph.
|
|
4869
|
+
An edge is created between two vertices if the distance between
|
|
4870
|
+
their corresponding topologies is <= max(ranges).
|
|
4871
|
+
|
|
4872
|
+
A BVH is used as a broad-phase accelerator: for each topology,
|
|
4873
|
+
a query box centred at its centroid and sized to cover the
|
|
4874
|
+
maximum proximity range is used to retrieve only nearby
|
|
4875
|
+
candidates. Exact distances are then computed only for those
|
|
4876
|
+
candidates.
|
|
4877
|
+
|
|
4878
|
+
Parameters
|
|
4879
|
+
----------
|
|
4880
|
+
topologies : list of topologic_core.Topology
|
|
4881
|
+
The input topologies to be represented as vertices.
|
|
4882
|
+
ranges : list of float
|
|
4883
|
+
A list of positive numeric thresholds (e.g. [1.0, 3.0, 5.0]).
|
|
4884
|
+
Interpreted as upper bounds of proximity bands.
|
|
4885
|
+
Distances larger than max(ranges) are ignored (no edge).
|
|
4886
|
+
labels : list of str
|
|
4887
|
+
A list of proximity labels, same length as `ranges`.
|
|
4888
|
+
For a pair distance d:
|
|
4889
|
+
- d <= ranges[0] -> labels[0]
|
|
4890
|
+
- ranges[0] < d <= ranges[1] -> labels[1]
|
|
4891
|
+
- ...
|
|
4892
|
+
useShortestDistance : bool , optional
|
|
4893
|
+
If True, use Topology.ShortestDistance(topologyA, topologyB)
|
|
4894
|
+
if available. If False (or if that fails), fall back to the
|
|
4895
|
+
distance between the centroids of the two topologies.
|
|
4896
|
+
Default is True.
|
|
4897
|
+
tolerance : float , optional
|
|
4898
|
+
A small numeric tolerance used when comparing distances to
|
|
4899
|
+
range bounds. Default is 0.0001.
|
|
4900
|
+
silent : bool , optional
|
|
4901
|
+
If False, basic sanity-check warnings are printed.
|
|
4902
|
+
Default is False.
|
|
4903
|
+
|
|
4904
|
+
Returns
|
|
4905
|
+
-------
|
|
4906
|
+
graph : topologic_core.Graph
|
|
4907
|
+
A graph whose vertices correspond to the input topologies
|
|
4908
|
+
and whose edges connect topologies that fall within the
|
|
4909
|
+
supplied distance ranges. Each edge dictionary contains:
|
|
4910
|
+
- "distance" : float (actual distance)
|
|
4911
|
+
- "proximity" : str (label from `labels`)
|
|
4912
|
+
- "range_max" : float (upper bound used for the bin)
|
|
4913
|
+
- "source_index" : int
|
|
4914
|
+
- "target_index" : int
|
|
4915
|
+
|
|
4916
|
+
Notes
|
|
4917
|
+
-----
|
|
4918
|
+
- Complexity is approximately O(n log n + k) where k is the
|
|
4919
|
+
number of candidate pairs returned by the BVH.
|
|
4920
|
+
- BVH is used only as a broad-phase filter; exact distance
|
|
4921
|
+
tests still guarantee correctness with respect to `ranges`.
|
|
4922
|
+
"""
|
|
4923
|
+
|
|
4924
|
+
# Basic validation
|
|
4925
|
+
if not isinstance(topologies, list) or len(topologies) < 2:
|
|
4926
|
+
if not silent:
|
|
4927
|
+
print("Graph.BySpatialRelationships - Error: Need a list of at least two topologies.")
|
|
4928
|
+
return None
|
|
4929
|
+
|
|
4930
|
+
if not isinstance(ranges, list) or not isinstance(labels, list):
|
|
4931
|
+
if not silent:
|
|
4932
|
+
print("Graph.BySpatialRelationships - Error: 'proximityValues' and 'proximityLabels' must be lists.")
|
|
4933
|
+
return None
|
|
4934
|
+
|
|
4935
|
+
if len(ranges) == 0 or len(ranges) != len(labels):
|
|
4936
|
+
if not silent:
|
|
4937
|
+
print("Graph.BySpatialRelationships - Error: 'proximityValues' must be non-empty and "
|
|
4938
|
+
"have the same length as 'labels'.")
|
|
4939
|
+
return None
|
|
4940
|
+
|
|
4941
|
+
# Sort ranges and labels together (ascending by range)
|
|
4942
|
+
try:
|
|
4943
|
+
rl = sorted(zip(ranges, labels), key=lambda x: x[0])
|
|
4944
|
+
except Exception:
|
|
4945
|
+
if not silent:
|
|
4946
|
+
print("Graph.BySpatialRelationships - Error: Could not sort ranges; check they are numeric.")
|
|
4947
|
+
return None
|
|
4948
|
+
|
|
4949
|
+
sorted_ranges = [r for (r, _) in rl]
|
|
4950
|
+
sorted_labels = [lab for (_, lab) in rl]
|
|
4951
|
+
|
|
4952
|
+
max_range = sorted_ranges[-1]
|
|
4953
|
+
|
|
4954
|
+
# Precompute representative vertices (centroids) for each topology
|
|
4955
|
+
vertices = []
|
|
4956
|
+
n = len(topologies)
|
|
4957
|
+
for i, topo in enumerate(topologies):
|
|
4958
|
+
if not topo:
|
|
4959
|
+
if not silent:
|
|
4960
|
+
print(f"Graph.BySpatialRelationships - Warning: Ignoring None topology at index {i}.")
|
|
4961
|
+
vertices.append(None)
|
|
4962
|
+
continue
|
|
4963
|
+
try:
|
|
4964
|
+
c_vtx = Topology.Centroid(topo)
|
|
4965
|
+
except Exception:
|
|
4966
|
+
# Fallback if centroid fails
|
|
4967
|
+
if not silent:
|
|
4968
|
+
print(f"Graph.BySpatialRelationships - Error: Failed to compute centroid for topology {i}, "
|
|
4969
|
+
f"using origin as placeholder.")
|
|
4970
|
+
c_vtx = Vertex.ByCoordinates(0, 0, 0)
|
|
4971
|
+
|
|
4972
|
+
# Attach index dictionary to the vertex (not to the original topology)
|
|
4973
|
+
d_keys = ["index"]
|
|
4974
|
+
d_vals = [i]
|
|
4975
|
+
v_dict = Dictionary.ByKeysValues(d_keys, d_vals)
|
|
4976
|
+
c_vtx = Topology.SetDictionary(c_vtx, v_dict)
|
|
4977
|
+
vertices.append(c_vtx)
|
|
4978
|
+
|
|
4979
|
+
# Build BVH on the original topologies
|
|
4980
|
+
try:
|
|
4981
|
+
bvh = BVH.ByTopologies(topologies)
|
|
4982
|
+
except Exception as e:
|
|
4983
|
+
if not silent:
|
|
4984
|
+
print(f"Graph.BySpatialRelationships - Error: Failed to build BVH, falling back to O(n^2): {e}")
|
|
4985
|
+
# Fallback: use the non-BVH variant if you like,
|
|
4986
|
+
# or just early-return None. Here we just bail out.
|
|
4987
|
+
return None
|
|
4988
|
+
|
|
4989
|
+
# Map from topology identity to index for fast lookup
|
|
4990
|
+
id_to_index = {id(topo): i for i, topo in enumerate(topologies)}
|
|
4991
|
+
|
|
4992
|
+
# Helper to compute distance between two topologies
|
|
4993
|
+
def _distance(topoA, topoB, vA, vB):
|
|
4994
|
+
d_val = None
|
|
4995
|
+
if useShortestDistance:
|
|
4996
|
+
try:
|
|
4997
|
+
d_val = Topology.ShortestDistance(topoA, topoB)
|
|
4998
|
+
except Exception:
|
|
4999
|
+
d_val = None
|
|
5000
|
+
if d_val is None:
|
|
5001
|
+
try:
|
|
5002
|
+
d_val = Vertex.Distance(vA, vB)
|
|
5003
|
+
except Exception:
|
|
5004
|
+
d_val = None
|
|
5005
|
+
return d_val
|
|
5006
|
+
|
|
5007
|
+
edges = []
|
|
5008
|
+
|
|
5009
|
+
# Main loop: for each topology, query BVH for candidates within
|
|
5010
|
+
# a bounding box of size 2*max_range around its centroid.
|
|
5011
|
+
for i in range(n):
|
|
5012
|
+
topo_i = topologies[i]
|
|
5013
|
+
v_i = vertices[i]
|
|
5014
|
+
if topo_i is None or v_i is None:
|
|
5015
|
+
continue
|
|
5016
|
+
|
|
5017
|
+
# Build a query box centered at the centroid with size 2 * max_range
|
|
5018
|
+
try:
|
|
5019
|
+
query_box = Cell.Prism(
|
|
5020
|
+
origin=v_i,
|
|
5021
|
+
width=2 * max_range,
|
|
5022
|
+
length=2 * max_range,
|
|
5023
|
+
height=2 * max_range
|
|
5024
|
+
)
|
|
5025
|
+
except Exception as q_err:
|
|
5026
|
+
if not silent:
|
|
5027
|
+
print(f"Graph.BySpatialRelationships - Error: Failed to build query box for {i}: {q_err}")
|
|
5028
|
+
continue
|
|
5029
|
+
|
|
5030
|
+
try:
|
|
5031
|
+
candidates = BVH.Clashes(bvh, query_box)
|
|
5032
|
+
except Exception as c_err:
|
|
5033
|
+
if not silent:
|
|
5034
|
+
print(f"Graph.BySpatialRelationships - Error: BVH.Clashes failed for {i}: {c_err}")
|
|
5035
|
+
continue
|
|
5036
|
+
|
|
5037
|
+
if not candidates:
|
|
5038
|
+
continue
|
|
5039
|
+
|
|
5040
|
+
for cand in candidates:
|
|
5041
|
+
j = id_to_index.get(id(cand), None)
|
|
5042
|
+
if j is None:
|
|
5043
|
+
continue
|
|
5044
|
+
# Enforce i < j to avoid duplicate edges
|
|
5045
|
+
if j <= i:
|
|
5046
|
+
continue
|
|
5047
|
+
|
|
5048
|
+
topo_j = topologies[j]
|
|
5049
|
+
v_j = vertices[j]
|
|
5050
|
+
if topo_j is None or v_j is None:
|
|
5051
|
+
continue
|
|
5052
|
+
|
|
5053
|
+
# Compute exact distance
|
|
5054
|
+
d = _distance(topo_i, topo_j, v_i, v_j)
|
|
5055
|
+
if d is None:
|
|
5056
|
+
if not silent:
|
|
5057
|
+
print(f"Graph.BySpatialRelationships - Error: Could not compute distance between "
|
|
5058
|
+
f"{i} and {j}.")
|
|
5059
|
+
continue
|
|
5060
|
+
|
|
5061
|
+
# Skip if beyond max range (plus tolerance)
|
|
5062
|
+
if d > max_range + tolerance:
|
|
5063
|
+
continue
|
|
5064
|
+
|
|
5065
|
+
# Bin the distance into the appropriate range/label
|
|
5066
|
+
label = None
|
|
5067
|
+
range_max = None
|
|
5068
|
+
for r, lab in zip(sorted_ranges, sorted_labels):
|
|
5069
|
+
if d <= r + tolerance:
|
|
5070
|
+
label = lab
|
|
5071
|
+
range_max = r
|
|
5072
|
+
break
|
|
5073
|
+
|
|
5074
|
+
if label is None:
|
|
5075
|
+
continue
|
|
5076
|
+
|
|
5077
|
+
e_keys = ["distance", "proximity", "range_max", "source_index", "target_index"]
|
|
5078
|
+
e_values = [float(d), str(label), float(range_max), i, j]
|
|
5079
|
+
d = Dictionary.ByKeysValues(e_keys, e_values)
|
|
5080
|
+
edges.append(d)
|
|
5081
|
+
|
|
5082
|
+
return edges
|
|
5083
|
+
|
|
4853
5084
|
# ---------- BVH once ----------
|
|
4854
5085
|
bvh = BVH.ByTopologies(topologyList, silent=True)
|
|
4855
5086
|
|
|
@@ -4924,31 +5155,23 @@ class Graph:
|
|
|
4924
5155
|
))
|
|
4925
5156
|
|
|
4926
5157
|
# ---------- main loops (each unordered pair once) ----------
|
|
4927
|
-
|
|
4928
|
-
|
|
5158
|
+
if "proximity" in include:
|
|
5159
|
+
prox_dicts = _calc_proximity(topologies = topologyList,
|
|
5160
|
+
ranges = proximityValues,
|
|
5161
|
+
labels = proximityLabels,
|
|
5162
|
+
useShortestDistance = useShortestDistance,
|
|
5163
|
+
tolerance = tolerance,
|
|
5164
|
+
silent = silent)
|
|
5165
|
+
for prox_dict in prox_dicts:
|
|
5166
|
+
ai = Dictionary.ValueAtKey(prox_dict, "source_index")
|
|
5167
|
+
bj = Dictionary.ValueAtKey(prox_dict, "target_index")
|
|
5168
|
+
rel = Dictionary.ValueAtKey(prox_dict, "proximity")
|
|
5169
|
+
if (rel in proximityLabels):
|
|
5170
|
+
_add_edge(ai, bj, rel)
|
|
5171
|
+
|
|
4929
5172
|
for i, a in enumerate(topologyList):
|
|
4930
5173
|
candidates = []
|
|
4931
5174
|
ai = i
|
|
4932
|
-
if "proximity" in include:
|
|
4933
|
-
for j, b in enumerate(topologyList):
|
|
4934
|
-
bj = index_of.get(id(b))
|
|
4935
|
-
if i == bj or (i,bj) in used or (bj,i) in used:
|
|
4936
|
-
continue
|
|
4937
|
-
else:
|
|
4938
|
-
used.append((i,bj))
|
|
4939
|
-
used.append((bj,i))
|
|
4940
|
-
rel = Topology.SpatialRelationship( a,
|
|
4941
|
-
b,
|
|
4942
|
-
include=["proximity"],
|
|
4943
|
-
proximityValues = proximityValues,
|
|
4944
|
-
proximityLabels = proximityLabels,
|
|
4945
|
-
mantissa=mantissa,
|
|
4946
|
-
tolerance=tolerance,
|
|
4947
|
-
silent=True
|
|
4948
|
-
)
|
|
4949
|
-
rel_ok = (rel in proximityLabels)
|
|
4950
|
-
if rel_ok:
|
|
4951
|
-
_add_edge(ai, bj, rel)
|
|
4952
5175
|
candidates = BVH.Clashes(bvh, a) or []
|
|
4953
5176
|
if not candidates:
|
|
4954
5177
|
# If you want to connect "disjoint" to *all* non-candidates, that would be O(n) per i.
|
topologicpy/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.8.
|
|
1
|
+
__version__ = '0.8.89'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: topologicpy
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.89
|
|
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,7 +12,7 @@ topologicpy/Dictionary.py,sha256=goODXIM6AoC5Qn_d8LGc5pRoxZKgIWbkn3IOEbsQ4c4,449
|
|
|
12
12
|
topologicpy/Edge.py,sha256=aiRd1xZgG2GGYHxva0bM-kDy3AVmwGA_S2pMur8EeMg,74911
|
|
13
13
|
topologicpy/EnergyModel.py,sha256=MEai1GF1hINeH5bhclJj_lpMU3asFTvW2RlPm40GNj4,57794
|
|
14
14
|
topologicpy/Face.py,sha256=qAl36LcwiyRclMM2pI9NyWHzmgNlaykXiJx1wu10RmA,201317
|
|
15
|
-
topologicpy/Graph.py,sha256=
|
|
15
|
+
topologicpy/Graph.py,sha256=AfX4M_55zGBOm5RwpVEkr3JHv-hITFh9ckW1_jsLxFs,808052
|
|
16
16
|
topologicpy/Grid.py,sha256=3OsBMyHh4w8gpFOTMKHMNTpo62V0CwRNu5cwm87yDUA,18421
|
|
17
17
|
topologicpy/Helper.py,sha256=NsmMlbbKFPRX6jfoko-ZQVQ7MBsfVp9FD0ZvC2U7q-8,32002
|
|
18
18
|
topologicpy/Honeybee.py,sha256=dBk01jIvxjQMGHqSarM1Cukv16ot4Op7Dwlitn2OMoc,48990
|
|
@@ -31,9 +31,9 @@ topologicpy/Vector.py,sha256=pEC8YY3TeHGfGdeNgvdHjgMDwxGabp5aWjwYC1HSvMk,42236
|
|
|
31
31
|
topologicpy/Vertex.py,sha256=26TrlX9OCZUN-lMlZG3g4RHTWBqw69NW4AOEgRz_YMo,91269
|
|
32
32
|
topologicpy/Wire.py,sha256=au0ZkuuZgVzHYE5E1fRwflRT3win0yTivHKOhonAzUk,234116
|
|
33
33
|
topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
|
|
34
|
-
topologicpy/version.py,sha256=
|
|
35
|
-
topologicpy-0.8.
|
|
36
|
-
topologicpy-0.8.
|
|
37
|
-
topologicpy-0.8.
|
|
38
|
-
topologicpy-0.8.
|
|
39
|
-
topologicpy-0.8.
|
|
34
|
+
topologicpy/version.py,sha256=YhsAc7e0YNllH7mN5LamhWpA_q-FmHWAFByeNTu5xTk,23
|
|
35
|
+
topologicpy-0.8.89.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
|
36
|
+
topologicpy-0.8.89.dist-info/METADATA,sha256=55cNepIhl59TBcxtlcTmihxcT8dcait0fIEuKHXUq3A,10535
|
|
37
|
+
topologicpy-0.8.89.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
+
topologicpy-0.8.89.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
|
39
|
+
topologicpy-0.8.89.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|