topologicpy 0.7.80__py3-none-any.whl → 0.7.82__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/Face.py +236 -6
- topologicpy/version.py +1 -1
- {topologicpy-0.7.80.dist-info → topologicpy-0.7.82.dist-info}/METADATA +1 -1
- {topologicpy-0.7.80.dist-info → topologicpy-0.7.82.dist-info}/RECORD +7 -7
- {topologicpy-0.7.80.dist-info → topologicpy-0.7.82.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.80.dist-info → topologicpy-0.7.82.dist-info}/WHEEL +0 -0
- {topologicpy-0.7.80.dist-info → topologicpy-0.7.82.dist-info}/top_level.txt +0 -0
topologicpy/Face.py
CHANGED
@@ -1649,7 +1649,7 @@ class Face():
|
|
1649
1649
|
return are_planes_coplanar(plane_a, plane_b, tolerance=tolerance)
|
1650
1650
|
|
1651
1651
|
@staticmethod
|
1652
|
-
def Isovist(face, vertex, obstacles: list = [], direction: list = [0,1,0], fov: float = 360, mantissa: int = 6, tolerance: float = 0.0001):
|
1652
|
+
def Isovist(face, vertex, obstacles: list = [], direction: list = [0,1,0], fov: float = 360, transferDictionaries: bool = False, metrics: bool = False, mantissa: int = 6, tolerance: float = 0.0001):
|
1653
1653
|
"""
|
1654
1654
|
Returns the face representing the isovist projection from the input viewpoint.
|
1655
1655
|
This method assumes all input is in 2D. Z coordinates are ignored.
|
@@ -1671,6 +1671,25 @@ class Face():
|
|
1671
1671
|
fov : float , optional
|
1672
1672
|
The horizontal field of view (fov) angle in degrees. See https://en.wikipedia.org/wiki/Field_of_view.
|
1673
1673
|
The acceptable range is 1 to 360. The default is 360.
|
1674
|
+
transferDictionaries : bool , optional
|
1675
|
+
If set to True, the dictionaries of the encountered edges will be transfered to the isovist edges. The default is False.
|
1676
|
+
metrics : bool , optional
|
1677
|
+
If set to True, the following metrics are calculated and stored in the dictionary of the returned isovist. The keys of the values are:
|
1678
|
+
- viewpoint : list , the x , y , z coordinates of the location of the viewpoint.
|
1679
|
+
- direction : list , the direction of the view.
|
1680
|
+
- fov : int, Field of view angle.
|
1681
|
+
- area : float , the area of the isovist.
|
1682
|
+
- perimeter : float , the perimeter length of the isovist
|
1683
|
+
- compactness : float , how closely the shape of the isovist approximates a circle (the most compact geometric shape).
|
1684
|
+
- d_max : float, Maximum Visibility Distance. the length of the longest straight line that can be seen from the viewpoint.
|
1685
|
+
- v_max : list, Furthest Point measures the x , y , z coordinates of the furthest visible point from the viewpoint.
|
1686
|
+
- v_d : list, Visibility Distribution quantifies the angular distribution (in degrees) of visible points across the isovist.
|
1687
|
+
This metric can tell you whether the visibility from a point is more spread out or concentrated in a certain direction. A uniform visibility distribution indicates a more balanced visual field, while a skewed distribution suggests that the observer's line of sight is constrained in certain directions.
|
1688
|
+
- v_density : float, Viewpoint Density which refers to the number of visible points per unit area within the isovist.
|
1689
|
+
- symmetry : float, Symmetry measures how balanced or symmetrical the isovist is around the point of observation.
|
1690
|
+
- d_f : float, Fractal Dimension measures the complexity of the isovist's boundary.
|
1691
|
+
- e_c : float , Edge Complexity measures how complex the edges of the isovist boundary are.
|
1692
|
+
- theta : float, Mean Visual Field Angle measures the average angular extent of the visible area from the observation point.
|
1674
1693
|
mantissa : int , optional
|
1675
1694
|
The desired length of the mantissa. The default is 6.
|
1676
1695
|
tolerance : float , optional:
|
@@ -1690,8 +1709,132 @@ class Face():
|
|
1690
1709
|
from topologicpy.Cluster import Cluster
|
1691
1710
|
from topologicpy.Topology import Topology
|
1692
1711
|
from topologicpy.Vector import Vector
|
1712
|
+
from topologicpy.Dictionary import Dictionary
|
1713
|
+
from topologicpy.Helper import Helper
|
1714
|
+
|
1715
|
+
import math
|
1716
|
+
import numpy as np
|
1717
|
+
|
1718
|
+
def calculate_angle(viewpoint, vertex):
|
1719
|
+
# Calculate the angle between the viewpoint and the vertex in the 2D plane
|
1720
|
+
# Viewpoint is (x, y, z), and vertex is (x, y)
|
1721
|
+
dx = vertex[0] - viewpoint[0]
|
1722
|
+
dy = vertex[1] - viewpoint[1]
|
1723
|
+
|
1724
|
+
# Return the angle in radians using the arctangent of the y/x difference
|
1725
|
+
return math.degrees(math.atan2(dy, dx))
|
1726
|
+
|
1727
|
+
def visibility_distribution(viewpoint, isovist_vertices):
|
1728
|
+
angles = []
|
1729
|
+
# Calculate the angle of each vertex with respect to the viewpoint
|
1730
|
+
for vertex in isovist_vertices:
|
1731
|
+
angle = calculate_angle(viewpoint, vertex)
|
1732
|
+
angles.append(angle)
|
1733
|
+
# Sort the angles to analyze the distribution
|
1734
|
+
angles = np.sort(angles)
|
1735
|
+
return list(angles)
|
1736
|
+
|
1737
|
+
def isovist_symmetry(viewpoint, isovist_vertices):
|
1738
|
+
"""
|
1739
|
+
Calculates the symmetry of the isovist polygon.
|
1740
|
+
|
1741
|
+
Parameters:
|
1742
|
+
- viewpoint: a tuple (x, y) of the viewpoint.
|
1743
|
+
- isovist_vertices: a list of tuples, each representing a vertex (x, y) of the isovist polygon.
|
1744
|
+
|
1745
|
+
Returns:
|
1746
|
+
- symmetry value: A measure of the symmetry of the isovist.
|
1747
|
+
"""
|
1748
|
+
angles = [calculate_angle(viewpoint, vertex) for vertex in isovist_vertices]
|
1749
|
+
angles.sort()
|
1750
|
+
|
1751
|
+
# Calculate angular deviations from the mean direction
|
1752
|
+
mean_angle = np.mean(angles)
|
1753
|
+
angular_deviation = np.std(angles)
|
1754
|
+
symmetry = angular_deviation / mean_angle if mean_angle != 0 else 0
|
1755
|
+
return float(symmetry)
|
1756
|
+
|
1757
|
+
# Fractal Dimension (D_f) using Box-counting
|
1758
|
+
def fractal_dimension(isovist_vertices):
|
1759
|
+
"""
|
1760
|
+
Calculates the fractal dimension of the isovist boundary using box-counting.
|
1761
|
+
|
1762
|
+
Parameters:
|
1763
|
+
- isovist_vertices: a list of tuples, each representing a vertex (x, y) of the isovist polygon.
|
1764
|
+
|
1765
|
+
Returns:
|
1766
|
+
- fractal dimension: A measure of the boundary's complexity.
|
1767
|
+
"""
|
1768
|
+
# Convert isovist vertices into a boundary path (x, y coordinates)
|
1769
|
+
boundary_points = np.array(isovist_vertices)
|
1693
1770
|
|
1694
|
-
|
1771
|
+
# Box-counting approach
|
1772
|
+
sizes = np.logspace(0, 2, 10) # Varying box sizes (log scale)
|
1773
|
+
sizes[sizes == 0] = 1e-10 # Replace zero counts with a small value
|
1774
|
+
counts = []
|
1775
|
+
|
1776
|
+
for size in sizes:
|
1777
|
+
count = 0
|
1778
|
+
for i in range(len(boundary_points)):
|
1779
|
+
if np.abs(boundary_points[i][0] - boundary_points[(i+1)%len(boundary_points)][0]) > size or \
|
1780
|
+
np.abs(boundary_points[i][1] - boundary_points[(i+1)%len(boundary_points)][1]) > size:
|
1781
|
+
count += 1
|
1782
|
+
counts.append(count)
|
1783
|
+
|
1784
|
+
# To avoid log(0), add a small constant to counts
|
1785
|
+
counts = np.array(counts)
|
1786
|
+
counts = np.where(counts == 0, 1e-10, counts)
|
1787
|
+
# Linear regression of log(count) vs log(size) to estimate fractal dimension
|
1788
|
+
log_sizes = np.log(sizes)
|
1789
|
+
log_counts = np.log(counts)
|
1790
|
+
|
1791
|
+
# Perform linear regression (log-log scale)
|
1792
|
+
slope, _ = np.polyfit(log_sizes, log_counts, 1)
|
1793
|
+
|
1794
|
+
return slope
|
1795
|
+
|
1796
|
+
# Edge Complexity (E_C)
|
1797
|
+
def edge_complexity(isovist_vertices):
|
1798
|
+
"""
|
1799
|
+
Calculates the edge complexity of the isovist boundary.
|
1800
|
+
|
1801
|
+
Parameters:
|
1802
|
+
- isovist_vertices: a list of tuples, each representing a vertex (x, y) of the isovist polygon.
|
1803
|
+
|
1804
|
+
Returns:
|
1805
|
+
- edge complexity: A measure of the complexity of the boundary.
|
1806
|
+
"""
|
1807
|
+
angles = []
|
1808
|
+
for i in range(len(isovist_vertices)):
|
1809
|
+
p1 = isovist_vertices[i]
|
1810
|
+
p2 = isovist_vertices[(i + 1) % len(isovist_vertices)]
|
1811
|
+
p3 = isovist_vertices[(i + 2) % len(isovist_vertices)]
|
1812
|
+
|
1813
|
+
# Calculate the angle between each consecutive edge
|
1814
|
+
angle = np.arctan2(p3[1] - p2[1], p3[0] - p2[0]) - np.arctan2(p1[1] - p2[1], p1[0] - p2[0])
|
1815
|
+
angles.append(np.abs(angle))
|
1816
|
+
|
1817
|
+
# Complexity is the number of abrupt angle changes
|
1818
|
+
complexity = np.sum(np.array(angles) > np.pi / 4) # e.g., large changes in angles
|
1819
|
+
return float(complexity)
|
1820
|
+
|
1821
|
+
# Mean Visual Field Angle (θ)
|
1822
|
+
def mean_visual_field_angle(viewpoint, isovist_vertices):
|
1823
|
+
"""
|
1824
|
+
Calculates the mean visual field angle from the viewpoint to the isovist vertices.
|
1825
|
+
|
1826
|
+
Parameters:
|
1827
|
+
- viewpoint: a tuple (x, y) of the viewpoint.
|
1828
|
+
- isovist_vertices: a list of tuples, each representing a vertex (x, y) of the isovist polygon.
|
1829
|
+
|
1830
|
+
Returns:
|
1831
|
+
- mean visual field angle in degrees.
|
1832
|
+
"""
|
1833
|
+
angles = [calculate_angle(viewpoint, vertex) for vertex in isovist_vertices]
|
1834
|
+
# Return the average angle
|
1835
|
+
return np.mean(angles)
|
1836
|
+
|
1837
|
+
def vertex_part_of_face(vertex, face, tolerance):
|
1695
1838
|
vertices = Topology.Vertices(face)
|
1696
1839
|
for v in vertices:
|
1697
1840
|
if Vertex.Distance(vertex, v) < tolerance:
|
@@ -1716,6 +1859,7 @@ class Face():
|
|
1716
1859
|
normal = Face.Normal(face)
|
1717
1860
|
flat_face = Topology.Flatten(face, origin=origin, direction=normal)
|
1718
1861
|
flat_vertex = Topology.Flatten(vertex, origin=origin, direction=normal)
|
1862
|
+
flat_obstacles = [Topology.Flatten(obstacle, origin=origin, direction=normal) for obstacle in obstacles]
|
1719
1863
|
|
1720
1864
|
eb = Face.ExternalBoundary(flat_face)
|
1721
1865
|
vertices = Topology.Vertices(eb)
|
@@ -1732,7 +1876,7 @@ class Face():
|
|
1732
1876
|
new_ib_list.append(Wire.ByVertices(new_vertices, close=True))
|
1733
1877
|
|
1734
1878
|
flat_face = Face.ByWires(eb, new_ib_list)
|
1735
|
-
for obs in
|
1879
|
+
for obs in flat_obstacles:
|
1736
1880
|
flat_face = Topology.Difference(flat_face, Face.ByWire(obs))
|
1737
1881
|
|
1738
1882
|
targets = Topology.Vertices(flat_face)
|
@@ -1751,7 +1895,7 @@ class Face():
|
|
1751
1895
|
faces = Topology.Faces(shell)
|
1752
1896
|
final_faces = []
|
1753
1897
|
for f in faces:
|
1754
|
-
if
|
1898
|
+
if vertex_part_of_face(flat_vertex, f, tolerance=0.001):
|
1755
1899
|
final_faces.append(f)
|
1756
1900
|
shell = Shell.ByFaces(final_faces)
|
1757
1901
|
return_face = Topology.RemoveCoplanarFaces(shell, epsilon=0.1)
|
@@ -1795,8 +1939,94 @@ class Face():
|
|
1795
1939
|
return None
|
1796
1940
|
simpler_face = Face.RemoveCollinearEdges(return_face)
|
1797
1941
|
if Topology.IsInstance(simpler_face, "face"):
|
1798
|
-
|
1799
|
-
|
1942
|
+
if transferDictionaries == True:
|
1943
|
+
j_edges = [Topology.Edges(t) for t in obstacles]
|
1944
|
+
j_edges = Helper.Flatten(j_edges)
|
1945
|
+
j_edges += Topology.Edges(face)
|
1946
|
+
i_edges = Topology.Edges(simpler_face)
|
1947
|
+
used = [0 for _ in range(len(j_edges))]
|
1948
|
+
for i, i_edge in enumerate(i_edges):
|
1949
|
+
d_i = Topology.Dictionary(i_edge)
|
1950
|
+
for j, j_edge in enumerate(j_edges):
|
1951
|
+
if used[j] == 0:
|
1952
|
+
if Edge.IsCollinear(i_edge, j_edge):
|
1953
|
+
d_j = Topology.Dictionary(j_edge)
|
1954
|
+
d_result = Dictionary.ByMergedDictionaries([d_i, d_j])
|
1955
|
+
i_edge = Topology.SetDictionary(i_edge, d_result)
|
1956
|
+
used[j] == 1
|
1957
|
+
|
1958
|
+
return_face = Topology.Unflatten(simpler_face, origin=origin, direction=normal)
|
1959
|
+
return_face = Topology.Unflatten(return_face, origin=origin, direction=normal)
|
1960
|
+
if metrics == True:
|
1961
|
+
# 1 Viewpoint
|
1962
|
+
viewpoint = Vertex.Coordinates(vertex, mantissa=mantissa)
|
1963
|
+
# 2 Direction
|
1964
|
+
# direction is given
|
1965
|
+
# 3 Field of View (FOV)
|
1966
|
+
# fov is given
|
1967
|
+
# 4 Area
|
1968
|
+
area = round(abs(Face.Area(return_face)), mantissa)
|
1969
|
+
# 5 Perimeter
|
1970
|
+
perimeter = round(Wire.Length(Face.Wires(return_face)[0]), mantissa)
|
1971
|
+
# 6 Compactness
|
1972
|
+
compactness = round(Face.Compactness(return_face), mantissa)
|
1973
|
+
# 7 Maximum Distance (d_max)
|
1974
|
+
vertices = Topology.Vertices(return_face)
|
1975
|
+
furthest_vertex = vertices[0]
|
1976
|
+
d_max = round(Vertex.Distance(vertex, vertices[0]), mantissa)
|
1977
|
+
# 8 Furthest Visible Vertex
|
1978
|
+
coords = []
|
1979
|
+
for v in vertices:
|
1980
|
+
coords.append(Vertex.Coordinates(v, mantissa=mantissa))
|
1981
|
+
dis = Vertex.Distance(vertex, v, mantissa=mantissa)
|
1982
|
+
if dis > d_max:
|
1983
|
+
d_max = dis
|
1984
|
+
furthest_vertex = v
|
1985
|
+
v_max = Vertex.Coordinates(furthest_vertex, mantissa=mantissa)
|
1986
|
+
# 9 Visibility Distribution (v_d)
|
1987
|
+
v_d = visibility_distribution(viewpoint, coords)
|
1988
|
+
v_d = [round(x) for x in v_d]
|
1989
|
+
# 10 Viewpoint density
|
1990
|
+
v_density = round(float(len(vertices)) / abs(Face.Area(return_face)), mantissa)
|
1991
|
+
# 11 Isovist Symmetry
|
1992
|
+
symmetry = round(isovist_symmetry(viewpoint, coords), mantissa)
|
1993
|
+
# 12 Fractal Dimension
|
1994
|
+
d_f = round(fractal_dimension(coords), mantissa)
|
1995
|
+
# 13 Edge Complexity
|
1996
|
+
e_c = round(edge_complexity(coords), mantissa)
|
1997
|
+
# 14 Mean Visual Field Angle
|
1998
|
+
theta = round(mean_visual_field_angle(viewpoint, coords), mantissa)
|
1999
|
+
keys = ["viewpoint",
|
2000
|
+
"direction",
|
2001
|
+
"fov",
|
2002
|
+
"area",
|
2003
|
+
"perimeter",
|
2004
|
+
"compactness",
|
2005
|
+
"d_max",
|
2006
|
+
"v_max",
|
2007
|
+
"v_d",
|
2008
|
+
"v_density",
|
2009
|
+
"symmetry",
|
2010
|
+
"d_f",
|
2011
|
+
"e_c",
|
2012
|
+
"theta"]
|
2013
|
+
values = [viewpoint,
|
2014
|
+
direction,
|
2015
|
+
fov,
|
2016
|
+
area,
|
2017
|
+
perimeter,
|
2018
|
+
compactness,
|
2019
|
+
d_max,
|
2020
|
+
v_max,
|
2021
|
+
v_d,
|
2022
|
+
v_density,
|
2023
|
+
symmetry,
|
2024
|
+
d_f,
|
2025
|
+
e_c,
|
2026
|
+
theta]
|
2027
|
+
d = Dictionary.ByKeysValues(keys, values)
|
2028
|
+
return_face = Topology.SetDictionary(return_face, d)
|
2029
|
+
return return_face
|
1800
2030
|
|
1801
2031
|
@staticmethod
|
1802
2032
|
def MedialAxis(face, resolution: int = 0, externalVertices: bool = False, internalVertices: bool = False, toLeavesOnly: bool = False, angTolerance: float = 0.1, tolerance: float = 0.0001):
|
topologicpy/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = '0.7.
|
1
|
+
__version__ = '0.7.82'
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: topologicpy
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.82
|
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
|
@@ -10,7 +10,7 @@ topologicpy/DGL.py,sha256=Dd6O08D-vSxpjHYgKm45JpKiaeGvWlg1BRMzYMAXGNc,138991
|
|
10
10
|
topologicpy/Dictionary.py,sha256=0AsGoz48pGTye_F4KcJopNjD9STeQ50LHc6PPvERFaA,31932
|
11
11
|
topologicpy/Edge.py,sha256=9u9SdUxuenLUIK26xwFvPoYV34p0dCfXmHHBxdgvAdM,67164
|
12
12
|
topologicpy/EnergyModel.py,sha256=AqTtmXE35SxvRXhG3vYAQd7GQDW-6HtjYPHua6ME4Eg,53762
|
13
|
-
topologicpy/Face.py,sha256=
|
13
|
+
topologicpy/Face.py,sha256=Y13hw84bjJO8bzEG7sB7PRDxcSqqmGZnNAbIhIUQ1Ys,136243
|
14
14
|
topologicpy/Graph.py,sha256=ZhMVB6ntuhIgTrKLUPryeAJFFBF0cnRsNgESbaohOiw,416914
|
15
15
|
topologicpy/Grid.py,sha256=9N6PE84qCm40TRi2WtlVZSBwXXr47zHpscEpZHg_JW4,18205
|
16
16
|
topologicpy/Helper.py,sha256=vUWbnZJxn0e9YsFUNyJ5Dee8Nh6TmDp9uCzCk4e0qAA,21897
|
@@ -28,9 +28,9 @@ topologicpy/Vector.py,sha256=A1g83zDHep58iVPY8WQ8iHNrSOfGWFEzvVeDuMnjDNY,33078
|
|
28
28
|
topologicpy/Vertex.py,sha256=mcLJaWFCct03dkVmT25wAl6k0k2EaYvB1PWNO9WQHWg,73465
|
29
29
|
topologicpy/Wire.py,sha256=o6MlOjubkJ_f5s1tUamizCsoPKvcQ2OudtJv0yCF-2o,186477
|
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=rXEi2ZZcGFgf2u3AMH7tyJUFE9nvS8L279fdB54xeCk,23
|
32
|
+
topologicpy-0.7.82.dist-info/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
|
33
|
+
topologicpy-0.7.82.dist-info/METADATA,sha256=CZZqWePZAWM-hGdFkhmjQf9qbm3Ub_fKCPzHnu7RYvk,10513
|
34
|
+
topologicpy-0.7.82.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
35
|
+
topologicpy-0.7.82.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
|
36
|
+
topologicpy-0.7.82.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|