Fix location folders

This commit is contained in:
2024-05-26 16:26:32 +02:00
parent 5bc7b25e82
commit 0d4bd5906b
3 changed files with 3 additions and 3 deletions

View File

@@ -0,0 +1,58 @@
import networks.geometry.curve as curve
import networks.geometry.segment as segment
import numpy as np
class CurveSurface:
def __init__(self, points, reshape=True, spacing_distance=10):
self.points = np.array(points)
if reshape:
self.resolution, self.length = curve.resolution_distance(
self.points, spacing_distance=spacing_distance)
self.curve = curve.curve(self.points, self.resolution)
else: # Point can also be given already in curved form
self.curve = self.points
def compute_curvature(self):
self.curvature = curve.curvature(self.curve)
def compute_surface_perpendicular(self, width, normals):
self.offset_left = curve.offset(self.curve, width, normals)
self.offset_right = curve.offset(self.curve, -width, normals)
self.perpendicular_segment = []
for i in range(len(self.offset_left)):
self.perpendicular_segment.append(segment.discrete_segment(
self.offset_left[i], self.offset_right[i], pixel_perfect=False))
self.surface = []
for i in range(len(self.perpendicular_segment)-1):
self.surface.append([])
for j in range(len(self.perpendicular_segment[i])):
# Hypothesis
max_length_index = i
min_length_index = i+1
proportion = len(
self.perpendicular_segment[min_length_index])/len(self.perpendicular_segment[max_length_index])
# Reverse order if wrong hypothesis
if proportion > 1:
max_length_index = i+1
min_length_index = i
proportion = len(
self.perpendicular_segment[min_length_index])/len(self.perpendicular_segment[max_length_index])
self.surface[i].append([])
for k in range(len(self.perpendicular_segment[max_length_index])-1):
self.surface[i][j].append(segment.discrete_segment(
self.perpendicular_segment[max_length_index][k], self.perpendicular_segment[min_length_index][round(k * proportion)], pixel_perfect=False))
def compute_surface_parallel(self, inner_range, outer_range, resolution, normals):
self.left_side = []
self.right_side = []
for current_range in range(inner_range * resolution, outer_range * resolution):
self.left_side.append(curve.offset(
self.curve, current_range/resolution, normals))
self.right_side.append(curve.offset(
self.curve, -current_range/resolution, normals))

141
networks/geometry/curve.py Normal file
View File

