Generates normals with multiple three-point coordinates

6 views (last 30 days)
Hi!,I wanted to generate multiple three-dimensional coordinate normals in matlab and output them in fixed format to generate stl files, but I kept making mistakes. I want to know why, and I hope you can help me.
Nodes = [0 0 0;
4 0 0;
4 4 0;
0 4 0
4 0 2;
0 0 2;
4 1 2;
0 1 2;
0 1 1;
4 1 1;
4 2 1;
4 2 2;
0 2 2;
0 2 1;
0 4 2;
4 4 2];
singletriangle = [5 10 7;
5 2 10;
10 2 3;
3 11 10;
3 16 11;
16 12 11;
6 8 9;
6 1 9;
9 1 4;
9 4 14;
14 4 15;
14 15 13];
% Find the point coordinates according to the triangle index
points_triangles = [Nodes(singletriangle,1),Nodes(singletriangle,2),Nodes(singletriangle,3)];
% Find the point coordinates of each column (three-dimensional)
points_one=points_triangles(1:length(points_triangles)/3,:);
points_two=points_triangles(length(points_triangles)/3+1:length(points_triangles)/3*2,:);
points_three=points_triangles(length(points_triangles)/3*2+1:length(points_triangles),:);
% Subtract the matrix and compute the vector
vectors_one=points_two-points_one;
vectors_two=points_three-points_one;
% Take the cross product of two vectors
normal_vectors=cross(vectors_one,vectors_two);
% Normalizes the 3D vector so that its modulus is equal to 1
normalized_normal_vectors=normal_vectors./norm(normal_vectors);
% Initializes the empty array
output=zeros(length(points_one)*4,3);
% Write output data
for i=1:length(points_one)
output(i*4-3,:)=normalized_normal_vectors(i,:);
output(i*4-2,:)=points_one(i,:);
output(i*4-1,:)=points_two(i,:);
output(i*4,:)=points_three(i,:);
end
% Output content
output=output';
STL_file_name=['lin123','.stl'] ;
STL_file = fopen (STL_file_name,'wt');
fprintf (STL_file,'solid %s\n','solid');
fprintf (STL_file, ' facet normal %14e %14e %14e\n outer loop\n vertex %14e %14e %14e\n vertex %14e %14e %14e\n vertex %14e %14e %14e\n endloop\n endfacet\n',output);
fprintf (STL_file,'endsolid %s\n','solid' );
fclose (STL_file);
  2 Comments
DGM
DGM on 27 Sep 2025 at 4:18
Unless you're just doing this as an exercise in making an STL encoder for sake of whimsy (been there), none of this is necessary. Just write the FV data to an STL. The normals are calculated internally by the encoder.
% the model
F = [5 10 7; 5 2 10; 10 2 3; 3 11 10; 3 16 11; 16 12 11; 6 8 9; 6 1 9; 9 1 4; 9 4 14; 14 4 15; 14 15 13];
V = [0 0 0; 4 0 0; 4 4 0; 0 4 0; 4 0 2; 0 0 2; 4 1 2; 0 1 2; 0 1 1; 4 1 1; 4 2 1; 4 2 2; 0 2 2; 0 2 1; 0 4 2; 4 4 2];
% write the file to a binary STL
T = triangulation(F,V);
stlwrite(T,'test.stl')
That will write a compact, portable binary STL. Unless you want to read the triangulation to your children as a bedtime story, send it via teletype, or are trying to create a file which is compatible with a pre-1987 version of the slicer used with the prototype of the SLA-1, ASCII STL is just a waste of space (and often a silent degradation in precision). Otherwise, if unquestionably desired or absolutely necessary, you can just do:
% okay fine, write an ASCII STL instead
% this will be written with enough precision
% to losslessly represent float32 coordinates and normals
stlwrite(T,'test.stl','text')
Otherwise, if you just need to get the unit normals for some other reason, you can get them from the triangulation object:
% just get the face normals
N = faceNormal(T); % bam done.
The STL encoder is available in R2018b+. The use of triangulation.faceNormal is available in R2013a+, and similar functionality is available via the TriRep class back to R2009a.

