Welcome to the Onshape forum! Ask questions and join in the discussions about everything Onshape.

First time visiting? Here are some places to start:
  1. Looking for a certain topic? Check out the categories filter or use Search (upper right).
  2. Need support? Ask a question to our Community Support category.
  3. Please submit support tickets for bugs but you can request improvements in the Product Feedback category.
  4. Be respectful, on topic and if you see a problem, Flag it.

If you would like to contact our Community Manager personally, feel free to send a private message or an email.

New Feature: Attractor Pattern

EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
edited August 2020 in General
I'm happy to release Attractor Pattern for use. There are more things I'd like to do with this sometime, but the core functionality is there. Since it's a kind of complex UI, I made a 6-minute video explaining what everything is. Here's an example of one of the many things you could use it for.


I want to thank @maximilian_schommer for his Surface Pattern feature, which I dissected and borrowed from heavily for this feature. Couldn't have done it without that great reference. Also, big thanks to @ilya_baran whose pseudo-random number generator I co-opted for the random pattern function.

If you run into unexpected behavior while using it, please let me know and share a document with an example so I can improve it.

If you check out my code and see any obvious ways to optimize the performance, I'm very open to ideas. Since it's such a heavy feature efficiency is extra important, and I don't know much about shaving milliseconds off of operations.
Evan Reese

