"""Concrete shape."""
import math
from math import pi
from typing import Union, List, Tuple
import numpy as np
from sympy import symbols, Abs, sqrt, Max, Min, cos, sin, log, sign, Heaviside, asin
from sympy.vector import CoordSys3D
from .geo import Edge, Geometry1D, Geometry2D, Geometry3D
__all__ = [
"Line1D",
"Line",
"Tube2D",
"Rectangle",
"Circle",
"Heart",
"Triangle",
"Polygon",
"Plane",
"Tube3D",
"Tube",
"CircularTube",
"Box",
"Sphere",
"Cylinder",
]
[docs]class Line1D(Geometry1D):
def __init__(self, point_1, point_2):
x, none = symbols("x none")
ranges = {none: (0, 1)}
edge_1 = Edge(functions={"x": point_1, "normal_x": -1}, area=1.0, ranges=ranges)
edge_2 = Edge(functions={"x": point_2, "normal_x": 1}, area=1.0, ranges=ranges)
self.edges = [edge_1, edge_2]
dist = point_2 - point_1
center_x = point_1 + dist / 2
self.sdf = dist / 2 - Abs(x - center_x)
self.bounds = {"x": (point_1, point_2)}
[docs]class Line(Geometry2D):
def __init__(self, point_1, point_2, normal=1):
x, y, l = symbols("x y l") # noqa
ranges = {l: (0, 1)}
dist_x = point_2[0] - point_1[0]
dist_y = point_2[1] - point_1[1]
normal_vector = (-dist_y * normal, dist_x * normal)
normal_norm = math.sqrt(normal_vector[0] ** 2 + normal_vector[1] ** 2)
normal_vector = (normal_vector[0] / normal_norm, normal_vector[1] / normal_norm)
line_1 = Edge(
functions={
"x": point_1[0] + l * dist_x,
"y": point_1[1] + l * dist_y,
"normal_x": normal_vector[0],
"normal_y": normal_vector[1],
},
ranges=ranges,
area=normal_norm,
)
self.edges = [line_1]
self.sdf = ((x - point_1[0]) * dist_y - (y - point_1[1]) * dist_x) / normal_norm
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
}
[docs]class Tube2D(Geometry2D):
def __init__(self, point_1, point_2):
l, y = symbols("l y")
ranges = {l: (0, 1)}
dist_x = point_2[0] - point_1[0]
dist_y = point_2[1] - point_1[1]
line_1 = Edge(
functions={
"x": l * dist_x + point_1[0],
"y": point_1[1],
"normal_x": 0,
"normal_y": -1,
},
ranges=ranges,
area=dist_x,
)
line_2 = Edge(
functions={
"x": l * dist_x + point_1[0],
"y": point_2[1],
"normal_x": 0,
"normal_y": 1,
},
ranges=ranges,
area=dist_x,
)
self.edges = [line_1, line_2]
center_y = point_1[1] + (dist_y) / 2
y_diff = Abs(y - center_y) - (point_2[1] - center_y)
outside_distance = sqrt(Max(y_diff, 0) ** 2)
inside_distance = Min(y_diff, 0)
self.sdf = -(outside_distance + inside_distance)
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
}
[docs]class Rectangle(Geometry2D):
def __init__(self, point_1, point_2):
l, x, y = symbols("l x y")
ranges = {l: (0, 1)}
dist_x = point_2[0] - point_1[0]
dist_y = point_2[1] - point_1[1]
edge_1 = Edge(
functions={
"x": l * dist_x + point_1[0],
"y": point_1[1],
"normal_x": 0,
"normal_y": -1,
},
ranges=ranges,
area=dist_x,
)
edge_2 = Edge(
functions={
"x": point_2[0],
"y": l * dist_y + point_1[1],
"normal_x": 1,
"normal_y": 0,
},
ranges=ranges,
area=dist_y,
)
edge_3 = Edge(
functions={
"x": l * dist_x + point_1[0],
"y": point_2[1],
"normal_x": 0,
"normal_y": 1,
},
ranges=ranges,
area=dist_x,
)
edge_4 = Edge(
functions={
"x": point_1[0],
"y": -l * dist_y + point_2[1],
"normal_x": -1,
"normal_y": 0,
},
ranges=ranges,
area=dist_y,
)
self.edges = [edge_1, edge_2, edge_3, edge_4]
center_x = point_1[0] + (dist_x) / 2
center_y = point_1[1] + (dist_y) / 2
x_diff = Abs(x - center_x) - (point_2[0] - center_x)
y_diff = Abs(y - center_y) - (point_2[1] - center_y)
outside_distance = sqrt(Max(x_diff, 0) ** 2 + Max(y_diff, 0) ** 2)
inside_distance = Min(Max(x_diff, y_diff), 0)
self.sdf = -(outside_distance + inside_distance)
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
}
[docs]class Circle(Geometry2D):
def __init__(self, center, radius):
theta, x, y = symbols("theta x y")
ranges = {theta: (0, 2 * math.pi)}
edge = Edge(
functions={
"x": center[0] + radius * cos(theta),
"y": center[1] + radius * sin(theta),
"normal_x": 1 * cos(theta),
"normal_y": 1 * sin(theta),
},
ranges=ranges,
area=2 * pi * radius,
)
self.edges = [edge]
self.sdf = radius - sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
self.bounds = {
"x": (center[0] - radius, center[0] + radius),
"y": (center[1] - radius, center[1] + radius),
}
[docs]class Heart(Geometry2D):
def __init__(self, center=(0, 0.5), radius=0.5):
c1, c2 = center
theta, t, x, y = symbols("t theta x y")
ranges = {theta: (0, math.pi), t: (0, 1)}
edge_1 = Edge(
functions={
"x": center[0] - t * radius,
"y": center[1] - (1 - t) * radius,
"normal_x": -1.0,
"normal_y": -1.0,
},
ranges=ranges,
area=math.sqrt(2) * radius,
)
edge_2 = Edge(
functions={
"x": center[0] + t * radius,
"y": center[1] - (1 - t) * radius,
"normal_x": 1.0,
"normal_y": -1.0,
},
ranges=ranges,
area=math.sqrt(2) * radius,
)
edge_3 = Edge(
functions={
"x": center[0]
- radius / 2
+ radius / math.sqrt(2) * cos(math.pi / 4 * 5 - theta),
"y": center[1]
+ radius / 2
+ radius / math.sqrt(2) * sin(math.pi / 4 * 5 - theta),
"normal_x": cos(math.pi / 4 * 5 - theta),
"normal_y": sin(math.pi / 4 * 5 - theta),
},
ranges=ranges,
area=math.sqrt(2) * radius * math.pi,
)
edge_4 = Edge(
functions={
"x": center[0]
+ radius / 2
+ radius / math.sqrt(2) * cos(math.pi / 4 * 3 - theta),
"y": center[1]
+ radius / 2
+ radius / math.sqrt(2) * sin(math.pi / 4 * 3 - theta),
"normal_x": cos(math.pi / 4 * 3 - theta),
"normal_y": sin(math.pi / 4 * 3 - theta),
},
ranges=ranges,
area=math.sqrt(2) * radius * math.pi,
)
self.edges = [edge_1, edge_2, edge_3, edge_4]
x, y = symbols("x y")
x = (x - c1) * 0.5 / radius
y = (y - c2) * 0.5 / radius + 0.5
part1 = Heaviside(Abs(x) + y - 1) * (
sqrt((Abs(x) - 0.25) ** 2 + (y - 0.75) ** 2) - math.sqrt(2) / 4
)
part_i = 0.5 * Max(Abs(x) + y, 0)
part2 = (
(1 - Heaviside(Abs(x) + y - 1))
* sign(Abs(x) - y)
* Min(
sqrt(Abs(x) ** 2 + (y - 1) ** 2),
sqrt((Abs(x) - part_i) ** 2 + (y - part_i) ** 2),
)
)
self.sdf = (-part1 - part2) * radius * 2
self.bounds = {
"x": (
center[0] - 0.5 * radius - 0.5 * math.sqrt(2) * radius,
center[0] + 0.5 * radius + 0.5 * math.sqrt(2) * radius,
),
"y": (
center[1] - radius,
center[1] + 0.5 * radius + 0.5 * math.sqrt(2) * radius,
),
}
[docs]class Triangle(Geometry2D):
def __init__(self, p0, p1, p2):
x, y, t = symbols("x y t")
N = CoordSys3D("N")
P0 = p0[0] * N.i + p0[1] * N.j
P1 = p1[0] * N.i + p1[1] * N.j
P2 = p2[0] * N.i + p2[1] * N.j
p = x * N.i + y * N.j
e0, e1, e2 = P1 - P0, P2 - P1, P0 - P2
v0, v1, v2 = p - P0, p - P1, p - P2
pq0 = v0 - e0 * Max(Min(v0.dot(e0) / e0.dot(e0), 1), 0)
pq1 = v1 - e1 * Max(Min(v1.dot(e1) / e1.dot(e1), 1), 0)
pq2 = v2 - e2 * Max(Min(v2.dot(e2) / e2.dot(e2), 1), 0)
s = sign(e0.dot(N.i) * e2.dot(N.j) - e0.dot(N.j) * e2.dot(N.i))
u = sqrt(Min(pq0.dot(pq0), pq1.dot(pq1), pq2.dot(pq2)))
v = Min(
s * (v0.dot(N.i) * e0.dot(N.j) - v0.dot(N.j) * e0.dot(N.i)),
s * (v1.dot(N.i) * e1.dot(N.j) - v1.dot(N.j) * e1.dot(N.i)),
s * (v2.dot(N.i) * e2.dot(N.j) - v2.dot(N.j) * e2.dot(N.i)),
)
self.sdf = u * sign(v)
l0 = sqrt(e0.dot(e0))
l1 = sqrt(e1.dot(e1))
l2 = sqrt(e2.dot(e2))
ranges = {t: (0, 1)}
in_out_sign = -sign(e0.cross(e1).dot(N.k))
edge_1 = Edge(
functions={
"x": p1[0] + t * (p0[0] - p1[0]),
"y": p1[1] + t * (p0[1] - p1[1]),
"normal_x": (p0[1] - p1[1]) / l0 * in_out_sign,
"normal_y": (p1[0] - p0[0]) / l0 * in_out_sign,
},
ranges=ranges,
area=l0,
)
edge_2 = Edge(
functions={
"x": p2[0] + t * (p1[0] - p2[0]),
"y": p2[1] + t * (p1[1] - p2[1]),
"normal_x": (p1[1] - p2[1]) / l1 * in_out_sign,
"normal_y": (p2[0] - p1[0]) / l1 * in_out_sign,
},
ranges=ranges,
area=l1,
)
edge_3 = Edge(
functions={
"x": p0[0] + t * (p2[0] - p0[0]),
"y": p0[1] + t * (p2[1] - p0[1]),
"normal_x": (p2[1] - p0[1]) / l2 * in_out_sign,
"normal_y": (p0[0] - p2[0]) / l2 * in_out_sign,
},
ranges=ranges,
area=l2,
)
self.edges = [edge_1, edge_2, edge_3]
self.bounds = {
"x": (min(p0[0], p1[0], p2[0]), max(p0[0], p1[0], p2[0])),
"y": (min(p0[1], p1[1], p2[1]), max(p0[1], p1[1], p2[1])),
}
[docs]class Polygon(Geometry2D):
def __init__(self, points):
v = points
t = symbols("t")
ranges = {t: (0, 1)}
def _sdf(x: np.ndarray, y: np.ndarray, **kwargs):
s = np.ones_like(x)
_points = np.concatenate([x, y], axis=1)
d = ((np.array(v[0]) - _points) ** 2).sum(axis=1, keepdims=True)
for i in range(len(v)):
e = np.array(v[i - 1]) - np.array(v[i])
w = _points - np.array(v[i])
b = w - e * np.clip(
(w * e).sum(axis=1, keepdims=True) / (e * e).sum(), 0, 1
)
d = np.minimum(d, (b * b).sum(keepdims=True, axis=1))
cond1 = _points[:, 1:] >= v[i][1]
cond2 = _points[:, 1:] < v[i - 1][1]
cond3 = e[0] * w[:, 1:] > e[1] * w[:, :1]
inverse_idx1 = np.all([cond1, cond2, cond3], axis=0)
inverse_idx2 = np.all(
[
np.logical_not(cond1),
np.logical_not(cond2),
np.logical_not(cond3),
],
axis=0,
)
inverse_idx = np.any([inverse_idx1, inverse_idx2], axis=0)
s[inverse_idx] *= -1
return -np.sqrt(d) * s
self.sdf = _sdf
self.edges = []
for i, _ in enumerate(points):
length = math.sqrt(
(points[i - 1][0] - points[i][0]) ** 2
+ (points[i - 1][1] - points[i][1]) ** 2
)
edge = Edge(
functions={
"x": points[i - 1][0] - t * (points[i - 1][0] - points[i][0]),
"y": points[i - 1][1] - t * (points[i - 1][1] - points[i][1]),
"normal_x": (points[i][1] - points[i - 1][1]) / length,
"normal_y": (points[i - 1][0] - points[i][0]) / length,
},
ranges=ranges,
area=length,
)
self.edges.append(edge)
_p = iter(zip(*points))
_p1 = next(_p)
_p2 = next(_p)
self.bounds = {"x": (min(_p1), max(_p1)), "y": (min(_p2), max(_p2))}
[docs] def translation(self, direction: Union[List, Tuple]):
raise NotImplementedError
[docs] def rotation(self, angle: float, axis: str = "z", center=None):
raise NotImplementedError
[docs] def scaling(self, scale: float, center: Tuple = None):
raise NotImplementedError
[docs]class Plane(Geometry3D):
def __init__(self, point_1, point_2, normal):
assert point_1[0] == point_2[0], "Points must have the same x coordinate"
x, y, z, s_1, s_2 = symbols("x y z s_1 s_2")
center = (
point_1[0] + (point_2[0] - point_1[0]) / 2,
point_1[1] + (point_2[1] - point_1[1]) / 2,
point_1[2] + (point_2[2] - point_1[2]) / 2,
)
side_y = point_2[1] - point_1[1]
side_z = point_2[2] - point_1[2]
ranges = {s_1: (-1, 1), s_2: (-1, 1)}
edge = Edge(
functions={
"x": center[0],
"y": center[1] + 0.5 * s_1 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": 1e-10 + normal, # TODO rm 1e-10
"normal_y": 0,
"normal_z": 0,
},
ranges=ranges,
area=side_y * side_z,
)
self.edges = [edge]
self.sdf = normal * (center[0] - x)
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
"z": (min(point_1[2], point_2[2]), max(point_1[2], point_2[2])),
}
[docs]class Tube3D(Geometry3D):
def __init__(self, point_1, point_2):
x, y, z, s_1, s_2 = symbols("x y z s_1 s_2")
center = (
point_1[0] + (point_2[0] - point_1[0]) / 2,
point_1[1] + (point_2[1] - point_1[1]) / 2,
point_1[2] + (point_2[2] - point_1[2]) / 2,
)
side_x = point_2[0] - point_1[0]
side_y = point_2[1] - point_1[1]
side_z = point_2[2] - point_1[2]
ranges = {s_1: (-1, 1), s_2: (-1, 1)}
edge_1 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] + 0.5 * s_2 * side_y,
"z": center[2] + 0.5 * side_z,
"normal_x": 0,
"normal_y": 0,
"normal_z": 1,
},
ranges=ranges,
area=side_x * side_y,
)
edge_2 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] + 0.5 * s_2 * side_y,
"z": center[2] - 0.5 * side_z,
"normal_x": 0,
"normal_y": 0,
"normal_z": -1,
},
ranges=ranges,
area=side_x * side_y,
)
edge_3 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] + 0.5 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": 0,
"normal_y": 1,
"normal_z": 0,
},
ranges=ranges,
area=side_x * side_z,
)
edge_4 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] - 0.5 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": 0,
"normal_y": -1,
"normal_z": 0,
},
ranges=ranges,
area=side_x * side_z,
)
self.edges = [edge_1, edge_2, edge_3, edge_4]
y_dist = Abs(y - center[1]) - 0.5 * side_y
z_dist = Abs(z - center[2]) - 0.5 * side_z
outside_distance = sqrt(Max(y_dist, 0) ** 2 + Max(z_dist, 0) ** 2)
inside_distance = Min(Max(y_dist, z_dist), 0)
self.sdf = -(outside_distance + inside_distance)
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
"z": (min(point_1[2], point_2[2]), max(point_1[2], point_2[2])),
}
[docs]class Tube(Tube3D):
def __init__(self, point_1, point_2):
super(Tube, self).__init__(point_1, point_2)
[docs]class CircularTube(Geometry3D):
def __init__(self, center, radius, height):
x, y, z, h, theta = symbols("x y z h theta")
ranges = {h: (-1, 1), theta: (0, 2 * pi)}
edge_1 = Edge(
functions={
"x": center[0] + radius * cos(theta),
"y": center[1] + radius * sin(theta),
"z": center[2] + 0.5 * h * height,
"normal_x": 1 * cos(theta),
"normal_y": 1 * sin(theta),
"normal_z": 0,
},
ranges=ranges,
area=height * 2 * pi * radius,
)
self.edges = [edge_1]
self.sdf = radius - sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
self.bounds = {
"x": (center[0] - radius, center[0] + radius),
"y": (center[1] - radius, center[1] + radius),
"z": (center[2] - height / 2, center[2] + height / 2),
}
[docs]class Box(Geometry3D):
def __init__(self, point_1, point_2):
x, y, z, s_1, s_2 = symbols("x y z s_1 s_2")
center = (
point_1[0] + (point_2[0] - point_1[0]) / 2,
point_1[1] + (point_2[1] - point_1[1]) / 2,
point_1[2] + (point_2[2] - point_1[2]) / 2,
)
side_x = point_2[0] - point_1[0]
side_y = point_2[1] - point_1[1]
side_z = point_2[2] - point_1[2]
ranges = {s_1: (-1, 1), s_2: (-1, 1)}
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
"z": (min(point_1[2], point_2[2]), max(point_1[2], point_2[2])),
}
edge_1 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] + 0.5 * s_2 * side_y,
"z": center[2] + 0.5 * side_z,
"normal_x": 0,
"normal_y": 0,
"normal_z": 1,
},
ranges=ranges,
area=side_x * side_y,
)
edge_2 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] + 0.5 * s_2 * side_y,
"z": center[2] - 0.5 * side_z,
"normal_x": 0,
"normal_y": 0,
"normal_z": -1,
},
ranges=ranges,
area=side_x * side_y,
)
edge_3 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] + 0.5 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": 0,
"normal_y": 1,
"normal_z": 0,
},
ranges=ranges,
area=side_x * side_z,
)
edge_4 = Edge(
functions={
"x": center[0] + 0.5 * s_1 * side_x,
"y": center[1] - 0.5 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": 0,
"normal_y": -1,
"normal_z": 0,
},
ranges=ranges,
area=side_x * side_z,
)
edge_5 = Edge(
functions={
"x": center[0] + 0.5 * side_x,
"y": center[1] + 0.5 * s_1 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": 1,
"normal_y": 0,
"normal_z": 0,
},
ranges=ranges,
area=side_y * side_z,
)
edge_6 = Edge(
functions={
"x": center[0] - 0.5 * side_x,
"y": center[1] + 0.5 * s_1 * side_y,
"z": center[2] + 0.5 * s_2 * side_z,
"normal_x": -1,
"normal_y": 0,
"normal_z": 0,
},
ranges=ranges,
area=side_y * side_z,
)
self.edges = [edge_1, edge_2, edge_3, edge_4, edge_5, edge_6]
x_dist = Abs(x - center[0]) - 0.5 * side_x
y_dist = Abs(y - center[1]) - 0.5 * side_y
z_dist = Abs(z - center[2]) - 0.5 * side_z
outside_distance = sqrt(
Max(x_dist, 0) ** 2 + Max(y_dist, 0) ** 2 + Max(z_dist, 0) ** 2
)
inside_distance = Min(Max(x_dist, y_dist, z_dist), 0)
self.sdf = -(outside_distance + inside_distance)
self.bounds = {
"x": (min(point_1[0], point_2[0]), max(point_1[0], point_2[0])),
"y": (min(point_1[1], point_2[1]), max(point_1[1], point_2[1])),
"z": (min(point_1[2], point_2[2]), max(point_1[2], point_2[2])),
}
[docs]class Sphere(Geometry3D):
def __init__(self, center, radius):
x, y, z, v_1, v_2 = symbols("x y z v_1 v_2")
ranges = {v_1: (0, 1), v_2: (0, 1)}
theta = 2 * pi * v_1
r = sqrt(v_2)
phi = 2 * asin(r) # latitude
r_1 = cos(theta) * sin(phi)
r_2 = sin(theta) * sin(phi)
r_3 = cos(phi)
# x, y, z, v_1, v_2, u_1, u_2 = symbols("x y z v_1 v_2 u_1 u_2")
# ranges = {v_1: (0, 1), v_2: (0, 1), u_1: (0, 1), u_2: (0, 1)}
# r_1 = sqrt(-log(v_1)) * cos(2 * pi * u_1)
# r_2 = sqrt(-log(v_1)) * sin(2 * pi * u_1)
# r_3 = sqrt(-log(v_2)) * cos(2 * pi * u_2)
norm = sqrt(r_1 ** 2 + r_2 ** 2 + r_3 ** 2)
edge_1 = Edge(
functions={
"x": center[0] + radius * r_1 / norm,
"y": center[1] + radius * r_2 / norm,
"z": center[2] + radius * r_3 / norm,
"normal_x": r_1 / norm,
"normal_y": r_2 / norm,
"normal_z": r_3 / norm,
},
ranges=ranges,
area=4 * pi * radius ** 2,
)
self.edges = [edge_1]
self.sdf = radius - sqrt(
(x - center[0]) ** 2 + (y - center[1]) ** 2 + (z - center[2]) ** 2
)
self.bounds = {
"x": (center[0] - radius, center[0] + radius),
"y": (center[1] - radius, center[1] + radius),
"z": (center[2] - radius, center[2] + radius),
}
[docs]class Cylinder(Geometry3D):
def __init__(self, center, radius, height):
x, y, z, h, r, theta = symbols("x y z h r theta")
ranges = {h: (-1, 1), r: (0, 1), theta: (0, 2 * pi)}
edge_1 = Edge(
functions={
"x": center[0] + radius * cos(theta),
"y": center[1] + radius * sin(theta),
"z": center[2] + 0.5 * h * height,
"normal_x": 1 * cos(theta),
"normal_y": 1 * sin(theta),
"normal_z": 0,
},
ranges=ranges,
area=height * 2 * pi * radius,
)
edge_2 = Edge(
functions={
"x": center[0] + sqrt(r) * radius * cos(theta),
"y": center[1] + sqrt(r) * radius * sin(theta),
"z": center[2] + 0.5 * height,
"normal_x": 0,
"normal_y": 0,
"normal_z": 1,
},
ranges=ranges,
area=math.pi * radius ** 2,
)
edge_3 = Edge(
functions={
"x": center[0] + sqrt(r) * radius * cos(theta),
"y": center[1] + sqrt(r) * radius * sin(theta),
"z": center[2] - 0.5 * height,
"normal_x": 0,
"normal_y": 0,
"normal_z": -1,
},
ranges=ranges,
area=pi * radius ** 2,
)
self.edges = [edge_1, edge_2, edge_3]
r_dist = sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
z_dist = Abs(z - center[2])
outside_distance = sqrt(
Min(0, radius - r_dist) ** 2 + Min(0, 0.5 * height - z_dist) ** 2
)
inside_distance = -1 * Min(
Abs(Min(0, r_dist - radius)), Abs(Min(0, z_dist - 0.5 * height))
)
self.sdf = -(outside_distance + inside_distance)
self.bounds = {
"x": (center[0] - radius, center[0] + radius),
"y": (center[1] - radius, center[1] + radius),
"z": (center[2] - height / 2, center[2] + height / 2),
}