Sign in to comment.

Accepted Answer

William Rose
William Rose on 25 Feb 2023
Edited: William Rose on 25 Feb 2023
[edit: I simplified the calculation of the normalized normal vectors]
I ran your code. It runs without error. It makes an STL file (attached). I can render the STL file at https://www.viewstl.com without getting an error. What is the problem?
The rendered object looks like two 2-D sheets, rather than a 3-D object. I don;t know what you expect your object to look like. I recommend that you start with a simple set of points. I re-ran your code with 4 points and 4 triangles, to make a tetrahedron. It works. The STL file produces a tetrahedron when viewed at https://www.viewstl.com.
Nodes = [0 0 0;
1 0 0;
0 1 0;
0 0 1];
singletriangle = [1 3 2;
1 2 4;
1 4 3;
2 3 4];
% Find the point coordinates according to the triangle index
points_triangles = [Nodes(singletriangle,1),Nodes(singletriangle,2),Nodes(singletriangle,3)];
% Find the point coordinates of each column (three-dimensional)
points_one=points_triangles(1:length(points_triangles)/3,:);
points_two=points_triangles(length(points_triangles)/3+1:length(points_triangles)/3*2,:);
points_three=points_triangles(length(points_triangles)/3*2+1:length(points_triangles),:);
% Subtract the matrix and compute the vector
vectors_one=points_two-points_one;
vectors_two=points_three-points_one;
% Take the cross product of two vectors
normal_vectors=cross(vectors_one,vectors_two);
% Normalizes the 3D vector so that its modulus is equal to 1
normalized_normal_vectors=normal_vectors./norm(normal_vectors);
% Initializes the empty array
output=zeros(length(points_one)*4,3);
% Write output data
for i=1:length(points_one)
output(i*4-3,:)=normalized_normal_vectors(i,:);
output(i*4-2,:)=points_one(i,:);
output(i*4-1,:)=points_two(i,:);
output(i*4,:)=points_three(i,:);
end
% Output content
output=output';
STL_file_name=['tetra.stl'] ;
STL_file = fopen (STL_file_name,'wt');
fprintf (STL_file,'solid %s\n','solid');
fprintf (STL_file, ' facet normal %14e %14e %14e\n outer loop\n vertex %14e %14e %14e\n vertex %14e %14e %14e\n vertex %14e %14e %14e\n endloop\n endfacet\n',output);
fprintf (STL_file,'endsolid %s\n','solid' );
fclose (STL_file);
The code above makes a tetrahedron. Maybe you are not happy with the normal vectors. Let us inspect them:
disp(normal_vectors)
0 0 -1 0 -1 0 -1 0 0 1 1 1
These normal vectors point in the expected directions: outward from each face.
However, the normalized normal vectors do not have unit length:
disp(normalized_normal_vectors)
0 0 -0.5000 0 -0.5000 0 -0.5000 0 0 0.5000 0.5000 0.5000
That is a problem. Modify the normalization command. You did
normalized_normal_vectors=normal_vectors./norm(normal_vectors);
That does not give the desired result, because norm(X) returns a scalar when X is a matrix. Use vecnorm() instead:
nnv=normal_vectors./(vecnorm(normal_vectors'))';
disp(nnv)
0 0 -1.0000 0 -1.0000 0 -1.0000 0 0 0.5774 0.5774 0.5774
nnv looks like the correct set of normal vectors with unit length.
Good luck.
  2 Comments
Chenglin Li
Chenglin Li on 25 Feb 2023
Moved: DGM on 12 Jul 2025
Thank you very much. I wanted to generate a concave surface before, but the figure was not what I wanted. After your program test, I found that it reached my expected effect, which helped me a lot, thank you!!!

Sign in to comment.

More Answers (0)

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!