File Exchange

## surf2solid - make a solid volume from a surface for 3D printing

version 1.5 (6.08 KB) by

Turns thin surfaces into closed solids by adding a flat base or offsetting by a given thickness.

4.82609
28 Ratings

Updated

Editor's Note: This file was selected as MATLAB Central Pick of the Week

SOLID_FV = SURF2SOLID(FV,...) takes in a triangulated patch defined by
FV (a structure with fields 'vertices' and 'faces'), and returns a
solid patch SOLID_FV closed by options (described below).

SOLID_FV = SURF2SOLID(F, V,...) takes faces and vertices separately.

[F,V] = SURF2SOLID(...) returns solid faces and vertices separately.

SURF2SOLID(...) with no output argument plots the 3 components
(orig-surface, side-walls, under-surface) to a new figure.

SURF2SOLID(X, Y, Z, ...) reads in surface data in X, Y, and Z matrices,
and triangulates this gridded data into a surface using triangulation
options specified below. Z must be a 2D matrix. X and Y can be 2D
matrices of the same size as Z, or vectors of length equal to SIZE(Z,2)
and SIZE(Z,1), respectively. If X or Y are scalar values, they are used
to specify the X and Y spacing between grid points.

SURF2SOLID(...,'PropertyName',VALUE,...) makes a solid volume from thin
surface using any of the following property/value options:

ELEVATION - Extends the surface down to a flat base at the given
(Z) elevation value. Useful for turning a thin
elevation map into a solid block with a flat base. The
ELEVATION value should be below the lowest (or above
the highest) data point. If no other options are given,
ELEVATION defaults to MIN(Z)-0.1*(MAX(Z)-MIN(Z)).
Variable ELEVATION may also be given per-point, via a
2D matrix (the same size as Z for X,Y,Z style input) or
a 1D array (with length equal to the number of vertices
given in face/vertex input).

THICKNESS - Value to offset the given thin surface to make a
thickened solid slab. Each node on the surface will be
projected along its normal direction by thickness. When
negative thickness is given, offset will be away from
face normal direction. Variable thickness can also be
specified via a 2D matrix (of same size as Z, for X,Y,Z
input) or an N-by-1 array of thicknesses (where N is
the number of vertices in the thin surface)

TRIANGULATION - When used with gridded data, TRIANGULATION is either:
'delaunay' - (default) Delaunay triangulation of X, Y
'f' - Forward slash division of grid quads
'b' - Back slash division of quadrilaterals
'x' - Cross division of quadrilaterals
Note that 'f', 'b', or 'x' triangulations use an
inbuilt version of FEX entry 28327, "mesh2tri". 'x'
style triangulation cannot be used with variable
ELEVATION or THICKNESS parameters.

NORMALS - When THICKNESS options is used, the direction to
thicken the surface is (by default) determined by the
surface (unit vector) normal directions at each vertex.
To override these default directions, you may specify
NORMALS as an N-by-3 array of normal directions (where
N is the number of vertices in the thin surface). This
is useful when underlying data gives more precise
normal directions than face orienatations (for an
example, see the isonormals function).

Note 1: Currently surf2solid will return a closed surface with face
normals pointing "out". With user feedback, I'd be happy to change this
behaviour to either "in" or "unchanged from input direction".
Note 2: If a single ELEVATION value is specified (i.e., flat base), the
resulting patch will have minimal triangles on the flat base to reduce
patch/file size.

Example (shows both THICKNESS and ELEVATION forms):
n = 30;
[X,Y] = meshgrid(linspace(0,1,2*n+1));
L = (40/51/0.9)*membrane(1,n);
figure, subplot(2,2,[1 3]), title 'Thin surface'
surf(X,Y,L,'EdgeColor','none'); colormap pink; axis image; camlight
subplot(2,2,2), title 'Block elevation'
surf2solid(X,Y,L,'elevation',min(L(:))-0.05); axis image; camlight; camlight
subplot(2,2,4), title 'Thickness'
surf2solid(X,Y,L,'thickness',-0.1); axis image; camlight;

