demo16 of Im2mesh package

demo16 - Add mesh seeds/nodes
Cite as: Ma, J., & Li, Y. (2025). Im2mesh: A MATLAB/Octave package for generating finite element mesh based on 2D multi-phase image (2.1.5). Zenodo. https://doi.org/10.5281/zenodo.14847059
Jiexian Ma, mjx0799@gmail.com. Project website.
Table of Contents

Note

I suggest familiarizing yourself with Im2mesh_GUI before learning Im2mesh package. With graphical user interface, Im2mesh_GUI will help you better understand the workflow and parameters of Im2mesh package.
Im2mesh_GUI: https://www.mathworks.com/matlabcentral/fileexchange/179684-im2mesh_gui-2d-image-to-finite-element-mesh
If you are using Im2mesh package in MATLAB, you need to install MATLAB Image Processing Toolbox and Mapping Toolbox because Im2mesh package use a few functions in these toolboxes.

Setup

Before we start, please set folder "Im2mesh_Matlab" as your current folder of MATLAB.
clearvars
Set default image size.
x = 250; y = 250; width = 250; height = 250;
set(groot, 'DefaultFigurePosition', [x,y,width,height])
% To reset:
% set(groot, 'DefaultFigurePosition', 'factory')
Function bounds2mesh use a mesh generator called MESH2D (developed by Darren Engwirda). We can use the following command to add the folder 'mesh2d-master' to the path of MATLAB.
addpath(genpath('mesh2d-master'))

Overview

Please take a look at demo14 before starting demo16.
In Im2mesh package, mesh seeds/nodes are defined as markers that you place along the edges of a region. We use mesh seeds to specify target mesh density in a region.
There are two different cases for adding mesh seeds. We will show them in this demo.
We have demostrated how to create polygonal boundary in previous demo. There are two approaches:
In this demo, we'll use polyshape to create polygonal boundary, and then add mesh seeds/nodes to boundary. For boundaries created based on 2d image, the workflow of adding mesh seeds/nodes is the same.

Case 1. Coordniates of the seeds to be added is known

Define polyshape and create boundary

Define a cell array of polyshape objects
% create polyshape
vert1 = [ 0 0; 15 0; 15 10; 0 10 ];
ps1 = polyshape(vert1);
vert2 = [15 0] + [ 0 0; 10 0; 10 10; 0 10 ];
ps2 = polyshape(vert2);
vert3 = [15 10] + [ 0 0; 10 0; 10 25; 0 25 ];
ps3 = polyshape(vert3);
ps13 = union( ps1, ps3 );
psCell = { ps2; ps13 };
% plot psCell
figure
hold on; axis equal;
for i = 1: length(psCell)
plot(psCell{i});
end
hold off
We saw 2 phases (or 2 parts) and 3 regions.
Create polygonal boundary from polyshape
% bounds is a nested cell array of polygonal boundary
bounds = polyshape2bound(psCell);
tol_intersect = 1e-6; % distance tolerance for intersect
bounds = addIntersectPnts( bounds, tol_intersect );
% plot boundaries and show all vertices
plotBounds(bounds,false,'ko-')
We can use function xyRange to get the range of x y coordinate in boundary.
[xmin, xmax, ymin, ymax] = xyRange( bounds )
xmin = 0
xmax = 25
ymin = 0
ymax = 35

Add a midpoint to the bottom edge

Suppose we want to add a midpoint to the bottom edge of 'bounds' .
Based on xmin, xmax, ymin, ymax, we know that the midpoint in the bottom edge of 'bounds' would be
xMid = (xmin + xmax)/2;
yMid = ymin;
We use function addPnt2Bound to add point (xMid,yMid) to boundary.
point = [xMid, yMid];
tol_dist = 1e-2; % distance tolerance
newB = addPnt2Bound( point, bounds, tol_dist );
% show all vertices
plotBounds( newB, false, 'ko-' );
We saw that the midpoint of the bottom edge is added to the boundary.
When we generate finite element mesh, the mesh will have this point in the vertices.

Add a point near the bottom edge

The point to be added does not have to lie exactly on the pristine boundary. The point can have some distance away from the pristine boundary. Check the following example.
Here we set distance tolerance 'tol_dist' to 5. If the shortest distance between the point and the pristine boundary is less than 'tol_dist', the point will be added to the boundary. Otherwise, the point will not be added.
point = [7,-2];
tol_dist = 5; % distance tolerance
newB = addPnt2Bound( point, newB, tol_dist );
% show all vertices
plotBounds( newB, false, 'ko-' );

