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.

Options

Bodies define geometry, entities are the instantiation of bodies !?!?

traveler_hauptmantraveler_hauptman Member, OS Professional, Mentor, Developers Posts: 419 PRO
edited April 2016 in FeatureScript
Over the past couple days of learning the basics of FS I kept writing queries that didn't work. They made sense to my mental model of the dataset I was querying on... but obviously I was wrong so I would scratch my head, form a new model that kind of incorporates the new info and continue. Fail & repeat this cycle a few times and it gets really frustrating.

So I just spent the afternoon running every permutation of EntityType and BodyType trying to figure out what the heck is going on. I think I understand the concepts now and I'll try to relate what I think it is and some potential improvements to the docs.

Here's my current model.

All the geometry in a context is defined by a set of bodies. These bodies are 0,1,2, and 3 dimensional. This is a set in the mathematical sense and there are no sub-bodies. In other words, a cube body does not have 6 2D sub-bodies. There is just the one cube. I make this point because the enum BodyType docs imply otherwise and this is one of the things that prevented understanding until todays deep experimentation.
/**
* Specifies the topological type of a body.
<snip>
* For example, the result of an extrude with `NewBodyOperationType.NEW` is a
* body. This body will have `BodyType.SOLID` for a solid extrude, and
*`BodyType.SHEET` for a surface extrude.
<snip>
*/
export enum BodyType
{
I think the above comment is false now after some experimentation. The 'NEW' body does not have two sub bodies, a 'SOLID' and a 'SHEET'. The result of an extrude is just 1 solid body.

While bodies define what the geometry will be, they are not themselves the geometry (when the discussion is about how to do stuff with featurescript and queries).

All the geometry in a context can be viewed as a set of geometric entities. Vertices, Edges, Faces, and SolidEntities. So if my context has one body, a cube, then I can say that there is 1 SolidEntity, 6 Faces, 12 Edges, and 8 Vertices. No geometry is shared between bodies. It's common for bodies to be coincident at faces, edges, and points, and at those locations will be multiple faces, edges, and points, one for each body.

That's it, that's the model. The contents of a context are defined by it's bodies. Each of those bodies creates a collection of 0D-3D entities when instantiated. If that had been the message in the docs and especially query code, then I would have reached this point a lot quicker.

Some thoughts on other things that misled me and could be considered for cleanup:

Part of the problem is qEverything(). qEverything is spread across two fundamentally different concepts, the definition of the geometry, and the instantiation of the geometry. Keeping them separate, qBodies( ALL = POINTS | WIRES | SHEETS | SOLIDS) and qEntities(subquery, ALL = VERTICES | EDGES | FACES | SOLIDENTITIES), would not reduce functionality and would help keep the concepts separate for someone learning.

EntityType.BODY is too easily misconstrued as 3D solid entity. Obviously a Vertex is 0D, Edge 1D, Face 2D and Body 3D of the same concept. But NO. It's a trap!
Bodies are not an entity type and EntityType.BODY is not an entity type. One would assume that EntityType is an enumeration of entity types... but really it's an enumeration of qEverything searches.

Also, while it might seem a little counterintuitive, for me it would have helped understand entities and bodies quicker if you had used the same names (eg BodyType.FACE instead of SHEET, etc).

Something else that took me a while to get is that sketches don't exist as bodies. All the things in a sketch (points, curves, enclosed areas) exist as 0D, 1D and 2D bodies on their own. Also, some sketch entities (arcs and circles for instance) create multiple bodies in one go.

Anyway, that's my current understanding. Please let me know if I'm still using the wrong mental model.

PS, One might be of the mind that bodies and entities are part of the same collection of things, that there is no need to separate the definition from the result. I'm cool with that. Just reflect that in your code by having only one enum. Something like:

export enum EntityType
{
VERTEX,
POINT_BODY,
EDGE,
WIRE_BODY,
FACE,
SHEET_BODY,
SOLID_BODY
}

export var ALL_BODIES = qUnion([
qEverything(EntityType.SOLID_BODY),
qEverything(EntityType.SHEET_BODY),
qEverything(EntityType.WIRE_BODY),
qEverything(EntityType.POINT_BODY)]);

Comments

