More viewers in an OCAF document

Max

CAD practitioner
That's quite an investment. You should have some good reason to switch horses then.
still not decided. I guess I'll give a try to OSG, on a small, sample codebase, to see how difficult it would be.
And I've to revert some too hard multithreading that i put into my display code, it works, it allows me to look at drawings and start working before it's fully triangulated, but it's becoming a nightmare to maintain...
 

Quaoar

Administrator
Staff member
I serialize BReps as binary blobs text encoded inside XML
Are you using this BinTools::Write for B-rep serialization or something, say, more compact? I was not happy about the size of base64-encoded string even for simple shapes.
 

blobfish

CAD community veteran
The serialization for me is not a problem at all... I coded long time ago a class / class factory that can do XML (de)serialization quite well. I serialize BReps as binary blobs text encoded inside XML, and the whole stuff is zipped before going on disk, to overcome the usual big size of XML files.
As an example, the drawing of the screenshot is, on file, just 8.1 MB in size, and loads quite fast. I've even opensourced the serialization library long time ago.
My biggest problem is that I'm always reinventing the wheel...
Pretty interesting topic, so I will throw in my 2 cents:
FreeCAD tried using OCAF and gave up. That combined with my "never use opencascade for anything that can be done somewhere else" policy, I did an xml based document structure like Max. I don't do any compression/zip and just work out of a folder. Each feature is it's own xml file and all occt data gets wrapped into a compound and wrote to disk. That keeps the implicit sharing between loads. My projects have built in git integration. Every project update, each 'dirty' feature writes it's xml file to disk along with Brep compound and git commit is made. I will spare you the story but I learned early in my career that undo in a parametric cad is really not necessary. I know that statement raises eyebrows and will elaborate if anyone cares. Anyway. With my git integration I get revision control and a 'sudo' undo by just resetting the revision. It is very liberating to code and not worry about undo. With this setup, I don't worry about temp files for crash data recovery either. Not that my programs ever crash. ;)
 

Quaoar

Administrator
Staff member
That combined with my "never use opencascade for anything that can be done somewhere else" policy
That is what distinguishes OpenCascade from other kernels: you wouldn't use its SDK unless you have to :) As for undo in parametric CAD, personally, I'd love to hear your take on it. Please, elaborate!
 

blobfish

CAD community veteran
As for undo in parametric CAD, personally, I'd love to hear your take on it. Please, elaborate!
After some frustrations on other 3d modeling programs our design department ended up on Unigraphics V15 running in a hummingbird emulator on windows NT 4 circa 1998. That setup was rock solid compared to the previous 3d cad systems we tried. However it did crash on occasion and only using the undo command. Like any user would, I started avoiding that command as I don't like losing work. Coming from autocad undo/redo where 'in my blood'. So I broke the 'undo' habit and realized I didn't miss it much. In parametric cad, undoing a single command is just a matter of changing the parameters/selections back to what they were before. In a good GUI, this shouldn't be a big deal. Now if you want to start getting into numerous 'undos' and heaven forbid redo, then I think a revision control workflow is a better fit than having the user mindlessly hitting undo and redo until the picture on the screen matches his/her mental vision. With my revision control(picture), it is basically an automated closing the project, 'git reset --hard' and re-opening the project. Yes that is slower and at some scale will get annoying for user.

I do agree with your take on the serialization being a pain.

revision.png
 

Max

CAD practitioner
Are you using this BinTools::Write for B-rep serialization or something, say, more compact? I was not happy about the size of base64-encoded string even for simple shapes.
This one is the snippet that encodes a brep onto a string:

C++:
// encode/decode shape in zip+encode64 format
// this is the format on which shapes are inside xml uppcad files
String UppCadShapeTools::Encode(TopoDS_Shape const &shape)
{
    std::ostringstream ss;
    BRepTools::Clean(shape);
    BRepTools::Write(shape, ss);
    String s = ss.str();
    String compressed = GZCompress(s);
    return Base64Encode(compressed);
}

TopoDS_Shape UppCadShapeTools::Decode(String const &encoded)
{
    String compressed = Base64Decode(encoded);
    std::string s = GZDecompress(compressed).ToStd();
    std::istringstream ss(s);
    BRep_Builder builder;
    TopoDS_Shape shape;
    BRepTools::Read(shape, ss, builder);
    return shape;
}

