Tutorial: Generation of 3D objects from 2D contours through extrusion and rotation

Prof. Dr. Tim C. Lueth, Professor at Technical University of Munich, MIMED - Department of Mechanical Engineering, Munich, August 2025
clear, close all % Important for script development. The line can be removed later.

==== PART 1 ============================================================

An example contour that we want to extrude

CPLsample(7); CPL=ans;
When two contours, CPLA and CPLB, are connected to form a body, then in the simplest case there are

Converting the embedded CPL into a point list and an edge list with "PLELofCPL"

Wir zerlegen CPLA in PLA und ELA
[PL,EL]=PLELofCPL(CPL)
PL = 22×2
0 -5 10 -10 5 0 10 5 5 10 0 5 0 10 -5 5 -10 10 -5 0
EL = 22×2
1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11
SGfigure(0,90); VLELplots(PL,EL,'r.-',.5,.5,.5);

For the ground floor and ceiling cover, we tessellate PLA and ELA into triangles using "delaunayTriangulation."

TR=delaunayTriangulation(PL,EL);
VLFLplot(TR.Points,TR.ConnectivityList,'r',0.1);

However, we are only looking for triangles that are enclosed by the edge list with "isInterior".

We select the facets enclosed by the edges of the contour. "isInterior" selects only the facets that are inside of edges that define a triangle facet by tight hand rule
iia=isInterior(TR);
VLA=TR.Points; FLA=TR.ConnectivityList(iia,:);

We still need to flip the normal vector of the surfaces, as the ground floor of the body must point downwards.

FLA=FLA(:,[1 3 2]); % Change in the direction of the nromal vector of the surface from top to bottom
VLA=VLaddz(VLA,0);
VLFLplotalpha(VLA,FLA,'m',1,'k');
VLFLplot(VLA,FLA,'m',2,5); view(-30,30)

We can now represent the bottom of the body as a surface.

SGfigure(-30,30); VLFLplot(VLA,FLA,'g');

Ceiling cover with the same contour but normal vector pointing upwards

VLB=VLaddz(VLA(:,1:2),30); FLB=TR.ConnectivityList(iia,:);

We can now represent the bottom of the body and the lid as surfaces.

SGfigure(-30,30); VLFLplot(VLB,FLB,'r','',5); VLFLplot(VLA,FLA,'g','',5);

We can automatically determine the wall surfaces of a contour from the edge lists.

nva=size(VLA,1)
nva = 22
VL=[VLA;VLaddz(VLA(:,1:2),30)];
FL1=[EL(:,1) EL(:,2) EL(:,2)+nva]; % FLcontourwallELn
FL2=[EL(:,1) EL(:,2)+nva EL(:,1)+nva]; % FLcontourwallELn
FLWA=[FL1;FL2];
VLFLplot(VL,FLWA,'y','',5);

However, this can only be achieved so easily (with two lines) if the two contours at the top and bottom are similar

For embedded contours, the two contours must also

[VL,FL,FLW,FLU,FLO]=VLFLofPLELz(VLA(:,1:2),EL,30);
SGfigure(-30,30);
VLFLplotalpha(VL,FLW,'y',0.9);
VLFLplotalpha(VL,FLU,'g',1);
VLFLplotalpha(VL,FLO,'r',0.9);

==== PART 2 ============================================================

"SGofCPLz" Erzeugen von Körper durch Extrusion in der Höhe z

SGofCPLz(CPLsample(38),15);
SGofCPLz(CPLsample(38),[-10 10]);
SGofCPLz([PLcircle(15);nan nan;PLcircle(16)],15)
ans = struct with fields:
VL: [224×3 double] FL: [448×3 double]
SGofCPLz([PLcircle(15);nan nan;PLcircle(16)],15,'floor',5); VLFLplotlight(1,0.5);
SGofCPLz([PLcircle(15);nan nan;PLcircle(16)],15,'cover',5); VLFLplotlight(1,0.5);
SGofCPLz(PLsquare(30,20),10);
SGofCPLz(PLsquare(30,20),10,'wall',2,'floor',2);
SGofCPLz(PLsquare(30,20),10,'wall',2,'floor',2,'cover',2); VLFLplotlight(1,0.5);
SGofCPLz(PLsquare(30,20),10,'wall',2,'floor',2,'flsub',PLcircle(7)); VLFLplotlight(1,0.5);
SGofCPLz(PLsquare(30,20),10,'wall',2,'cover',2,'csub',PLcircle(7)); % csub works only without walls
SGofCPLz([PLcircle(15);nan nan;PLcircle(16)],15,'cover',5,'csub',PLsquare(10)); % csub works only without walls

