import k3d
import numpy as np
# for clear functions
import ddg
import ddg._error_messages as em
from ddg.conversion.arrays import Curve, CurveList, Mesh, Points
from ddg.math.discrete_objects import triangulated_faces
point_size = 0.1
line_size = 0.01
_conversion_modes = ["FACES", "EDGES", "POINTS"]
def _raise_mode_not_matched():
raise ValueError(f"Mesh mode should be a value in {_conversion_modes}")
def _from_points(points, color):
"""Convert points array to k3d.objects.Points
Parameters
----------
points: array_like of shape (_, 3)
The positions of the points in 3D euclidean space.
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
Returns
-------
k3d.objects.Points
"""
points = points.astype(np.float32)
return k3d.points(points, point_size=point_size, shader="mesh", color=color)
def _from_curve(curve, color, mode):
"""Convert curve array to instance of k3d.objects.Drawable
Parameters
----------
curve: ddg.conversion.arrays.Curve
a curve in R^3
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
mode: str in ["FACES", "EDGES", "POINTS"]
Conversion mode.
Returns
-------
k3d.objects.Points or k3d.objects.Line
"""
points, periodicity = curve
points = points.astype(np.float32)
match mode:
case "FACES" | "EDGES":
if periodicity:
points = np.vstack((points, points[0]))
return k3d.line(points, width=line_size, shader="mesh", color=color)
case "POINTS":
return _from_points(points, color)
case _:
_raise_mode_not_matched()
def _from_mesh(mesh_array, color, mode):
"""Convert mesh array to instance of k3d.objects.Drawable
Parameters
----------
mesh_array: ddg.conversion.arrays.Mesh
a mesh in R^3
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
mode: str in ["FACES", "EDGES", "POINTS"]
Conversion mode.
Returns
-------
k3d.objects.Points, k3d.objects.Mesh or k3d.objects.Lines
"""
points, faces, _ = mesh_array
if len(points) == 0:
# empty object
return None
points = points.astype(np.float32)
faces = faces.astype(np.uint32)
match mode:
case "FACES":
faces = triangulated_faces(faces)
mesh = k3d.mesh(
points, faces, side="double", flat_shading=False, color=color
)
return mesh
case "EDGES":
edges = ddg.conversion.arrays.edges(mesh_array).astype(np.float32)
lines = k3d.lines(
points,
edges,
indices_type="segment",
width=line_size,
shader="mesh",
color=color,
)
return lines
case "POINTS":
return _from_points(points, color)
case _:
_raise_mode_not_matched()
def _to_k3d(convertible, color=0x888888, mode="FACES"):
"""Convert pyddg objects, points, curves and meshes to K3D objects.
Parameters
----------
convertible:
The object to convert
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
mode: str in ["FACES", "EDGES", "POINTS"]
Conversion mode.
Returns
-------
k3d.objects.Group
"""
arrays = ddg.conversion.arrays._arrays(convertible)
embedded = ddg.conversion.arrays._embed(arrays)
objects = []
match embedded:
case Points(points):
objects.append(_from_points(points, color))
case Curve() as curve:
objects.append(_from_curve(curve, color, mode))
case CurveList() as curves:
objects += [_from_curve(curve, color, mode) for curve in curves]
case Mesh() as mesh:
obj = _from_mesh(mesh, color, mode)
if obj is not None:
objects.append(obj)
else:
# emtpy object
pass
case _:
raise Exception(em.should_never_happen)
group = k3d.objects.Group(objects)
for obj in group:
obj.color = color
if isinstance(obj, k3d.objects.Line):
obj.width = line_size
return group
[docs]def to_drawable(convertible, color=0x888888):
"""Convert pyddg objects, points, curves and meshes to K3D objects.
Default conversion mode showing faces.
Parameters
----------
convertible:
The object to convert
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
Returns
-------
k3d.objects.Group
"""
return _to_k3d(convertible, color=color, mode="FACES")
[docs]def to_tubes(convertible, color=0x000000):
"""Convert pyddg objects, points, curves and meshes to K3D objects.
Special conversion mode showing edges.
Parameters
----------
convertible:
The object to convert
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
Returns
-------
k3d.objects.Group
"""
return _to_k3d(convertible, color=color, mode="EDGES")
[docs]def to_spheres(convertible, color=0x000000):
"""Convert pyddg objects, points, curves and meshes to K3D objects.
Special conversion mode showing points as spheres.
Parameters
----------
convertible:
The object to convert
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
Returns
-------
k3d.objects.Group
"""
return _to_k3d(convertible, color=color, mode="POINTS")
[docs]def to_tubes_and_spheres(convertible, color=0x000000):
"""Convert pyddg objects, points, curves and meshes to K3D objects.
Special conversion mode showing points and edges
as tubes and spheres.
Parameters
----------
convertible:
The object to convert
color: int, hexadecimal between 0x000000 and 0xffffff
RGB color value as hexadecimal interger
Returns
-------
k3d.objects.Group
"""
tubes = to_tubes(convertible, color=color)
spheres = to_spheres(convertible, color=color)
return k3d.objects.Group(tubes + spheres)
[docs]def setup_plot():
"""Setup and return a K3D plot.
Returns
-------
k3d.plot.Plot
"""
return k3d.plot(
grid_visible=False, camera_mode="orbit", camera_auto_fit=False, antialias=5
)
[docs]def clear_plot(plot):
"""Remove objects from plot
Parameters
----------
plot: k3d.plot.Plot
Plot from which to remove the objects from.
"""
while plot.objects:
obj = plot.objects[0]
plot -= obj
[docs]def show_3d(*drawables):
"""Plot a 3D scene containing the drawables.
Parameters
----------
drawables: k3d.objects.Drawable
The objects to plot.
Returns
-------
k3d.plot.Plot
"""
plot = setup_plot()
camera_positon = [0, -8, 3]
camera_target = [0, 0, 0]
camera_up = [0, 0, 1]
plot.camera = camera_positon + camera_target + camera_up
for drawable in drawables:
plot += drawable
return plot
[docs]def show_2d(*drawables):
"""Plot a 2D scene containing the drawables.
Parameters
----------
drawables: k3d.objects.Drawable
The objects to plot.
Returns
-------
k3d.plot.Plot
"""
plot = setup_plot()
plot.camera_fov = 90
plot.lighting = 0
camera_positon = [0, 0, 3]
camera_target = [0, 0, 0]
camera_up = [0, 1, 0]
plot.camera = camera_positon + camera_target + camera_up
for drawable in drawables:
plot += drawable
return plot