Don't be "afraid" about types (String, for example...), I'm using a quite unusual toolkit, Ultimate++.
I dump data into a string, zip it, base64 encode the zipped data and put into XML field.
At the end I also zip the whole XML, but that's optional.
I get small filesizes, BReps are quite redundant objects and get compressed quite well.
I don't store triangulation, as the benefit of not having to recalculate it upon load is overcomed by far by file size and load time.
 

Max

CAD practitioner
That is what distinguishes OpenCascade from other kernels: you wouldn't use its SDK unless you have to :) As for undo in parametric CAD, personally, I'd love to hear your take on it. Please, elaborate!
I don't see much difference on undo on parametric or non-parametric cad. Once you enclose data operations in transactions, end store the reversed operation in a transaction too, the undo becomes rock solid.
The only "difficult" part is to account for operations that have side effects, as for example in my application changes to layers, which must trigger update on the whole visualization. Or a change in a block that must update all insertions of it.
But again, with transactions it's quite easy.

Just to give you the idea, I store active transactions (that can be nested...), undo and redo records like this :
C++:
        // array of active transactions on database
        Upp::Array<UppCadTransaction> transactions;
        
        // commits undo from transaction data
        void CommitUndo(Upp::ArrayMap<UppCadIdx, Upp::One<UppCadTransaction::TransRec> > pick_ &records);
        
        // drops last transaction data
        void DropTransaction(void);
        
        // undo records
        Upp::Array<Upp::ArrayMap<UppCadIdx, Upp::One<UppCadTransaction::TransRec> > >undoRecords;
        
        // redo records
        Upp::Array<Upp::ArrayMap<UppCadIdx, Upp::One<UppCadTransaction::TransRec> > >redoRecords;

        // initialize transaction on database
        // returns true on success, false if error
        // for example if a transaction is already opened
        UppCadTransaction *StartTransaction(void) const;

In short, I open a transaction, get a smart pointer on a transaction object and do ALL operation on it.
At the end I can commit the transaction or abort it. If aborted, no changes to database. If committed, a record into the undo database
is added, whith reverse transaction type (create->delete, delete->create, modify<>modify with swapped records).
On undo I just pick from undo queue, apply the reversed transactions, and as a plus I maintain a redo record queue too.
 
Last edited:

Max

CAD practitioner
Btw, if you're interested in my serialization code, you can find it on Ultimate++website, Bazaar page, it names PolyXml.
It's able to (de)serialize any kind of class derived from a WithPolyXML template, and registered on system by a REGISTERCLASS macro.
The whole stuff stays in a couple of header files, but it't tied to U++ toolkit quite a lot, so to be reused in some other toolkit it would need some work.
 

blobfish

CAD community veteran
I don't see much difference on undo on parametric or non-parametric cad.
My argument was in the context of the user. The usefulness of 'undo/redo' to the user in parametric cad is significantly lower than non-parametric cad.


The only "difficult" part is to account for operations that have side effects, as for example in my application changes to layers, which must trigger update on the whole visualization. Or a change in a block that must update all insertions of it.
But again, with transactions it's quite easy.
I will be curious if you feel the same way after a few years of working with it. I am guessing it will be a big source of head-aches and bugs.


Btw, if you're interested in my serialization code, you can find it on Ultimate++website, Bazaar page, it names PolyXml.
It's able to (de)serialize any kind of class derived from a WithPolyXML template, and registered on system by a REGISTERCLASS macro.
The whole stuff stays in a couple of header files, but it't tied to U++ toolkit quite a lot, so to be reused in some other toolkit it would need some work.
I have never used Ultimate++. First glance reminds a little of Qt. I went with xsdcxx for my xml parsing. You basically create a schema file and xsdcxx generates the parser. There is that generation step, but the output is less intrusive. objects I can use that I don't have to derive from.
 

natalia

Moderator
Staff member
Hi guys,

It's not a problem to use AIS for it. It's developed exactly for task described by you. You may think about it like it's some user-oriented interface over low-level library like OpenGl.
What I need is the ability to display separate part of data in separate windows, but keeping all the data in a single document.
If you need different content in separate windows, the simpliest way is creating several AIS_InteractiveContext instances for each of them. We used such approach in several applications with no problems. Just give an own id of the window when you initialize v3d_Viewer and implement managing of these contexts on the application level.
There are another ways in AIS, but it's the simpliest one, sure if I correctly understand your task.
 

