Skip to content

Instantly share code, notes, and snippets.

@nicoguaro
Last active June 18, 2024 02:04
Show Gist options
  • Save nicoguaro/560e7805693eb44c7854530d15158438 to your computer and use it in GitHub Desktop.
Save nicoguaro/560e7805693eb44c7854530d15158438 to your computer and use it in GitHub Desktop.
Visualize origami configurations using Matplotlib.
name: ori-py
channels:
- conda-forge
dependencies:
- python
- numpy
- scipy
- matplotlib
- jupyterlab
- pyvista
# -*- coding: utf-8 -*-
"""
Visualize origami panels in Matplotlib
@author: Nicolás Guarín-Zapata
@date: May 2024
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.colors import LightSource
nodes = np.array([
[0.000, 0, 0],
[0.707, 0, 0.707],
[1.414, 0, 0],
[0.000, 1, 0],
[0.707, 1, 0.707],
[1.414, 1, 0]])
panels = [
[0, 1, 4, 3],
[1, 2, 5, 4]]
x, y, z = nodes.T
nodes2 = nodes.copy()
nodes2[:, 2] += 0.01
poly3d = [nodes[panels[0]], nodes[panels[1]]]
poly3d2 = [nodes2[panels[0]], nodes2[panels[1]]]
#%%
ls = LightSource()
poly_collection = Poly3DCollection(poly3d, linewidths=1,
facecolors='#d86a96',
edgecolors="#3c3c3c", shade=True,
lightsource=ls)
poly_collection2 = Poly3DCollection(poly3d2, linewidths=1,
facecolors='#d86a96',
edgecolors="#3c3c3c", shade=True,
lightsource=ls)
#%% Visualization
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(poly_collection)
ax.add_collection3d(poly_collection2)
# Fix aspect ratio
max_range = np.array([x.max()-x.min(), y.max()-y.min(),
z.max()-z.min()]).max() / 2.0
mean_x = x.mean()
mean_y = y.mean()
mean_z = z.mean()
ax.set_xlim(mean_x - max_range, mean_x + max_range)
ax.set_ylim(mean_y - max_range, mean_y + max_range)
ax.set_zlim(mean_z - max_range, mean_z + max_range)
plt.show()
# -*- coding: utf-8 -*-
"""
Visualize origami panels in Matplotlib and
PyVista
@author: Nicolás Guarín-Zapata
@date: June 2024
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.colors import LightSource
import pyvista as pv
from PIL import Image
import os
def save_gif_PIL(outfile, files, fps=5, loop=0):
"""Helper function for saving GIFs
Parameters
----------
outfile : string
Path to the output file.
files : list
List of paths with the PNG files.
fps : int (optional)
Frames per second.
loop : int
The number of times the GIF should loop.
0 means that it will loop forever.
"""
imgs = [Image.open(file) for file in files]
imgs[0].save(fp=outfile, format='GIF', append_images=imgs[1:],
save_all=True, duration=int(1000/fps), loop=loop)
def plot_panels(nodes, panels, ax=None, **plot_kwargs):
"""Plot origami panels in 3D as a 3D collection of polygons
Parameters
----------
nodes : ndarray, float
Coordinates of the vertices (n_nodes, 3).
panels : list
List with the vertices number for each panel.
The numbers should be integers.
ax : Matplotlib.axes (optional)
Axes to add the graphic. None by default. If None is
passed it creates a new one.
fix_aspect_ratio : bool
Flag to fix the aspect ratio of the figure according
to the location of the nodes. True by default.
Returns
-------
ax : Matplotlib.axesg
Axes to add the graphic. If None is passed it creates a
new one.
"""
if ax is None:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
poly3d = list(nodes[panel] for panel in panels)
ls = LightSource()
poly_collection = Poly3DCollection(poly3d,
shade=True,
lightsource=ls,
**plot_kwargs)
ax.add_collection3d(poly_collection)
return ax
def plot_panels_pv(nodes, panels, plotter=None, **plot_kwargs):
"""Plot origami panels in 3D as a 3D collection of polygons
Parameters
----------
nodes : ndarray, float
Coordinates of the vertices (n_nodes, 3).
panels : list
List with the vertices number for each panel.
The numbers should be integers.
Returns
-------
ax : Matplotlib.axesg
Axes to add the graphic. If None is passed it creates a
new one.
"""
if plotter is None:
plotter = pv.Plotter()
faces = list([len(panel), *panel] for panel in panels)
faces = np.hstack(faces)
surf = pv.PolyData(nodes, faces)
plotter.add_mesh(surf, **plot_kwargs)
return plotter
if __name__ == "__main__":
# Example with 3 panels
nodes = np.array([
[0.000, 0, 0],
[0.707, 0, 0.707],
[1.414, 0, 0],
[0.000, 1, 0],
[0.707, 1, 0.707],
[1.414, 1, 0.707]])
# One square and two triangles
panels = [
[0, 1, 4, 3],
[1, 2, 4],
[2, 5, 4]]
# PyVista
pyvista_kwargs = {"line_width": 1, "show_edges": True, "lighting": True,
"color": "#d86a96"}
pl = pv.Plotter()
plot_panels_pv(nodes, panels, plotter=pl, **pyvista_kwargs)
pl.enable_shadows()
pl.show()
# Matplotlib
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plot_kwargs_wire = {"linewidths": 1, "edgecolors": "#3c3c3c",
"facecolors": "#d86a96", "alpha": 0.4}
plot_kwargs = {"linewidths": 1, "edgecolors": "#3c3c3c",
"facecolors": "#d86a96"}
nodes2 = nodes.copy()
niter = 10
dz = 1/niter
files = []
for cont in range(niter):
plt.cla()
nodes2[:, 2] += dz
plot_panels(nodes, panels, ax=ax, **plot_kwargs_wire)
plot_panels(nodes2, panels, ax=ax, **plot_kwargs)
ax.auto_scale_xyz([0, 1.5], [0, 1], [0, 2])
plt.axis("image")
file = f"ori_{str(cont).zfill(2)}.png"
plt.savefig(file)
files.append(file)
plt.show()
save_gif_PIL("ori_anim.gif", files, fps=5, loop=0)
[os.remove(file) for file in files]
# -*- coding: utf-8 -*-
"""
Visualize Miura Pattern using Matplotlib
@author: Nicolás Guarín-Zapata
@date: June 2024
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from plot_ori_panels import plot_panels, save_gif_PIL
data = np.load("miura_folding_data.npy", allow_pickle=True).item()
nodes_ini = data["Node"]
nodes_his = data["Node_history"]
nodes_his = np.stack(nodes_his)
panels = data["Panel"]
mins = nodes_his.min(axis=0).min(axis=0)
maxs = nodes_his.max(axis=0).max(axis=0)
plot_kwargs_wire = {"linewidths": 1, "edgecolors": "#3c3c3c22",
"alpha": 0.0}
plot_kwargs = {"linewidths": 1, "edgecolors": "#192231",
"facecolors": "#99C961"}
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
files = []
for cont in range(60):
plt.cla()
plot_panels(nodes_ini - np.array([0, 0, 0.1]), panels, ax=ax, **plot_kwargs_wire)
plot_panels(nodes_his[cont,:,:], panels, ax=ax, **plot_kwargs)
plt.xticks([0, 5, 10, 15, 20])
plt.yticks([0, 5, 10, 15, 20])
ax.set_zticks([0, 2])
ax.set(xticklabels=[], yticklabels=[], zticklabels=[])
ax.auto_scale_xyz([mins[0], maxs[0]],
[mins[1], maxs[1]],
[mins[2], maxs[2]])
plt.axis("image")
plt.tight_layout()
file = f"ori_{str(cont).zfill(2)}.png"
plt.savefig(file, dpi=600)
files.append(file)
save_gif_PIL("ori_anim.gif", files, fps=5, loop=0)
[os.remove(file) for file in files]
# -*- coding: utf-8 -*-
"""
Visualize Miura Pattern using PyVista
@author: Nicolás Guarín-Zapata
@date: June 2024
"""
#import os
import numpy as np
import pyvista as pv
from plot_ori_panels import plot_panels_pv
data = np.load("miura_folding_data.npy", allow_pickle=True).item()
nodes_ini = data["Node"]
nodes_his = data["Node_history"]
nodes_his = np.stack(nodes_his)
panels = data["Panel"]
plot_kwargs_wire = {"line_width": 0.5, "show_edges": True, "lighting": True,
"opacity": 0.5, "color": "#d86a96"}
plot_kwargs = {"line_width": 1, "show_edges": True, "lighting": True,
"color": "#d86a96"}
pl = pv.Plotter()
plot_panels_pv(nodes_ini, panels, plotter=pl, **plot_kwargs_wire)
plot_panels_pv(nodes_his[-1, :, :], panels, plotter=pl, **plot_kwargs)
pl.show()
# -*- coding: utf-8 -*-
"""
Visualize origami panels in Matplotlib
@author: Nicolás Guarín-Zapata
@date: May 2024
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.colors import LightSource
nodes = np.array([
[0.000, 0, 0],
[0.707, 0, 0.707],
[1.414, 0, 0],
[0.000, 1, 0],
[0.707, 1, 0.707],
[1.414, 1, 0]])
panels = [
[0, 1, 4, 3],
[1, 2, 5, 4]]
x, y, z = nodes.T
poly3d = [nodes[panels[0]], nodes[panels[1]]]
#%%
ls = LightSource()
poly_collection = Poly3DCollection(poly3d, linewidths=1,
facecolors='#d86a96',
edgecolors="#3c3c3c", shade=True,
lightsource=ls)
#%% Visualization
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(poly_collection)
# Fix aspect ratio
max_range = np.array([x.max()-x.min(), y.max()-y.min(),
z.max()-z.min()]).max() / 2.0
mean_x = x.mean()
mean_y = y.mean()
mean_z = z.mean()
ax.set_xlim(mean_x - max_range, mean_x + max_range)
ax.set_ylim(mean_y - max_range, mean_y + max_range)
ax.set_zlim(mean_z - max_range, mean_z + max_range)
plt.show()
# -*- coding: utf-8 -*-
"""
Visualize origami triangular panels in Matplotlib
@author: Nicolás Guarín-Zapata
@date: May 2024
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LightSource
nodes = np.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, -0.5],
[0, 1, 0]])
panels = [
[0, 1, 3],
[1, 2, 3]]
x, y, z = nodes.T
fig = plt.figure()
ls = LightSource()
ax = fig.add_subplot(projection='3d')
ax.plot_trisurf(x, y, z, triangles=panels, linewidth=1,
facecolor='#d86a96', edgecolor="#3c3c3c",
shade=True, lightsource=ls)
# Fix aspect ratio
max_range = np.array([x.max()-x.min(), y.max()-y.min(),
z.max()-z.min()]).max() / 2.0
mean_x = x.mean()
mean_y = y.mean()
mean_z = z.mean()
ax.set_xlim(mean_x - max_range, mean_x + max_range)
ax.set_ylim(mean_y - max_range, mean_y + max_range)
ax.set_zlim(mean_z - max_range, mean_z + max_range)
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment