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 create a new type of Query?

dave_cowdendave_cowden Member, Developers Posts: 445 ✭✭✭
I'd like to create a new type of query that i'm used to using in cadquery:  

ability to select faces that line parallel to, and most 'positive' with respect to, a specified direction.

As an example, suppose i extrude a circle with an extrusion axis in the positive "Z" direction. the resulting object has 3 faces. 

What i want to do is select the 'top most' face: the one that is furthest in the extrusion ( in this case, z)  direction

 in cadquery, you would do this with a query like:

w = Workplane("XY").circle(0.4).extrude(1.0).faces(">Z")  

There seems to be three ways to do this in FS, but they are all very inconvenient.  
  (1) i could use qIntersectsPlane. but it is a pain to create an intersecting plane just to run a query
  (2) use qContainsPoint. again, it is a pain to manually find a point that should lie on the surface. Plus in testing, when i tried, it didnt select anything: this could be  a precision issue.
  (3) use qNthElement . this works reliably, but it seems like a gamble to assume that the faces will be generated in the right order every time. It feels like it would always be indeterministic to use this selector.

What i want to make  is something like qFarthestAlong( direction, qCreatedBy ( context, id, EntityType.FACE ))


 I've found the makeQuery function, but its not obvious how to use it.  I think i know how to implement what i want to do using evTangentPlane, if i could get some pointers on what the right way to produce queries that decorate other queries is.   I know i could use evaluateQuery to get the result of the query i'm composing, but then how do i return a query that executes a filter?

Thanks as always for the help

Best Answer

Answers

  • billy2billy2 Member, OS Professional, Mentor, Developers Posts: 1,322 PRO
    I'm interested in this also, otherwise picking cylinders is difficult.


  • kevin_o_toole_1kevin_o_toole_1 Onshape Employees, Developers, HDM Posts: 455
    edited February 2016
    Getting the "cap face" of an extrude is something that Onshape keeps track of via historical queries. We're currently looking at ways of making these historical queries easy to write for cases just like this.

    Using just the geometry (and not the history) can often still be useful, though. After all, what if the geometry I'm querying is an import?

    So, let's make a function that gets an entity like you're proposing. To do this, we'll be using evDistance, which may be more powerful than you'd first realize.

    But first, let's first make one minor adjustment: To make the choice of entity unambiguous between the cap faces and side faces (both of which touch the farthest plane), let's replace "farthest along this vector" with "closest to the point farthest along this line".

    A function which does that can be constructed as follows:
    export function closestToEndOfLine(context is Context, subQuery is Query, line is Line) returns Query
    {
        const farPoint is Vector = pointFarthestAlong(context, subQuery, line);
    
        const distanceResult is DistanceResult = evDistance(context, {
                "side0" : farPoint,
                "side1" : subQuery
        });
    
        const indexOfFarthestEntity is number = distanceResult.sides[1].index;
    
        return qNthElement(subQuery, indexOfFarthestEntity);
    }
    
    export function pointFarthestAlong(context is Context, query is Query, line is Line) returns Vector
    {
        const cSys is CoordSystem = coordSystem(line.origin, perpendicularVector(line.direction), line.direction);
        const boundingBox is Box3d = evBox3d(context, {
                "topology" : query,
                "cSys" : cSys
        });
        // The point farthest along this vector is found by down the line by the z-coordinate of the max corner of the bounding box
        const pointFarthestAlong is Vector = line.origin + boundingBox.maxCorner[2] * line.direction;
        return pointFarthestAlong;
    }
    
    And it can be used like so:
    annotation { "Feature Type Name" : "My Feature" }
    export const myFeature = defineFeature(function(context is Context, id is Id, definition is map)
        precondition
        {
            annotation { "Name" : "Direction", "Filter" : QueryFilterCompound.ALLOWS_AXIS, "MaxNumberOfPicks" : 1 }
            definition.axis is Query;
        }
        {
            const axis is Line = evAxis(context, {
                    "axis" : definition.axis
            });
            debug(context, axis);
            debug(context, closestToEndOfLine(context, qBodyType(qEverything(EntityType.FACE), BodyType.SOLID), axis)); 
        }, { /* default parameters */ });
    For a result like this:


    The closestToEndOfLine function returns a Query, so to whomever calls it, it will behave a lot like any of the other query functions. However, there are some key differences:

    1. You have to pass in a context.

    2. A subtle consequence of #1: You should only use this call function immediately before using its result. Onshape queries specify what they want independently of the context, so the same query can be used after the context has changed, yielding different results that still correctly match the query in the new context. The query returned by closestToEndOfLine, in contrast, needs a context just to build the correct query. This means calling closestToEndOfLine() in a context, then using it after changing that context, won't necessarily be accurate. (Trivial example: You could have built a new entity further along the line in the meantime).

    3. The function likely has slower performance than an Onshape native query would. This won't be relevant unless you're using said function quite a lot.

    Making utility functions like this is definitely encouraged, so long as you're aware of these differences. 

    I agree that having a query-only version of something would be nice. For the most part, we're likely not going to be putting major emphasis on expanding the query library with things like this until (possibly) this summer.

  • ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 946
    I agree that having a query-only version of something would be nice. For the most part, we're likely not going to be putting major emphasis on expanding the query library with things like this until (possibly) this summer.

    This one looks useful enough though, that we may make an exception.

    Ilya Baran \ Director, Architecture and FeatureScript \ Onshape Inc
  • dave_cowdendave_cowden Member, Developers Posts: 445 ✭✭✭
    Hey, Kevin:

    I"m trying to use some code like you pasted above:

        const distanceResult is DistanceResult = evDistance(context, {
                "side0" : farPoint,
                "side1" : subQuery
        });

    But to use that function, evidently i needed to upgrade my FeatureScript file to version 293.

    But then, i cannot use the feature in part studio, because part studio defaults to version 275-- and I get this error:
    "Cannot import a module with FeatureScript version: 293 from module with older version 275"

    What's the trick to get a part studio to be a later FS version so you can use later modules?

  • dave_cowdendave_cowden Member, Developers Posts: 445 ✭✭✭
    Thanks for the awesome support Ilya  :)

    I'm off and running again!
Sign In or Register to comment.