  • Options
    ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,185
    I agree that this stuff is not intuitiive -- I guess I've been working in this mental model for so long that I've forgotten that a bit.

    I don't think what you describe is still quite the right mental model.  Let me try to explain and migrate what useful parts come out into documentation.

    We have four entity types: vertices, edges, faces, and bodies.  The way to think about bodies is they are containers for other entity types.
    All geometry in a context is partitioned into bodies: every entity is owned by exactly one body (bodies are considered to own themselves).  qOwnerBody will tell you for a given entity, which body owns it.

    Bodies come in a few varieties -- each body has a "dimension:" solid, sheet, wire, or point.  Some bodies are also special: a construction plane is a special kind of sheet body, a mate connector is a special kind of point body, and a sketch produces slightly special wire and sheet bodies.  The type of body determines the structure of the entities inside: the entities of a solid body enclose a volume, a wire body may not have faces, etc.  Two vertices/edges/faces in one body may only overlap geometrically when they are topologically adjacent (e.g. two vertices may not be at the same 3D point).

    Two different bodies are generally not "aware" of each other: for instance, they may occupy the same physical space.  Entities in two different bodies cannot be adjacent, so I would write your isClosed function as follows:

    function isClosed(context is Context, edge is Query) returns boolean
    {
    return size(evaluateQuery(context, qVertexAdjacent(edge, EntityType.VERTEX))) < 2; }
    Kevin's method also works, but is a bit more expensive.

    There is no concept of "solid entity" -- each solid body defines exactly one connected region of space, but that region is not a distinct "thing" you can work with.

    Does this make it more or less clear?
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Options
    ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,185
    One more thing -- the qBodyType query filters entities *owned* by a body of a certain type, and so may return entities of any type, not just bodies.
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Options
    traveler_hauptmantraveler_hauptman Member, OS Professional, Mentor, Developers Posts: 419 PRO
    The mental model is quickly solidifying and so the points of confusion are fading. This is just some stream of consciousness:

    A body is a different type of thing versus solid, sheet, and wire. Different behavior, different role in how things work. Munging them together as "entities" leads one to try to build a pattern, 'What is this entity thing? Oh, there's a point, edge, face, and body, 0D, 1D, 2D and 3D things. The 3D thing has the same name as this BODY object but I'm smart, I can keep them separate..."

    Had you called the enum QuerySpecifiers, I might have been more inclined to allow for the body in the enum to be the body object. Had you also had a separate enum for BodyContents that had point,edge and face as members, it would have given me another hint.

    qBodyType -> qOwnerBodyType would further add clues of the Body=partition/container
    There is no concept of "solid entity" -- each solid body defines exactly one connected region of space, but that region is not a distinct "thing" you can work with.
    Do you mean that a solid body, which is a container, only has one volume so there is no point in differentiating it? Having a wire that contains an edge and a solid that contains nothing of the same dimensionality does not help one build a mental model. Anytime you break the pattern, that's the one experiment that destroys a theory, and I have to start all over with a more complicated theory.

    For a while I remember working with the idea that bodies were hierachical. That a sheet body had wire bodies which had point bodies.

    There's not a huge difference in bodies=definition,[points,edges,faces]=output  and bodies=container, stuff=contents from a mental model point of view.

    What the heck do you call entities that are not bodies? I mean in your discussions with colleagues.

    End stream of conciousness....

    Anyway, I think, based on your response, that I have a pretty good model now. Hopefully you can tease out some of my stumbling blocks for the docs. Telling the reader what the model is not (not hierachical, entities are a construct for specifying queries rather than storing geometry) can be really useful. Also, it could be useful to link the content. eg put the FsDoc:Features:Bodies link in the query source code enum BodyType docs. Not all of us attack the documentation in the same way and it can be easy to miss a sentence here and there. If it's a key concept, duplicate it.

    I think what I'm still struggling to get my head around is that if bodies are containers, why does the following query not work? (A conceptual question, I can guess, close enough, why the implementation limits this.)
    qEntityFilter(qBodyType(qEverything(EntityType.BODY),BodyType.WIRE),EntityType.VERTEX)
    I would want that to grab all the vertices belonging to wire bodies.

