Skip to content

Commit b7510d1

Browse files
authored
Merge pull request #116 from nschloe/new-generate
better generate
2 parents 775f698 + d7634d0 commit b7510d1

12 files changed

Lines changed: 156 additions & 209 deletions

README.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import pygalmesh
3636
points = numpy.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]])
3737
constraints = [[0, 1], [1, 2], [2, 3], [3, 0]]
3838

39-
mesh = pygalmesh.generate_2d(points, constraints, cell_size=1.0e-1, num_lloyd_steps=10)
39+
mesh = pygalmesh.generate_2d(points, constraints, edge_size=1.0e-1, num_lloyd_steps=10)
4040
# mesh.points, mesh.cells
4141
```
4242
The quality of the mesh isn't very good, but can be improved with
@@ -213,24 +213,19 @@ to CGAL's mesh generator.
213213
#### Local refinement
214214
<img src="https://nschloe.github.io/pygalmesh/ball-local-refinement.png" width="30%">
215215

216-
Use `generate_mesh` with a `SizingFieldBase` object as `cell_size`.
216+
Use `generate_mesh` with a function (regular or lambda) as `cell_size`. The same goes
217+
for `edge_size`, `facet_size`, and `facet_distance`.
217218
```python
218219
import numpy
219220
import pygalmesh
220221