@@ -0,0 +1,141 @@
import numpy as np
import networks.geometry.segment as segment
from scipy import interpolate
from math import sqrt
def curve(target_points, resolution=40):
"""
Returns a list of spaced points that approximate a smooth curve following target_points.
https://stackoverflow.com/questions/18962175/spline-interpolation-coefficients-of-a-line-curve-in-3d-space
"""
# Remove duplicates. Curve can't intersect itself
points = tuple(map(tuple, np.array(target_points)))
points = sorted(set(points), key=points.index)
# Change coordinates structure to (x1, x2, x3, ...), (y1, y2, y3, ...) (z1, z2, z3, ...)
coords = np.array(points, dtype=np.float32)
x = coords[:, 0]
y = coords[:, 1]
z = coords[:, 2]
# Compute
tck, u = interpolate.splprep([x, y, z], s=3, k=2)
x_knots, y_knots, z_knots = interpolate.splev(tck[0], tck)
u_fine = np.linspace(0, 1, resolution)
x_fine, y_fine, z_fine = interpolate.splev(u_fine, tck)
x_rounded = np.round(x_fine).astype(int)
y_rounded = np.round(y_fine).astype(int)
z_rounded = np.round(z_fine).astype(int)
return [(x, y, z) for x, y, z in zip(
x_rounded, y_rounded, z_rounded)]
def curvature(curve):
"""Get the normal vector at each point of the given points representing the direction in wich the curve is turning.
https://stackoverflow.com/questions/28269379/curve-curvature-in-numpy
Args:
curve (np.array): array of points representing the curve
Returns:
np.array: array of points representing the normal vector at each point in curve array
>>> curvature(np.array(([0, 0, 0], [0, 0, 1], [1, 0, 1])))
[[ 0.92387953 0. -0.38268343]
[ 0.70710678 0. -0.70710678]
[ 0.38268343 0. -0.92387953]]
"""
curve_points = np.array(curve)
dx_dt = np.gradient(curve_points[:, 0])
dy_dt = np.gradient(curve_points[:, 1])
dz_dt = np.gradient(curve_points[:, 2])
velocity = np.array([[dx_dt[i], dy_dt[i], dz_dt[i]]
for i in range(dx_dt.size)])
ds_dt = np.sqrt(dx_dt * dx_dt + dy_dt * dy_dt + dz_dt * dz_dt)
tangent = np.array([1/ds_dt]).transpose() * velocity
tangent_x = tangent[:, 0]
tangent_y = tangent[:, 1]
tangent_z = tangent[:, 2]
deriv_tangent_x = np.gradient(tangent_x)
deriv_tangent_y = np.gradient(tangent_y)
deriv_tangent_z = np.gradient(tangent_z)
dT_dt = np.array([[deriv_tangent_x[i], deriv_tangent_y[i], deriv_tangent_z[i]]
for i in range(deriv_tangent_x.size)])
length_dT_dt = np.sqrt(
deriv_tangent_x * deriv_tangent_x + deriv_tangent_y * deriv_tangent_y + deriv_tangent_z * deriv_tangent_z + 0.0001)
normal = np.array([1/length_dT_dt]).transpose() * dT_dt
return normal
def offset(curve, distance, normals):
if len(normals) != len(curve):
raise ValueError(
'Number of normals and number of points in the curve do not match')
# Offsetting
offset_segments = [segment.parallel(
(curve[i], curve[i+1]), distance, normals[i]) for i in range(len(curve) - 1)]
# Combining segments
combined_curve = []
combined_curve.append(np.round(offset_segments[0][0]).tolist())
for i in range(0, len(offset_segments)-1):
combined_curve.append(segment.middle_point(
offset_segments[i][1], offset_segments[i+1][0]))
combined_curve.append(np.round(offset_segments[-1][1]).tolist())
return combined_curve
def resolution_distance(target_points, spacing_distance):
length = 0
for i in range(len(target_points) - 1):
length += sqrt(
((target_points[i][0] - target_points[i + 1][0]) ** 2)
+ ((target_points[i][1] - target_points[i + 1][1]) ** 2)
+ ((target_points[i][2] - target_points[i + 1][2]) ** 2)
)
return round(length / spacing_distance), length
def simplify_segments(points, epsilon):
if len(points) < 3:
return points
# Find the point with the maximum distance
max_distance = 0
max_index = 0
end_index = len(points) - 1
for i in range(1, end_index):
distance = get_distance(points[i], points[0])
if distance > max_distance:
max_distance = distance
max_index = i
simplified_points = []
# If the maximum distance is greater than epsilon, recursively simplify
if max_distance > epsilon:
rec_results1 = simplify_segments(points[:max_index+1], epsilon)
rec_results2 = simplify_segments(points[max_index:], epsilon)
# Combine the simplified sub-results
simplified_points.extend(rec_results1[:-1])
simplified_points.extend(rec_results2)
else:
# The maximum distance is less than epsilon, retain the endpoints
simplified_points.append(points[0])
simplified_points.append(points[end_index])
return simplified_points

View File