Max

CAD practitioner
I will be curious if you feel the same way after a few years of working with it. I am guessing it will be a big source of head-aches and bugs.
No, why ? It's just matter of moving records around, along with some operation codes on how to manage them.
You even don't need to know which kind of data they are, from inside transaction mechanics.
I'm using a sort of "labels" (in my case they're IDs that identify objects inside a big map), but with a flat structure, as I don't need to keep track of solid evolutions nor to manage assemblies.
When I delete an object, I create a "delete" record, which is a container for the object and some data explaining what happened, and put that record inside an undo database.
When I add an object, I create a "create" record, which contains nothing but some data on what happened, and I add it to undo db too.
And so on. The only requirement on objects is that they must be cloneable, as I need a persistent copy of them inside undo database.
On undo, I pop a record from undo db, add to redo db, and apply the changes (reverted) to main db.
The kind of objects is totally ininfluent, as long as they can be cloned.
As I told you, the only "critical" part, but that's for almost everything, even when you delete an object without undo, is to propagate the change if object is tied to others. I don't store the undo db on disk, but I could do it, so making undo persistent between edit sessions.
In all that "record moving" stuffs U++ was very helpful, having developed an analog of std::move long before it was added to c++ standards (the pick_ that you can see in my code), alowing fast code avoiding most copy of objects around.

These are declaration of transaction records :
C++:
        // transaction record operation type
        typedef enum { nop, create, remove, modify } TransOp;
        
        // transaction record
        struct TransRec
        {
            TransOp op;
            UppCadIdx idx;
            Upp::One<UppCadDbObject> obj;
            
            TransRec(TransOp _op, UppCadIdx _idx, UppCadDbObject *_obj) : op(_op), idx(_idx), obj(_obj) {}
            TransRec() : op(nop), idx(-1), obj(NULL) {}
            ~TransRec() {}
        };
        
        // all operations done inside transaction
        Upp::ArrayMap<UppCadIdx, Upp::One<TransRec> > records;

the
C++:
Upp::One<UppCadDbObject> obj;

Holds the object's copy, it's a kind of smart pointer.
 
Last edited:

Max

CAD practitioner
If you need different content in separate windows, the simpliest way is creating several AIS_InteractiveContext instances for each of them. We used such approach in several applications with no problems. Just give an own id of the window when you initialize v3d_Viewer and implement managing of these contexts on the application level.
There are another ways in AIS, but it's the simpliest one, sure if I correctly understand your task.
Yep, is more or less this task... But don't you need a viewer x context ? Or you can have more contexts inside same viewer ?
BTW, is not that I dislike AIS, I find it quite fast and, in some parts, quite well coded.
I really hate drawer stuffs, I dont' understand it almost at all. I resort creating a drawer for each object, which should be the normal. I dont' understand the purpose of "inheriting" other drawers, nor to have so many different kinds of them.
I don't see why they didn't apply all drawer's properties to highlight / selection presentations, and so on.
My thoughts about OSG were mostly because of the handling of detail level, which should speed up display on complex, very detailed scenes.
Maybe at the end it will be even more cumbersome to use...
 

natalia

Moderator
Staff member
Or you can have more contexts inside same viewer ?
The relation between context and viewer is one to one. Using this approach, you will create a new viewer and new view/views inside for each context. But, be sure and analyse whether you really need different presentations in different windows as it influences on your application logic because you need to write the code of managing the several contexts.

Regarding the drawers stuff, firstly I had the similar opinion - too complex, too much parameters. Now, it seems more or less appropriate. Anyway, it is better than getting Gl context and set/reset glColor for it). We are working with object-oriented architecture here.

What is not pleasant here is that the documentation is poor for understanding. It does not allow to use the drawers in optimal way.

I dont' understand the purpose of "inheriting" other drawers, nor to have so many different kinds of them.
It seems that you mean here the ‘linkage’ of Prs3d_Drawer to another drawer. The purpose of it is that the presentation (drawer) uses parameters of context drawer. So, the general way is managing parameters for drawer of context, e.g. not drawing faces boundaries on AIS_Shape and so on. As the result, all presentations will use this setting during paining of them. In case if some presentation requires its own parameters, e.g. color of shading or other, we should set using of own (shading) aspect into the presentation drawer and set the color inside.