Original idea adapted from Paul Kassebaum's blog post
http://blogs.mathworks.com/community/2013/06/20/paul-prints-the-l-shaped-membrane/
Many thanks to Paul for his further input and improvements.

Leland Muller

Jonathan Haydak

### Jonathan Haydak (view profile)

been using this thing for a while. author deserves to get a cookie. if i ever meet you i will give you a cookie.

Andrew Bliss

### Andrew Bliss (view profile)

Great function. Thanks!

@Nicholas I found that just eliminating the NaNs worked well. E.g. for matrices X,Y,Z where Z has NaNs:
ind=isnan(Z);
X(ind)=[];
Y(ind)=[];
Z(ind)=[];

Tom

### Tom (view profile)

@Nicholas, you could try inpaint_nans or 3d_inpaint_nans written by John D'Errico. They provide a choice of methods for substituting better values for NaNs. These functions can be found in the Matlab File Exchange (like this one). I have not used these particular functions but implemented something similar many years ago.

Nicolas Alvarez

### Nicolas Alvarez (view profile)

Hi Sven,
Nice work! I do have one issue. My shape is irregular and therefore has many points in the matrix that are empty (i.e. zero). I typically set these to NaN so that surf omits them. However, your code does not allow for NaN points. If I leave the points as "0", then surf2solid uses them to create the solid object. How do you suppose I work around the issue? Thanks in advance, Nicolas

Akhilesh Gupta

Rahul Dutta

### Rahul Dutta (view profile)

Hi Sven,
I have used surf2solid to create a solid block of my NURBs surface. The thing is that at the trough portions of the surface, faces form and the surface looks more like it is bounded by walls on all sides rather than just having a block with a base.

John Logan

Amirali Nojoomi

danny

### danny (view profile)

Hi Sven (+ all MATLAB guru),

I have a question regarding setting/fixing the dimension in MATLAB for 3D printing that I hope you could advice me. I have read through your code (it is soooooo good!!), my question is if I want the thickness to be exactly 1mm and the width of the object to be printed to be 5cm in length, where/how do I specify the dimension. Do I do it for the initial surface plot first?

Many thanks for your time and wisdom shared.

Regards,

Kim

uncletat

uncletat

### uncletat (view profile)

Hi Sven,

I am having problems using the thickness feature of surf2solid.

I understand that the thickness feature will only create one offset plane from the surface normal and extrude the space in between (Single-sided extrusion).

However, in my application, a single-sided extrusion would mess up the proportionality of the geometry. I would need a double-sided extrusion (or a mid-plane extrusion). In other words, is there a way to create two offset planes so that I can extrude in both positive and negative direction from the normal, so that the original surface of the geometry would be at the exact middle of the plane.

Thank you very much!

Alex

Sven

### Sven (view profile)

@Thalles,
I was just picking the particular vertices to remove from the synthetic data that my code generated (I generated the MATLAB logo, the vertices to remove are in one quadrant of that logo).
You will have your own choice of vertices to remove from your data.

Sven

### Sven (view profile)

@Mico,
I don't use MuPAD, but can you sample your function at intervals so as to decimate the implicit surface and make it explicit (i.e., make a set of faces/vertices that lie on your surface)?

It would seem that this is the difficult step (and sorry, like I said, no experience with MuPAD). After that you can send your surface to surf2solid to thicken it.

Mico Stanojevic

### Mico Stanojevic (view profile)