Comments

  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,050 ✭✭✭✭✭
    @Evan_Reese

    Could you add a HSV input for the colour?

    It seems more intuitive than RGB for some colours (at least for me).

    I did do an algorithm a little while ago for converting HSV to a colour (based on a StackExchange answer):
    https://cad.onshape.com/documents/44a687b24ed2559c60798e23/w/376aa1c19dcc30a04d5ed003/e/7491f2707f2ef42a181f1a22

    export const HUE_BOUNDS = { (unitless) : [0, 0, 360] } as IntegerBoundSpec;
    export const SATURATION_VALUE_BOUNDS = { (unitless) : [0, 0.75, 1] } as RealBoundSpec;
    
    export function colorHsv(hue is number, saturation is number, value is number) returns Color
    precondition
    {
        isInteger(hue, HUE_BOUNDS);
        isReal(saturation, SATURATION_VALUE_BOUNDS);
        isReal(value, SATURATION_VALUE_BOUNDS);
    }
    {
        // Algorithm from https://cs.stackexchange.com/questions/64549/convert-hsv-to-rgb-colors
        var h is number = hue / 60;
        if (hue >= 300)
            h = (hue - 300) / 60;
    
        const max is number = value;
        const chroma is number = saturation * value;
        const min is number = max - chroma;
    
        var red;
        var green;
        var blue;
    
        if (h < 0)
        {
            red = max;
            green = min;
            blue = min - h * chroma;
        }
        else if (h < 1)
        {
            red = max;
            green = min + h * chroma;
            blue = min;
        }
        else if (h < 2)
        {
            red = min - (h - 2) * chroma;
            green = max;
            blue = min;
        }
        else if (h < 3)
        {
            red = min;
            green = max;
            blue = min + (h - 2) * chroma;
        }
        else if (h < 4)
        {
            red = min;
            green = min - (h - 4) * chroma;
            blue = max;
        }
        else if (h < 5)
        {
            red = min + (h - 4) * chroma;
            green = min;
            blue = max;
        }
    
        return color(red, green, blue);
    }
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    @Evan_Reese

    Could you add a HSV input for the colour?
    Yup. I'll review your code and implement it sometime.
    Evan Reese
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    As a test, I just added HSL (this is what I'm used calling it) to my Part Color feature. I'll add it to Attractor Pattern soon too. Thanks for the suggestion and code @MBartlett21 !

    Evan Reese
  • mark_leichlitermark_leichliter Member Posts: 12 ✭✭
    Great work, Evan! And thanks for including an example of multiple patterns - looking forward to messing around with this when I have a chance. Thanks again!
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    @Evan_Reese

    Could you add a HSV input for the colour?

    It seems more intuitive than RGB for some colours (at least for me).

    I did do an algorithm a little while ago for converting HSV to a colour (based on a StackExchange answer):
    https://cad.onshape.com/documents/44a687b24ed2559c60798e23/w/376aa1c19dcc30a04d5ed003/e/7491f2707f2ef42a181f1a22

    export const HUE_BOUNDS = { (unitless) : [0, 0, 360] } as IntegerBoundSpec;
    export const SATURATION_VALUE_BOUNDS = { (unitless) : [0, 0.75, 1] } as RealBoundSpec;
    
    export function colorHsv(hue is number, saturation is number, value is number) returns Color
    precondition
    {
        isInteger(hue, HUE_BOUNDS);
        isReal(saturation, SATURATION_VALUE_BOUNDS);
        isReal(value, SATURATION_VALUE_BOUNDS);
    }
    {
        // Algorithm from https://cs.stackexchange.com/questions/64549/convert-hsv-to-rgb-colors
        var h is number = hue / 60;
        if (hue >= 300)
            h = (hue - 300) / 60;
    
        const max is number = value;
        const chroma is number = saturation * value;
        const min is number = max - chroma;
    
        var red;
        var green;
        var blue;
    
        if (h < 0)
        {
            red = max;
            green = min;
            blue = min - h * chroma;
        }
        else if (h < 1)
        {
            red = max;
            green = min + h * chroma;
            blue = min;
        }
        else if (h < 2)
        {
            red = min - (h - 2) * chroma;
            green = max;
            blue = min;
        }
        else if (h < 3)
        {
            red = min;
            green = max;
            blue = min + (h - 2) * chroma;
        }
        else if (h < 4)
        {
            red = min;
            green = min - (h - 4) * chroma;
            blue = max;
        }
        else if (h < 5)
        {
            red = min + (h - 4) * chroma;
            green = min;
            blue = max;
        }
    
        return color(red, green, blue);
    }
    It's implemented 👍🏼
    Evan Reese
  • romeograhamromeograham Member, csevp Posts: 682 PRO
    My Goodness @Evan_Reese
    This is very, very nice.
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    My Goodness @Evan_Reese
    This is very, very nice.
    Thanks, Romeo.
    Evan Reese
  • romeograhamromeograham Member, csevp Posts: 682 PRO
    One initial thought - I think I'd like radial options (particularly for rotation). Think of a mandala / sunburst pattern - could be quite simple if the pattern was build radially, rather than on a grid. This becomes (maybe) useful for seed bodies that are not round / square. 

    Or - could we have all the instance's Y-axis (say) point toward your attractor...if the attractor was a point, it would be kind of a radial pattern.

    Anyway - super cool feature. Thanks for sharing it with us!
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,050 ✭✭✭✭✭
    edited September 2020
    @Evan_Reese
    I had a bit of a look at the speed of the feature, and found that the default transforms for scaling and rotation are taking up a fair proportion of the time. If they are instead done with identityTransform, the total time reduces somewhat.
    Edited Code:
                // part rotation
                var angleTransform = identityTransform();
                if (definition.rotationBool)
                {
                    var remapAngle = remap(distances[i], measuredMin, measuredMax, angleNear, angleFar);
                    angleTransform = rotationAround(angleAxis, remapAngle);
                }
    
                // scale
                var scaleTransform = identityTransform();
                if (definition.scaleBool)
                {
                    var remapX = remap(distances[i], measuredMin, measuredMax, scaleNear.x, scaleFar.x);
                    var remapY = remap(distances[i], measuredMin, measuredMax, scaleNear.y, scaleFar.y);
                    var remapZ = remap(distances[i], measuredMin, measuredMax, scaleNear.z, scaleFar.z);
                    scaleTransform = scaleNonuniformly(remapX, remapY, remapZ, bodyMate);
                }
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,050 ✭✭✭✭✭
    Also, would you be able to change the rotation inputs to allow full 360 bounds (including negative)

    If you use the ANGLE_360_BOUNDS and ANGLE_360_ZERO_DEFAULT_BOUNDS for the input, it will allow all available angles including negative ones.
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    Thanks for looking at the performance! I'm sure I don't know enough about that. I'll look into your suggestion there, but it's new to me, so I'll have some reading to do.
    As for the angles, I'm using custom bounds so you can actually put in numbers much higher than 360, which can give some cool effects. I can update it so it can go just as far in the negative direction in case that's useful. I actually don't think I understand the request fully though since I can't imagine why I'd need to enter a negative value, when I can just tick the opposite direction boolean. What am I missing?



    Evan Reese
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    One initial thought - I think I'd like radial options (particularly for rotation). Think of a mandala / sunburst pattern - could be quite simple if the pattern was build radially, rather than on a grid. This becomes (maybe) useful for seed bodies that are not round / square. 

    Or - could we have all the instance's Y-axis (say) point toward your attractor...if the attractor was a point, it would be kind of a radial pattern.

    Anyway - super cool feature. Thanks for sharing it with us!
    I like the idea of adding some more base grids, maybe similar to my Speaker Pattern feature. I could also do just a standard polar grid. Maybe someday. I've also considered making them all point toward the attractor, which could be done, but is definitely more complex. It's on my list, but not at the top. Next, I'd like to get some kind of falloff types other than linear so the pattern transitions more smoothly. I'm also interested in finding a way to pull/repell instances toward and away from the attractor (while keeping them on the surface).
    Evan Reese
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,050 ✭✭✭✭✭
    @Evan_Reese
    The names of the Onshape bounds are misleading.
    If you look at the source[1], it allows from -100000 to 100000.

    [1]: https://cad.onshape.com/documents/12312312345abcabcabcdeff/v/812aa61a84190f49bc3c509d/e/87b09e244a234eb791b47826?jumpToIndex=7367&showReturnToWorkspaceLink=true

    The reason I would like it to be able to have negative numbers is to have it configurable more easily, rather than having to configure opposite direction. For instance, the move face feature does not allow a negative number, so I have to configure its opposite direction input as well.
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    That makes sense, and thanks for pointing that out about the default bounds. I've added negative bounds but kept them custom so I can have different defaults for Near and Far values, so users see something cool happen as soon as they check the box.
    Evan Reese
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    I just updated the feature to now have one non-linear falloff type to get a wavy look. I also made it so that composite parts can be colored by coloring the faces of each object.
    Evan Reese
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,050 ✭✭✭✭✭
    @Evan_Reese

    Could you make it use booleanBodies instead of opBoolean, please, so that it can work with sheet metal parts?
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    @Evan_Reese

    Could you make it use booleanBodies instead of opBoolean, please, so that it can work with sheet metal parts?
    Yes at some point. It's been on my list to figure out sheet metal compatibility. Thanks for the nudge in the right direction. Is it as simple as just replacing that section of code with another equally short one or is there more to it?

    I still also need to implement your identity transform suggestion too
    Evan Reese
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,050 ✭✭✭✭✭
    Yup
    mb - draftsman - also FS author: View FeatureScripts
    IR for AS/NZS 1100
  • roman_jurt190roman_jurt190 Member Posts: 37 EDU
    Hey Evan. I love your scrips! They are the best! Especially the attractor pattern FS! I have a - maybe not so simple - feature request: what would it take to make "multiple base surfaces selection" possible? very often, the surfaces are made from different faces and the edges can not be erased... attached a simple but quite unsolvable problem of a pattern around a corner...
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    Hi @roman_jurt190
    Thanks!

    The solution is the make a new base surface out of a single surface, which isn't always easy. One solution for you would be to use the Fit Spline feature in Edges mode, then sweep it to make the entire set of gray faces as one surface, and use it as the base. You could still keep your gray part as-is, with the different faces, and just make a new unified base surface. Here's an example: https://cad.onshape.com/documents/43a99515b33961b8380b7217/w/44e0d2c915df97cb45b76506/e/ba1ffb71ee263fa0091c235f

    Failing that, I did make a feature to solve this problem for myself, but I didn't make a big deal out of announcing it it, because it's very finicky, unintuitive, and requires a lot of understanding of what the feature is actually doing, and I don't feel like fielding a bunch of questions about it :D. I don't expect that it will ever be "production ready", but you're welcome to give it a try if you like. You need to make a single surface that has a UV structure you like, then it will find points on a nearby body, and try to either make a bunch of edges and loft them, or make a fill surface through the points. For that reason, it's fairly approximate, but can totally be close enough for use with Attractor Pattern, which is its only purpose. I wouldn't use it for an actual production surface, because the surface quality tends to be pretty wobbly. https://cad.onshape.com/documents/47de2b0c7db2d5832380fcd3/w/86e1200ed3cc9b96ed6f7d7f/e/e9054bb9ce23d613323dc398
    Evan Reese
  • roman_jurt190roman_jurt190 Member Posts: 37 EDU
    Wow! Thanks a lot for that quick answer and the two solutions! making a sweep to get just one surface works great. even with 8000 holes:) thanks! the "pull surface" FS will come in handy, i'm sure... there is quire often a finished product that needs some texture but the surface is made of several patches... It's not the most intuitive way, but hey... it's something that no other cad (besides rhino/grashopper maybe) can't do at all... so, i'm not complaining:)
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    Glad it worked! Even in Rhino, making a single patch is often part of the process, though there are better tools for it.
    Evan Reese
  • ShaoloShaolo Member Posts: 13
    Man, do I hate reviving old threads. But I feel like this question belongs in here. How do you get this to apply the pattern evenly spaced around all 360 degrees on say a cylinder face?

    I've tried everything I can think of and I can't get the pattern to space evenly.

  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    @Shaolo
    This is an issue I'm aware of, but not something I've not gotten around to improving. Not sure if/when I'd get to it though. I agree it should be able to handle it though.
    Evan Reese
  • ShaoloShaolo Member Posts: 13
    I appreciate the answer.  I thought maybe I was going mad.  :)
  • Javier_López_del_PueyoJavier_López_del_Pueyo Member Posts: 74 PRO
    @Evan_Reese first of all thanks for this awesome feature!! 

    @Shaolo I think I found a workaround to get a evenly distributted pattern around a 360º revolved surface.

    This is my case and how I found a way to solve it.

    I wanted to use an attractor pattern to look something similar to this



    Which looked good, except for this detail over here



    What I did was to revolve only 30º the base surface and then apply a rotational pattern that would overlap the patterned surface by 1/3, so that it would match the attractor pattern evenly.




    I grouped the remaining parts into a composite part since the bolean addition would be too costly in terms of regeneration time.

    I know it's not the ideal solution, but it worked while mantaining a fairly low regen. time (5.00 sec)
  • EvanReeseEvanReese Member, Mentor Posts: 2,188 ✭✭✭✭✭
    nice! I've done similar things. This doesn't work if you want to do something less symmetrical, but for this it's great. The real solution is to update the feature to recognize whether a surface connects to itself or not, which I'd love to do some day.
    Evan Reese
Sign In or Register to comment.