topologicpy 0.7.73__py3-none-any.whl → 0.7.74__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 +607 -70
- topologicpy/Plotly.py +32 -146
- topologicpy/version.py +1 -1
- {topologicpy-0.7.73.dist-info → topologicpy-0.7.74.dist-info}/METADATA +1 -1
- {topologicpy-0.7.73.dist-info → topologicpy-0.7.74.dist-info}/RECORD +8 -8
- {topologicpy-0.7.73.dist-info → topologicpy-0.7.74.dist-info}/WHEEL +1 -1
- {topologicpy-0.7.73.dist-info → topologicpy-0.7.74.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.73.dist-info → topologicpy-0.7.74.dist-info}/top_level.txt +0 -0
topologicpy/Graph.py
CHANGED
@@ -1373,6 +1373,7 @@ class Graph:
|
|
1373
1373
|
print("Graph.BetweenessCentrality - Error: The input graph is not a valid graph. Returning None.")
|
1374
1374
|
return None
|
1375
1375
|
graphVertices = Graph.Vertices(graph)
|
1376
|
+
|
1376
1377
|
if not isinstance(vertices, list):
|
1377
1378
|
vertices = graphVertices
|
1378
1379
|
else:
|
@@ -1394,7 +1395,15 @@ class Graph:
|
|
1394
1395
|
if len(destinations) < 1:
|
1395
1396
|
print("Graph.BetweenessCentrality - Error: The input list of destinations does not contain valid vertices. Returning None.")
|
1396
1397
|
return None
|
1397
|
-
|
1398
|
+
graphEdges = Graph.Edges(graph)
|
1399
|
+
if len(graphEdges) == 0:
|
1400
|
+
print("Graph.BetweenessCentrality - Warning: The input graph is a null graph.")
|
1401
|
+
scores = [0 for t in vertices]
|
1402
|
+
for i, v in enumerate(vertices):
|
1403
|
+
d = Topology.Dictionary(v)
|
1404
|
+
d = Dictionary.SetValueAtKey(d, key, scores[i])
|
1405
|
+
v = Topology.SetDictionary(v, d)
|
1406
|
+
return scores
|
1398
1407
|
paths = []
|
1399
1408
|
try:
|
1400
1409
|
for so in tqdm(sources, desc="Computing Shortest Paths", leave=False):
|
@@ -2002,8 +2011,6 @@ class Graph:
|
|
2002
2011
|
for i, vertices, in enumerate(vertices_ds):
|
2003
2012
|
edges = edges_ds[i]
|
2004
2013
|
g = Graph.ByVerticesEdges(vertices, edges)
|
2005
|
-
temp_v = Graph.Vertices(g)
|
2006
|
-
temp_e = Graph.Edges(g)
|
2007
2014
|
if Topology.IsInstance(g, "Graph"):
|
2008
2015
|
if len(graphFeaturesKeys) == 0:
|
2009
2016
|
values = [graph_ids[i], graph_labels[i], graph_features[i]]
|
@@ -5622,19 +5629,35 @@ class Graph:
|
|
5622
5629
|
return False
|
5623
5630
|
|
5624
5631
|
@staticmethod
|
5625
|
-
def
|
5626
|
-
|
5627
|
-
|
5632
|
+
def Reshape(graph,
|
5633
|
+
shape="spring_2d",
|
5634
|
+
k=0.8, seed=None,
|
5635
|
+
iterations=50,
|
5636
|
+
rootVertex=None,
|
5637
|
+
size=1,
|
5638
|
+
sides=16,
|
5639
|
+
key="",
|
5640
|
+
tolerance=0.0001,
|
5641
|
+
silent=False):
|
5642
|
+
"""
|
5643
|
+
Reshapes the input graph according to the desired input shape parameter.
|
5628
5644
|
|
5629
5645
|
Parameters
|
5630
5646
|
----------
|
5631
5647
|
graph : topologic_core.Graph
|
5632
5648
|
The input graph.
|
5633
|
-
|
5634
|
-
The desired
|
5635
|
-
If set to '
|
5636
|
-
If set to '
|
5637
|
-
If set to '
|
5649
|
+
shape : str , optional
|
5650
|
+
The desired shape of the graph.
|
5651
|
+
If set to 'spring_2d' or 'spring_3d', the algorithm uses a simplified version of the Fruchterman-Reingold force-directed algorithm to distribute the vertices.
|
5652
|
+
If set to 'radial_2d', the nodes will be distributed along concentric circles in the XY plane.
|
5653
|
+
If set to 'tree_2d' or 'tree_3d', the nodes will be distributed using the Reingold-Tillford layout.
|
5654
|
+
If set to 'circle_2d', the nodes will be distributed on the cirumference of a segemented circles in the XY plane, based on the size and sides input parameter (radius=size/2).
|
5655
|
+
If set to 'line_2d', the nodes will be distributed on a line in the XY plane based on the size input parameter (length=size).
|
5656
|
+
If set to 'spehere_3d', the nodes will be distributed on the surface of a sphere based on the size input parameter raidus=size/2).
|
5657
|
+
If set to 'grid_2d', the nodes will be distributed on a grid in the XY plane with size based on the size input parameter (length=width=size).
|
5658
|
+
If set to 'grid_3d', the nodes will be distributed on a 3D cubic grid/matrix based on the size input parameter(width=length=height=size).
|
5659
|
+
If set to 'cluster_2d', or 'cluster_3d, the nodes will be clustered according to the 'key' input parameter. The overall radius of the cluster is determined by the size input parameter (radius = size/2)
|
5660
|
+
The default is 'spring_2d'.
|
5638
5661
|
k : float, optional
|
5639
5662
|
The desired spring constant to use for the attractive and repulsive forces. The default is 0.8.
|
5640
5663
|
seed : int , optional
|
@@ -5643,20 +5666,38 @@ class Graph:
|
|
5643
5666
|
The desired maximum number of iterations to solve the forces in the 'spring' mode. The default is 50.
|
5644
5667
|
rootVertex : topologic_core.Vertex , optional
|
5645
5668
|
The desired vertex to use as the root of the tree and radial layouts.
|
5646
|
-
|
5647
|
-
The desired
|
5669
|
+
sides : int , optional
|
5670
|
+
The desired number of sides of the circle layout option. The default is 16
|
5671
|
+
length : float, optional
|
5672
|
+
The desired horizontal length for the line layout option. The default is 1.0.
|
5673
|
+
key : string, optional
|
5674
|
+
The key under which to find the clustering value for the 'cluster_2d' and 'cluster_3d' options. The default is "".
|
5648
5675
|
tolerance : float , optional
|
5649
5676
|
The desired tolerance. The default is 0.0001.
|
5677
|
+
silent : bool , optional
|
5678
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
5650
5679
|
|
5651
5680
|
Returns
|
5652
5681
|
-------
|
5653
5682
|
topologic_core.Graph
|
5654
|
-
The
|
5683
|
+
The reshaped graph.
|
5655
5684
|
|
5656
5685
|
"""
|
5657
5686
|
from topologicpy.Vertex import Vertex
|
5687
|
+
from topologicpy.Edge import Edge
|
5688
|
+
from topologicpy.Wire import Wire
|
5689
|
+
from topologicpy.Face import Face
|
5690
|
+
from topologicpy.Graph import Graph
|
5691
|
+
from topologicpy.Grid import Grid
|
5692
|
+
from topologicpy.Helper import Helper
|
5693
|
+
from topologicpy.Vector import Vector
|
5658
5694
|
from topologicpy.Topology import Topology
|
5695
|
+
from topologicpy.Dictionary import Dictionary
|
5659
5696
|
import numpy as np
|
5697
|
+
import math
|
5698
|
+
from collections import defaultdict
|
5699
|
+
import random
|
5700
|
+
|
5660
5701
|
|
5661
5702
|
def buchheim(tree):
|
5662
5703
|
dt = firstwalk(_DrawTree(tree))
|
@@ -5770,7 +5811,7 @@ class Graph:
|
|
5770
5811
|
Returns:
|
5771
5812
|
A numpy array representing the adjacency matrix.
|
5772
5813
|
"""
|
5773
|
-
|
5814
|
+
|
5774
5815
|
# Get the number of nodes from the edge list.
|
5775
5816
|
flat_list = Helper.Flatten(edge_list)
|
5776
5817
|
flat_list = [x for x in flat_list if not x == None]
|
@@ -5810,15 +5851,226 @@ class Graph:
|
|
5810
5851
|
new_roots.extend(children)
|
5811
5852
|
old_roots = new_roots
|
5812
5853
|
return root, num_nodes
|
5854
|
+
|
5855
|
+
def generate_cubic_matrix(size, min_points):
|
5856
|
+
# Calculate the minimum points per axis to reach or exceed min_points in total
|
5857
|
+
points_per_axis = int(np.ceil(min_points ** (1/3)))
|
5858
|
+
|
5859
|
+
# Calculate the spacing based on the size and points per axis
|
5860
|
+
spacing = size / (points_per_axis - 1) if points_per_axis > 1 else 0
|
5861
|
+
|
5862
|
+
# Generate linearly spaced points from -size/2 to size/2 along each axis
|
5863
|
+
x = np.linspace(-size / 2, size / 2, points_per_axis)
|
5864
|
+
y = np.linspace(-size / 2, size / 2, points_per_axis)
|
5865
|
+
z = np.linspace(-size / 2, size / 2, points_per_axis)
|
5866
|
+
|
5867
|
+
# Create a meshgrid and stack them to get XYZ coordinates for each point
|
5868
|
+
X, Y, Z = np.meshgrid(x, y, z)
|
5869
|
+
points = np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T
|
5870
|
+
return points
|
5871
|
+
|
5872
|
+
def vertex_max_degree(graph, vertices):
|
5873
|
+
degrees = [Graph.VertexDegree(graph, vertex) for vertex in vertices]
|
5874
|
+
i = degrees.index(max(degrees))
|
5875
|
+
return vertices[i], i
|
5876
|
+
|
5877
|
+
def circle_layout_2d(graph, radius=0.5, sides=16):
|
5878
|
+
vertices = Graph.Vertices(graph)
|
5879
|
+
edges = Graph.Edges(graph)
|
5880
|
+
edge_dict = {}
|
5813
5881
|
|
5882
|
+
for i, edge in enumerate(edges):
|
5883
|
+
sv = Edge.StartVertex(edge)
|
5884
|
+
ev = Edge.EndVertex(edge)
|
5885
|
+
si = Vertex.Index(sv, vertices)
|
5886
|
+
ei = Vertex.Index(ev, vertices)
|
5887
|
+
edge_dict[str(si)+"_"+str(ei)] = i
|
5888
|
+
edge_dict[str(ei)+"_"+str(si)] = i
|
5889
|
+
n = len(vertices)
|
5890
|
+
c = Wire.Circle(radius=radius, sides=sides)
|
5891
|
+
c_vertices = []
|
5892
|
+
for i in range(n):
|
5893
|
+
u = i*(1/n)
|
5894
|
+
c_vertices.append(Wire.VertexByParameter(c, u))
|
5814
5895
|
|
5815
|
-
|
5816
|
-
|
5817
|
-
|
5818
|
-
|
5819
|
-
|
5820
|
-
|
5896
|
+
for i, c_v in enumerate(c_vertices):
|
5897
|
+
d = Topology.Dictionary(vertices[i])
|
5898
|
+
c_v = Topology.SetDictionary(c_v, d)
|
5899
|
+
adj_dict = Graph.AdjacencyDictionary(graph)
|
5900
|
+
keys = adj_dict.keys()
|
5901
|
+
|
5902
|
+
c_edges = []
|
5903
|
+
used = [[0] * n for _ in range(n)]
|
5904
|
+
for key in keys:
|
5905
|
+
x = int(key)
|
5906
|
+
adj_vertices = [int(v) for v in adj_dict[key]]
|
5907
|
+
for y in adj_vertices:
|
5908
|
+
if used[x][y] == 0:
|
5909
|
+
v1 = Vector.ByCoordinates(Vertex.X(c_vertices[x]), Vertex.Y(c_vertices[x]), Vertex.Z(c_vertices[x]))
|
5910
|
+
v2 = Vector.ByCoordinates(Vertex.X(c_vertices[y]), Vertex.Y(c_vertices[y]), Vertex.Z(c_vertices[y]))
|
5911
|
+
ang1 = Vector.CompassAngle(v1, [0,1,0])
|
5912
|
+
ang2 = Vector.CompassAngle(v2, [0,1,0])
|
5913
|
+
if ang2-ang1 < 180:
|
5914
|
+
e = Edge.ByVertices(c_vertices[x], c_vertices[y])
|
5915
|
+
else:
|
5916
|
+
e = Edge.ByVertices(c_vertices[y], c_vertices[x])
|
5917
|
+
orig_edge_index = edge_dict.get(str(x)+"_"+str(y), edge_dict.get(str(y)+"_"+str(x), None))
|
5918
|
+
if orig_edge_index:
|
5919
|
+
d = Topology.Dictionary(edges[orig_edge_index])
|
5920
|
+
e = Topology.SetDictionary(e, d)
|
5921
|
+
c_edges.append(e)
|
5922
|
+
used[x][y] = 1
|
5923
|
+
used[y][x] = 1
|
5924
|
+
new_g = Graph.ByVerticesEdges(c_vertices, c_edges)
|
5925
|
+
return new_g
|
5926
|
+
|
5927
|
+
def cluster_layout_2d(graph, key, radius=0.5):
|
5928
|
+
|
5929
|
+
d = Graph.MeshData(graph)
|
5930
|
+
edges = d['edges']
|
5931
|
+
v_dicts = d['vertexDictionaries']
|
5932
|
+
e_dicts = d['edgeDictionaries']
|
5933
|
+
vertices = Graph.Vertices(graph)
|
5934
|
+
# Step 1: Group objects by key value while remembering their original indices
|
5935
|
+
grouped_objects = defaultdict(list)
|
5936
|
+
object_indices = [] # Stores original indices of objects in the order they were grouped
|
5937
|
+
|
5938
|
+
for idx, obj in enumerate(vertices):
|
5939
|
+
d = Topology.Dictionary(obj)
|
5940
|
+
value = Dictionary.ValueAtKey(d, key)
|
5941
|
+
grouped_objects[value].append((obj, idx))
|
5942
|
+
object_indices.append((value, idx))
|
5943
|
+
|
5944
|
+
# Step 2: Compute cluster centers on the circumference of a circle
|
5945
|
+
cluster_centers = {}
|
5946
|
+
num_clusters = len(grouped_objects)
|
5947
|
+
|
5948
|
+
# Function to generate cluster center on the circle's circumference
|
5949
|
+
def generate_cluster_center(index, total_clusters, circle_radius):
|
5950
|
+
# Distribute cluster centers evenly along the circumference of a circle
|
5951
|
+
angle = (2 * math.pi * index) / total_clusters # Equal angle separation
|
5952
|
+
x = circle_radius * math.cos(angle)
|
5953
|
+
y = circle_radius * math.sin(angle)
|
5954
|
+
return (x, y)
|
5955
|
+
|
5956
|
+
# Step 3: Compute vertices for each cluster
|
5957
|
+
object_positions = [None] * len(vertices) # Placeholder list for ordered vertices
|
5958
|
+
cluster_index = 0
|
5959
|
+
|
5960
|
+
for value, objs in grouped_objects.items():
|
5961
|
+
intra_cluster_radius = radius*(len(objs)/len(vertices) + 0.1)
|
5962
|
+
# Determine the center of the current cluster
|
5963
|
+
if value not in cluster_centers:
|
5964
|
+
cluster_center = generate_cluster_center(cluster_index, num_clusters, radius)
|
5965
|
+
cluster_centers[value] = cluster_center
|
5966
|
+
cluster_index += 1
|
5967
|
+
else:
|
5968
|
+
cluster_center = cluster_centers[value]
|
5969
|
+
|
5970
|
+
# Step 4: Place objects randomly around the cluster center
|
5971
|
+
for obj, original_index in objs:
|
5972
|
+
# Randomly place the object within the intra-cluster circle
|
5973
|
+
r = intra_cluster_radius * math.sqrt(random.random()) # Random distance with sqrt for uniform distribution
|
5974
|
+
angle = random.uniform(0, 2 * math.pi) # Random angle
|
5975
|
+
|
5976
|
+
# Polar coordinates to Cartesian for local positioning
|
5977
|
+
x = cluster_center[0] + r * math.cos(angle)
|
5978
|
+
y = cluster_center[1] + r * math.sin(angle)
|
5979
|
+
|
5980
|
+
# Save the coordinates in the correct order
|
5981
|
+
object_positions[original_index] = [x, y]
|
5982
|
+
|
5983
|
+
positions = [[p[0], p[1], 0] for p in object_positions]
|
5984
|
+
new_g = Graph.ByMeshData(positions, edges, v_dicts, e_dicts, tolerance=0.001)
|
5985
|
+
return new_g
|
5821
5986
|
|
5987
|
+
def cluster_layout_3d(graph, key, radius=0.5):
|
5988
|
+
d = Graph.MeshData(graph)
|
5989
|
+
edges = d['edges']
|
5990
|
+
v_dicts = d['vertexDictionaries']
|
5991
|
+
e_dicts = d['edgeDictionaries']
|
5992
|
+
vertices = Graph.Vertices(graph)
|
5993
|
+
|
5994
|
+
# Step 1: Group objects by key value while remembering their original indices
|
5995
|
+
grouped_objects = defaultdict(list)
|
5996
|
+
object_indices = [] # Stores original indices of objects in the order they were grouped
|
5997
|
+
|
5998
|
+
for idx, obj in enumerate(vertices):
|
5999
|
+
d = Topology.Dictionary(obj)
|
6000
|
+
value = Dictionary.ValueAtKey(d, key)
|
6001
|
+
grouped_objects[value].append((obj, idx))
|
6002
|
+
object_indices.append((value, idx))
|
6003
|
+
|
6004
|
+
# Step 2: Compute cluster centers on the surface of a sphere
|
6005
|
+
cluster_centers = {}
|
6006
|
+
num_clusters = len(grouped_objects)
|
6007
|
+
|
6008
|
+
# Function to generate cluster center on the surface of a sphere
|
6009
|
+
def generate_cluster_center(index, total_clusters, sphere_radius):
|
6010
|
+
# Use a spiral algorithm to distribute cluster centers evenly on a sphere's surface
|
6011
|
+
phi = math.acos(1 - 2 * (index + 0.5) / total_clusters) # Inclination angle
|
6012
|
+
theta = math.pi * (1 + 5**0.5) * index # Azimuthal angle (Golden angle)
|
6013
|
+
|
6014
|
+
x = sphere_radius * math.sin(phi) * math.cos(theta)
|
6015
|
+
y = sphere_radius * math.sin(phi) * math.sin(theta)
|
6016
|
+
z = sphere_radius * math.cos(phi)
|
6017
|
+
return (x, y, z)
|
6018
|
+
|
6019
|
+
# Step 3: Compute vertices for each cluster
|
6020
|
+
object_positions = [None] * len(vertices) # Placeholder list for ordered vertices
|
6021
|
+
cluster_index = 0
|
6022
|
+
|
6023
|
+
for value, objs in grouped_objects.items():
|
6024
|
+
# Determine the center of the current cluster
|
6025
|
+
if value not in cluster_centers:
|
6026
|
+
cluster_center = generate_cluster_center(cluster_index, num_clusters, radius)
|
6027
|
+
cluster_centers[value] = cluster_center
|
6028
|
+
cluster_index += 1
|
6029
|
+
else:
|
6030
|
+
cluster_center = cluster_centers[value]
|
6031
|
+
|
6032
|
+
intra_cluster_radius = radius*(len(objs)/len(vertices) + 0.1)
|
6033
|
+
|
6034
|
+
# Step 4: Place objects randomly within the cluster's spherical volume
|
6035
|
+
for obj, original_index in objs:
|
6036
|
+
# Randomly place the object within the intra-cluster sphere
|
6037
|
+
u = random.random()
|
6038
|
+
v = random.random()
|
6039
|
+
r = intra_cluster_radius * (u ** (1/3)) # Random distance with cube root for uniform distribution
|
6040
|
+
|
6041
|
+
theta = 2 * math.pi * v # Random azimuthal angle
|
6042
|
+
phi = math.acos(2 * u - 1) # Random polar angle
|
6043
|
+
|
6044
|
+
# Spherical to Cartesian for local positioning
|
6045
|
+
x = cluster_center[0] + r * math.sin(phi) * math.cos(theta)
|
6046
|
+
y = cluster_center[1] + r * math.sin(phi) * math.sin(theta)
|
6047
|
+
z = cluster_center[2] + r * math.cos(phi)
|
6048
|
+
|
6049
|
+
# Save the coordinates in the correct order
|
6050
|
+
object_positions[original_index] = [x, y, z]
|
6051
|
+
|
6052
|
+
positions = [[p[0], p[1], p[2]] for p in object_positions]
|
6053
|
+
new_g = Graph.ByMeshData(positions, edges, v_dicts, e_dicts, tolerance=0.001)
|
6054
|
+
return new_g
|
6055
|
+
|
6056
|
+
def sphere_layout_3d(graph, radius=0.5):
|
6057
|
+
def points_on_sphere(n, r):
|
6058
|
+
points = []
|
6059
|
+
phi = math.pi * (3. - math.sqrt(5.)) # Golden angle in radians
|
6060
|
+
|
6061
|
+
for i in range(n):
|
6062
|
+
y = 1 - (i / float(n - 1)) * 2 # y goes from 1 to -1
|
6063
|
+
radius = math.sqrt(1 - y * y) # radius at y
|
6064
|
+
|
6065
|
+
theta = phi * i # Golden angle increment
|
6066
|
+
|
6067
|
+
x = math.cos(theta) * radius * r
|
6068
|
+
z = math.sin(theta) * radius * r
|
6069
|
+
y *= r
|
6070
|
+
|
6071
|
+
points.append([x, y, z])
|
6072
|
+
return points
|
6073
|
+
|
5822
6074
|
vertices = Graph.Vertices(graph)
|
5823
6075
|
edges = Graph.Edges(graph)
|
5824
6076
|
edge_dict = {}
|
@@ -5830,16 +6082,15 @@ class Graph:
|
|
5830
6082
|
ei = Vertex.Index(ev, vertices)
|
5831
6083
|
edge_dict[str(si)+"_"+str(ei)] = i
|
5832
6084
|
edge_dict[str(ei)+"_"+str(si)] = i
|
5833
|
-
|
5834
6085
|
n = len(vertices)
|
5835
|
-
|
5836
|
-
c_vertices =
|
5837
|
-
|
6086
|
+
c_points = points_on_sphere(n, r=radius)
|
6087
|
+
c_vertices = [Vertex.ByCoordinates(coord) for coord in c_points]
|
5838
6088
|
for i, c_v in enumerate(c_vertices):
|
5839
6089
|
d = Topology.Dictionary(vertices[i])
|
5840
6090
|
c_v = Topology.SetDictionary(c_v, d)
|
5841
6091
|
adj_dict = Graph.AdjacencyDictionary(graph)
|
5842
6092
|
keys = adj_dict.keys()
|
6093
|
+
|
5843
6094
|
c_edges = []
|
5844
6095
|
used = [[0] * n for _ in range(n)]
|
5845
6096
|
for key in keys:
|
@@ -5855,6 +6106,94 @@ class Graph:
|
|
5855
6106
|
e = Edge.ByVertices(c_vertices[x], c_vertices[y])
|
5856
6107
|
else:
|
5857
6108
|
e = Edge.ByVertices(c_vertices[y], c_vertices[x])
|
6109
|
+
orig_edge_index = edge_dict.get(str(x)+"_"+str(y), edge_dict.get(str(y)+"_"+str(x), None))
|
6110
|
+
if orig_edge_index:
|
6111
|
+
d = Topology.Dictionary(edges[orig_edge_index])
|
6112
|
+
e = Topology.SetDictionary(e, d)
|
6113
|
+
c_edges.append(e)
|
6114
|
+
used[x][y] = 1
|
6115
|
+
used[y][x] = 1
|
6116
|
+
new_g = Graph.ByVerticesEdges(c_vertices, c_edges)
|
6117
|
+
return new_g
|
6118
|
+
|
6119
|
+
def grid_layout_2d(graph, size=1):
|
6120
|
+
vertices = Graph.Vertices(graph)
|
6121
|
+
n = len(vertices)
|
6122
|
+
u = int(math.sqrt(n))
|
6123
|
+
if u*u < n:
|
6124
|
+
u += 1
|
6125
|
+
u_range = [t/(u-1) for t in range(u)]
|
6126
|
+
edges = Graph.Edges(graph)
|
6127
|
+
edge_dict = {}
|
6128
|
+
|
6129
|
+
for i, edge in enumerate(edges):
|
6130
|
+
sv = Edge.StartVertex(edge)
|
6131
|
+
ev = Edge.EndVertex(edge)
|
6132
|
+
si = Vertex.Index(sv, vertices)
|
6133
|
+
ei = Vertex.Index(ev, vertices)
|
6134
|
+
edge_dict[str(si)+"_"+str(ei)] = i
|
6135
|
+
edge_dict[str(ei)+"_"+str(si)] = i
|
6136
|
+
f = Face.Rectangle(width=size, length=size)
|
6137
|
+
c = Grid.VerticesByParameters(face=f, uRange=u_range, vRange=u_range)
|
6138
|
+
c_vertices = Topology.Vertices(c)[:len(vertices)]
|
6139
|
+
|
6140
|
+
for i, c_v in enumerate(c_vertices):
|
6141
|
+
d = Topology.Dictionary(vertices[i])
|
6142
|
+
c_v = Topology.SetDictionary(c_v, d)
|
6143
|
+
adj_dict = Graph.AdjacencyDictionary(graph)
|
6144
|
+
keys = adj_dict.keys()
|
6145
|
+
|
6146
|
+
c_edges = []
|
6147
|
+
used = [[0] * n for _ in range(n)]
|
6148
|
+
for key in keys:
|
6149
|
+
x = int(key)
|
6150
|
+
adj_vertices = [int(v) for v in adj_dict[key]]
|
6151
|
+
for y in adj_vertices:
|
6152
|
+
if used[x][y] == 0:
|
6153
|
+
e = Edge.ByVertices(c_vertices[x], c_vertices[y])
|
6154
|
+
orig_edge_index = edge_dict.get(str(x)+"_"+str(y), edge_dict.get(str(y)+"_"+str(x), None))
|
6155
|
+
if orig_edge_index:
|
6156
|
+
d = Topology.Dictionary(edges[orig_edge_index])
|
6157
|
+
e = Topology.SetDictionary(e, d)
|
6158
|
+
c_edges.append(e)
|
6159
|
+
used[x][y] = 1
|
6160
|
+
used[y][x] = 1
|
6161
|
+
new_g = Graph.ByVerticesEdges(c_vertices, c_edges)
|
6162
|
+
return new_g
|
6163
|
+
|
6164
|
+
def line_layout_2d(graph, length=1):
|
6165
|
+
vertices = Graph.Vertices(graph)
|
6166
|
+
edges = Graph.Edges(graph)
|
6167
|
+
edge_dict = {}
|
6168
|
+
|
6169
|
+
for i, edge in enumerate(edges):
|
6170
|
+
sv = Edge.StartVertex(edge)
|
6171
|
+
ev = Edge.EndVertex(edge)
|
6172
|
+
si = Vertex.Index(sv, vertices)
|
6173
|
+
ei = Vertex.Index(ev, vertices)
|
6174
|
+
edge_dict[str(si)+"_"+str(ei)] = i
|
6175
|
+
edge_dict[str(ei)+"_"+str(si)] = i
|
6176
|
+
|
6177
|
+
n = len(vertices)
|
6178
|
+
c = Wire.Line(length=length, sides=n-1)
|
6179
|
+
c_vertices = Topology.Vertices(c)
|
6180
|
+
|
6181
|
+
for i, c_v in enumerate(c_vertices):
|
6182
|
+
d = Topology.Dictionary(vertices[i])
|
6183
|
+
c_v = Topology.SetDictionary(c_v, d)
|
6184
|
+
adj_dict = Graph.AdjacencyDictionary(graph)
|
6185
|
+
keys = adj_dict.keys()
|
6186
|
+
c_edges = []
|
6187
|
+
used = [[0] * n for _ in range(n)]
|
6188
|
+
for key in keys:
|
6189
|
+
x = int(key)
|
6190
|
+
adj_vertices = [int(v) for v in adj_dict[key]]
|
6191
|
+
for y in adj_vertices:
|
6192
|
+
if used[x][y] == 0:
|
6193
|
+
if Vertex.X(c_vertices[x]) < Vertex.X(c_vertices[y]):
|
6194
|
+
e = Edge.ByVertices(c_vertices[x], c_vertices[y])
|
6195
|
+
else:
|
6196
|
+
e = Edge.ByVertices(c_vertices[y], c_vertices[x])
|
5858
6197
|
|
5859
6198
|
orig_edge_index = edge_dict[str(x)+"_"+str(y)]
|
5860
6199
|
d = Topology.Dictionary(edges[orig_edge_index])
|
@@ -5865,10 +6204,51 @@ class Graph:
|
|
5865
6204
|
new_g = Graph.ByVerticesEdges(c_vertices, c_edges)
|
5866
6205
|
return new_g
|
5867
6206
|
|
5868
|
-
def
|
5869
|
-
|
5870
|
-
|
6207
|
+
def grid_layout_3d(graph, size=1):
|
6208
|
+
vertices = Graph.Vertices(graph)
|
6209
|
+
n = len(vertices)
|
6210
|
+
edges = Graph.Edges(graph)
|
6211
|
+
edge_dict = {}
|
5871
6212
|
|
6213
|
+
for i, edge in enumerate(edges):
|
6214
|
+
sv = Edge.StartVertex(edge)
|
6215
|
+
ev = Edge.EndVertex(edge)
|
6216
|
+
si = Vertex.Index(sv, vertices)
|
6217
|
+
ei = Vertex.Index(ev, vertices)
|
6218
|
+
edge_dict[str(si)+"_"+str(ei)] = i
|
6219
|
+
edge_dict[str(ei)+"_"+str(si)] = i
|
6220
|
+
c_coords = generate_cubic_matrix(size, n)
|
6221
|
+
c_vertices = [Vertex.ByCoordinates(list(coord)) for coord in c_coords[:n]]
|
6222
|
+
|
6223
|
+
for i, c_v in enumerate(c_vertices):
|
6224
|
+
d = Topology.Dictionary(vertices[i])
|
6225
|
+
c_v = Topology.SetDictionary(c_v, d)
|
6226
|
+
adj_dict = Graph.AdjacencyDictionary(graph)
|
6227
|
+
keys = adj_dict.keys()
|
6228
|
+
|
6229
|
+
c_edges = []
|
6230
|
+
used = [[0] * n for _ in range(n)]
|
6231
|
+
for key in keys:
|
6232
|
+
x = int(key)
|
6233
|
+
adj_vertices = [int(v) for v in adj_dict[key]]
|
6234
|
+
for y in adj_vertices:
|
6235
|
+
if used[x][y] == 0:
|
6236
|
+
e = Edge.ByVertices(c_vertices[x], c_vertices[y])
|
6237
|
+
orig_edge_index = edge_dict.get(str(x)+"_"+str(y), edge_dict.get(str(y)+"_"+str(x), None))
|
6238
|
+
if orig_edge_index:
|
6239
|
+
d = Topology.Dictionary(edges[orig_edge_index])
|
6240
|
+
e = Topology.SetDictionary(e, d)
|
6241
|
+
c_edges.append(e)
|
6242
|
+
used[x][y] = 1
|
6243
|
+
used[y][x] = 1
|
6244
|
+
new_g = Graph.ByVerticesEdges(c_vertices, c_edges)
|
6245
|
+
return new_g
|
6246
|
+
|
6247
|
+
def spring_layout_2d(edge_list, iterations=500, k=None, seed=None):
|
6248
|
+
# Compute the layout of a graph using the Fruchterman-Reingold algorithm
|
6249
|
+
# with a force-directed
|
6250
|
+
|
6251
|
+
iterations = max(1, iterations)
|
5872
6252
|
adj_matrix = edge_list_to_adjacency_matrix(edge_list)
|
5873
6253
|
# Set the random seed
|
5874
6254
|
if seed is not None:
|
@@ -5916,7 +6296,60 @@ class Graph:
|
|
5916
6296
|
|
5917
6297
|
return pos
|
5918
6298
|
|
5919
|
-
def
|
6299
|
+
def spring_layout_3d(edge_list, iterations=500, k=None, seed=None):
|
6300
|
+
# Compute the layout of a graph using the Fruchterman-Reingold algorithm
|
6301
|
+
# with a force-directed layout approach.
|
6302
|
+
|
6303
|
+
iterations = max(1,iterations)
|
6304
|
+
|
6305
|
+
adj_matrix = edge_list_to_adjacency_matrix(edge_list)
|
6306
|
+
# Set the random seed
|
6307
|
+
if seed is not None:
|
6308
|
+
np.random.seed(seed)
|
6309
|
+
|
6310
|
+
# Set the optimal distance between nodes
|
6311
|
+
if k is None or k <= 0:
|
6312
|
+
k = np.cbrt(1.0 / adj_matrix.shape[0]) # Adjusted for 3D
|
6313
|
+
|
6314
|
+
# Initialize the positions of the nodes randomly in 3D
|
6315
|
+
pos = np.random.rand(adj_matrix.shape[0], 3)
|
6316
|
+
|
6317
|
+
# Compute the initial temperature
|
6318
|
+
t = 0.1 * np.max(pos)
|
6319
|
+
|
6320
|
+
# Compute the cooling factor
|
6321
|
+
cooling_factor = t / iterations
|
6322
|
+
|
6323
|
+
# Iterate over the specified number of iterations
|
6324
|
+
for i in range(iterations):
|
6325
|
+
# Compute the distance between each pair of nodes
|
6326
|
+
delta = pos[:, np.newaxis, :] - pos[np.newaxis, :, :]
|
6327
|
+
distance = np.linalg.norm(delta, axis=-1)
|
6328
|
+
|
6329
|
+
# Avoid division by zero
|
6330
|
+
distance = np.where(distance == 0, 0.1, distance)
|
6331
|
+
|
6332
|
+
# Compute the repulsive force between each pair of nodes
|
6333
|
+
repulsive_force = k ** 2 / distance ** 2
|
6334
|
+
|
6335
|
+
# Compute the attractive force between each pair of adjacent nodes
|
6336
|
+
attractive_force = adj_matrix * distance / k
|
6337
|
+
|
6338
|
+
# Compute the total force acting on each node
|
6339
|
+
force = np.sum((repulsive_force - attractive_force)[:, :, np.newaxis] * delta, axis=1)
|
6340
|
+
|
6341
|
+
# Compute the displacement of each node
|
6342
|
+
displacement = t * force / np.linalg.norm(force, axis=1)[:, np.newaxis]
|
6343
|
+
|
6344
|
+
# Update the positions of the nodes
|
6345
|
+
pos += displacement
|
6346
|
+
|
6347
|
+
# Cool the temperature
|
6348
|
+
t -= cooling_factor
|
6349
|
+
|
6350
|
+
return pos
|
6351
|
+
|
6352
|
+
def tree_layout_2d(edge_list, root_index=0):
|
5920
6353
|
|
5921
6354
|
root, num_nodes = tree_from_edge_list(edge_list, root_index)
|
5922
6355
|
dt = buchheim(root)
|
@@ -5942,8 +6375,46 @@ class Graph:
|
|
5942
6375
|
pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1]
|
5943
6376
|
|
5944
6377
|
return pos
|
6378
|
+
|
6379
|
+
def tree_layout_3d(edge_list, root_index=0, base_radius=1.0, radius_factor=1.5):
|
6380
|
+
root, num_nodes = tree_from_edge_list(edge_list, root_index)
|
6381
|
+
dt = buchheim(root)
|
6382
|
+
pos = np.zeros((num_nodes, 3)) # Initialize 3D positions
|
6383
|
+
|
6384
|
+
pos[int(dt.tree.node), 0] = dt.x
|
6385
|
+
pos[int(dt.tree.node), 1] = dt.y
|
6386
|
+
pos[int(dt.tree.node), 2] = 0 # Root at z = 0
|
6387
|
+
|
6388
|
+
old_roots = [dt]
|
6389
|
+
new_roots = []
|
6390
|
+
depth = 1 # Start at depth level 1 for children
|
6391
|
+
|
6392
|
+
while len(old_roots) > 0:
|
6393
|
+
new_roots = []
|
6394
|
+
for temp_root in old_roots:
|
6395
|
+
children = temp_root.children
|
6396
|
+
num_children = len(children)
|
6397
|
+
if num_children > 0:
|
6398
|
+
# Increase the radius dynamically based on the number of children
|
6399
|
+
dynamic_radius = base_radius + (num_children - 1) * radius_factor * depth
|
6400
|
+
|
6401
|
+
angle_step = 2 * np.pi / num_children # Angle between each child
|
6402
|
+
for i, child in enumerate(children):
|
6403
|
+
angle = i * angle_step
|
6404
|
+
pos[int(child.tree.node), 0] = pos[int(temp_root.tree.node), 0] + dynamic_radius * np.cos(angle) # X position
|
6405
|
+
pos[int(child.tree.node), 1] = pos[int(temp_root.tree.node), 1] + dynamic_radius * np.sin(angle) # Y position
|
6406
|
+
pos[int(child.tree.node), 2] = -dynamic_radius*depth # Z-coordinate based on depth
|
6407
|
+
|
6408
|
+
new_roots.extend(children)
|
6409
|
+
|
6410
|
+
old_roots = new_roots
|
6411
|
+
depth += 1 # Increment depth for the next level
|
6412
|
+
|
6413
|
+
pos[:, 1] = np.max(pos[:, 1]) - pos[:, 1] # Flip y-coordinates if necessary
|
5945
6414
|
|
5946
|
-
|
6415
|
+
return pos
|
6416
|
+
|
6417
|
+
def radial_layout_2d(edge_list, root_index=0):
|
5947
6418
|
root, num_nodes = tree_from_edge_list(edge_list, root_index)
|
5948
6419
|
dt = buchheim(root)
|
5949
6420
|
pos = np.zeros((num_nodes, 2))
|
@@ -5984,53 +6455,119 @@ class Graph:
|
|
5984
6455
|
|
5985
6456
|
return new_pos
|
5986
6457
|
|
5987
|
-
def
|
6458
|
+
def spherical_layout_3d(edge_list, root_index=0):
|
6459
|
+
root, num_nodes = tree_from_edge_list(edge_list, root_index)
|
6460
|
+
dt = buchheim(root)
|
5988
6461
|
|
5989
|
-
|
5990
|
-
|
5991
|
-
elif layout == 'spring':
|
5992
|
-
return spring_layout(edge_list, k=k, seed=seed, iterations=iterations)
|
5993
|
-
elif layout == 'radial':
|
5994
|
-
return radial_layout(edge_list, root_index=root_index)
|
5995
|
-
else:
|
5996
|
-
raise NotImplementedError(f"{layout} is not implemented yet. Please choose from ['radial', 'spring', 'tree']")
|
6462
|
+
# Initialize positions with 3 columns for x, y, z coordinates
|
6463
|
+
pos = np.zeros((num_nodes, 3))
|
5997
6464
|
|
5998
|
-
|
5999
|
-
|
6000
|
-
|
6001
|
-
|
6465
|
+
# Set initial coordinates and depth for the root node
|
6466
|
+
pos[int(dt.tree.node), 0] = dt.x
|
6467
|
+
pos[int(dt.tree.node), 1] = dt.y
|
6468
|
+
depth = np.zeros(num_nodes) # To track the depth of each node
|
6469
|
+
|
6470
|
+
old_roots = [(dt, 0)] # Store nodes with their depth levels
|
6471
|
+
new_roots = []
|
6472
|
+
|
6473
|
+
while len(old_roots) > 0:
|
6474
|
+
new_roots = []
|
6475
|
+
for temp_root, current_depth in old_roots:
|
6476
|
+
children = temp_root.children
|
6477
|
+
for child in children:
|
6478
|
+
node_index = int(child.tree.node)
|
6479
|
+
pos[node_index, 0] = child.x
|
6480
|
+
pos[node_index, 1] = child.y
|
6481
|
+
depth[node_index] = current_depth + 1 # Increase depth for children
|
6482
|
+
new_roots.extend([(child, current_depth + 1) for child in children])
|
6483
|
+
|
6484
|
+
old_roots = new_roots
|
6485
|
+
|
6486
|
+
# Normalize x and y coordinates to a [0, 1] range
|
6487
|
+
pos[:, 0] = pos[:, 0] - np.min(pos[:, 0])
|
6488
|
+
pos[:, 1] = pos[:, 1] - np.min(pos[:, 1])
|
6489
|
+
|
6490
|
+
pos[:, 0] = pos[:, 0] / np.max(pos[:, 0])
|
6491
|
+
pos[:, 0] = pos[:, 0] - pos[:, 0][root_index]
|
6492
|
+
|
6493
|
+
range_ = np.max(pos[:, 0]) - np.min(pos[:, 0])
|
6494
|
+
pos[:, 0] = pos[:, 0] / range_
|
6495
|
+
|
6496
|
+
pos[:, 0] = pos[:, 0] * np.pi * 1.98 # Longitude (azimuthal angle)
|
6497
|
+
pos[:, 1] = pos[:, 1] / np.max(pos[:, 1]) # Latitude (polar angle)
|
6498
|
+
|
6499
|
+
# Convert the 2D coordinates to 3D spherical coordinates
|
6500
|
+
new_pos = np.zeros((num_nodes, 3)) # 3D position array
|
6501
|
+
base_radius = 1 # Base radius for the first sphere
|
6502
|
+
radius_increment = 1.3 # Increase in radius per depth level
|
6503
|
+
|
6504
|
+
# pos[:, 0] is the azimuth angle (longitude) in radians
|
6505
|
+
# pos[:, 1] is the polar angle (latitude) in radians (adjusted to go from 0 to pi)
|
6506
|
+
polar_angle = pos[:, 1] * np.pi # Scaling to go from 0 to pi
|
6507
|
+
azimuth_angle = pos[:, 0]
|
6508
|
+
|
6509
|
+
# Calculate the 3D Cartesian coordinates based on depth
|
6510
|
+
for i in range(num_nodes):
|
6511
|
+
r = base_radius + depth[i] * radius_increment # Radius grows with depth
|
6512
|
+
new_pos[i, 0] = r * np.sin(polar_angle[i]) * np.cos(azimuth_angle[i]) # X = r * sin(θ) * cos(φ)
|
6513
|
+
new_pos[i, 1] = r * np.sin(polar_angle[i]) * np.sin(azimuth_angle[i]) # Y = r * sin(θ) * sin(φ)
|
6514
|
+
new_pos[i, 2] = r * np.cos(polar_angle[i]) # Z = r * cos(θ)
|
6515
|
+
|
6516
|
+
return new_pos
|
6002
6517
|
|
6003
6518
|
if not Topology.IsInstance(graph, "Graph"):
|
6004
|
-
|
6519
|
+
if not silent:
|
6520
|
+
print("Graph.Flatten - Error: The input graph is not a valid topologic graph. Returning None.")
|
6005
6521
|
return None
|
6006
6522
|
|
6007
|
-
if 'circ' in
|
6008
|
-
|
6009
|
-
|
6010
|
-
|
6011
|
-
|
6012
|
-
|
6013
|
-
|
6014
|
-
|
6015
|
-
|
6016
|
-
|
6017
|
-
|
6018
|
-
|
6019
|
-
|
6020
|
-
|
6021
|
-
root_index = 0
|
6022
|
-
if 'rad' in layout.lower():
|
6023
|
-
positions = radial_layout(edges, root_index=root_index)
|
6024
|
-
elif 'spring' in layout.lower():
|
6025
|
-
positions = spring_layout(edges, k=k, seed=seed, iterations=iterations)
|
6026
|
-
elif 'tree' in layout.lower():
|
6027
|
-
positions = tree_layout(edges, root_index=root_index)
|
6523
|
+
if 'circ' in shape.lower():
|
6524
|
+
return circle_layout_2d(graph, radius=size/2, sides=sides)
|
6525
|
+
elif 'lin' in shape.lower():
|
6526
|
+
return line_layout_2d(graph, length=size)
|
6527
|
+
elif 'grid' in shape.lower() and '2d' in shape.lower():
|
6528
|
+
return grid_layout_2d(graph, size=size)
|
6529
|
+
elif 'sphere' == shape.lower() and '3d' in shape.lower():
|
6530
|
+
return sphere_layout_3d(graph, radius=size/2)
|
6531
|
+
elif 'grid' in shape.lower() and '3d' in shape.lower():
|
6532
|
+
return grid_layout_3d(graph, size=size)
|
6533
|
+
elif 'cluster' in shape.lower() and '2d' in shape.lower():
|
6534
|
+
return cluster_layout_2d(graph, radius=size/2, key=key)
|
6535
|
+
elif 'cluster' in shape.lower() and '3d' in shape.lower():
|
6536
|
+
return cluster_layout_3d(graph, radius=size/2, key=key)
|
6028
6537
|
else:
|
6029
|
-
|
6030
|
-
|
6031
|
-
|
6032
|
-
|
6033
|
-
|
6538
|
+
d = Graph.MeshData(graph)
|
6539
|
+
edges = d['edges']
|
6540
|
+
v_dicts = d['vertexDictionaries']
|
6541
|
+
e_dicts = d['edgeDictionaries']
|
6542
|
+
vertices = Graph.Vertices(graph)
|
6543
|
+
if rootVertex == None:
|
6544
|
+
rootVertex, root_index = vertex_max_degree(graph, vertices)
|
6545
|
+
else:
|
6546
|
+
root_index = Vertex.Index(rootVertex, vertices, tolerance=tolerance)
|
6547
|
+
if root_index == None:
|
6548
|
+
root_index = 0
|
6549
|
+
if 'rad' in shape.lower() and '2d' in shape.lower():
|
6550
|
+
positions = radial_layout_2d(edges, root_index=root_index)
|
6551
|
+
elif 'spherical' in shape.lower() and '3d' in shape.lower():
|
6552
|
+
positions = spherical_layout_3d(edges, root_index=root_index)
|
6553
|
+
elif 'spring' in shape.lower() and "3d" in shape.lower():
|
6554
|
+
positions = spring_layout_3d(edges, k=k, seed=seed, iterations=iterations)
|
6555
|
+
elif 'spring' in shape.lower() and '2d' in shape.lower():
|
6556
|
+
positions = spring_layout_2d(edges, k=k, seed=seed, iterations=iterations)
|
6557
|
+
elif 'tree' in shape.lower() and '2d' in shape.lower():
|
6558
|
+
positions = tree_layout_2d(edges, root_index=root_index)
|
6559
|
+
elif 'tree' in shape.lower() and '3d' in shape.lower():
|
6560
|
+
positions = tree_layout_3d(edges, root_index=root_index, base_radius=1.0, radius_factor=1.5)
|
6561
|
+
else:
|
6562
|
+
if not silent:
|
6563
|
+
print(f"{shape} is not implemented yet. Please choose from ['circle 2D', 'grid 2D', 'line 2D', 'radial 2D', 'spring 2D', 'tree 2D', 'grid 3D', 'sphere 3D', 'tree 3D']. Returning None.")
|
6564
|
+
return None
|
6565
|
+
positions = positions.tolist()
|
6566
|
+
if len(positions[0]) == 3:
|
6567
|
+
positions = [[p[0], p[1], p[2]] for p in positions]
|
6568
|
+
else:
|
6569
|
+
positions = [[p[0], p[1], 0] for p in positions]
|
6570
|
+
return Graph.ByMeshData(positions, edges, v_dicts, e_dicts, tolerance=tolerance)
|
6034
6571
|
|
6035
6572
|
@staticmethod
|
6036
6573
|
def GlobalClusteringCoefficient(graph):
|
topologicpy/Plotly.py
CHANGED
@@ -415,23 +415,24 @@ class Plotly:
|
|
415
415
|
e_cluster = Cluster.ByTopologies(vertices)
|
416
416
|
geo = Topology.Geometry(e_cluster, mantissa=mantissa)
|
417
417
|
vertices = geo['vertices']
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
418
|
+
if len(vertices) > 0:
|
419
|
+
v_data = Plotly.DataByTopology(e_cluster,
|
420
|
+
vertexColor=vertexColor,
|
421
|
+
vertexColorKey=vertexColorKey,
|
422
|
+
vertexSize=vertexSize,
|
423
|
+
vertexSizeKey=vertexSizeKey,
|
424
|
+
vertexLabelKey=vertexLabelKey,
|
425
|
+
showVertexLabel=showVertexLabel,
|
426
|
+
vertexGroupKey=vertexGroupKey,
|
427
|
+
vertexMinGroup=vertexMinGroup,
|
428
|
+
vertexMaxGroup=vertexMaxGroup,
|
429
|
+
vertexGroups=vertexGroups,
|
430
|
+
vertexLegendLabel=vertexLegendLabel,
|
431
|
+
vertexLegendGroup=vertexLegendGroup,
|
432
|
+
vertexLegendRank=vertexLegendRank,
|
433
|
+
colorScale=colorScale)
|
434
|
+
|
435
|
+
data += v_data
|
435
436
|
|
436
437
|
if showEdges:
|
437
438
|
e_dictionaries = []
|
@@ -458,12 +459,12 @@ class Plotly:
|
|
458
459
|
else:
|
459
460
|
new_edges = edges
|
460
461
|
e_dictionaries.append(d)
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
462
|
+
if len(new_edges) > 0:
|
463
|
+
e_cluster = Cluster.ByTopologies(new_edges)
|
464
|
+
geo = Topology.Geometry(e_cluster, mantissa=mantissa)
|
465
|
+
vertices = geo['vertices']
|
466
|
+
edges = geo['edges']
|
467
|
+
data.extend(Plotly.edgeData(vertices, edges, dictionaries=e_dictionaries, color=edgeColor, colorKey=edgeColorKey, width=edgeWidth, widthKey=edgeWidthKey, labelKey=edgeLabelKey, showEdgeLabel=showEdgeLabel, groupKey=edgeGroupKey, minGroup=edgeMinGroup, maxGroup=edgeMaxGroup, groups=edgeGroups, legendLabel=edgeLegendLabel, legendGroup=edgeLegendGroup, legendRank=edgeLegendRank, showLegend=showEdgeLegend, colorScale=colorScale))
|
467
468
|
return data
|
468
469
|
|
469
470
|
@staticmethod
|
@@ -496,7 +497,7 @@ class Plotly:
|
|
496
497
|
x.append(v[0])
|
497
498
|
y.append(v[1])
|
498
499
|
z.append(v[2])
|
499
|
-
label = ""
|
500
|
+
label = " "
|
500
501
|
group = None
|
501
502
|
if len(dictionaries) > 0:
|
502
503
|
d = dictionaries[m]
|
@@ -506,15 +507,13 @@ class Plotly:
|
|
506
507
|
color = Color.AnyToHex(d_color)
|
507
508
|
groupList.append(color)
|
508
509
|
if not labelKey == None:
|
509
|
-
label = str(Dictionary.ValueAtKey(d, key=labelKey)) or ""
|
510
|
+
label = str(Dictionary.ValueAtKey(d, key=labelKey)) or " "
|
510
511
|
if not sizeKey == None:
|
511
512
|
size = Dictionary.ValueAtKey(d, key=sizeKey) or size
|
512
513
|
if not groupKey == None:
|
513
|
-
group = Dictionary.ValueAtKey(d, key=groupKey) or ""
|
514
|
+
group = Dictionary.ValueAtKey(d, key=groupKey) or " "
|
514
515
|
try:
|
515
|
-
if group == "":
|
516
|
-
#color = 'white'
|
517
|
-
#groupList.append(Color.AnyToHex(color))
|
516
|
+
if group == " ":
|
518
517
|
pass
|
519
518
|
elif type(group) == int or type(group) == float:
|
520
519
|
if group < minGroup:
|
@@ -853,121 +852,6 @@ class Plotly:
|
|
853
852
|
def closest_index(input_value, values):
|
854
853
|
return int(min(range(len(values)), key=lambda i: abs(values[i] - input_value)))
|
855
854
|
|
856
|
-
def edgeData_old(vertices, edges, dictionaries=None, color="black", colorKey=None, width=1, widthKey=None, labelKey=None, showEdgeLabel = False, groupKey=None, minGroup=None, maxGroup=None, groups=[], legendLabel="Topology Edges", legendGroup=2, legendRank=2, showLegend=True, colorScale="Viridis"):
|
857
|
-
traces = []
|
858
|
-
x = []
|
859
|
-
y = []
|
860
|
-
z = []
|
861
|
-
labels = []
|
862
|
-
groupList = []
|
863
|
-
label = ""
|
864
|
-
group = ""
|
865
|
-
|
866
|
-
if showEdgeLabel == True:
|
867
|
-
mode = "lines+text"
|
868
|
-
else:
|
869
|
-
mode = "lines"
|
870
|
-
|
871
|
-
if showEdgeLabel == True:
|
872
|
-
mode = "lines+text"
|
873
|
-
else:
|
874
|
-
mode = "lines"
|
875
|
-
if groups:
|
876
|
-
if len(groups) > 0:
|
877
|
-
if type(groups[0]) == int or type(groups[0]) == float:
|
878
|
-
if not minGroup:
|
879
|
-
minGroup = min(groups)
|
880
|
-
if not maxGroup:
|
881
|
-
maxGroup = max(groups)
|
882
|
-
else:
|
883
|
-
minGroup = 0
|
884
|
-
maxGroup = len(groups) - 1
|
885
|
-
else:
|
886
|
-
minGroup = 0
|
887
|
-
maxGroup = 1
|
888
|
-
|
889
|
-
if colorKey or widthKey or labelKey or groupKey:
|
890
|
-
keys = [x for x in [colorKey, widthKey, labelKey, groupKey] if not x == None]
|
891
|
-
temp_dict = Helper.ClusterByKeys(edges, dictionaries, keys, silent=False)
|
892
|
-
dict_clusters = temp_dict["dictionaries"]
|
893
|
-
elements_clusters = temp_dict['elements']
|
894
|
-
for j, elements_cluster in enumerate(elements_clusters):
|
895
|
-
d = dict_clusters[j][0] # All dicitonaries have same values in dictionaries, so take first one.
|
896
|
-
if d:
|
897
|
-
if not colorKey == None:
|
898
|
-
d_color = Dictionary.ValueAtKey(d, key=colorKey) or color
|
899
|
-
color = Color.AnyToHex(d_color)
|
900
|
-
if not labelKey == None:
|
901
|
-
label = str(Dictionary.ValueAtKey(d, key=labelKey)) or ""
|
902
|
-
if not widthKey == None:
|
903
|
-
width = Dictionary.ValueAtKey(d, key=edgeWidthKey) or width
|
904
|
-
if not groupKey == None:
|
905
|
-
group = Dictionary.ValueAtKey(d, key=groupKey)
|
906
|
-
if not group == None:
|
907
|
-
if type(group) == int or type(group) == float:
|
908
|
-
if group < minGroup:
|
909
|
-
group = minGroup
|
910
|
-
if group > maxGroup:
|
911
|
-
group = maxGroup
|
912
|
-
d_color = Color.ByValueInRange(group, minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
|
913
|
-
else:
|
914
|
-
d_color = Color.ByValueInRange(groups.index(group), minValue=minGroup, maxValue=maxGroup, colorScale=colorScale)
|
915
|
-
color = d_color
|
916
|
-
x = []
|
917
|
-
y = []
|
918
|
-
z = []
|
919
|
-
for e in elements_cluster:
|
920
|
-
sv = vertices[e[0]]
|
921
|
-
ev = vertices[e[1]]
|
922
|
-
x+=[sv[0], ev[0], None] # x-coordinates of edge ends
|
923
|
-
y+=[sv[1], ev[1], None] # y-coordinates of edge ends
|
924
|
-
z+=[sv[2], ev[2], None] # z-coordinates of edge ends
|
925
|
-
if showEdgeLabel == True:
|
926
|
-
mode = "lines+text"
|
927
|
-
else:
|
928
|
-
mode = "lines"
|
929
|
-
trace = go.Scatter3d(x=x,
|
930
|
-
y=y,
|
931
|
-
z=z,
|
932
|
-
name=label,
|
933
|
-
showlegend=showLegend,
|
934
|
-
marker_size=0,
|
935
|
-
mode=mode,
|
936
|
-
line=dict(color=color, width=width),
|
937
|
-
legendgroup=legendGroup,
|
938
|
-
legendrank=legendRank,
|
939
|
-
text=label,
|
940
|
-
hoverinfo='text')
|
941
|
-
traces.append(trace)
|
942
|
-
else:
|
943
|
-
x = []
|
944
|
-
y = []
|
945
|
-
z = []
|
946
|
-
for e in edges:
|
947
|
-
sv = vertices[e[0]]
|
948
|
-
ev = vertices[e[1]]
|
949
|
-
x+=[sv[0], ev[0], None] # x-coordinates of edge ends
|
950
|
-
y+=[sv[1], ev[1], None] # y-coordinates of edge ends
|
951
|
-
z+=[sv[2], ev[2], None] # z-coordinates of edge ends
|
952
|
-
if showEdgeLabel == True:
|
953
|
-
mode = "lines+text"
|
954
|
-
else:
|
955
|
-
mode = "lines"
|
956
|
-
trace = go.Scatter3d(x=x,
|
957
|
-
y=y,
|
958
|
-
z=z,
|
959
|
-
name=label,
|
960
|
-
showlegend=showLegend,
|
961
|
-
marker_size=0,
|
962
|
-
mode=mode,
|
963
|
-
line=dict(color=color, width=width),
|
964
|
-
legendgroup=legendGroup,
|
965
|
-
legendrank=legendRank,
|
966
|
-
text=label,
|
967
|
-
hoverinfo='text')
|
968
|
-
traces.append(trace)
|
969
|
-
|
970
|
-
return traces
|
971
855
|
|
972
856
|
def faceData(vertices, faces, dictionaries=None, color="#FAFAFA", colorKey=None,
|
973
857
|
opacity=0.5, labelKey=None, groupKey=None,
|
@@ -1150,7 +1034,8 @@ class Plotly:
|
|
1150
1034
|
geo = Topology.Geometry(e_cluster, mantissa=mantissa)
|
1151
1035
|
vertices = geo['vertices']
|
1152
1036
|
edges = geo['edges']
|
1153
|
-
|
1037
|
+
if len(edges) > 0:
|
1038
|
+
data.extend(Plotly.edgeData(vertices, edges, dictionaries=e_dictionaries, color=edgeColor, colorKey=edgeColorKey, width=edgeWidth, widthKey=edgeWidthKey, labelKey=edgeLabelKey, showEdgeLabel=showEdgeLabel, groupKey=edgeGroupKey, minGroup=edgeMinGroup, maxGroup=edgeMaxGroup, groups=edgeGroups, legendLabel=edgeLegendLabel, legendGroup=edgeLegendGroup, legendRank=edgeLegendRank, showLegend=showEdgeLegend, colorScale=colorScale))
|
1154
1039
|
|
1155
1040
|
if showFaces and Topology.Type(topology) >= Topology.TypeID("Face"):
|
1156
1041
|
if not faceColorKey == None:
|
@@ -1183,7 +1068,8 @@ class Plotly:
|
|
1183
1068
|
geo = Topology.Geometry(f_cluster, mantissa=mantissa)
|
1184
1069
|
vertices = geo['vertices']
|
1185
1070
|
faces = geo['faces']
|
1186
|
-
|
1071
|
+
if len(faces) > 0:
|
1072
|
+
data.append(faceData(vertices, faces, dictionaries=f_dictionaries, color=faceColor, colorKey=faceColorKey, opacity=faceOpacity, labelKey=faceLabelKey, groupKey=faceGroupKey, minGroup=faceMinGroup, maxGroup=faceMaxGroup, groups=faceGroups, legendLabel=faceLegendLabel, legendGroup=faceLegendGroup, legendRank=faceLegendRank, showLegend=showFaceLegend, intensities=intensityList, colorScale=colorScale))
|
1187
1073
|
return data
|
1188
1074
|
|
1189
1075
|
@staticmethod
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.7.
|
1
|
+
__version__ = '0.7.74'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.74
|
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
|
@@ -11,13 +11,13 @@ topologicpy/Dictionary.py,sha256=0AsGoz48pGTye_F4KcJopNjD9STeQ50LHc6PPvERFaA,319
|
|
11
11
|
topologicpy/Edge.py,sha256=9u9SdUxuenLUIK26xwFvPoYV34p0dCfXmHHBxdgvAdM,67164
|
12
12
|
topologicpy/EnergyModel.py,sha256=AqTtmXE35SxvRXhG3vYAQd7GQDW-6HtjYPHua6ME4Eg,53762
|
13
13
|
topologicpy/Face.py,sha256=q7x6auTju6IS3mdOhhXZdU3rqKSuJCE-5EOfxofDDMI,124348
|
14
|
-
topologicpy/Graph.py,sha256=
|
14
|
+
topologicpy/Graph.py,sha256=BuSg8ilbZd2Qqoenmtw5FrkSe1fDLFS6jshljvq4dOs,416822
|
15
15
|
topologicpy/Grid.py,sha256=9N6PE84qCm40TRi2WtlVZSBwXXr47zHpscEpZHg_JW4,18205
|
16
16
|
topologicpy/Helper.py,sha256=Sv35czP_j0oLDeJcN8usswUm4U3auiK1LQ_Z_HBvxxg,21716
|
17
17
|
topologicpy/Honeybee.py,sha256=HfTaEV1R8K1xOVQQy9sBOhBTF_ap8A2RxZOYhirp_Mw,21835
|
18
18
|
topologicpy/Matrix.py,sha256=umgR7An919-wGInXJ1wpqnoQ2jCPdyMe2rcWTZ16upk,8079
|
19
19
|
topologicpy/Neo4j.py,sha256=t52hgE9cVsqkGc7m7fjRsLnyfRHakVHwdvF4ms7ow78,22342
|
20
|
-
topologicpy/Plotly.py,sha256
|
20
|
+
topologicpy/Plotly.py,sha256=VNUHRUNu4NUmf3HScQRml3TcwHe_ZoqXjqwttGSuU88,112642
|
21
21
|
topologicpy/Polyskel.py,sha256=EFsuh2EwQJGPLiFUjvtXmAwdX-A4r_DxP5hF7Qd3PaU,19829
|
22
22
|
topologicpy/PyG.py,sha256=LU9LCCzjxGPUM31qbaJXZsTvniTtgugxJY7y612t4A4,109757
|
23
23
|
topologicpy/Shell.py,sha256=8OJjlWk9eCZ3uGOTht6ZVrcMczCafw-YWoDGueaz7eg,87673
|
@@ -28,9 +28,9 @@ topologicpy/Vector.py,sha256=A1g83zDHep58iVPY8WQ8iHNrSOfGWFEzvVeDuMnjDNY,33078
|
|
28
28
|
topologicpy/Vertex.py,sha256=ZS6xK89JKokBKc0W8frdRhhuzR8c-dI1TTLt7pTf1iA,71032
|
29
29
|
topologicpy/Wire.py,sha256=eVet2OToVsXi9AkDYo35LpfMPqJ6aKGD6QkiU4-Jvs8,182271
|
30
30
|
topologicpy/__init__.py,sha256=vlPCanUbxe5NifC4pHcnhSzkmmYcs_UrZrTlVMsxcFs,928
|
31
|
-
topologicpy/version.py,sha256=
|
32
|
-
topologicpy-0.7.
|
33
|
-
topologicpy-0.7.
|
34
|
-
topologicpy-0.7.
|
35
|
-
topologicpy-0.7.
|
36
|
-
topologicpy-0.7.
|
31
|
+
topologicpy/version.py,sha256=_XwNUtWN5b4vRt6q08wKnk-7iLHDZXyRKXWBhgnWyP4,23
|
32
|
+
topologicpy-0.7.74.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
33
|
+
topologicpy-0.7.74.dist-info/METADATA,sha256=QQ-v5awljewQVDPhAMmdZdVtUcKzJh4LRiYGoCePtmk,10493
|
34
|
+
topologicpy-0.7.74.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
35
|
+
topologicpy-0.7.74.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
36
|
+
topologicpy-0.7.74.dist-info/RECORD,,
|
File without changes
|
File without changes
|