221-
# define a cell_size function
222-
class Field(pygalmesh.SizingFieldBase):
223-
def eval(self, x):
224-
return abs(numpy.sqrt(numpy.dot(x, x)) - 0.5) / 5 + 0.025
225-
226-
227222
mesh = pygalmesh.generate_mesh(
228223
pygalmesh.Ball([0.0, 0.0, 0.0], 1.0),
229224
facet_angle=30,
230225
facet_size=0.1,
231226
facet_distance=0.025,
232227
cell_radius_edge_ratio=2,
233-
cell_size=Field(),
228+
cell_size=lambda x: abs(numpy.sqrt(numpy.dot(x, x)) - 0.5) / 5 + 0.025,
234229
)
235230
```
236231

pygalmesh/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
RingExtrude,
1515
Rotate,
1616
Scale,
17-
SizingFieldBase,
1817
Stretch,
1918
Tetrahedron,
2019
Torus,
@@ -42,7 +41,6 @@
4241
"_cli",
4342
#
4443
"DomainBase",
45-
"SizingFieldBase",
4644
"Translate",
4745
"Rotate",
4846
"Scale",

pygalmesh/main.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,19 @@
1313
_generate_mesh,
1414
_generate_periodic_mesh,
1515
_generate_surface_mesh,
16-
_generate_with_sizing_field,
1716
_remesh_surface,
1817
)
1918

2019

20+
class Wrapper(SizingFieldBase):
21+
def __init__(self, f):
22+
self.f = f
23+
super().__init__()
24+
25+
def eval(self, x):
26+
return self.f(x)
27+
28+
2129
def generate_mesh(
2230
domain,
2331
feature_edges=None,
@@ -40,13 +48,18 @@ def generate_mesh(
4048
fh, outfile = tempfile.mkstemp(suffix=".mesh")
4149
os.close(fh)
4250

43-
if isinstance(cell_size, float):
44-
genfun = _generate_mesh
45-
else:
46-
assert isinstance(cell_size, SizingFieldBase)
47-
genfun = _generate_with_sizing_field
51+
def _select(obj):
52+
if isinstance(obj, float):
53+
return obj, None
54+
assert callable(obj)
55+
return -1.0, Wrapper(obj)
4856

49-
genfun(
57+
edge_size_value, edge_size_field = _select(edge_size)
58+
cell_size_value, cell_size_field = _select(cell_size)
59+
facet_size_value, facet_size_field = _select(facet_size)
60+
facet_distance_value, facet_distance_field = _select(facet_distance)
61+
62+
_generate_mesh(
5063
domain,
5164
outfile,
5265
feature_edges=feature_edges,
@@ -55,12 +68,16 @@ def generate_mesh(
5568
odt=odt,
5669
perturb=perturb,
5770
exude=exude,
58-
edge_size=edge_size,
71+
edge_size_value=edge_size_value,
72+
edge_size_field=edge_size_field,
5973
facet_angle=facet_angle,
60-
facet_size=facet_size,
61-
facet_distance=facet_distance,
74+
facet_size_value=facet_size_value,
75+
facet_size_field=facet_size_field,
76+
facet_distance_value=facet_distance_value,
77+
facet_distance_field=facet_distance_field,
6278
cell_radius_edge_ratio=cell_radius_edge_ratio,
63-
cell_size=cell_size,
79+
cell_size_value=cell_size_value,
80+
cell_size_field=cell_size_field,
6481
verbose=verbose,
6582
seed=seed,
6683
)
@@ -70,7 +87,7 @@ def generate_mesh(
7087
return mesh
7188

7289

73-
def generate_2d(points, constraints, B=math.sqrt(2), cell_size=0.0, num_lloyd_steps=0):
90+
def generate_2d(points, constraints, B=math.sqrt(2), edge_size=0.0, num_lloyd_steps=0):
7491
# some sanity checks
7592
points = numpy.asarray(points)
7693
constraints = numpy.asarray(constraints)
@@ -82,7 +99,7 @@ def generate_2d(points, constraints, B=math.sqrt(2), cell_size=0.0, num_lloyd_st
8299
if numpy.any(length2 < 1.0e-15):
83100
raise RuntimeError("Constraint of (near)-zero length.")
84101

85-
points, cells = _generate_2d(points, constraints, B, cell_size, num_lloyd_steps)
102+
points, cells = _generate_2d(points, constraints, B, edge_size, num_lloyd_steps)
86103
return meshio.Mesh(numpy.array(points), {"triangle": numpy.array(cells)})
87104

88105

setup.cfg

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pygalmesh
3-
version = 0.7.2
3+
version = 0.8.0
44
url = https://github.com/nschloe/pygalmesh
55
author = Nico Schlömer
66
author_email = nico.schloemer@gmail.com
@@ -21,6 +21,13 @@ classifiers =
2121
Topic :: Scientific/Engineering :: Mathematics
2222
Topic :: Scientific/Engineering :: Physics
2323
Topic :: Scientific/Engineering :: Visualization
24+
keywords =
25+
mathematics
26+
physics
27+
engineering
28+
cgal
29+
mesh
30+
mesh generation
2431
2532
[options]
2633
packages = find:

src/generate.cpp

Lines changed: 73 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3t3;
2424

2525
// Mesh Criteria
2626
typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria;
27+
typedef Mesh_criteria::Edge_criteria Edge_criteria;
2728
typedef Mesh_criteria::Facet_criteria Facet_criteria;
2829
typedef Mesh_criteria::Cell_criteria Cell_criteria;
2930

@@ -54,12 +55,23 @@ generate_mesh(
5455
const bool odt,
5556
const bool perturb,
5657
const bool exude,
57-
const double edge_size,
58+
//
59+
const double edge_size_value,
60+
const std::shared_ptr<pygalmesh::SizingFieldBase> & edge_size_field,
61+
//
5862
const double facet_angle,
59-
const double facet_size,
60-
const double facet_distance,
63+
//
64+
const double facet_size_value,
65+
const std::shared_ptr<pygalmesh::SizingFieldBase> & facet_size_field,
66+
//
67+
const double facet_distance_value,
68+
const std::shared_ptr<pygalmesh::SizingFieldBase> & facet_distance_field,
69+
//
6170
const double cell_radius_edge_ratio,
62-
const double cell_size,
71+
//
72+
const double cell_size_value,
73+
const std::shared_ptr<pygalmesh::SizingFieldBase> & cell_size_field,
74+
//
6375
const bool verbose,
6476
const int seed
6577
)
@@ -81,26 +93,73 @@ generate_mesh(
8193
K::Sphere_3(CGAL::ORIGIN, bounding_sphere_radius2)
8294
);
8395

96+
// cgal_domain.detect_features();
97+
8498
const auto native_features = translate_feature_edges(domain->get_features());
8599
cgal_domain.add_features(native_features.begin(), native_features.end());
86100

87101
const auto polylines = translate_feature_edges(feature_edges);
88102
cgal_domain.add_features(polylines.begin(), polylines.end());
89103

90-
Mesh_criteria criteria(
91-
CGAL::parameters::edge_size=edge_size,
92-
CGAL::parameters::facet_angle=facet_angle,
93-
CGAL::parameters::facet_size=facet_size,
94-
CGAL::parameters::facet_distance=facet_distance,
95-
CGAL::parameters::cell_radius_edge_ratio=cell_radius_edge_ratio,
96-
CGAL::parameters::cell_size=cell_size
97-
);
98-
99-
// Mesh generation
104+
// perhaps there's a more elegant solution here
105+
// see <https://github.com/CGAL/cgal/issues/4145>
100106
if (!verbose) {
101107
// suppress output
102108
std::cerr.setstate(std::ios_base::failbit);
103109
}
110+
111+
// Build the float/field values according to
112+
// <https://github.com/CGAL/cgal/issues/5044#issuecomment-705526982>.
113+
114+
// nested ternary operator
115+
const auto facet_criteria = facet_size_field ? (
116+
facet_distance_field ?
117+
Facet_criteria(
118+
facet_angle,
119+
[&](K::Point_3 p, const int, const Mesh_domain::Index&) {
120+
return facet_size_field->eval({p.x(), p.y(), p.z()});
121+
},
122+
[&](K::Point_3 p, const int, const Mesh_domain::Index&) {
123+
return facet_distance_field->eval({p.x(), p.y(), p.z()});
124+
}
125+
) : Facet_criteria(
126+
facet_angle,
127+
[&](K::Point_3 p, const int, const Mesh_domain::Index&) {
128+
return facet_size_field->eval({p.x(), p.y(), p.z()});
129+
},
130+
facet_distance_value
131+
)
132+
) : (
133+
facet_distance_field ?
134+
Facet_criteria(
135+
facet_angle,
136+
facet_size_value,
137+
[&](K::Point_3 p, const int, const Mesh_domain::Index&) {
138+
return facet_distance_field->eval({p.x(), p.y(), p.z()});
139+
}
140+
) : Facet_criteria(
141+
facet_angle,
142+
facet_size_value,
143+
facet_distance_value
144+
)
145+
);
146+
147+
const auto edge_criteria = edge_size_field ?
148+
Edge_criteria(
149+
[&](K::Point_3 p, const int, const Mesh_domain::Index&) {
150+
return edge_size_field->eval({p.x(), p.y(), p.z()});
151+
}) : Edge_criteria(edge_size_value);
152+
153+
const auto cell_criteria = cell_size_field ?
154+
Cell_criteria(
155+
cell_radius_edge_ratio,
156+
[&](K::Point_3 p, const int, const Mesh_domain::Index&) {
157+
return cell_size_field->eval({p.x(), p.y(), p.z()});
158+
}) : Cell_criteria(cell_radius_edge_ratio, cell_size_value);
159+
160+
const auto criteria = Mesh_criteria(edge_criteria, facet_criteria, cell_criteria);
161+
162+
// Mesh generation
104163
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(
105164
cgal_domain,
106165
criteria,
@@ -121,115 +180,4 @@ generate_mesh(
121180
return;
122181
}
123182

124-
void
125-
generate_with_sizing_field(
126-
const std::shared_ptr<pygalmesh::DomainBase> & domain,
127-
const std::string & outfile,
128-
const std::vector<std::vector<std::array<double, 3>>> & feature_edges,
129-
const double bounding_sphere_radius,
130-
const bool lloyd,
131-
const bool odt,
132-
const bool perturb,
133-
const bool exude,
134-
const double edge_size,
135-
const double facet_angle,
136-
const double facet_size,
137-
const double facet_distance,
138-
const double cell_radius_edge_ratio,
139-
const std::shared_ptr<pygalmesh::SizingFieldBase> & cell_size,
140-
const bool verbose,
141-
const int seed
142-
)
143-
{
144-
CGAL::get_default_random() = CGAL::Random(seed);
145-
146-
const double bounding_sphere_radius2 = bounding_sphere_radius > 0 ?
147-
bounding_sphere_radius*bounding_sphere_radius :
148-
// some wiggle room
149-
1.01 * domain->get_bounding_sphere_squared_radius();
150-
151-
// wrap domain
152-
const auto d = [&](K::Point_3 p) {
153-
return domain->eval({p.x(), p.y(), p.z()});
154-
};
155-
156-
Mesh_domain cgal_domain = Mesh_domain::create_implicit_mesh_domain(
157-
d,
158-
K::Sphere_3(CGAL::ORIGIN, bounding_sphere_radius2)
159-
);
160-
161-
// cgal_domain.detect_features();
162-
163-
const auto native_features = translate_feature_edges(domain->get_features());
164-
cgal_domain.add_features(native_features.begin(), native_features.end());
165-
166-
const auto polylines = translate_feature_edges(feature_edges);
167-
cgal_domain.add_features(polylines.begin(), polylines.end());
168-
169-
// perhaps there's a more elegant solution here
170-
// see <https://github.com/CGAL/cgal/issues/4145>
171-
if (!verbose) {
172-
// suppress output
173-
std::cerr.setstate(std::ios_base::failbit);
174-
}
175-
if (cell_size) {
176-
const auto size = [&](K::Point_3 p, const int, const Mesh_domain::Index&) {
177-
return cell_size->eval({p.x(), p.y(), p.z()});
178-
};
179-
auto criteria = Mesh_criteria(
180-
CGAL::parameters::edge_size=edge_size,
181-
CGAL::parameters::facet_angle=facet_angle,
182-
CGAL::parameters::facet_size=facet_size,
183-
CGAL::parameters::facet_distance=facet_distance,
184-
CGAL::parameters::cell_radius_edge_ratio=cell_radius_edge_ratio,
185-
CGAL::parameters::cell_size=size
186-
);
187-
188-
// Mesh generation
189-
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(
190-
cgal_domain,
191-
criteria,
192-
lloyd ? CGAL::parameters::lloyd() : CGAL::parameters::no_lloyd(),
193-
odt ? CGAL::parameters::odt() : CGAL::parameters::no_odt(),
194-
perturb ? CGAL::parameters::perturb() : CGAL::parameters::no_perturb(),
195-
exude ? CGAL::parameters::exude() : CGAL::parameters::no_exude()
196-
);
197-
if (!verbose) {
198-
std::cerr.clear();
199-
}
200-
201-
// Output
202-
std::ofstream medit_file(outfile);
203-
c3t3.output_to_medit(medit_file);
204-
medit_file.close();
205-
} else {
206-
auto criteria = Mesh_criteria(
207-
CGAL::parameters::edge_size=edge_size,
208-
CGAL::parameters::facet_angle=facet_angle,
209-
CGAL::parameters::facet_size=facet_size,
210-
CGAL::parameters::facet_distance=facet_distance,
211-
CGAL::parameters::cell_radius_edge_ratio=cell_radius_edge_ratio
212-
);
213-
214-
// Mesh generation
215-
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(
216-
cgal_domain,
217-
criteria,
218-
lloyd ? CGAL::parameters::lloyd() : CGAL::parameters::no_lloyd(),
219-
odt ? CGAL::parameters::odt() : CGAL::parameters::no_odt(),
220-
perturb ? CGAL::parameters::perturb() : CGAL::parameters::no_perturb(),
221-
exude ? CGAL::parameters::exude() : CGAL::parameters::no_exude()
222-
);
223-
if (!verbose) {
224-
std::cerr.clear();
225-
}
226-
227-
// Output
228-
std::ofstream medit_file(outfile);
229-
c3t3.output_to_medit(medit_file);
230-
medit_file.close();
231-
}
232-
return;
233-
}
234-
235183
} // namespace pygalmesh

0 commit comments

Comments
 (0)