Generate mesh

hmax = 3;
grad_limit = 0.25;
opt = [];
opt.disp = inf; % silence verbosity
[vert,tria,tnum] = bounds2mesh( newB, hmax, grad_limit, opt );
Plot mesh.
plotMeshes(vert,tria,tnum);

Add multiple points

Suppose we want to add multiple points/seeds to the bottom edge of 'bounds' .
Based on xmin, xmax, ymin, ymax, we know that two end point in the bottom edge of 'bounds' would be
xyLeft = [xmin, ymin];
xyRight = [xmax, ymin];
We can use function insertMidPnt to add seeds.
points = [ xyLeft; xyRight ];
iters = 5;
points = insertMidPnt( points, iters );
plot( points(:,1), points(:,2), 'k.-' );
axis equal
We use function addPnt2Bound to add points to boundary.
tol_dist = 1e-2; % distance tolerance
newB = addPnt2Bound( points, bounds, tol_dist );
% show all vertices
plotBounds( newB, false, 'k.-' );

Generate mesh

hmax = 3;
grad_limit = 0.25;
opt = [];
opt.disp = inf; % silence verbosity
[vert,tria,tnum] = bounds2mesh( newB, hmax, grad_limit, opt );
Plot mesh.
plotMeshes(vert,tria,tnum);

Add biased seeds

We can use function insertBiasedSeed to add biased seeds (non-uniform distributed).
Note that the 1st input of function insertBiasedSeed should be a 2-by-2 array (two points), and the output result depends on the vertex ordering in the input edge.
points = [ xyLeft; xyRight ];
iters = 6;
ratio = 0.5;
points = insertBiasedSeed( points, iters, ratio );
plot( points(:,1), points(:,2), 'ko-' );
axis equal
We can set ratio to negative value to flip direction.
points = [ xyLeft; xyRight ];
iters = 6;
ratio = -0.5;
points = insertBiasedSeed( points, iters, ratio );
plot( points(:,1), points(:,2), 'ko-' );
axis equal
We use function addPnt2Bound to add points to boundary.
tol_dist = 1e-2; % distance tolerance
newB = addPnt2Bound( points, bounds, tol_dist );
% show all vertices
plotBounds( newB, false, 'ko-' );

Generate mesh

hmax = 3;
grad_limit = 0.25;
opt = [];
opt.disp = inf; % silence verbosity
[vert,tria,tnum] = bounds2mesh( newB, hmax, grad_limit, opt );
Plot mesh.
plotMeshes(vert,tria,tnum);

Case 2. Coordniates of the seeds to be added is unknown

This case is often for complicated polygonal boundaries, such as boundary extracted from multi-phase image.
In this case, we will need to find the polygonal boundary that we want to add mesh seed. I'll demostrate how to do this.
We will use the same geometry for demostration.
% plot boundaries and show all vertices
plotBounds(bounds,false,'ko-')

Find edges we interested in

Suppose we are interested in the bottom edge and the right edge in the topmost rectangle. Let's find these edges in 'bounds'.
bounds is a nested cell array of polygonal boundaries.
bounds
bounds = 2×1 cell
 1
11×1 cell
22×1 cell
We can plot polygons one by one.
figure;
counter = 1;
for i = 1: length(bounds)
for j = 1: length(bounds{i})
subplot(2,2,counter);
poly = bounds{i}{j};
plot( poly(:,1), poly(:,2) ); axis equal;
title(['bounds\{', num2str(i), '\}\{', num2str(j), '\}']);
counter = counter + 1;
end
end
The edge we interested in belongs to bounds{2}{2}.
bounds{2}{2}
ans = 5×2
15 10 25 10 25 35 15 35 15 10
These are x and y coordinates of vetices.
Let's plot it.
poly = bounds{2}{2};
figure; hold on; axis equal
plot( poly(:,1), poly(:,2), 'ko-' );
% add numbering
for i = 1: length(poly)
text( poly(i,1), poly(i,2), num2str(i), ...
'HorizontalAlignment', 'left', ...
'VerticalAlignment', 'bottom', ...
'FontSize', 12, 'Color', 'red');
end
hold off
In the figure, the numberings of the 1st and the last vertex overlap.

Method 1. Add seed by inserting midpoint

