Skip to main content

A Python library to create simple 3D Surface Meshs using Quadrilateral dominant faces.

Project description

SurfMesh - A Surface Meshing Python Library

Python Package Coverage Status License: MIT Python >= 3.10

Surface Mesher is a Python library for generating structured 3D surface meshes of primitive shapes, with a strong focus on quadrilateral-dominant (quad) meshing. The meshes are particularly suited for visualization and Boundary Element Method (BEM) simulations.

⚠️ This project is currently under active development.


🎯 Objective

This library aims to provide a minimal, intuitive interface for constructing quad-based surface meshes of primitive solids.

Use cases include:

  • Geometry visualization
  • Boundary Element Methods (BEM)
  • Educational tooling
  • Preprocessing for surface-based solvers

⚙️ Requirements

  • Python: >= 3.10
  • Dependencies:
    • numpy>=1.24
    • Optional (for examples and visualization):
      • ipykernel
      • jupyterlab
      • matplotlib

🚀 Installation

To install stable version via PyPI:

pip install surfmesh

For the latest development version via Git:

pip install git+https://github.com/ckesanapalli/surface-mesher.git

🧱 Basic Usage

Below are examples of how to use the library to generate and visualize meshes.

from pathlib import Path

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

import surfmesh as sm

plot_res = (4, 4)

1. Mesh Between Two Edges

# Define two edges
x = np.linspace(0, np.pi / 2, 20)
edge1 = np.array([x, np.sin(x)])
edge2 = np.array([x, np.exp(x)])

# Generate the mesh
radial_resolution = 10
mesh = sm.mesh_between_edges([edge1, edge2], radial_resolution)

print(f"Generated mesh with {mesh.shape[0]} quadrilateral faces.")

fig, ax = plt.subplots(figsize=plot_res)
collection = PatchCollection(map(Polygon, mesh), facecolor='lightblue', edgecolor='k', linewidth=0.3)
ax.add_collection(collection)
ax.set_xlim(mesh[:, :, 0].min() - 0.1, mesh[:, :, 0].max() + 0.1)
ax.set_ylim(mesh[:, :, 1].min() - 0.1, mesh[:, :, 1].max() + 0.1)
ax.set_title("Mesh Between Edges")
plt.show()
Generated mesh with 190 quadrilateral faces.

png

2. Radial Disk Mesh

# Parameters for the radial disk mesh
radius = 1.0
radial_resolution = 10
segment_resolution = 20

# Generate the radial disk mesh
radial_mesh = sm.disk_mesher_radial(radius, radial_resolution, segment_resolution)

print(f"Generated radial disk mesh with {radial_mesh.shape[0]} quadrilateral faces.")

fig, ax = plt.subplots(figsize=plot_res)
patches = [Polygon(face, closed=True) for face in radial_mesh]
collection = PatchCollection(patches, facecolors="lightblue", edgecolors="k", alpha=0.7)
ax.add_collection(collection)

ax.set_xlim(-radius, radius)
ax.set_ylim(-radius, radius)
ax.set_aspect("equal")
ax.set_title("Radial Disk Mesh")
plt.show()
Generated radial disk mesh with 200 quadrilateral faces.

png

3. Square-Centered Disk Mesh

square_resolution = 5
radial_resolution = 10
square_side_radius_ratio = 0.5

# Generate the square-centered disk mesh
square_centered_mesh = sm.disk_mesher_square_centered(radius, square_resolution, radial_resolution, square_side_radius_ratio)

print(f"Generated square-centered disk mesh with {square_centered_mesh.shape[0]} quadrilateral faces.")

fig, ax = plt.subplots(figsize=plot_res)
collection = PatchCollection(map(Polygon, square_centered_mesh), facecolors="lightgreen", edgecolors="k", alpha=0.7)
ax.add_collection(collection)