Second question :)
If I make implicit function like im := plot::Implicit3d(cos(x) + cos(y) + cos(z) ....., is it possible to make thickness for solid generation and then to generate 3D volume mesh for FE model (for example to export Nastran file). Respectfully , Mico

Mico Stanojevic

### Mico Stanojevic (view profile)

Dear Sven, dear all,
if I have implicit function like im := plot::Implicit3d(cos(x) + cos(y) + cos(z),
x = 0..2*PI,
y = 0..2*PI,
z = 0..2*PI):
How to give desired thickness to this surface (getting "solid" for 3D printing) and export to stl (In MuPAD). BR, Mico

Thalles Leite

### Thalles Leite (view profile)

@Sven:

I have only one question, in this line:

Why are you searching on the first and second columns?

What do they represent? I'm sorry for the stupid question.

Sven

### Sven (view profile)

@Josafat:
There is a function called meshVolume in the geom3d package from David Legland.

Sven

### Sven (view profile)

@Thalles, maybe you can just delete them:

n = 10;
[X,Y] = meshgrid(linspace(0,1,2*n+1));
L = (40/51/0.9)*membrane(1,n);
XX = surf2solid(X,Y,L,'elevation',0);
% Find the vertices you want to remove
% Find the faces that use those vertices
% Remove them
figure, patch(XX,'FaceColor','g')

Thalles Leite

### Thalles Leite (view profile)

Hi Sven,

Can I use this code without a elevation?

I'm trying to use

fv=surf2solid(x,y,z,'elevation',0)

to use it later with the stlwrite, but I'm getting a bad file stl.

When I use any other elevation, the stl works fine.

I have already tryied to use thickness 0 in the nodes I dont want, but it generated the same bad file, and when I print then, the printer still creates a basis.

sumana

Josafat Quiroz

### Josafat Quiroz (view profile)

Hi seven,

is there any way to get the area and volumen of the generated solid?

Leila Ghanbari

### Leila Ghanbari (view profile)

Hi Sven,

Great job. I would like to generate a smooth surface;
FV_closed = surf2solid(x,y,z,'triangulation', 'delaunay', 'thickness',-2);

I am using 'triangulation', 'delaunay', since only specific range of x and y is desirable. But the surface is not smooth. Is there any way to fix it?

Meghan Kazanski

### Meghan Kazanski (view profile)

Is there any way to combine multiple surfaces (as two sets of X,Y,Z matrices) into one solid?

Sven

### Sven (view profile)

@Sarah: You gave a property ('elevation') without a value. You need to specify the actual elevation that you desire. Try this code, which specifies an elevation of "3" such that faces are extruded down to a Z-coordinate of 3:

V = [0 0 10; 1 2 11; 2 4 12; 4 2 11; 5 0 10; 2.5 0 9];
F = [1 2 6; 6 4 5; 2 4 6; 2 3 4];
surf2solid(F,V,'Elevation',3)

Sarah Weissenberger

### Sarah Weissenberger (view profile)

Hi Sven,

I would like to use your code, giving it faces and vertices.
I tried using: surf2solid (F,V, 'elevation');

But Matlab is throwing an error. Can you imagine what is wrong with that?

Thank you very much.

Evan

### Evan (view profile)

Hi, a question when trying to figure out a solid with holes inside. For example, the "meshgrid" creates a 5*5 matrix, but I only need to use the central ring area to create solid ring-cylinder. I tried to set the thickness of those pixels(unwanted) to 0, but it still carried out a flat plan covering the whole 5*5 area (I expect no solid in the center hole). How could I set it? Thanks so much.

Nathan Tomlin

### Nathan Tomlin (view profile)

Works really great and imports into openscad perfectly!

Nathan Tomlin

Sven

### Sven (view profile)

Hi Mona, the output from surf2solid (a faces/vertices structure) can be given directly to stlwrite:

FV = surf2solid(...)
stlwrite('yourfile.stl', FV)

Mona Mahboob Kanafi

### Mona Mahboob Kanafi (view profile)

Hi Sven,

Could you give me a clue on how to convert this generated solid topography into STL format? For instance using your older 'stlwrite - Write binary or ascii STL file' code.

Thanks for the awesome work in advance! :)

Sven

### Sven (view profile)

Hi Marios:

1) Use the triangulated faces/vertices input (SURF2SOLID(F, V,...)) instead of the gridded input (SURF2SOLID(X, Y, Z, ...)) and you can make arbitrary shaped surfaces instead of a grid. I would simply put 0 thickness at the nodes you don't want solid instead of NaN thickness.

