Home Technical Talk

[MAXScript] Begining and End of Selected Edge Loop

interpolator
Offline / Send Message
Revel interpolator
Quick question for you guys that do maxscript; is there a way to identified which edges are there on the begining and end of a selected edges loop (of course when a non full loop selected)?

By simply extracting the edgeLoop[1] and edgeLoop[edgeLoop.count] wont do the trick...(in this example case; edgeLoop = (polyOp.getEdgeSelection $) as array)

Replies

  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    Sure, they are the edges where one of the verts isn't shared with any of the other selected edges.
  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    Basically
    (
        local getEdgesByVerts = polyOp.getEdgesUsingVert
        local getVertsByEdges = polyOp.getVertsUsingEdge
    
        fn getEdges obj loopEdges =
            for edge in loopEdges where
                (getEdgesByVerts obj (getVertsByEdges obj edge) * loopEdges).numberSet != 3
                    collect edge
    
        getEdges $ (polyOp.getEdgeSelection $)
    )
    
  • Revel
    Options
    Offline / Send Message
    Revel interpolator
    Thanks fo much Swordslayer, it works but I still don't get it about the [...].numberSet != 3 part though...

    I'm inspired to do my own version of Angle Loop because I don't really like how he implemented the rollout into the edit poly rollout (and causes flash everytime) so I decided to practice write my own version.

    Here is what I've got so far;
    (
        -- veriables
        local
        selEdges = #(),
        getVertsByEdges = polyOp.getVertsUsingEdge,
        getEdgesByVerts = polyOp.getEdgesUsingVert
        for edge in (polyOp.getEdgeSelection $) do
            append selEdges edge
        
        -- functions
        fn getEdges obj loopEdges =
            for edge in loopEdges
                where (getEdgesByVerts obj (getVertsByEdges obj edge) * loopEdges).numberSet != 3
                collect edge
        
        fn getVerts obj loopVerts =
            for vert in loopVerts
                where (getVertsByEdges obj (getEdgesByVerts obj vert) * loopVerts).numberSet != 3
                collect vert
        
        fn angleLoop obj vOp eOp =
        (
            eTest = getEdgesByVerts obj vOp
            for edgeOp in eOp do
            (
                for edgeTest in eTest do
                (
                    edge1verts = (polyop.getVertsUsingEdge $ edgeTest) as array
                    vector1 = (polyop.getVert $ edge1verts[1]) - (polyop.getVert $ edge1verts[2])
                    edge2verts = (polyop.getVertsUsingEdge $ edgeOp) as array
                    vector2 = (polyop.getVert $ edge2verts[1]) - (polyop.getVert $ edge2verts[2])
                    vAngle = acos (dot (normalize vector1) (normalize vector2))
                    if    vAngle < 20 or
                        vAngle > 160 do
                            append selEdges edgeTest
                )
            )
        )
        
        -- operation
        vOp = getVerts $ (polyop.getVertsUsingEdge $ (polyOp.getEdgeSelection $))
        eOp = getEdges $ (polyOp.getEdgeSelection $)
        angleLoop $ vOp eOp
        
        polyop.setEdgeSelection $ selEdges
        redrawViews()
    )
    
    .. but with this it's only select the next possible edges instead of select the whole possible edges like the version on Scriptspot and also somehow it ignored the last edge, still looking into what's causing this. If you have any suggestion on how to improve this, as always please post it here! :)
  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    Quite a few suggestions, actually the post was getting out of hands :) In the end, I've opted to upload the thing to scriptspot instead, no encryption, of course: heuristic edge select. Hope it gives you the results you were after; feel free to adapt it to your needs eitherway.

    The [...].numberSet != 3 part basically checks if there are three selected edges sharing the two verts of the middle one. If it is so, it means the edge has another selected edge on both its sides; if not, that edge has to be at the end of the loop and it's the edge we were looking for.
  • Revel
    Options
    Offline / Send Message
    Revel interpolator
    Yeah! nice one Swordslayer! I did however slightly alter the behavior of how user execute your script. There is a double click functions that Mark posted a while ago here on the forum so I assigned a single click for normal execute and double clicks for opening a dialog box setting, a similar setup for my chamfer/ inset/ extrude/ etc.

    And no way I can wrote a script that complex myself at the moment..haha!
  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    Glad to hear that, could you post a link to the double click functions? I'm more or less a random visitor here and it sounds like I missed that one, sounds interesting.
  • Revel
    Options
    Offline / Send Message
    Revel interpolator
    Here you go LINK. As always I like to modified people's script, so here is my little modified version;
    lastClicked = 0
    fn multiClicks singleCmd doubleCmd =
    (
        thisClicked = timeStamp()
        
        if (thisClicked - lastClicked) > 500 then singleClick = 1 else singleClick = 0
        if (thisClicked - lastClicked) < 500 then doubleClick = 1 else doubleClick = 0
        lastClicked = thisClicked
        
        case of
        (
            (singleClick == 1): singleCmd()
            (doubleClick == 1): doubleCmd()
        )
    )
    
    Well I wanted to make it so the command call either singleCmd OR doubleCmd, cus on it's current form it run the singleCmd and then run the doubleCmd. Some action like extrude/ chamfer works well this this setup but something like connect, it's just didn't work cus the singleCmd call the connect and then when we double click it to call the doubleCmd (in this case; connect dialog box), the previous selected edges has been connected thus calling the dialog box after that become useless.
  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    That's a nice idea, you can do that, too ;) Proof of concept:
    macroscript DoubleTap
    category:"Advanced"
    toolTip:"Double Tap"
    (
        local lastTapped = 0
        local delay = 500
        local waiting = false
        local timer = dotNetObject "Timer"
        
        fn singleFn = print "single"
        fn doubleFn = print "double"
    
        fn onTick sender evnt =
        (
            if waiting do singleFn()
            sender.Stop()
            waiting = false
        )
    
        fn multiClicks =
        (
            local tapped = timeStamp()
            waiting = tapped - lastTapped > delay
        
            if waiting then timer.Start()
            else doubleFn()
            
            lastTapped = tapped
        )
    
        timer.Interval = delay
        dotnet.addEventHandler timer "Tick" onTick
    
        on execute do multiClicks()
    )
    
  • Revel
    Options
    Offline / Send Message
    Revel interpolator
    Yeah, I does work like I planned at first! but, hmmm...at the current state of the script we cant really make it as a global function and use it across many other scripts locally, right?
  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    Okay, this one was a bit tricky, for details refer to the thread at cgtalk. Place this in your ..\stdplugs\stdscripts\ folder (will work for max 9+):
    struct tapWatcherDef
    (
        singleFn, doubleFn,
    
        delay = 250,
        timer = dotNetObject "Timer",
        lastTapped = timeStamp() - 2*delay,
        waiting = false,
    
        fn onTick sender evnt =
        (
            sender.Stop()
            if sender.Tag.Value.waiting do
                sender.Tag.Value.singleFn()
            sender.Tag.Value.waiting = false
        ),
    
        fn init self =
        (
            dotNet.addEventHandler timer "Tick" onTick
            timer.Interval = delay
            timer.Tag = dotNetMXSValue self
        ),
    
        fn multiClick =
        (
            local tapped = timeStamp()
            waiting = tapped - lastTapped > delay
    
            if waiting then timer.Start()
            else doubleFn()
    
            lastTapped = tapped
        )
    )
    

    and call it from the macroscript like this:
    macroScript DoubleTap
    category:"Advanced"
    toolTip:"Double Tap"
    (
        fn singleFn = print "single tap"
        fn doubleFn = print "double tap"
    
        local tapFn = if globalVars.isGlobal #tapWatcherDef AND isStructDef ::tapWatcherDef then
        (
            local tapWatcher = tapWatcherDef singleFn:singleFn doubleFn:doubleFn
            tapWatcher.init tapWatcher
            tapWatcher.multiClick
        )
        else singleFn
    
        on execute do tapFn()
    )
    
  • Revel
    Options
    Offline / Send Message
    Revel interpolator
    Hey Swordslayer thanks for the solution, couldn't figured that out on my own for sure when it touch dotnet stuff..anyway, I've play around with your script for a while but something still doesn't feel quite right so I go ahead and read your thread on CGTalk..and to be honest I don't quite understand, heh!

    I did tried the Polytools' solution and it seems like by combining only using one global instance work more similar to my previous script. But one thing that still question in my head is; what are the ids (senderMcr)? it seems like a random number, so I removed it from the script and it still works like normal..hmm..
  • Swordslayer
    Options
    Offline / Send Message
    Swordslayer interpolator
    Yes, random numbers, could be anything; but it is needed - it will work as expected most of the time yet when you press two such single keyboard shortcuts in a short time, it will get detected as one shortcut pressed twice.
Sign In or Register to comment.