ax.set_xlim(-radius, radius)
ax.set_ylim(-radius, radius)
ax.set_aspect("equal")
ax.set_title("Square-Centered Disk Mesh")
plt.show()
Generated square-centered disk mesh with 225 quadrilateral faces.

png

4. Cuboid Mesh using Explicit Coordinates

# Define coordinate arrays for a cuboid
x_coords = [0.0, 1.0, 2.0]
y_coords = [0.0, 1.0, 2.0]
z_coords = [0.0, 0.5, 1.0]

# Generate the cuboid surface mesh
faces = sm.cuboid_mesher(x_coords, y_coords, z_coords)

print(f"Generated {faces.shape[0]} quadrilateral faces.")
print(faces.shape)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection="3d")

# Add each quad to the 3D plot
poly = Poly3DCollection(faces, facecolors="skyblue", edgecolors="k", alpha=0.7)
ax.add_collection3d(poly)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Cuboid Surface Mesh")
plt.tight_layout()
plt.show()
Generated 24 quadrilateral faces.
(24, 4, 3)

png

5. Cuboid Mesh using Resolution

# Generate a cuboid mesh with resolution
length, width, height = 2.0, 1.0, 1.0
resolution = (4, 2, 2)

mesh = sm.cuboid_mesher_with_resolution(length, width, height, resolution=resolution)

print(f"Generated cuboid with {mesh.shape[0]} quadrilateral faces.")
print(mesh.shape)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection="3d")

poly = Poly3DCollection(mesh, facecolors="lightgreen", edgecolors="k", alpha=0.6)
ax.add_collection3d(poly)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Cuboid Mesh with Resolution")
plt.tight_layout()
plt.show()
Generated cuboid with 40 quadrilateral faces.
(40, 4, 3)

png

6. Revolve a Curve Along a Circular Path

# Sample 2D curve coordinates
x = np.linspace(0, 1, 20)
z = x ** 2  # Example curve (parabola)

curves = np.array([x, z]).T
# Revolve the curve
segment_resolution = 20
faces = sm.circular_revolve(curves, segment_resolution, start_angle=0, end_angle=2*np.pi)

# Plotting
fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(Poly3DCollection(faces, facecolors='g', linewidths=1, alpha=0.5))
ax.set_xlim(-x.max(), x.max())
ax.set_ylim(-x.max(), x.max())
ax.set_zlim(z.min(), z.max())
ax.set_xlabel('X-axis')
ax.set_ylabel('Y-axis')
ax.set_zlabel('Z-axis')
plt.show()

png

7. Revolve a Curve Along a Custom Path

x = np.linspace(1, 10, 100)
z = np.log(x)
main_curve = np.array([x, z]).T

angle_rad = np.linspace(0, 4*np.pi, 100)
radius = angle_rad/10
revolve_path = np.array([angle_rad, radius]).T

revolved_mesh = sm.revolve_curve_along_path(main_curve, revolve_path)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(Poly3DCollection(revolved_mesh, alpha=0.5))
ax.set_xlim(-x.max(), x.max())
ax.set_ylim(-x.max(), x.max())
ax.set_zlim(z.min(), z.max())
plt.show()

png

8. Generate a Radial Cylinder Mesh

radius = 1.0
height = 2.0
radial_resolution = 8
segment_resolution = 16
height_resolution = 10

# Generate the cylinder mesh
mesh = sm.cylinder_mesher_radial(radius, height, radial_resolution, segment_resolution, height_resolution)

print(f"Generated a Radial Cylinder Mesh {mesh.shape[0]}.")
print(mesh.shape)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection="3d")

poly = Poly3DCollection(mesh, facecolors="lightgreen", edgecolors="k", alpha=0.6)
ax.add_collection3d(poly)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Radial Cylinder Mesh")
plt.tight_layout()
plt.show()
Generated a Radial Cylinder Mesh 416.
(416, 4, 3)

png

9. Generate a Square-Centered Cylinder Mesh