2) This is possible, you just need to calculate the distance you want each node to be offset and use that 'thickness'. Basically, give the thickness as the difference between the original surface and your desired surface.

Here's a small example that uses most of those options:

v = [2 4 0; 2 6 0; 8 4 1; 8 0 0; 0 4 0]
f = [1 2 3; 1 3 4; 5 2 1]
figure, surf2solid(f,v,'thickness',[0 0 3 2 0])

Marios

### Marios (view profile)

Hi Sven,

Very good work. Two questions.

1) Can I change the rectangular mesh to an irregular (curved) one. I have NaN to areas I don't want to create a solid, but it says that it is not supported.

2) Is it possible that user provide a different bottom surface (assuming that up and down surface have the same x,y coordinates).

Great!

Awesome! Thanks!

Daniel

### Daniel (view profile)

Thanks for the update Sven! After removing degenerate faces from my surfaces, I feel like this works very well. I've even fed the result through stlwrite, and successfully printed one.

-Daniel

Sven

### Sven (view profile)

Hi Daniel, I've had time to make this fix - it was simply a case of adding an if-isempty clause to line 100 to detect surfaces without any boundary. It's in submission now, and here's a working example of a torus that had the error you described but now works fine:

% Make a torus
a=5;
c=10;
[u,v]=meshgrid(0:10:360);
x=(c+a*cosd(v)).*cosd(u);
y=(c+a*cosd(v)).*sind(u);
z=a*sind(v);
figure, t = surf(x,y,z); p = surf2patch(t); close(gcf)
% Triangulate faces (removing coincident vertices)
tri = cat(1, p.faces(:, [1 2 3]), p.faces(:, [1 3 4]));
[unqV, ~, unqI] = unique(p.vertices,'rows');
pTri = struct('vertices',unqV,'faces',unqI(tri));
% Solidify thin surface
pSol = surf2solid(pTri,'Thickness',1)
% Show the result
figure, patch(pSol,'FaceColor','g','FaceAlpha',0.2)

Sven

### Sven (view profile)

Hi Daniel,
That sounds like the type of thing that surf2solid should handle but I never used it as a test case. I think it should be manageable to work out what to do when "edge" faces are empty. I'll try to put something in, but it will be a week or so... I'm travelling right now.

Daniel

### Daniel (view profile)

Thanks for the m-file! I am generally pleased with what I see.

I would like to use this file to thicken surfaces generated from implicit equations. Such an example is a torus. I have a coarse set of faces which describe the surface, and want to pass it through the thickener for printing, but I am getting an error at and beyond line 99 (getting boundVerts), because there are no bounding vertices (the surface is closed!)... Do you have a workaround? It is not immediately clear to me how to correct for this.
Thanks again!

Awesome work!
here is a link to an excellent blog post (written by Sven) with examples of how to use surf2solid function:
http://imageprocessingblog.com/surf2solid-a-tool-for-3d-printing/

Phalgun Lolur

### Phalgun Lolur (view profile)

What I meant to say was, I use the hold on command to add new objects to my axis. But how do I create an stl file from that? My objective is to print a multi-layered 3D file. But I'm not quite sure how to do that.

Sven

### Sven (view profile)

Hi Phalgun,
I don't quite know what you mean by "multi-layered data". As far as I can tell, the "hold on" command will simply let you add new objects to your current plot axes... nothing about multi-layers and nothing about printing there. What is the "multi-layered 3d image file" type that your 3d printer accepts, and how do you create one of these files normally?

Phalgun Lolur

### Phalgun Lolur (view profile)

Dear Sven,

The file worked wonderfully well. But if I needed to use it for printing multi-layered data, how would I do it? I usually use the "hold on" command to generate these multi layered 3d image files in matlab. But I was wondering how to create an stl file from it.

Paul Kassebaum

Love it!

mathgirl

### mathgirl (view profile)

The function is amazing. It works fast and efficiently, and is very easy to use. The resulting stl has high resolution and reasonable size.

Thank you Sven!