    Consider tweaking EntityType. Why do you have EDGE but not WIRE there? Why do you have entries for 0,1,2D contents but not 0,1,2,3D containers? (Again, a conceptual question, I get how the implementation works).

  • Options
    traveler_hauptmantraveler_hauptman Member, OS Professional, Mentor, Developers Posts: 419 PRO
    Still refining my understanding. Reading the note in the creat-a-slot tutorial.

    With bodies, is the max dimension of it (WIRE, SHEET, etc) an attribute (it arises from the dimensions of it's vertex, edge, face contents) or part of it's definition (I define a sheet, which is a 2D manifold, and the faces, edges and vertices of geometry come from the sheet definition)?


  • Options
    traveler_hauptmantraveler_hauptman Member, OS Professional, Mentor, Developers Posts: 419 PRO
    So I took a stab in the dark and guessed you inherited some of your terminology from the libraries you are using. Any chance that the terminology in pages 20-27 of this doc matches your terminology? If so it makes everything clear, including why a solid body has no matching 3D entity.

    It also helps me understand why the BodyType and EntityType enums have the members that they do.

    Bodies are not containers, nor are they the definitions for the vertex,edge, face. Vertices, Edges, and Faces are the elements used to define bodies. Even as I got close to the right mental model with your help, it was still inverted with regards to input and output.

    Consider separating the definition from the result and removing BODY from EntityType. Replace it with a qBodies(BodyType) (same behavior as qEverything, no parameter gives you everything). qCreatedBy seems to be the only function this would affect in the std library and since queries are apparently just building a map and not executing anything:
    qCreatedBy(Id,EntityType.BODY);
    //becomes
    qBodies(qCreatedBy(Id));

    should not impact performance, nor clarity.

    Ok, now I'm off to see what I can get Fs to do for me. Please let me know if vertex, edge, face, wire, sheet, et al are not the same as the parasolids terms. 

  • Options
    ilya_baranilya_baran Onshape Employees, Developers, HDM Posts: 1,185
    A couple of things: you are right that some of the terminology we did inherit from Parasolid, though some was too obscure (e.g., acorn body became point body).  However, we tried to simplify the concepts relative to Parasolid as much as we could (e.g., no shells, loops, fins, regions and also disallowing bodies from having multiple connected components).

    I'm not sure what you mean when you talk about input and output -- edges, for instance, are not the "output" of bodies nor vice versa.  I do think of bodies as containers, keeping in mind that a container is itself an entity (that may have its own data attached) and a container (body) and its contents (set of edges, faces, vertices) are distinct concepts.

    qFoo functions are a little confusing because there's what they *return* (a Query) and what they *evaluate to* within a context (an ordered set of entities).  In most cases, it's helpful to think about what queries evaluate to.  Analyzing your query:

    qEntityFilter(qBodyType(qEverything(EntityType.BODY), BodyType.WIRE), EntityType.VERTEX)

    We start with the subquery qEverything(EntityType.BODY) -- it evaluates to all bodies in the context.
    The subquery qBodyType(qEverything(EntityType.BODY),BodyType.WIRE) evaluates to all bodies of type wire.
    Now qEntityFilter asks which of the passed in query results are vertices -- and because they are all bodies, it evaluates to nothing.

    Instead of qEntityFilter, you could have done:

    qOwnedByBody(qBodyType(qEverything(EntityType.BODY), BodyType.WIRE), EntityType.VERTEX)

    asking for all entities of type vertex owned by the given body, or more simply:

    qBodyType(qEverything(EntityType.VERTEX), BodyType.WIRE)

    which asks for all vertices whose bodies are of type WIRE.
    Ilya Baran \ VP, Architecture and FeatureScript \ Onshape Inc
  • Options
    traveler_hauptmantraveler_hauptman Member, OS Professional, Mentor, Developers Posts: 419 PRO
    edited April 2016
    I think I have a handle on it now. Thanks.

    I may be the exception in getting stuck on this, but if you see a lot of others hit the same wall, really consider if EntityType.BODY needs to be there.

Sign In or Register to comment.