We can use function insertMidPnt to add seeds.
The edge we interested in is between vertex 1 and vertex 3 in the polygon
Let's extract it.
index = 1:3;
polyline = poly( index, : );
plot( polyline(:,1), polyline(:,2), 'ko-' ); axis equal
Then, we can add seeds to the interested edges by repeatedly inserting midpoints to edges. We do this by function insertMidPnt.
iters = 3;
polyline = insertMidPnt( polyline, iters );
plot( polyline(:,1), polyline(:,2), 'ko-' ); axis equal
Add points to bounday
Use function addPnt2Bound to add the points to the bounday.
tol_dist = 1e-2; % distance tolerance
newB = addPnt2Bound( polyline, bounds, tol_dist );
% show all vertices
plotBounds( newB, false, 'ko-' );
Awesome! But we notice that the space between seeds in different edges are different. This is not good.

Method 2. Add uniform seeds

We can use function insertEleSizeSeed to add better uniform seeds.
The edge we interested in is between vertex 1 and vertex 3 in the polygon. Let's extract it.
index = 1:3;
polyline = poly( index, : );
plot( polyline(:,1), polyline(:,2), 'ko-' ); axis equal
Then, we can add uniform seeds to the interested edges by function insertEleSizeSeed. We can specify the space between seeds.
target_size = 1; % space between seeds
polyline = insertEleSizeSeed( polyline, target_size );
plot( polyline(:,1), polyline(:,2), 'ko-' ); axis equal
Very good! The space between seeds in different edges are the same now.
Add points to bounday
Use function addPnt2Bound to add the points to the bounday.
tol_dist = 1e-2; % distance tolerance
newB = addPnt2Bound( polyline, bounds, tol_dist );
% show all vertices
plotBounds( newB, false, 'ko-' );
Generate mesh
hmax = 3;
grad_limit = 0.25;
opt = [];
opt.disp = inf; % silence verbosity
[vert,tria,tnum] = bounds2mesh( newB, hmax, grad_limit, opt );
Plot mesh.
plotMeshes(vert,tria,tnum);
Awesome!
Add seeds to all boundaries
We can also add uniform seeds to all boundaries, which may be useful in some cases.
target_size = 1; % space between seeds
n_digit = 2;
newB = bounds;
for i = 1: length(newB)
for j = 1: length(newB{i})
newB{i}{j} = insertEleSizeSeed( newB{i}{j}, target_size );
newB{i}{j} = round( newB{i}{j}, n_digit );
end
end
% show all vertices
plotBounds( newB, false, 'k.-' );
Generate mesh
hmax = 3;
grad_limit = 0.25;
opt = [];
opt.disp = inf; % silence verbosity
[vert,tria,tnum] = bounds2mesh( newB, hmax, grad_limit, opt );
Plot mesh.
plotMeshes(vert,tria,tnum);

Method 3. Add biased seeds

The above example is with uniform seeds. How about biased seeds (non-uniform distributed)?
We can use function insertBiasedSeed to do that.
Suppose we are interested in the edge between vertex 1 and vertex 2 in the polygon.
index = 1:2;
edge = poly( index, : );
plot( edge(:,1), edge(:,2), 'ko-' ); axis equal
We can use function insertBiasedSeed to insert biased seeds into a single edge. Note that the 1st input of function insertBiasedSeed should be a 2-by-2 array (two points), and the output result depends on the vertex ordering in the input edge.
iters = 6;
ratio = 0.5;
edge = insertBiasedSeed( edge, iters, ratio );
plot( edge(:,1), edge(:,2), 'ko-' ); axis equal
Let's use function insertMidPnt to make seeds denser.
iters = 1;
edge = insertMidPnt( edge, iters );
plot( edge(:,1), edge(:,2), 'ko-' ); axis equal
Add points to bounday
Use function addPnt2Bound to add the points to the bounday.
tol_dist = 1e-2; % distance tolerance
newB = addPnt2Bound( edge, bounds, tol_dist );
% show all vertices
plotBounds( newB, false, 'ko-' );
Generate mesh
hmax = 3;
grad_limit = 0.25;
opt = [];
opt.disp = inf; % silence verbosity
[vert,tria,tnum] = bounds2mesh( newB, hmax, grad_limit, opt );
Plot mesh.
plotMeshes(vert,tria,tnum);
Awesome!
% reset image size
set(groot, 'DefaultFigurePosition', 'factory')
% end of demo