topologicpy 0.7.90__py3-none-any.whl → 0.7.92__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 CHANGED
@@ -1690,6 +1690,8 @@ class Face():
1690
1690
  """
1691
1691
  Returns the face representing the isovist projection from the input viewpoint.
1692
1692
  This method assumes all input is in 2D. Z coordinates are ignored.
1693
+ This method and the metrics are largely derived from isovists.org. Even if not explicitly listed, please assume that all credit
1694
+ goes to the authors of that website and its associated software.
1693
1695
 
1694
1696
  Parameters
1695
1697
  ----------
@@ -1731,6 +1733,12 @@ class Face():
1731
1733
  - d_f : float, Fractal Dimension measures the complexity of the isovist's boundary.
1732
1734
  - e_c : float , Edge Complexity measures how complex the edges of the isovist boundary are.
1733
1735
  - theta : float, Mean Visual Field Angle measures the average angular extent of the visible area from the observation point.
1736
+ - occlusivity: float, the proportion of edges of an isovist that are not physically defined.
1737
+ - drift: float, the distance from the observation point to the centroid of its isovist.
1738
+ - closed_perimeter: float, the total length of non-occluded edges of the isovist.
1739
+ - average_radial: float, "the mean view length of all space visible from a location." (from isovists.org)
1740
+ - variance: float, "the mean of the square of deviation between all radial lengths and average radial length of an isovist (Benedikt, 1979)." (from isovists.org)
1741
+ - skewness: float, "the mean of the cube of deviation between all radial lengths and average radial length of an isovist (Benediky, 1979)." (from isovists.org)
1734
1742
  triangles : bool , optional
1735
1743
  If set to True, the subtended triangles of the isovist are created and stored as contents of the returned isovist face. The default is False.
1736
1744
  mantissa : int , optional
@@ -1934,6 +1942,81 @@ class Face():
1934
1942
 
1935
1943
  return float(distance), Vertex.ByCoordinates(list(closest_point))
1936
1944
 
1945
+
1946
+
1947
+ def compute_average_radial_variance_skewness(vertex, edges, mantissa=6):
1948
+ from math import atan2, pi, sqrt, pow
1949
+
1950
+ def subtended_angle(vertex, edge, mantissa=6):
1951
+ """Compute the angle subtended by the edge at point V."""
1952
+ v = Vertex.Coordinates(vertex, mantissa=mantissa)
1953
+ start = Vertex.Coordinates(Edge.StartVertex(edge), mantissa=mantissa)
1954
+ end = Vertex.Coordinates(Edge.EndVertex(edge), mantissa=mantissa)
1955
+ # Calculate the angles of the start and end vertices relative to V
1956
+ angle_start = atan2(start[1] - v[1], start[0] - v[0])
1957
+ angle_end = atan2(end[1] - v[1], end[0] - v[0])
1958
+ # Ensure the angle is in the range [0, 2*pi]
1959
+ angle_start = angle_start if angle_start >= 0 else angle_start + 2 * pi
1960
+ angle_end = angle_end if angle_end >= 0 else angle_end + 2 * pi
1961
+ # Compute the difference and handle wrapping around 2*pi
1962
+ angle_diff = abs(angle_end - angle_start)
1963
+ return min(angle_diff, 2 * pi - angle_diff)
1964
+
1965
+ total_weighted_distance = 0
1966
+ total_angle_weight = 0
1967
+ total_weighted_squared_deviation = 0
1968
+ total_weighted_cubed_deviation = 0
1969
+ distances = []
1970
+ angles = []
1971
+ for edge in edges:
1972
+ # Calculate the distance between V and the edge
1973
+ distance = Vertex.Distance(vertex, edge, mantissa=mantissa)
1974
+ distances.append(distance)
1975
+
1976
+ # Calculate the subtended angle for the edge
1977
+ angle = subtended_angle(vertex, edge, mantissa=mantissa)
1978
+ angles.append(angle)
1979
+
1980
+ # Weight the distance by the subtended angle
1981
+ total_weighted_distance += distance * angle
1982
+ total_angle_weight += angle
1983
+
1984
+ # Compute the Average Radial value
1985
+ if total_angle_weight == 0:
1986
+ average_radial = 0 # Avoid division by zero
1987
+ else:
1988
+ average_radial = round(total_weighted_distance / total_angle_weight, mantissa)
1989
+
1990
+ # Compute Variance
1991
+ for i, edge in enumerate(edges):
1992
+ # Calculate the distance between V and the edge
1993
+ distance = distances[i]
1994
+ # Calculate the subtended angle for the edge
1995
+ angle = angles[i]
1996
+
1997
+ # Calculate the deviation squared from the average radial
1998
+ deviation_squared = (distance - average_radial) ** 2
1999
+ # Calculate the deviation cubed from the average radial
2000
+ deviation_cubed = (distance - average_radial) ** 3
2001
+ # Weight the squared deviation by the subtended angle
2002
+ total_weighted_squared_deviation += deviation_squared * angle
2003
+ total_weighted_cubed_deviation += deviation_cubed * angle
2004
+
2005
+ # Compute the Variance value
2006
+ if total_angle_weight == 0:
2007
+ variance = 0 # Avoid division by zero
2008
+ else:
2009
+ variance = round(sqrt(total_weighted_squared_deviation / total_angle_weight), mantissa)
2010
+
2011
+ # Compute the Skewness value
2012
+ if total_angle_weight == 0:
2013
+ skewness = 0 # Avoid division by zero
2014
+ else:
2015
+ skewness = round(pow(total_weighted_cubed_deviation / total_angle_weight, 1/3), mantissa)
2016
+
2017
+ return average_radial, variance, skewness
2018
+
2019
+ # Main Code
1937
2020
  origin = Topology.Centroid(face)
1938
2021
  normal = Face.Normal(face)
1939
2022
  flat_face = Topology.Flatten(face, origin=origin, direction=normal)
@@ -2022,7 +2105,7 @@ class Face():
2022
2105
  return None
2023
2106
  simpler_face = Face.RemoveCollinearEdges(return_face)
2024
2107
  if Topology.IsInstance(simpler_face, "face"):
2025
- if transferDictionaries == True:
2108
+ if transferDictionaries == True or metrics == True:
2026
2109
  j_edges = [Topology.Edges(t) for t in obstacles]
2027
2110
  j_edges = Helper.Flatten(j_edges)
2028
2111
  j_edges += Topology.Edges(face)
@@ -2030,19 +2113,24 @@ class Face():
2030
2113
  used = [0 for _ in range(len(j_edges))]
2031
2114
  for i, i_edge in enumerate(i_edges):
2032
2115
  d_i = Topology.Dictionary(i_edge)
2116
+ d_i = Dictionary.SetValueAtKey(d_i, "occlusive", True)
2117
+ i_edge = Topology.SetDictionary(i_edge, d_i)
2033
2118
  for j, j_edge in enumerate(j_edges):
2034
2119
  if used[j] == 0:
2035
2120
  if Edge.IsCollinear(i_edge, j_edge):
2121
+ d_i = Dictionary.SetValueAtKey(d_i, "occlusive", False)
2122
+ i_edge = Topology.SetDictionary(i_edge, d_i)
2036
2123
  d_j = Topology.Dictionary(j_edge)
2037
2124
  d_result = Dictionary.ByMergedDictionaries([d_i, d_j])
2038
- i_edge = Topology.SetDictionary(i_edge, d_result)
2125
+ if transferDictionaries == True:
2126
+ i_edge = Topology.SetDictionary(i_edge, d_result)
2039
2127
  used[j] == 1
2040
2128
 
2041
2129
  return_face = Topology.Unflatten(simpler_face, origin=origin, direction=normal)
2042
- return_face = Topology.Unflatten(return_face, origin=origin, direction=normal)
2130
+ else:
2131
+ return_face = Topology.Unflatten(return_face, origin=origin, direction=normal)
2043
2132
  if metrics == True:
2044
2133
  vertices = Topology.Vertices(return_face)
2045
- edges = Topology.Edges(return_face)
2046
2134
  # 1 Viewpoint
2047
2135
  viewpoint = Vertex.Coordinates(vertex, mantissa=mantissa)
2048
2136
  # 2 Direction
@@ -2110,6 +2198,27 @@ class Face():
2110
2198
  e_c = round(edge_complexity(coords), mantissa)
2111
2199
  # 18 Mean Visual Field Angle
2112
2200
  theta = round(mean_visual_field_angle(viewpoint, coords), mantissa)
2201
+ # 19 Occlusivity
2202
+ occ_length = 0
2203
+ edges = Topology.Edges(return_face)
2204
+ for edge in edges:
2205
+ d = Topology.Dictionary(edge)
2206
+ if Dictionary.ValueAtKey(d, "occlusive") == True:
2207
+ occ_length += Edge.Length(edge)
2208
+ if perimeter > 0:
2209
+ occlusivity = round(occ_length/perimeter, mantissa)
2210
+ else:
2211
+ occlusivity = round(0.0, 6)
2212
+
2213
+ # 20 Drift
2214
+ drift = Vertex.Distance(vertex, Topology.Centroid(return_face), mantissa=mantissa)
2215
+
2216
+ # 21 Closed Perimeter
2217
+ closed_perimeter = round(perimeter - occ_length, mantissa)
2218
+
2219
+ # 22/23/24 Average Radial, Variance, and Skewness
2220
+ average_radial, variance, skewness = compute_average_radial_variance_skewness(vertex, edges, mantissa=6)
2221
+
2113
2222
  keys = ["viewpoint",
2114
2223
  "direction",
2115
2224
  "fov",
@@ -2127,7 +2236,14 @@ class Face():
2127
2236
  "symmetry",
2128
2237
  "d_f",
2129
2238
  "e_c",
2130
- "theta"]
2239
+ "theta",
2240
+ "occlusivity",
2241
+ "drift",
2242
+ "closed_perimeter",
2243
+ "average_radial",
2244
+ "variance",
2245
+ "skewness"]
2246
+
2131
2247
  values = [viewpoint,
2132
2248
  direction,
2133
2249
  fov,
@@ -2145,7 +2261,13 @@ class Face():
2145
2261
  symmetry,
2146
2262
  d_f,
2147
2263
  e_c,
2148
- theta]
2264
+ theta,
2265
+ occlusivity,
2266
+ drift,
2267
+ closed_perimeter,
2268
+ average_radial,
2269
+ variance,
2270
+ skewness]
2149
2271
  d = Dictionary.ByKeysValues(keys, values)
2150
2272
  return_face = Topology.SetDictionary(return_face, d)
2151
2273
  if triangles: