Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
745b238
added optional simplification and densification argument for lines, b…
oscarfasanchez Dec 8, 2025
decd562
added option for no densification of lines or custom densification if…
oscarfasanchez Dec 8, 2025
28d8324
added test and debugged the tests, fixed bug on line implementation
oscarfasanchez Dec 8, 2025
4a39c8e
Merge branch 'main' of https://github.com/oscarfasanchez/vorflow_os i…
oscarfasanchez Dec 8, 2025
145bd21
added fixes and recommendations to pull request of optional densify a…
oscarfasanchez Dec 9, 2025
a848493
adding second round of refinements from copilot, mainly grammar and s…
oscarfasanchez Dec 10, 2025
bb499e4
added some functions to use flexible fields
oscarfasanchez Dec 12, 2025
728765d
deleted the simplify default and standardized the densify keyword
oscarfasanchez Dec 14, 2025
c47ad11
added the frame in fields and blueprint mainly to set later the field…
oscarfasanchez Dec 14, 2025
61554ac
Merge branch 'optional_densify_simplify' into feat_flex_fields
oscarfasanchez Dec 14, 2025
16ef1db
added the not embedded features
oscarfasanchez Dec 14, 2025
750a65e
adding some framing for fields, added independent field distance, and…
oscarfasanchez Dec 16, 2025
2630007
set new engine to setup fields
oscarfasanchez Dec 17, 2025
8f40378
fixed bug sampling
oscarfasanchez Dec 17, 2025
a8e283b
got only field polygon out of the cleaning process
oscarfasanchez Dec 17, 2025
c293371
updated test with the new API
oscarfasanchez Dec 17, 2025
6b8bd75
added solution to handle many non embededd when retrieving the final …
oscarfasanchez Dec 17, 2025
87ca5b1
typo fix
oscarfasanchez Jan 9, 2026
137706e
simplifying examples to identify problems
oscarfasanchez Jan 12, 2026
2bde61b
convert one box to polyline to test behavior
oscarfasanchez Jan 12, 2026
b785330
added explicit embedding, which doesnt work yet
oscarfasanchez Jan 12, 2026
678edcc
solver silly bug with the conflict in field example
oscarfasanchez Feb 19, 2026
310c3e7
solved bug that caused after fragmentation embedding not to work
oscarfasanchez Feb 19, 2026
0de2797
asume surfaces are used for the field generation to solve bug about e…
oscarfasanchez Feb 20, 2026
6be208a
fixed how the gmsh gui is displayed for debugging
oscarfasanchez Feb 20, 2026
7711696
updating examples with the new field capabilities
oscarfasanchez Feb 20, 2026
c7d1b9d
hotfix for collapsed geometries
oscarfasanchez Feb 20, 2026
b66832f
Default snapping tolerance now can be changed to help topology proble…
oscarfasanchez Apr 16, 2026
0452179
added some cleaning helpers and some tests to be sure of the bookkeep…
oscarfasanchez Apr 17, 2026
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
107 changes: 94 additions & 13 deletions examples/basic_example.ipynb

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions examples/comprehensive_demo.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@
"cm.add_polygon(domain, zone_id=1, resolution=20.0)\n",
"\n",
"# 2. Add Refined Zone\n",
"# dist_max_out controls how fast the mesh grows outside this zone\n",
"cm.add_polygon(zone_poly,z_order=2, zone_id=2, resolution=3.0, dist_max_out=20.0)\n",
"# dist_max controls how fast the mesh grows outside this zone\n",
"cm.add_polygon(zone_poly,z_order=2, zone_id=2, resolution=3.0, dist_max=20.0)\n",
"\n",
"\n",
"# 3. Add River (Standard Line)\n",
Expand Down Expand Up @@ -258,7 +258,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.0"
"version": "3.14.2"
}
},
"nbformat": 4,
Expand Down
289 changes: 289 additions & 0 deletions examples/field_capabilities_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
#%%
"""Field capabilities example (pseudo-notebook).

Run this file in VS Code with the Python extension. The `#%%` markers create
cell-like execution, similar to a notebook.

This example duplicates polygon and line geometries to exercise multiple mesh
field types:
- Implicit threshold via `dist_min`/`dist_max`
- Explicit `ThresholdField`
- `ExponentialField`
- `AutoLinearField`
- `AutoExponentialField`
- Field-only polygon via `embed=False`
- Barrier/straddle line to validate point-pair representation
"""