"SGofCPLextrude"

SGofCPLextrude(CPLsample(7),50','y');
SGofCPLextrude(CPLsample(7),50','x');

"SGofCPLflat"

SGofCPLflat(CPLsample(7));

"SGofCPLrot"

SGofCPLrot(PLtransP(CPLsample(7),[30 0]));

"SGofCPLrota"

SGofCPLrota(PLtransP(CPLsample(7),[30 0]),pi);

"SGofCPLzchamfer"

SGofCPLzchamfer(CPLsample(8),10,1);

"SGofCPLzdelaunayGrid" if on edges and in plane x and y should be auxiliary grid points

SGofCPLzdelaunayGrid(CPLsample(8),10,1,1,1);
PLFLofCPLdelaunayGrid: (line 65)
PL = 96×2
9.8079 1.9509 9.4346 2.8521 9.0613 3.7533 8.6880 4.6545 8.3147 5.5557 7.6249 6.2455 6.9352 6.9352 6.2455 7.6249 5.5557 8.3147 4.6545 8.6880

"SGaddsurfpoints" if also in z there should be auxiliary grid points

SGaddsurfpoints(SGofCPLz(CPLsample(8),10),'',[1,1,1]);
SGmakedoublevertex
ans = struct with fields:
VL: [2632×3 double] FL: [5250×3 double] FC: [5245×3 double]

"SGofCPLztwist"

SGofCPLztwist(PLrope([4 1],[1 .5],false),5);

"SGofCPLextrudealongCPL"

SGofCPLextrudealongCPL(PLsquare(3),PLsquare(40)); % Edge Problems - Outdated better use "SGofCPLcontourinstead"

"SGofCPLcontour"

SGofCPLcontour(PLsquare(3),PLsquare(40));
r = 3
SGofCPLcontour(PLsquare(3),PLsquare(40),false);
r = 3

"SGofCPLTL" (uses "SGof2CPLzheurist")

CPLC=PLsquare([60 40]);
CPLC=[CPLC;nan nan;CPLbuffer(CPLC,-5)];
CPLC=CPLsample(33)*2;
PL=[0 0; 0 80; 80 80; 80 40; 160 0];
PL=VLaddz(PL); PL(end,3)=80;
Ro=rofcirclearoundCPL(CPLC);
TL=TLofCVL(PL,Ro*1.1,'','','rad',false);
% SGofCPLTL(CPLC,TL);
SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG,'y'); tplot(TL(:,:,1),30); tplot(TL(:,:,end),30);
The main function to create the path is "TLofCVL" that supports torsion or no torsion and rad, rmax, tan torsion is required, if start and end frame force a rotation of the solid in the direction of the path It is also possible to enforce an start frame in addition to the path
TL=TLofCVL(PL,Ro*1.1,eye(4),'','rad',true); % With Torsion
TLadjustR: NEED TO ROTATE STARTFRAME BY 1 degree TLadjustR: NEED TO ROTATE ENDFRAME BY 159 degree
% SGofCPLTL(CPLC,TL);
SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG,'y'); tplot(TL(:,:,1),30); tplot(TL(:,:,end),30);
It is also possible to enforce an end frame in addition to the path
TL=TLofCVL(PL,Ro*1.1,eye(4),TofPez([160 0 80],[0 0 1]),'rad',true); % With Torsion
TLadjustR: NEED TO ROTATE STARTFRAME BY 1 degree TLadjustR: NEED TO ROTATE ENDFRAME BY 147 degree
% SGofCPLTL(CPLC,TL);
SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG,'y'); tplot(TL(:,:,1),30); tplot(TL(:,:,end),30);
It is also possible to use the maximum radius (required for VLtangentcirc)
TL=TLofCVL(PL,Ro*2,eye(4),TofPez([160 0 80],[0 0 1]),'rma',true); % With Torsion
TLadjustR: NEED TO ROTATE STARTFRAME BY 1 degree TLadjustR: NEED TO ROTATE ENDFRAME BY 151 degree
% SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG);
SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG,'y'); tplot(TL(:,:,1),30); tplot(TL(:,:,end),30);
It is also possible to enforce tangential loop if the radius is to big (required for VLtangentcirc)
TL=TLofCVL(PL,Ro*2,eye(4),TofPez([160 0 80],[0 0 1]),'tan',true); % With Torsion
TLadjustR: NEED TO ROTATE STARTFRAME BY -1 degree TLadjustR: NEED TO ROTATE ENDFRAME BY 148 degree
% SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG);
SG=SGofCPLTL(CPLC,TL); SGfigure(-30,30); SGplotalpha(SG,'y'); tplot(TL(:,:,1),30); tplot(TL(:,:,end),30);

