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

Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
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 / Principal and Industrial Designer with Ovyl
Website: ovyl.io

Comments

  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,016 EDU
    @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 - draughtsman - also FS author: View FeatureScripts
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    @Evan_Reese

    Could you add a HSV input for the colour?
    Yup. I'll review your code and implement it sometime.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • 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!
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    @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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • romeograhamromeograham Member, Simulation EVP Posts: 612 PRO
    My Goodness @Evan_Reese
    This is very, very nice.
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    My Goodness @Evan_Reese
    This is very, very nice.
    Thanks, Romeo.
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • romeograhamromeograham Member, Simulation EVP Posts: 612 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,016 EDU
    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 - draughtsman - also FS author: View FeatureScripts
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,016 EDU
    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 - draughtsman - also FS author: View FeatureScripts
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Evan_ReeseEvan_Reese Member Posts: 1,464 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!
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,016 EDU
    @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 - draughtsman - also FS author: View FeatureScripts
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,016 EDU
    @Evan_Reese

    Could you make it use booleanBodies instead of opBoolean, please, so that it can work with sheet metal parts?
    mb - draughtsman - also FS author: View FeatureScripts
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    @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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    Evan Reese / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • MBartlett21MBartlett21 Member, OS Professional, Developers Posts: 2,016 EDU
    Yup
    mb - draughtsman - also FS author: View FeatureScripts
  • roman_jurt190roman_jurt190 Member Posts: 13 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...
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
  • roman_jurt190roman_jurt190 Member Posts: 13 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:)
  • Evan_ReeseEvan_Reese Member Posts: 1,464 PRO
    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 / Principal and Industrial Designer with Ovyl
    Website: ovyl.io
Sign In or Register to comment.