from __future__ import annotations

import matplotlib.pyplot as plt
import numpy as np

from shapely.affinity import translate
from shapely.geometry import LineString, Point, box

from vorflow import ConceptualMesh, MeshGenerator, VoronoiTessellator
from vorflow.fields import (
AutoExponentialField,
AutoLinearField,
ThresholdField,
)
from vorflow.utils import calculate_mesh_quality, summarize_quality

#%%
# Geometry
domain = box(0, 0, 400, 200)
# Base refinement polygon (simplified set)
poly_base = box(40, 40, 70, 70)

# Create a 2x3 layout (upper/lower × left/center/right)
ul = translate(poly_base, xoff=0, yoff=80)
uc = translate(poly_base, xoff=120, yoff=80)
ur = translate(poly_base, xoff=240, yoff=80)
ll = translate(poly_base, xoff=0, yoff=0)
lc = translate(poly_base, xoff=120, yoff=0)
lr = translate(poly_base, xoff=240, yoff=0)

polys = {
"upper-left": ul,
"upper-center": uc,
"upper-right": ur,
"lower-left": ll,
"lower-center": lc,
"lower-right": lr,
}

# A gentle sine-wave river
xs = np.linspace(-10, 430, 45)
river_y = 140 + 18 * np.sin(0.06 * xs)
river_base = LineString(list(zip(xs, river_y)))
rivers = {
"river-center-up": translate(river_base, xoff=0, yoff=0),
"river-center-down": translate(river_base, xoff=0, yoff=-60),
}

# Points (simplified)
points = {
"pt-lower-left": Point(25, 25),
"pt-lower-center": Point(185, 25),
"pt-lower-right": Point(325, 25),
}
#%%
# Fields used below

auto_linear = AutoLinearField(growth_factor=1.1)
auto_exp = AutoExponentialField(growth_factor=1.1)
threshold = ThresholdField(size_min=5.0, dist_min=5.0, dist_max=50.0, size_max=20.0)





#%%
# 1) Setup Blueprint + 2) Mesh Generation + 3) Voronoi Conversion

background_lc = 100
feature_lc = 10 # representative base feature length for features

blueprint = ConceptualMesh(crs="EPSG:3857")
blueprint.add_polygon(domain, zone_id="domain") # color: black (domain boundary)

# Polygons (IDs are location-based)
blueprint.add_polygon(
polys["upper-left"],
zone_id="upper-left",
resolution=feature_lc/5,
z_order=10,
dist_min=feature_lc/2,#using the implicit threshold approach here (instead of an explicit ThresholdField) to validate both code paths
dist_max=background_lc * 5.0,
)

blueprint.add_polygon(
polys["upper-center"],
zone_id="upper-center",
resolution=feature_lc/5,
z_order=10,
fields=[auto_exp],
embed=False, # field-only polygon
)

blueprint.add_polygon(
polys["upper-right"],
zone_id="upper-right",
resolution=feature_lc/5,
z_order=10,
fields=[auto_linear],
)


blueprint.add_line(
polys["lower-left"].boundary,
line_id='lower-left',# zone_id="lower-left",
resolution=feature_lc/5,
# z_order=5,
dist_min=feature_lc/2,
dist_max=background_lc * 1.5,
fields=[auto_linear],
embed=False, # field-only polygon
)

blueprint.add_polygon(
polys["lower-center"],
zone_id="lower-center",
resolution=feature_lc/5,
z_order=5,
fields=[auto_exp],
)

blueprint.add_polygon(
polys["lower-right"],
zone_id="lower-right",
resolution=feature_lc/5,
z_order=5,
fields=[threshold],
embed=False, # field-only polygon
)

blueprint.add_line(
rivers["river-center-up"],
line_id="river-center-up",
resolution=feature_lc/2,
is_barrier=False,
fields=[threshold],
)

blueprint.add_line(
rivers["river-center-down"],
line_id="river-center-down",
resolution=feature_lc/4,
is_barrier=False,
fields=[auto_exp],
embed=False, # field-only line
)

