[Closed] How to find if the object is cylinder?
The task is to find if an object is cylinder.
The objects are editable mesh, but if there is a solution for editable poly objects, then there is no problem the object to be converted(or Edit Poly mod to be applied).
Test scene with two cylinders can be downloaded from here: https://drive.google.com/open?id=1W2SkQjI7Vbga3wnZt7JcOqA76VRen1lq
The file is saved with 3ds Max 2014.
I hope that you can give me some advices or solutions of how to find it an object is cylinder or not.
Maybe something like:
- find longest edge
- check how many edges with the same length exist
- use them to find the cap of the cylinder
- calcualte radius of the cap
…
I think there’s a lot of corner cases possible:
can it have zero height or four sides?
can it have one or zero caps?
can it be composed of multiple elements?
what is your definition of cylinder?
In the scene you can see the cylinders looks. All of them have multiple sides(more than 10), and all of them have two caps. All cylinders have non zero height. Each cylinder is one element.
My definition for cylinder is the cylinder primitive in 3ds Max, but the cylinder is editable mesh object, all faces are triangles and the cap may not have the vertex at its center.
after thinking a little it looks pretty simple
but…
#1 it must not be reset xform
#2 it must not be deformed
now we know that a “cylinder” mesh organized by loops…
so some sequence of verts has to have the same one coordinate (original height)
step #1 :
m = $.mesh
n = 0
v1 = getvert m 1
v2 = getvert m 2
for k=1 to 3 while n == 0 where (v1[k] == v2[k]) do n = k
if n != 0 do ... <continue>
oops… your meshes have “broken” topology … it’s not loops
they are not originally cylinders
Yes, the side faces are not quads. If they was quads then I can easily find the longest edges(polygons) and sue them to find the caps.
Still can’t follow your logic.
What I have done so far:
find the shortest edges. They are part of the cap. Find the center of the cap. measure the distance from 3 verts to the center of the cap. If the distance is the same -> cylinder. But it reports sqashed cylidners as cylinder, so I have to see whay this happens.
(
function sortByXMember arr1 arr2 x:1 =
(
case of
(
(arr1[x] < arr2[x]):-1
(arr1[x] > arr2[x]):1
default:0
)
)
o = selection[1]
edgesIdxLengthArr = #()
for eIdx = 1 to o.edges.count do
(
edgeVertsBA = meshOp.getVertsUsingEdge o eIdx
vertPosArr = for v in edgeVertsBA collect getVert o v
append edgesIdxLengthArr #(eIDx, (distance vertPosArr[1] vertPosArr[2]))
)
qsort edgesIdxLengthArr SortByXMember x:2
shrotestLength = edgesIdxLengthArr[1][2]
shortestEdgesBA = #{}
for arr in edgesIdxLengthArr where ( abs (shrotestLength - arr[2]) ) < 0.1 do join shortestEdgesBA #{arr[1]}
if shortestEdgesBA.numberset > 12 then
(
edge01 = (shortestEdgesBA as array)[1]
verts01 = meshop.getVertsUsingEdge o #{edge01}
adjacentEdgesBA = meshop.getEdgesUsingVert o verts01
openAdjacentEdgesBA = (shortestEdgesBA * adjacentEdgesBA) - #{edge01}
verts02 = meshop.getVertsUsingEdge o openAdjacentEdgesBA
adjacentEdgesBA = meshop.getEdgesUsingVert o verts02
openAdjacentEdgesBA = (shortestEdgesBA * adjacentEdgesBA) - openAdjacentEdgesBA - #{edge01}
verts03 = meshop.getVertsUsingEdge o openAdjacentEdgesBA
p1 = getVert o (verts01 as array)[1]
p2 = getVert o (verts03 as array)[1]
p3 = getVert o (verts03 as array)[verts03.numberset]
center = FindCenterOfCircle p1 p2 p3
testVertPos = getVert o (verts02 as array)[1]
distTest01 = (distance p1 center)
distTest02 = (distance testVertPos center)
if abs ( distTest01 - distTest02 ) < 0.1 then
(
print "Cylinder"
)
else
(
print "Not cylinder"
)
)
)
just an idea
maybe we can use smoothgroups to determine if mesh has two caps?
and I think .numberset isn’t a way to go. using dot product of element average normal should fit better.
what do you think?
(
obj = snapshotAsMesh $
meshop.autoSmooth obj #{1..obj.numfaces} 89.9
smoothGroups = #()
for i=1 to obj.numfaces do (
sg = getFaceSmoothGroup obj i
if smoothGroups[sg] == undefined then smoothGroups[sg] = #{i} else smoothGroups[sg][i] = true
)
meshop.detachFaces obj smoothGroups[1]
allfaces = #{1..obj.numfaces}
elem1 = meshop.getElementsUsingFace obj 1
allfaces -= elem1
elem2 = meshop.getElementsUsingFace obj (allfaces as array)[1]
allfaces -= elem2
elem3 = meshop.getElementsUsingFace obj (allfaces as array)[1]
free obj
if elem1.numberset == elem2.numberset or elem1.numberset == elem3.numberset or elem2.numberset == elem3.numberset then true else false
)
Yep. It finds the caps. The strahge is that when I select a face in the Modify Panel its smoothing grop is not displayed. When I press the [Select By SG] button there are not SGs that canbe selected. But your code finds the caps of the cylinder and looking at the code I saw why.
another one
(
obj = snapshotAsMesh $
meshop.autoSmooth obj #{1..obj.numfaces} 89.9
smoothGroups = #()
for i=1 to obj.numfaces do (
sg = getFaceSmoothGroup obj i
if smoothGroups[sg] == undefined then smoothGroups[sg] = #{i} else smoothGroups[sg][i] = true
)
meshop.detachFaces obj smoothGroups[1]
allfaces = #{1..obj.numfaces}
elem1 = meshop.getElementsUsingFace obj 1
allfaces -= elem1
elem2 = meshop.getElementsUsingFace obj (allfaces as array)[1]
allfaces -= elem2
elem3 = meshop.getElementsUsingFace obj (allfaces as array)[1]
allOpenEdges = meshop.getOpenEdges obj
elem1edges = (meshop.getEdgesUsingFace obj elem1) * allOpenEdges
elem2edges = (meshop.getEdgesUsingFace obj elem2) * allOpenEdges
elem3edges = (meshop.getEdgesUsingFace obj elem3) * allOpenEdges
elem1edgesVerts = meshop.getVertsUsingEdge obj elem1edges
elem2edgesVerts = meshop.getVertsUsingEdge obj elem2edges
elem3edgesVerts = meshop.getVertsUsingEdge obj elem3edges
elem1center = [0,0,0]
for v in elem1edgesVerts do elem1center += GetVert obj v
elem1center /= elem1edgesVerts.numberset
elem2center = [0,0,0]
for v in elem2edgesVerts do elem2center += GetVert obj v
elem2center /= elem2edgesVerts.numberset
elem3center = [0,0,0]
for v in elem3edgesVerts do elem3center += GetVert obj v
elem3center /= elem3edgesVerts.numberset
delete helpers
point pos:elem1center centermarker:on cross:off wirecolor:yellow
point pos:elem2center centermarker:on cross:off wirecolor:red
point pos:elem3center centermarker:on cross:off wirecolor:green
elem1_total_radius = 0
for v in elem1edgesVerts do elem1_total_radius += distance elem1center (GetVert obj v)
elem2_total_radius = 0
for v in elem2edgesVerts do elem2_total_radius += distance elem2center (GetVert obj v)
elem3_total_radius = 0
for v in elem3edgesVerts do elem3_total_radius += distance elem3center (GetVert obj v)
free obj
case of (
((abs (elem1_total_radius - elem2_total_radius)) < 0.001) : true
((abs (elem1_total_radius - elem3_total_radius)) < 0.001) : true
((abs (elem2_total_radius - elem3_total_radius)) < 0.001) : true
default: false
)
)
If this have to return if the object is cylinder or not:
case of (
((abs (elem1_total_radius - elem2_total_radius)) < 0.001) : true
((abs (elem1_total_radius - elem3_total_radius)) < 0.001) : true
((abs (elem2_total_radius - elem3_total_radius)) < 0.001) : true
default: false
)
then it not works properly. For scaled in one axis cylidner it returns TRUE, but it should be FALSE.
Edit: the reason to return TRUE all the time is that all verts are used to calcualte the total radius. The cylinder is still a “symetrical” object. This is whay I use only 3 verts, fare enough, to check the radiuses.
I have this so far.
(
o = snapshotAsMesh $
meshop.autoSmooth o #{1..o.numfaces} 89.9
smoothGroups = #()
for i=1 to o.numfaces do
(
sg = getFaceSmoothGroup o i
if smoothGroups[sg] == undefined then smoothGroups[sg] = #{i} else smoothGroups[sg][i] = true
)
if smoothGroups.count > 1 then
(
meshop.detachFaces o smoothGroups[1]
allfaces = #{1..o.numfaces}
elem1 = meshop.getElementsUsingFace o 1
allfaces -= elem1
elem2 = meshop.getElementsUsingFace o (allfaces as array)[1]
allfaces -= elem2
elem3 = meshop.getElementsUsingFace o (allfaces as array)[1]
if elem1.numberset == elem2.numberset or elem1.numberset == elem3.numberset or elem2.numberset == elem3.numberset then
(
allOpenEdgesBA = meshop.getOpenEdges o
if allOpenEdgesBA.numberset > 12 then
(
edge01 = (allOpenEdgesBA as array)[1]
verts01 = meshop.getVertsUsingEdge o #{edge01}
adjacentEdgesBA = meshop.getEdgesUsingVert o verts01
openAdjacentEdgesBA = (allOpenEdgesBA * adjacentEdgesBA) - #{edge01}
verts02 = (meshop.getVertsUsingEdge o openAdjacentEdgesBA) - verts01
adjacentEdgesBA = meshop.getEdgesUsingVert o verts02
openAdjacentEdgesBA = (allOpenEdgesBA * adjacentEdgesBA) - openAdjacentEdgesBA - #{edge01}
verts031 = (meshop.getVertsUsingEdge o openAdjacentEdgesBA) - verts02 - verts01
verts03 = (meshop.getVertsUsingEdge o openAdjacentEdgesBA) - verts02 - verts01
vertsNextBA = #{}
for i = 1 to 15 do
(
adjacentEdgesBA = meshop.getEdgesUsingVert o verts031
openAdjacentEdgesBA = (allOpenEdgesBA * adjacentEdgesBA) - openAdjacentEdgesBA - #{edge01}
vertsNextBA = (meshop.getVertsUsingEdge o openAdjacentEdgesBA) - verts031 - verts02 - verts01
verts031 = vertsNextBA
)
p1 = getVert o (verts01 as array)[1]
p2 = getVert o (verts03 as array)[1]
p3 = getVert o (verts03 as array)[2]
center = FindCenterOfCircle p1 p2 p3
testVertPos = getVert o (vertsNextBA as array)[1]
distTest01 = (distance p1 center)
distTest02 = (distance testVertPos center)
if abs ( distTest01 - distTest02 ) < 0.1 then
(
print "Cylinder"
)
else
(
print "Not cylinder"
)
)
)
else
(
false
)
free o
)
else
(
free o
)
)
For now only the optimization of the if allOpenEdgesBA.numberset > 12 then() left. Maybe something smarter canbe used, but my math skills are not as yours.
in my previous example we should check not the total distance sum but average distance since caps may have different number of vertices.
If I use this”
elem1_total_radius /= elem1edgesVerts.numberset
[code/]
for the 3 elements(with proper variables) the result is the same. Or maybe I not understand you correctly.