"SGofCPLtransT" für embedded CPL and multiple cross sections

SGofCPLtransT(CPLsample(13),eye(4),TofR(rot(0,0,pi/20),[0 0 40]));
TL=TLofCVL(VLsample(4)/2,2,eye(3),rot(0,0,pi/2),'rad');
TLadjustR: NEED TO ROTATE ENDFRAME BY 90 degree
SG=SGofCPLtransT(CPLsample(27)/20,TL); SGfigure(SG); view(-30,30); zoompatch('',0.01); camlightTL;

"SGofCPLCVLR" uses "VLinsertEulerSteps" and "SGcontourtube" => Should be replaced by SGofCPLtransT combined with TLofCVL

VL=[0 0 0; 0 0 10; 0 0 20; 10 0 20; 15 0 20; 20 0 20;20 20 20];
CPL=[PLcircle(5);NaN NaN;PLcircle(3,4)];
CPL=CPLsample(33);
r=10;
SGofCPLCVLR(CPL/2,VL,r); % Solution with Radial Edges
Positive Radius leads to radial edges
r=3;
SGofCPLCVLR(CPL/2,VL,r); % Solution with Radial Edges
Negative Radius leads to Bezier contours
SGofCPLCVLR(CPL/2,VL,-r); % Solution with Bezier
Warning: 1st Euler angle does not fit to vertex list path direction
Warning: Last Euler angle does not fit to vertex list path direction

"SGofCPLtransT" but without Bezier and

r=10;
TL=TLofCVL(VL,r,eye(4));
TLadjustR: NEED TO ROTATE STARTFRAME BY -45 degree TLadjustR: NEED TO ROTATE ENDFRAME BY -90 degree
SGofCPLtransT(CPL/2,TL);

"SGofCPLreinforcement"

CPLi=CPLsample(36)*5; SGofCPLz([CPLbuffer(CPLi,1);nan nan;CPLi],5); SG=ans;
SGofCPLreinforcement(CPLbuffer(CPLi,1),[-2 2 0]); SGplotalpha(SG,'g',0.5) % Outer contour
Warning: Duplicate edge constraints have been detected and removed.
Warning: Duplicate edge constraints have been detected and removed.
ans =
Patch with properties: FaceColor: 'flat' FaceAlpha: 0.5000 EdgeColor: 'none' LineStyle: '-' Faces: [280×3 double] Vertices: [140×3 double] Show all properties

"SGofCPLbendonadrum" bends an extruded thin CPL on a drum (Stents)

CPL=[PLsquare(80);nan nan;PLsquare(100)]; SGofCPLbendonadrum(CPL,2)
PLFLofCPLdelaunayGrid: (line 65)
PL = 844×2
-40.0000 40.0000 -39.1489 40.0000 -38.2979 40.0000 -37.4468 40.0000 -36.5957 40.0000 -35.7447 40.0000 -34.8936 40.0000 -34.0426 40.0000 -33.1915 40.0000 -32.3404 40.0000
ans = struct with fields:
VL: [11016×3 double] FL: [22032×3 double]

==== PART 3 ============================================================

Finally, let's look at how to connect two different contours with the same configuration.

"SGof2CPLzheurist"

SGof2CPLzheurist(CPLsample(7),CPLsample(8),10); VLFLplotlight(0,1);
SGof2CPLzheurist(PLaddauxpoints(CPLsample(7),1),CPLsample(8),10); VLFLplotlight(0,1);
SGof2CPLzheurist(PLaddauxpoints(CPLsample(7),1),PLaddauxpoints(CPLsample(8),1),10); VLFLplotlight(0,1);
Warning: Duplicate edge constraints have been detected and removed.

Everything else on the topic of "connecting different contours to a solid" will follow in another tutorial.

stamp
stamp: Mac OSX 15.6 | R2024b Update 6 | SG-Lib 5.4 | Java 1.8.0_202-b08 | i7-Intelx64 6-Core 64GByte RAM | 14-Aug-2025 06:23:30