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.

How to check if an edge is a straight line?

eric_pestyeric_pesty Member Posts: 1,881 PRO
This is probably obvious but I'm struggling with figuring out if something is "straight"...

I'm computing the min radius of curvature of a sweep path and got it working (I stole/repurposed the code from @NeilCooke 's cable/wire routing) but i would like to do a check first so I don't sample curvature a bunch of times if the segment is just a straight line... 

I thought I could use "canBeLine" but it seems to always return false even when I pick a straight sketch segment as my input...

Comments

  • NeilCookeNeilCooke Moderator, Onshape Employees Posts: 5,671
    edited September 2022
    I will soon release a new version with better code you can steal :)
    Senior Director, Technical Services, EMEAI
  • Alex_KempenAlex_Kempen Member Posts: 248 EDU
    edited September 2022
    First, some terminology. Edges refer to physical edges in the model space, like those created by the sketch line tool and those that bound the intersection between two faces. In FeatureScript, edges exist solely as queries, which are used internally by Onshape to go into your model and find specific entities.
    Line refers to a FeatureScript data object which defines a mathematical line, e.g. an object with an origin and a direction. canBeLine is a predicate for checking if a given value is a Line object. Lines can be made from queries for edges using evEdgeTangentLine - for a straight line, the choice of parameter shouldn't matter too much, although the location of the origin will change (0.5 for the middle, 0/1 for the start/end, although note that the start and end of edges is generally arbitrary).

    Whether a query contains straight edges can be tested with qGeometry(GeometryType.LINE). Specifically, !isQueryEmpty(context, qGeometry(myQuery, GeometryType.LINE)) returns true if myQuery contains a reference to a straight edge and false otherwise.
    CS Student at UT Dallas
    Alex.Kempen@utdallas.edu
    Check out my FeatureScripts here:



  • NeilCookeNeilCooke Moderator, Onshape Employees Posts: 5,671
            var samples = 1;
    
            const wireGeometry = evCurveDefinition(context, {
                        "edge" : edge
                    });
    
            if (wireGeometry is Line)
            {
                continue;
            }
            else if (wireGeometry is Circle)
            {
                if (wireGeometry.radius >= minBendRadius)
                    continue;
            }
            else
            {
                samples = floor(evLength(context, {
                                    "entities" : edge
                                }) / (minBendRadius / 4));
            }
    
            const curveSamples = @evEdgeCurvatures(context, {
                        "edge" : edge,
                        "parameters" : samples == 1 ? [0.5] : range(0, 1, samples)
                    });
    excerpt from changed code
    Senior Director, Technical Services, EMEAI
  • eric_pestyeric_pesty Member Posts: 1,881 PRO
    Whether a query contains straight edges can be tested with qGeometry(GeometryType.LINE). Specifically, !isQueryEmpty(context, qGeometry(myQuery, GeometryType.LINE)) returns true if myQuery contains a reference to a straight edge and false otherwise.
    Thanks @Alex_Kempen, I think this is what I'm after. I'm trying to avoid doing "excessive" evEdgeTangentLine.
  • eric_pestyeric_pesty Member Posts: 1,881 PRO
    edited September 2022
    @NeilCooke
    I need to study this more but it looks like exactly what I need to do, I'll take a look when I get a chance! I'm assuming the "circle" case will detect a circular arc?

    This is what I currently have, the key difference being I need an extra loop as I'm looking for the curvature on a path that can have multiple segments:


    export function computeBendRadius(context is Context, OD is ValueWithUnits, path is Query) returns ValueWithUnits
    {
        var minRadius = 1e6;
        var minPoint;

        const edges = evaluateQuery(context, path);

        println("number of segments: " ~ size(edges));

        for (var e = 0; e < size(edges); e += 1)
        {

            var segmentLG = evLength(context, { "entities" : edges[e] });
            var segmentsamples = floor(segmentLG / OD * 3);
            println("segment " ~ e ~ ", Length: " ~ toString(segmentLG) ~ ", samples : " ~ segmentsamples);

            var segcurvesamples = @evEdgeCurvatures(context, {
                    "edge" : edges[e],
                    "parameters" : range(0, 1, segmentsamples)
                });
            for (var sample in segcurvesamples)
            {
                if (minRadius > 1 / sample.curvature)
                {
                    minRadius = 1 / sample.curvature;
                    minPoint = sample.frame;
                }

            }
            println("min radius so far : " ~ minRadius);
            if (isUndefinedOrEmptyString(minPoint))
            {
                minPoint = segcurvesamples[0].frame;
            }

        }
        println("overall min radius: " ~ minRadius);
        minRadius *= meter;

    EDIT: why is the "code" block so dumb in here?
  • NeilCookeNeilCooke Moderator, Onshape Employees Posts: 5,671
    @eric_pesty that’s a bit hard to read - select the font for “code” first then paste your code into the yellow box. 
    Senior Director, Technical Services, EMEAI
  • eric_pestyeric_pesty Member Posts: 1,881 PRO
    @NeilCooke
    I also meant to ask, where does the "length / (min bend / 4)" come from on the number of samples? Is that a "scientific" way to determine how many samples are required or just something that "works"? Just curious!

    In my case I am just trying to find the minimum bend radius and display in a table (rather than establish a pass/fail so I might need to do this a bit differently...). I replaced your min bend radius with the diameter of my sweep but really what I'm looking for is not dependent on what I'm sweeping!

    I should be doing whatever the "curvature comb" is doing to display the min curvature... (and as a side note it would be really nice if we had access to a function that would do that for us as efficiently as possible rather than writing our own!)
  • NeilCookeNeilCooke Moderator, Onshape Employees Posts: 5,671
    It’s just a number because it only needs to be approximate in my case - I should probably relate it to wire diameter.
    Senior Director, Technical Services, EMEAI
  • eric_pestyeric_pesty Member Posts: 1,881 PRO
    NeilCooke said:
    @eric_pesty that’s a bit hard to read - select the font for “code” first then paste your code into the yellow box. 
    export function computeBendRadius(context is Context, OD is ValueWithUnits, path is Query) returns ValueWithUnits
    {
        var minRadius = 1e6;
        var minPoint;
    
        const edges = evaluateQuery(context, path);
    
        println("number of segments: " ~ size(edges));
    
        for (var e = 0; e < size(edges); e += 1)
        {
    
            var segmentLG = evLength(context, { "entities" : edges[e] });
            var segmentsamples = floor(segmentLG / OD * 3);
            println("segment " ~ e ~ ", Length: " ~ toString(segmentLG) ~ ", samples : " ~ segmentsamples);
    
            var segcurvesamples = @evEdgeCurvatures(context, {
                    "edge" : edges[e],
                    "parameters" : range(0, 1, segmentsamples)
                });
            for (var sample in segcurvesamples)
            {
                if (minRadius > 1 / sample.curvature)
                {
                    minRadius = 1 / sample.curvature;
                    minPoint = sample.frame;
                }
    
            }
            println("min radius so far : " ~ minRadius);
            if (isUndefinedOrEmptyString(minPoint))
            {
                minPoint = segcurvesamples[0].frame;
            }
    
        }
        println("overall min radius: " ~ minRadius);
        minRadius *= meter;
    Ah ok! Create the "code" section first.
    I had pasted the code first and selected it and formatted as code and that was a disaster (the whole thing was on one line with extra HTML crap), talk about hard to read:

    <div>export function computeBendRadius(context is Context, OD is ValueWithUnits, path is Query) returns ValueWithUnits</div><div>{</div><div>&nbsp; &nbsp; var minRadius = 1e6;</div><div>&nbsp; &nbsp; var minPoint;</div><div><br></div><div>&nbsp; &nbsp; const edges = evaluateQuery(context, path);</div><div><br></div><div>&nbsp; &nbsp; println("number of segments: " ~ size(edges));</div><div><br></div><div>&nbsp; &nbsp; for (var e = 0; e < size(edges); e += 1)</div><div>&nbsp; &nbsp; {</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; var segmentLG = evLength(context, { "entities" : edges[e] });</div><div>&nbsp; &nbsp; &nbsp; &nbsp; var segmentsamples = floor(segmentLG / OD * 3);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; println("segment " ~ e ~ ", Length: " ~ toString(segmentLG) ~ ", samples : " ~ segmentsamples);</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; var segcurvesamples = @evEdgeCurvatures(context, {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "edge" : edges[e],</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "parameters" : range(0, 1, segmentsamples)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (var sample in segcurvesamples)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (minRadius > 1 / sample.curvature)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; minRadius = 1 / sample.curvature;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; minPoint = sample.frame;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; println("min radius so far : " ~ minRadius);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (isUndefinedOrEmptyString(minPoint))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; minPoint = segcurvesamples[0].frame;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div><br></div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; println("overall min radius: " ~ minRadius);</div><div>&nbsp; &nbsp; minRadius *= meter;</div>



  • eric_pestyeric_pesty Member Posts: 1,881 PRO
    Quick update,
    I ran into some difficulties with implementing the code above as I am doing things a bit differently but then I did a "profile" and sampling the curvature 50times on a straight line is quicker than 5 samples on a spline so the performance impact seems pretty negligible. 

    Interestingly, adding the debug arrow is quite a bit slower than the curvature sampling loops, and the opSweep are also slower so while it's not super efficient it doesn't really have a noticeable performance impact so I am going to leave it like this for now.

    export function computeBendRadius(context is Context, OD is ValueWithUnits, path is Query) returns ValueWithUnits
    {
        var minRadius = 1e6;
        var minPoint;
    
        const edges = evaluateQuery(context, path);
        const maxsampledist = .25 * inch;
    
        //    println("number of segments: " ~ size(edges));
    
        for (var e = 0; e < size(edges); e += 1)
        {
            var segmentLG = evLength(context, { "entities" : edges[e] });
            var sampleint = min(maxsampledist, OD);
            var segmentsamples = floor(segmentLG / sampleint * 3);
            // println("segment " ~ e ~ ", Length: " ~ toString(segmentLG) ~ ", samples : " ~ segmentsamples);
    
            var segcurvesamples = @evEdgeCurvatures(context, {
                    "edge" : edges[e],
                    "parameters" : range(0, 1, segmentsamples)
                });
            for (var sample in segcurvesamples)
            {
                if (minRadius > 1 / sample.curvature)
                {
                    minRadius = 1 / sample.curvature;
                    minPoint = sample.frame;
                }
            }
    
            //println("min radius so far : " ~ minRadius);
            if (isUndefinedOrEmptyString(minPoint))
            {
                minPoint = segcurvesamples[0].frame;
            }
    
        }
        //println("overall min radius: " ~ minRadius);
        minRadius *= meter;
    
    
        //display path and min radius location
        minPoint = coordSystemFromBuiltin(minPoint);
        addDebugEntities(context, path, DebugColor.BLUE);
        if (minRadius < OD / 2)
        {
            addDebugArrow(context, minPoint.origin + minPoint.xAxis * minRadius * 20, minPoint.origin, OD * 2, DebugColor.RED);
        }
        else
        {
            addDebugArrow(context, minPoint.origin + minPoint.xAxis * minRadius, minPoint.origin, OD, DebugColor.GREEN);
        }
    
        return minRadius;
    }

Sign In or Register to comment.