Find holes and gaps in .stl files
15 次查看(过去 30 天)
显示 更早的评论
Hi,
I need to find gaps and holes in a stl file. I don´t know if there´s a function developed that can help me. I started to work with MATLAB recently and i´m finding some dificulties. I found 2 ways of doing this, each triangle should have 3 adjacent triangles or each edge must have 2 triangles in common. Hope someone could give a hand. Thank´s.
Eduardo G.
3 个评论
agdfg sdgfdg
2013-12-10
[unqEdges, ~, edgeNos] = unique(edges,'rows'); | Error: Expression or statement is incorrect--possibly unbalanced (, {, or [. this the error i observed.
And i request you to suggest some other algorithms or codes for STL File Correction.
采纳的回答
Sven
2012-2-3
Hi Eduardo,
Firstly you need to read the .stl file in as faces and vertices. I'll presume you've already got that done (as Anton points out, stlread is available here http://www.mathworks.com/matlabcentral/fileexchange/22409-stl-file-reader).
Next, as you wrote: each edge must have 2 triangles in common... I think this is a good start. Here's some code that will make a test faces/vertices mesh, then determine such a thing:
% MAKE a face/vertices structure
tmpvol = zeros(20,20,20); % Empty voxel volume
tmpvol(8:12,8:12,5:15) = 1; % Turn some voxels on
fv = isosurface(tmpvol, 0.99);
% REMOVE a face
fv.faces(30,:) = []; % Remove a face!
% TEST for gaps or holes
edges = sort(cat(1, fv.faces(:,1:2), fv.faces(:,2:3), fv.faces(:,[3 1])),2);
[unqEdges, ~, edgeNos] = unique(edges,'rows');
if size(edges,1) == size(unqEdges,1)*2
% Every edge is used twice... consistent with a closed manifold mesh
disp('No problem!')
else
badEdgesMask = hist(edgeNos, 1:max(edgeNos))~=2;
badEdgeNos = edgeNos(badEdgesMask);
badNodeNos = edges(badEdgeNos,:);
badFaceNos = find(sum(ismember(fv.faces, badNodeNos),2)>=2);
end
Does this answer the question for you?
更多回答(4 个)
DGM
2025-3-31
编辑:DGM
2025-6-5
Single missing triangles are easy enough to fix, but holes that need to be triangulated are a problem. This doesn't seem to be a simple problem that can generally be solved with built-in triangulation tools, since the boundary curve in 3D would need to be somehow unrolled in order to do the constrained triangulation in 2D. -- or something.
Here's an attempt. It's a terrible naive approach that will absolutely fail on anything but the simplest of holes. To be more specific, the only holes which this can be relied upon to fill are convex holes defined by a set of coplanar vertices. It may work in other cases, but only coincidentally.
The triangulation class wasn't introduced until R2013a, but the trirep class was available (R2009a). I'm not going to bother making this garbage answer period-correct anyway, as I do know that there are some subtle undocumented behavioral changes that have happened at unknown points over the years anyway. I'm not sure what would work when.
The hole in the top of this model only coincidentally doesn't get connected across its non-convex side. That's just luck.
unzip testfiles.zip % for the forum
% this model has two holes:
% a single missing triangle on the -y side
% three missing triangles on the +z side (including two missing edges)
T = stlread('bad_stepholecube.stl');
Tpatched = demo(T,true);
A saddle formed by four points does not have a unique triangulation. It's also conceivable that a hole can destroy unrecoverable information. It's only due to the simplicity and symmetry of this model that we might suspect there's an entire missing corner. At least we can still manage to fill this one too.
% this model has one hole:
% four missing triangles on the top
% (and four missing edges and a missing vertex)
T = stlread('bad_stepholecube2.stl');
Tpatched = demo(T,true);
The shape of the holes plays a large part in how successfully you can blindly fill them. Even admesh will fail on this one.
% this model has three holes:
% the hole on the front will fail by itself,
% but all three holes are connected by single vertices,
% so _everything_ fails.
T = stlread('bad_stepholecube3.stl');
Tpatched = demo(T,true);
Are there better ways? Probably. I haven't figured one out yet today.
I think the bigger question is why it's necessary to do this complicated task not only blindly, but in MATLAB. If you want to do it blind, why not just call admesh or something using a system call? Better yet, start by fixing the worst of the problems manually. How? Well, you could sacrifice your hair to learn Blender or some other capable tool, or if your problem is small, just figure out what triangles need to be added and add them.
% using the same bad 3-hole model, display the bad model,
% inspect the vertices with the mouse cursor and figure out where triangles should be.
%inspect(T) % obviously i can't run this on the forum
% apply the manually selected triangles
newF = [15 5 4; 16 5 15; 8 6 5; 18 8 5; 10 17 15; 7 17 10];
Tpatched = triangulation([T.ConnectivityList; newF],T.Points);
% visualize the problem
figure(2); clf
hp = patch('faces',Tpatched.ConnectivityList, ...
'vertices',Tpatched.Points, ...
'facecolor','w','edgecolor','k');
view(3)
camlight
axis equal
view(10,33)
grid on
xlabel('X')
ylabel('Y')
zlabel('Z')
It's still missing some extra vertices, but at least it's a closed 2-manifold now.
function Tpatched = demo(T,doplot)
% blindly try to fill holes in a triangulation object
if doplot
% visualize the problem
figure(1); clf
hp = patch('faces',T.ConnectivityList,'vertices',T.Points,'facecolor','w','edgecolor','k');
view(3)
camlight
axis equal
view(10,33)
grid on
xlabel('X')
ylabel('Y')
zlabel('Z')
end
% these are the open edges, sorted and ordered
Fb = freeBoundary(T);
% go through the list of open edges and sort them into closed curves
% there shouldn't be any open curves
vertpile = Fb; % a temporary copy to consume
k = 1;
while ~isempty(vertpile)
endidx = find(vertpile(:,2) == vertpile(1,1),1,'first');
if isempty(endidx)
% if this vertex does not lie on a closed curve, ignore it
% unless you can come up with a better idea
vertpile(1,:) = [];
else
holeCL{k} = vertpile(1:endidx,:); %#ok<*AGROW>
vertpile(1:endidx,:) = [];
k = k + 1;
end
end
% triangulate the holes with a naive fan pattern
% this is likely to fail for holes with nonconvex boundaries
% but it coincidentally works in this case.
newF = cell(numel(holeCL),1);
for k = 1:numel(holeCL)
H = holeCL{k}(:,1);
newF{k} = zeros(size(H,1)-2,3);
for kt = 1:size(newF{k},1)
newF{k}(kt,:) = H([1 kt+(1:2)]);
end
end
newF = cell2mat(newF); % new triangles to add to the model
% create a new triangulation object with the added faces
Tpatched = triangulation([T.ConnectivityList; newF],T.Points);
% visualize the result
if doplot
figure(2); clf
hp = patch('faces',Tpatched.ConnectivityList, ...
'vertices',Tpatched.Points, ...
'facecolor','w','edgecolor','k');
view(3)
camlight
axis equal
view(10,33)
grid on
xlabel('X')
ylabel('Y')
zlabel('Z')
end
end
function inspect(T)
% visualize an object and use a custom datacursor to get the indices of
% individual vertices.
% visualize the problem
figure(1); clf
hp = patch('faces',T.ConnectivityList,'vertices',T.Points,'facecolor','w','edgecolor','k');
view(3)
camlight
axis equal
view(10,33)
grid on
xlabel('X')
ylabel('Y')
zlabel('Z')
% create custom datacursor to view index
dcm_obj = datacursormode(gcf);
set(dcm_obj,'UpdateFcn',@myupdatefcn);
% this is kind of flaky, but i don't care that much
function txt = myupdatefcn(~,event_obj)
% get rid of old tips
alltips = findall(gcf,'Type','hggroup');
if numel(alltips)>2
delete(alltips(3:end))
end
pos = get(event_obj,'Position');
didx = find(all(T.Points == pos,2),1,'first');
txt = {sprintf('x: %.4f',pos(1)),...
sprintf('y: %.4f',pos(2)),...
sprintf('z: %.4f',pos(3)),...
['Index: ',num2str(didx)]};
end
end
If someone has a better answer, post it.
0 个评论
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!