@@ -0,0 +1,176 @@
import numpy as np
def parallel(segment, distance, normal=np.array([0, 1, 0])):
"""Get parallel segment in 3D space at a distance.
Args:
segment (np.array, np.array): start and end points of the segement.
distance (int): distance between both segment. Thickness in the context of a line. Positive direction means left.
Returns:
(np.array(), np.array()): parallel segment.
>>> parrallel(((0, 0, 0), (0, 0, 10)), 10))
(array([-10., 0., 0.]), array([-10., 0., 10.]))
"""
return (orthogonal(segment[0], segment[1], distance, normal), orthogonal(segment[1], segment[0], -distance, normal))
def normalized(vector):
magnitude = np.linalg.norm(vector)
if magnitude != 0:
normalized_vector = vector / magnitude
return normalized_vector
else:
return [0, 0, 0]
def orthogonal(origin, point, distance, normal=np.array([0, 1, 0])):
"""Get orthogonal point from a given one at the specified distance in 3D space with normal direction.
Args:
origin (tuple or np.array): origin
point (tuple or np.array): (point-origin) makes the first vector. Only the direction is used.
distance (int): distance from the origin. Thickness in the context of a line. Positive direction means left.
normal (list or np.array, optional): second vector. Defaults to the vertical [0, 1, 0].
Raises:
ValueError: if vectors are not linearly independent.
Returns:
np.array: (x y z)
>>> orthogonal((5, 5, 5), (150, 5, 5), 10)
[ 5. 5. 15.]
"""
vector = np.subtract(point, origin)
normalized_vector = normalized(vector)
normalized_normal = normalized(normal)
orthogonal = np.cross(normalized_vector, normalized_normal)
if np.array_equal(orthogonal, np.zeros((3,))):
print(normalized_vector, normalized_normal, orthogonal, normal)
print(origin, point, distance)
raise ValueError("The input vectors are not linearly independent.")
orthogonal = np.add(np.multiply(orthogonal, distance), origin).astype(int)
return orthogonal
def discrete_segment(start_point, end_point, pixel_perfect=True):
"""
Calculate a line between two points in 3D space.
https://www.geeksforgeeks.org/bresenhams-algorithm-for-3-d-line-drawing/
Args:
start_point (tuple): (x, y, z) First coordinates.
end_point (tuple): (x, y, z) Second coordinates.
pixel_perfect (bool, optional): If true, remove unnecessary coordinates connecting to other coordinates side by side, leaving only a diagonal connection. Defaults to True.
Returns:
list: List of coordinates.
"""
(x1, y1, z1) = start_point
(x2, y2, z2) = end_point
x1, y1, z1, x2, y2, z2 = (
round(x1),
round(y1),
round(z1),
round(x2),
round(y2),
round(z2),
)
points = []
points.append((x1, y1, z1))
dx = abs(x2 - x1)
dy = abs(y2 - y1)
dz = abs(z2 - z1)
if x2 > x1:
xs = 1
else:
xs = -1
if y2 > y1:
ys = 1
else:
ys = -1
if z2 > z1:
zs = 1
else:
zs = -1
# Driving axis is X-axis
if dx >= dy and dx >= dz:
p1 = 2 * dy - dx
p2 = 2 * dz - dx
while x1 != x2:
x1 += xs
points.append((x1, y1, z1))
if p1 >= 0:
y1 += ys
if not pixel_perfect:
if points[-1][1] != y1:
points.append((x1, y1, z1))
p1 -= 2 * dx
if p2 >= 0:
z1 += zs
if not pixel_perfect:
if points[-1][2] != z1:
points.append((x1, y1, z1))
p2 -= 2 * dx
p1 += 2 * dy
p2 += 2 * dz
# Driving axis is Y-axis
elif dy >= dx and dy >= dz:
p1 = 2 * dx - dy
p2 = 2 * dz - dy
while y1 != y2:
y1 += ys
points.append((x1, y1, z1))
if p1 >= 0:
x1 += xs
if not pixel_perfect:
if points[-1][0] != x1:
points.append((x1, y1, z1))
p1 -= 2 * dy
if p2 >= 0:
z1 += zs
if not pixel_perfect:
if points[-1][2] != z1:
points.append((x1, y1, z1))
p2 -= 2 * dy
p1 += 2 * dx
p2 += 2 * dz
# Driving axis is Z-axis
else:
p1 = 2 * dy - dz
p2 = 2 * dx - dz
while z1 != z2:
z1 += zs
points.append((x1, y1, z1))
if p1 >= 0:
y1 += ys
if not pixel_perfect:
if points[-1][1] != y1:
points.append((x1, y1, z1))
p1 -= 2 * dz
if p2 >= 0:
x1 += xs
if not pixel_perfect:
if points[-1][0] != x1:
points.append((x1, y1, z1))
p2 -= 2 * dz
p1 += 2 * dy
p2 += 2 * dx
return points
def middle_point(start_point, end_point):
return (np.round((start_point[0] + end_point[0]) / 2.0).astype(int),
np.round((start_point[1] + end_point[1]) / 2.0).astype(int),
np.round((start_point[2] + end_point[2]) / 2.0).astype(int),
)