The same for highlight and selection – we may use either general drawers of context(HighlightStyle) and manage them or manage drawer of custom presentation.

If you still do not want to use drawers, you may implement your own presentation and do not use drawer parameters inside. Just fill group with your own primitive aspect in Compute/highlight. In this case you will not depend on the drawers at all) It is not the general way, but also possible.
 
Last edited:

Max

CAD practitioner
... because you need to write the code of managing the several contexts.
This is already done. My app handles many "drawings" in a single data file, each drawing takes its own viewer / context and as many views as needed.
What I find difficult is to manage different view styles in different view. It works, but it has some quirks, mostly because of drawers.
Regarding the drawers stuff, firstly I had the similar opinion - too complex, too much parameters. Now, it seems more or less appropriate. Anyway, it is better than getting Gl context and set/reset glColor for it). We are working with object-oriented architecture here.

What is not pleasant here is that the documentation is poor for understanding. It does not allow to use the drawers in optimal way.
Yep. I'm coding with a trial-and-error way, which is time consuming, and sometimes unrelated changes drive me crazy.
That's the main reason of trying OSG for me.
It seems that you mean here the ‘linkage’ of Prs3d_Drawer to another drawer. The purpose of it is that the presentation (drawer) uses parameters of context drawer.
I see, but in my app each "entity" gets its own presentation data (color, linetype) or gets a bylayer / byblock type, which refers to entitity layer settings or to block settings, so nothing to do with context default drawer.
So, the general way is managing parameters for drawer of context, e.g. not drawing faces boundaries on AIS_Shape and so on. As the result, all presentations will use this setting during paining of them. In case if some presentation requires its own parameters, e.g. color of shading or other, we should set using of own (shading) aspect into the presentation drawer and set the color inside.
Yes, but many attributes are spread into too many drawer kinds, which makes stuffs unnecessary complicated. I guess that latest changes (a sort of uniformation of drawer kinds...) goes in right direction, but to keep some sort of back compatibility is quite cumbersome.
The same for highlight and selection – we may use either general drawers of context(HighlightStyle) and manage them or manage drawer of custom presentation.
ALL highlight code is aimed to handle just color, not linetype. I got it to work handling directlty the presentation data, but that's an awful hack.
I really don't see the point of handling just color and not the remaining attributes.
If you still do not want to use drawers, you may implement your own presentation and do not use drawer parameters inside. Just fill group with your own primitive aspect in Compute/highlight. In this case you will not depend on the drawers at all) It is not the general way, but also possible.
Yes, but then I have to re-code all presentation part or all ais objects. I guess that it would be easier to switch to osg, then.
 

Max

CAD practitioner
... I feel comfortable in saying that you will be able to do anything you want/need in OSG as I am doing all those things in cadseer. I am sure there will be plenty of cases that OSG requires more setup as it is a general scene graph and is not tailored toward CAD...
Well... I see that it's not tailored toward CAD, but having half a dozen of view manipulators that can handle ONLY perspective view (which in AEC may be of some use, in mechanical and in my case is totally useless) and NONE able to zoom an ortographic view it's quite weird. It's even more weird when you see that the question about "zooming an orto view" appears since many years in osg forums and nobody coded a solution for it.

Anyways I'll code my own manipulator and try to do some tests before embedding in my application. OSG seems to have some stuffs that could be useful for me, even if they require some effort.
 

blobfish

CAD community veteran
Well... I see that it's not tailored toward CAD, but having half a dozen of view manipulators that can handle ONLY perspective view (which in AEC may be of some use, in mechanical and in my case is totally useless) and NONE able to zoom an ortographic view it's quite weird. It's even more weird when you see that the question about "zooming an orto view" appears since many years in osg forums and nobody coded a solution for it.

Anyways I'll code my own manipulator and try to do some tests before embedding in my application. OSG seems to have some stuffs that could be useful for me, even if they require some effort.
Yeah that was a little of a pain point. I am guessing orthographic projection isn't used much outside of cad.
cadseer spaceball manipulator
 
  • Like
Reactions: Max
Top