# Points (IDs are location-based)
# Colors: pt-lower-left -> 'tab:red', pt-lower-center -> 'tab:purple'
blueprint.add_point(
points["pt-lower-left"],
point_id="pt-lower-left",
resolution=feature_lc/5,
dist_min=feature_lc/5,
dist_max=background_lc * 1.5,
)
blueprint.add_point(
points["pt-lower-center"],
point_id="pt-lower-center",
resolution=feature_lc/5,
fields=[auto_exp],
embed=False, # field-only point
)

blueprint.add_point(
points["pt-lower-right"],
point_id="pt-lower-right",
resolution=feature_lc/5,
fields=[auto_linear],
embed=False, # field-only point
)

clean_polys, clean_lines, clean_pts = blueprint.generate()

mesher = MeshGenerator(background_lc=background_lc, verbosity=0)
mesher.generate(clean_polys, clean_lines, clean_pts, launch_gmsh_gui=False)

tessellator = VoronoiTessellator(mesher, blueprint, clip_to_boundary=True)
grid_gdf = tessellator.generate()


#%%
# 4) Visual sanity-check

fig, ax = plt.subplots(1, 1, figsize=(14, 7))
ax.set_aspect("equal")

# Domain
ax.plot(*domain.exterior.xy, color="black", lw=1)

# Polygons
plot_polys = polys
poly_colors = {
"upper-left": "tab:orange",
"upper-center": "tab:green",
"upper-right": "tab:blue",
"lower-left": "tab:purple",
"lower-center": "tab:brown",
"lower-right": "tab:pink",
}
field_only_polys = {"upper-center", "lower-left"}
for name, poly in plot_polys.items():
is_field_only = name in field_only_polys
label = f"{name} (field-only)" if is_field_only else name
ax.plot(
*poly.exterior.xy,
lw=1,
ls=":" if is_field_only else "--",
color=poly_colors.get(name),
label=label,
)

line_colors = {
"fault-left": "tab:red",
"fault-center": "tab:purple",
"fault-right": "tab:blue",
"river-center-up": "tab:cyan",
"river-center-down": "tab:cyan",
}

ax.plot(
*rivers["river-center-up"].xy,
lw=1,
color=line_colors["river-center-up"],
label="river-center-up",
)
ax.plot(
*rivers["river-center-down"].xy,
lw=1,
color=line_colors["river-center-down"],
label="river-center-down (field-only)",
)

# Points
point_colors = {
"pt-lower-left": "tab:red",
"pt-lower-center": "tab:purple",
"pt-lower-right": "tab:blue",
}
for name, pt in points.items():
ax.scatter(pt.x, pt.y, s=25, marker="x", color=point_colors.get(name), label=name)

grid_gdf.plot(ax=ax, alpha=0.35, edgecolor="k", linewidth=0.15)
ax.legend(loc="upper right", fontsize=7, ncol=2)
fig.tight_layout()
plt.show()


#%%
# Quick check: smaller cells => smaller polygon areas (proxy for refinement)

grid_gdf2 = grid_gdf.copy()
grid_gdf2["area"] = grid_gdf2.geometry.area

fig, ax = plt.subplots(1, 1, figsize=(14, 6))
ax.set_aspect("equal")
ax.plot(*domain.exterior.xy, color="black", lw=1)
grid_gdf2.plot(ax=ax, column="area", cmap="viridis", legend=True, linewidth=0.0)
ax.set_title("Voronoi cell area (proxy for refinement)")
fig.tight_layout()
plt.show()

# %%
quality_gdf = calculate_mesh_quality(grid_gdf, calc_ortho=True)
summarize_quality(quality_gdf)

# Plot Orthogonality Error
fig, ax = plt.subplots(figsize=(10, 8))
quality_gdf.plot(column='ortho_error', ax=ax, legend=True, cmap='Reds', vmin=0, vmax=1)
plt.title("Orthogonality Error (Degrees)")
plt.show()
# %%
7 changes: 6 additions & 1 deletion src/vorflow/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from .blueprint import ConceptualMesh
from .engine import MeshGenerator
from .tessellator import VoronoiTessellator
from .fields import (MeshField, ThresholdField,
ExponentialField, AutoLinearField,
AutoExponentialField)

__all__ = ["ConceptualMesh", "MeshGenerator", "VoronoiTessellator"]
__all__ = ["ConceptualMesh", "MeshGenerator", "VoronoiTessellator",
"MeshField", "ThresholdField", "ExponentialField",
"AutoLinearField", "AutoExponentialField"]
Loading