Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 90 additions & 44 deletions examples/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,134 @@
-------
Standalone Python demonstration of the 2D Piecewise Algebraic Implicit Spline.

Mirrors matlab/Demo.m — runs four examples that showcase the main features
of the Li & Tian (2009) implicit-spline construction:

Example 1 : Unit square (right-angle corners)
Example 2 : Equilateral triangle (acute corners)
Example 3 : Regular pentagon
Example 4 : Irregular pentagon — sweeping delta to show bandwidth effect

Run from the repository root::

python examples/demo.py

or from within the examples/ directory::

python demo.py

Reference
---------
Li, Q. & Tian, J. (2009). 2D Piecewise Algebraic Splines for Implicit
Modeling. ACM Transactions on Graphics, 28(3).
DOI: 10.1145/1516522.1516524
Includes both the original basic examples and paper-style visual panels inspired
by Li & Tian (2009).
"""

import sys
import os
import sys

# Allow running from examples/ or from the repo root
_here = os.path.dirname(os.path.abspath(__file__))
_python_dir = os.path.join(_here, '..', 'python')
_python_dir = os.path.join(_here, "..", "python")
if os.path.isdir(_python_dir):
sys.path.insert(0, os.path.abspath(_python_dir))

import numpy as np
import matplotlib.pyplot as plt

from implicit_spline import imp_spline_2d, draw_imp_spline
from implicit_spline.visualization import draw_surface, compare_delta
from implicit_spline import draw_imp_spline
from implicit_spline.visualization import (
compare_delta,
compare_n,
draw_surface,
panel_delta_shapes,
partition_basis_surfaces,
)


# ── Shared parameters ─────────────────────────────────────────────────────────
DELTA = 0.12 # transition bandwidth
N_ORDER = 2 # smoothness order (C^n near each edge)
N_GRID = 200 # grid resolution (N × N points)
def _set_window_title(fig, title):
manager = getattr(fig.canvas, "manager", None)
if manager is not None and hasattr(manager, "set_window_title"):
manager.set_window_title(title)


DELTA = 0.12
N_ORDER = 2
N_GRID = 200

print("=== 2D Implicit Spline Demo ===\n")

# ── Example 1: Unit square ────────────────────────────────────────────────────
print("Example 1: unit square")
P_sq = np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=float)

fig1, ax1 = plt.subplots(figsize=(6, 5))
fig1.canvas.manager.set_window_title("Demo — Example 1: Square")
_set_window_title(fig1, "Demo — Example 1: Square")
draw_imp_spline(P_sq, delta=DELTA, n=N_ORDER, N=N_GRID, ax=ax1,
title=rf"Example 1: Square ($\delta={DELTA}$, $n={N_ORDER}$)")
plt.tight_layout()

# ── Example 2: Equilateral triangle ──────────────────────────────────────────
print("Example 2: equilateral triangle")
P_tri = np.array([[0, 0], [2, 0], [1, np.sqrt(3)]], dtype=float)

fig2, ax2 = plt.subplots(figsize=(6, 5))
fig2.canvas.manager.set_window_title("Demo — Example 2: Triangle")
_set_window_title(fig2, "Demo — Example 2: Triangle")
draw_imp_spline(P_tri, delta=DELTA, n=N_ORDER, N=N_GRID, ax=ax2,
title=rf"Example 2: Triangle ($\delta={DELTA}$, $n={N_ORDER}$)")
plt.tight_layout()

# ── Example 3: Regular pentagon ───────────────────────────────────────────────
print("Example 3: regular pentagon")
theta = np.linspace(np.pi / 2, np.pi / 2 + 2 * np.pi, 6)[:-1] # 5 vertices, start at top
theta = np.linspace(np.pi / 2, np.pi / 2 + 2 * np.pi, 6)[:-1]
P_pent = np.column_stack([np.cos(theta), np.sin(theta)])

fig3, ax3 = plt.subplots(figsize=(6, 5))
fig3.canvas.manager.set_window_title("Demo — Example 3: Pentagon")
_set_window_title(fig3, "Demo — Example 3: Pentagon")
draw_imp_spline(P_pent, delta=DELTA, n=N_ORDER, N=N_GRID, ax=ax3,
title=rf"Example 3: Pentagon ($\delta={DELTA}$, $n={N_ORDER}$)")
plt.tight_layout()

# ── Example 4: Effect of delta (bandwidth) ────────────────────────────────────
print("Example 4: sweeping delta [0.05, 0.15, 0.30]")
print("Example 4: sweeping delta [0.05, 0.15, 0.30]")
P_irr = np.array([[0, 0], [2, 0], [2.5, 0.8], [1.5, 1.8], [-0.2, 1.2]], dtype=float)

fig4 = compare_delta(P_irr, deltas=(0.05, 0.15, 0.30), n=N_ORDER, N=N_GRID)
fig4.canvas.manager.set_window_title("Demo — Example 4: delta sweep")
_set_window_title(fig4, "Demo — Example 4: delta sweep")

print("Example 5: smoothness-order comparison [n=1,2,3]")
fig5 = compare_n(P_sq, delta=0.15, n_values=(1, 2, 3), N=N_GRID)
_set_window_title(fig5, "Demo — Example 5: smoothness comparison")

print("Example 6: paper-style six-panel delta evolution")
panel_deltas = (0.03, 0.06, 0.10, 0.16, 0.24, 0.36)
fig6 = panel_delta_shapes(
P_irr,
deltas=panel_deltas,
n=2,
N=260,
layout=(2, 3),
title=r"Paper-style contour evolution as $\delta$ increases",
)
_set_window_title(fig6, "Demo — Example 6: paper-style contour panels")

print("Example 7: freeform curve gallery")
gallery_polys = [
np.array([[0.0, 0.0], [1.6, -0.2], [2.1, 0.9], [1.1, 1.8], [-0.2, 1.2]]),
np.array([[0.0, 0.2], [0.9, -0.3], [1.8, 0.3], [1.4, 1.5], [0.3, 1.9], [-0.4, 1.0]]),
np.array([[0.0, 0.0], [0.8, -0.4], [1.8, 0.1], [2.0, 1.0], [1.2, 1.9], [0.2, 1.6], [-0.3, 0.8]]),
np.array([[0.0, 0.0], [1.4, -0.4], [2.4, 0.5], [1.7, 1.7], [0.6, 1.9], [-0.5, 1.1]]),
]
fig7, axes7 = plt.subplots(2, 2, figsize=(10, 8))
for i, (ax, poly) in enumerate(zip(axes7.ravel(), gallery_polys), start=1):
draw_imp_spline(poly, delta=0.12, n=2, N=220, ax=ax, title=f"freeform {i}")
fig7.suptitle("Freeform implicit-curve gallery", fontsize=13)
plt.tight_layout()
_set_window_title(fig7, "Demo — Example 7: freeform gallery")

print("Example 8: partition and summed-basis surfaces")
partition_polys = [
np.array([[0.0, 0.0], [1.1, 0.0], [0.95, 0.8], [0.1, 0.85]]),
np.array([[1.1, 0.0], [2.2, 0.0], [2.0, 0.95], [0.95, 0.8]]),
np.array([[0.1, 0.85], [0.95, 0.8], [0.9, 1.8], [0.0, 1.9]]),
np.array([[0.95, 0.8], [2.0, 0.95], [1.9, 1.9], [0.9, 1.8]]),
]
fig8 = partition_basis_surfaces(
partition_polys,
deltas=(0.04, 0.10, 0.22),
n=2,
N=130,
)
_set_window_title(fig8, "Demo — Example 8: partition-based surface figure")

print("Example 9: extra 3D wireframe surface view")
fig9 = plt.figure(figsize=(7, 5))
ax9 = fig9.add_subplot(111, projection="3d")
draw_surface(
P_irr,
delta=0.12,
n=2,
N=100,
ax=ax9,
wireframe=True,
elev=28,
azim=-58,
title="Wireframe view of implicit spline surface",
)
_set_window_title(fig9, "Demo — Example 9: wireframe surface")
plt.tight_layout()

print("\nDemo complete.")
plt.show()
57 changes: 34 additions & 23 deletions notebooks/01_basic_polygon.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,29 @@
"outputs": [],
"source": [
"# If running in Google Colab, clone the repo so imports work.\n",
"import sys, os\n",
"import os\n",
"import sys\n",
"from pathlib import Path\n",
"\n",
"IN_COLAB = 'google.colab' in sys.modules\n",
"if IN_COLAB:\n",
" if not os.path.exists('Implicit-Spline'):\n",
" os.system('git clone https://github.com/QL-UoHull/Implicit-Spline.git')\n",
" sys.path.insert(0, 'Implicit-Spline/python')\n",
"else:\n",
" # Running locally: add the python/ directory to the path\n",
" repo_root = os.path.abspath(os.path.join(os.path.dirname(os.getcwd()), '..'))\n",
" python_dir = os.path.join(os.path.dirname(os.getcwd()), 'python')\n",
" if os.path.isdir(python_dir):\n",
" sys.path.insert(0, python_dir)\n",
" cwd = Path.cwd().resolve()\n",
" candidates = [\n",
" cwd,\n",
" cwd.parent,\n",
" cwd / 'Implicit-Spline',\n",
" cwd.parent / 'Implicit-Spline',\n",
" ]\n",
" for root in candidates:\n",
" python_dir = root / 'python'\n",
" if python_dir.is_dir():\n",
" sys.path.insert(0, str(python_dir))\n",
" break\n",
" else:\n",
" # Fallback: try relative to notebooks/\n",
" sys.path.insert(0, os.path.join('..', 'python'))\n"
]
},
Expand All @@ -76,7 +85,7 @@
"import matplotlib.pyplot as plt\n",
"\n",
"from implicit_spline import imp_spline_2d, draw_imp_spline\n",
"from implicit_spline.visualization import draw_surface, compare_delta\n",
"from implicit_spline.visualization import draw_surface, compare_delta, compare_n\n",
"from implicit_spline.core import H\n",
"\n",
"%matplotlib inline\n",
Expand Down Expand Up @@ -153,10 +162,12 @@
"source": [
"P_square = np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=float)\n",
"\n",
"fig, axes = plt.subplots(1, 2, figsize=(11, 4))\n",
"draw_imp_spline(P_square, delta=delta, n=n, N=N, ax=axes[0],\n",
"fig = plt.figure(figsize=(11, 4))\n",
"ax1 = fig.add_subplot(1, 2, 1)\n",
"ax2 = fig.add_subplot(1, 2, 2, projection='3d')\n",
"draw_imp_spline(P_square, delta=delta, n=n, N=N, ax=ax1,\n",
" title='Contour plot')\n",
"draw_surface(P_square, delta=delta, n=n, N=80, ax=axes[1],\n",
"draw_surface(P_square, delta=delta, n=n, N=80, ax=ax2,\n",
" title='Surface plot')\n",
"fig.suptitle('Unit square', fontsize=13)\n",
"plt.tight_layout(); plt.show()\n"
Expand All @@ -179,10 +190,12 @@
"source": [
"P_tri = np.array([[0, 0], [2, 0], [1, np.sqrt(3)]], dtype=float)\n",
"\n",
"fig, axes = plt.subplots(1, 2, figsize=(11, 4))\n",
"draw_imp_spline(P_tri, delta=delta, n=n, N=N, ax=axes[0],\n",
"fig = plt.figure(figsize=(11, 4))\n",
"ax1 = fig.add_subplot(1, 2, 1)\n",
"ax2 = fig.add_subplot(1, 2, 2, projection='3d')\n",
"draw_imp_spline(P_tri, delta=delta, n=n, N=N, ax=ax1,\n",
" title='Contour plot')\n",
"draw_surface(P_tri, delta=delta, n=n, N=80, ax=axes[1],\n",
"draw_surface(P_tri, delta=delta, n=n, N=80, ax=ax2,\n",
" title='Surface plot')\n",
"fig.suptitle('Equilateral triangle', fontsize=13)\n",
"plt.tight_layout(); plt.show()\n"
Expand All @@ -206,10 +219,12 @@
"theta = np.linspace(np.pi/2, np.pi/2 + 2*np.pi, 6)[:-1] # 5 vertices, start at top\n",
"P_pent = np.column_stack([np.cos(theta), np.sin(theta)])\n",
"\n",
"fig, axes = plt.subplots(1, 2, figsize=(11, 4))\n",
"draw_imp_spline(P_pent, delta=delta, n=n, N=N, ax=axes[0],\n",
"fig = plt.figure(figsize=(11, 4))\n",
"ax1 = fig.add_subplot(1, 2, 1)\n",
"ax2 = fig.add_subplot(1, 2, 2, projection='3d')\n",
"draw_imp_spline(P_pent, delta=delta, n=n, N=N, ax=ax1,\n",
" title='Contour plot')\n",
"draw_surface(P_pent, delta=delta, n=n, N=80, ax=axes[1],\n",
"draw_surface(P_pent, delta=delta, n=n, N=80, ax=ax2,\n",
" title='Surface plot')\n",
"fig.suptitle('Regular pentagon', fontsize=13)\n",
"plt.tight_layout(); plt.show()\n"
Expand Down Expand Up @@ -259,12 +274,8 @@
"P_sq = np.array([[0, 0], [1, 0], [1, 1], [0, 1]], dtype=float)\n",
"d = 0.15\n",
"\n",
"fig, axes = plt.subplots(1, 3, figsize=(15, 4))\n",
"for ax, nn in zip(axes, [1, 2, 3]):\n",
" draw_imp_spline(P_sq, delta=d, n=nn, N=N, ax=ax,\n",
" title=rf'$n={nn}$ (C$^{{{nn}}}$)')\n",
"fig.suptitle(rf'Smoothness order $n$ \u2014 unit square, $\\delta={d}$', fontsize=13)\n",
"plt.tight_layout(); plt.show()\n"
"compare_n(P_sq, delta=d, n_values=(1, 2, 3), N=N)\n",
"plt.show()\n"
]
},
{
Expand Down