Notifications
Clear all

[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
42 Replies

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?

1 Reply
(@miauu)
Joined: 10 months ago

Posts: 0

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

1 Reply
(@miauu)
Joined: 10 months ago

Posts: 0

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
        
    )
    
)

1 Reply
(@miauu)
Joined: 10 months ago

Posts: 0

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.

1 Reply
(@miauu)
Joined: 10 months ago

Posts: 0

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.
Page 1 / 4