radius = 1.0
height = 2.0
radial_resolution = 8
half_square_side_resolution = 4
height_resolution = 10

# Generate the cylinder mesh
mesh = sm.cylinder_mesher_square_centered(radius, height, radial_resolution, half_square_side_resolution, height_resolution)

print(f"Generated a Square-Centered Cylinder Mesh.")
print(mesh.shape)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection="3d")

poly = Poly3DCollection(mesh, facecolors="lightgreen", edgecolors="k", alpha=0.6)
ax.add_collection3d(poly)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Square-Centered Cylinder Mesh")
plt.tight_layout()
plt.show()
Generated a Square-Centered Cylinder Mesh.
(960, 4, 3)

png

10. Generate a Sphere Mesh Using Cube Projection

mesh = sm.sphere_mesher_from_projection(radius=1.0, resolution=10)

print(f"Generated a Sphere Mesh from Cube Projection.")
print(mesh.shape)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection="3d")

poly = Poly3DCollection(mesh, facecolors="lightgreen", edgecolors="k", alpha=0.6)
ax.add_collection3d(poly)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Sphere Mesh from Cube Projection")
plt.tight_layout()
plt.show()
Generated a Sphere Mesh from Cube Projection.
(600, 4, 3)

png

11. Generate a Sphere Mesh Using Radial Divisions

radius = 1.0
radial_resolution = 20
segment_resolution = 20
mesh = sm.sphere_mesher_from_radial(radius, radial_resolution, segment_resolution)

print(f"Generated a Radial Sphere Mesh.")
print(mesh.shape)

fig = plt.figure(figsize=plot_res)
ax = fig.add_subplot(111, projection="3d")

poly = Poly3DCollection(mesh, facecolors="lightgreen", edgecolors="k", alpha=0.6)
ax.add_collection3d(poly)

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Sphere Mesh from Radial Sphere")
plt.tight_layout()
plt.show()
Generated a Radial Sphere Mesh.
(400, 4, 3)

png

📌 Roadmap

  • Cuboid surface mesh generation
  • Disk face mesh generation
  • Revolve curve mesh generation
  • Cylinder, and sphere support
  • Curvilinear mesh
  • STL/PLY export support
  • Mesh visualization utilities
  • Export to BEM-compatible formats

📄 License

MIT License © 2025 Chaitanya Kesanapalli

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

surfmesh-0.2.3.tar.gz (2.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

surfmesh-0.2.3-py3-none-any.whl (14.7 kB view details)

Uploaded Python 3

File details

Details for the file surfmesh-0.2.3.tar.gz.

File metadata

  • Download URL: surfmesh-0.2.3.tar.gz
  • Upload date:
  • Size: 2.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for surfmesh-0.2.3.tar.gz
Algorithm Hash digest
SHA256 a3ab6e97d8bf8922f6345f9f55d42fccb135d8cc2f45560ca0051380fb0e8dfe
MD5 edae3193d4b9fdd7d100469b0d9d5b40
BLAKE2b-256 ce8ed5c153ee816eccb557d842230ea900c5f5c7b5902ea0f68d69930db4fde2

See more details on using hashes here.

Provenance

The following attestation bundles were made for surfmesh-0.2.3.tar.gz:

Publisher: python-publish.yml on ckesanapalli/surfmesh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file surfmesh-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: surfmesh-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 14.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for surfmesh-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 0d01171c38f4ad4a260c5ec36bbf8a1a8cfd59daf71fb27d00de445904b2bee2
MD5 70dade78618fa9e7c45e78d82c21a871
BLAKE2b-256 4cbde3abcda7fc5a8bb047ac939afd93232d4b8e33bbb895f00c2c09f0d6129c

See more details on using hashes here.

Provenance

The following attestation bundles were made for surfmesh-0.2.3-py3-none-any.whl:

Publisher: python-publish.yml on ckesanapalli/surfmesh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page