1
/***************************************************************************
2
* Copyright (c) 2005 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
# include <Inventor/SbSphere.h>
26
# include <Inventor/SbString.h>
27
# include <Inventor/SoInteraction.h>
28
# include <Inventor/actions/SoGetBoundingBoxAction.h>
29
# include <Inventor/actions/SoToVRML2Action.h>
30
# include <Inventor/actions/SoWriteAction.h>
31
# include <Inventor/fields/SoMFNode.h>
32
# include <Inventor/fields/SoSFNode.h>
33
# include <Inventor/nodes/SoGroup.h>
34
# include <Inventor/VRMLnodes/SoVRMLGroup.h>
35
# include <Inventor/VRMLnodes/SoVRMLIndexedFaceSet.h>
36
# include <Inventor/VRMLnodes/SoVRMLNormal.h>
37
# include <Inventor/VRMLnodes/SoVRMLParent.h>
38
# include <Inventor/VRMLnodes/SoVRMLShape.h>
41
# include <QTemporaryFile>
45
#include <Base/FileInfo.h>
46
#include <Base/Stream.h>
47
#include <Base/Tools.h>
48
#include <zipios++/gzipoutputstream.h>
53
#include "GestureNavigationStyle.h"
54
#include "NavigationStyle.h"
55
#include "SelectionObject.h"
56
#include "SoAxisCrossKit.h"
57
#include "SoFCBackgroundGradient.h"
58
#include "SoFCBoundingBox.h"
59
#include "SoFCColorBar.h"
60
#include "SoFCColorGradient.h"
61
#include "SoFCColorLegend.h"
62
#include "SoFCCSysDragger.h"
63
#include "SoFCInteractiveElement.h"
64
#include "SoFCSelection.h"
65
#include "SoFCSelectionAction.h"
66
#include "SoFCUnifiedSelection.h"
67
#include "SoFCVectorizeSVGAction.h"
68
#include "SoFCVectorizeU3DAction.h"
69
#include "SoMouseWheelEvent.h"
70
#include "SoTextLabel.h"
71
#include "SoDatumLabel.h"
72
#include "Inventor/MarkerBitmaps.h"
73
#include "Inventor/SmSwitchboard.h"
74
#include "Inventor/SoAutoZoomTranslation.h"
75
#include "Inventor/SoDrawingGrid.h"
76
#include "propertyeditor/PropertyItem.h"
80
using namespace Gui::Inventor;
81
using namespace Gui::PropertyEditor;
83
static SbBool init_done = false;
84
static SoGroup *storage = nullptr;
86
SbBool Gui::SoFCDB::isInitialized()
91
void Gui::SoFCDB::init()
93
SoInteraction ::init();
94
SoGLRenderActionElement ::initClass();
95
SoFCInteractiveElement ::initClass();
96
SoGLWidgetElement ::initClass();
97
SoFCColorBarBase ::initClass();
98
SoFCColorBar ::initClass();
99
SoFCColorLegend ::initClass();
100
SoFCColorGradient ::initClass();
101
SoFCBackgroundGradient ::initClass();
102
SoFCBoundingBox ::initClass();
103
SoFCSelection ::initClass();
104
SoFCUnifiedSelection ::initClass();
105
SoFCHighlightAction ::initClass();
106
SoFCSelectionAction ::initClass();
107
SoFCDocumentAction ::initClass();
108
SoGLWidgetNode ::initClass();
109
SoGLVBOActivatedElement ::initClass();
110
SoFCEnableSelectionAction ::initClass();
111
SoFCEnableHighlightAction ::initClass();
112
SoFCSelectionColorAction ::initClass();
113
SoFCHighlightColorAction ::initClass();
114
SoFCDocumentObjectAction ::initClass();
115
SoGLSelectAction ::initClass();
116
SoVisibleFaceAction ::initClass();
117
SoUpdateVBOAction ::initClass();
118
SoBoxSelectionRenderAction ::initClass();
119
SoFCVectorizeSVGAction ::initClass();
120
SoFCVectorizeU3DAction ::initClass();
121
SoHighlightElementAction ::initClass();
122
SoSelectionElementAction ::initClass();
123
SoVRMLAction ::initClass();
124
SoSkipBoundingGroup ::initClass();
125
SoTextLabel ::initClass();
126
SoDatumLabel ::initClass();
127
SoColorBarLabel ::initClass();
128
SoStringLabel ::initClass();
129
SoFrameLabel ::initClass();
130
TranslateManip ::initClass();
131
SoShapeScale ::initClass();
132
SoAxisCrossKit ::initClass();
133
SoRegPoint ::initClass();
134
SoDrawingGrid ::initClass();
135
SoAutoZoomTranslation ::initClass();
136
MarkerBitmaps ::initClass();
137
SoFCCSysDragger ::initClass();
138
SmSwitchboard ::initClass();
139
SoFCSeparator ::initClass();
140
SoFCSelectionRoot ::initClass();
141
SoFCPathAnnotation ::initClass();
142
SoMouseWheelEvent ::initClass();
144
PropertyItem ::init();
145
PropertySeparatorItem ::init();
146
PropertyStringItem ::init();
147
PropertyFontItem ::init();
148
PropertyIntegerItem ::init();
149
PropertyIntegerConstraintItem ::init();
150
PropertyFloatItem ::init();
151
PropertyUnitItem ::init();
152
PropertyFloatConstraintItem ::init();
153
PropertyPrecisionItem ::init();
154
PropertyUnitConstraintItem ::init();
155
PropertyAngleItem ::init();
156
PropertyBoolItem ::init();
157
PropertyVectorItem ::init();
158
PropertyVectorListItem ::init();
159
PropertyVectorDistanceItem ::init();
160
PropertyPositionItem ::init();
161
PropertyDirectionItem ::init();
162
PropertyMatrixItem ::init();
163
PropertyPlacementItem ::init();
164
PropertyRotationItem ::init();
165
PropertyEnumItem ::init();
166
PropertyStringListItem ::init();
167
PropertyFloatListItem ::init();
168
PropertyIntegerListItem ::init();
169
PropertyColorItem ::init();
170
PropertyMaterialItem ::init();
171
PropertyMaterialListItem ::init();
172
PropertyFileItem ::init();
173
PropertyPathItem ::init();
174
PropertyTransientFileItem ::init();
175
PropertyLinkItem ::init();
176
PropertyLinkListItem ::init();
178
NavigationStyle ::init();
179
UserNavigationStyle ::init();
180
InventorNavigationStyle ::init();
181
CADNavigationStyle ::init();
182
RevitNavigationStyle ::init();
183
BlenderNavigationStyle ::init();
184
MayaGestureNavigationStyle ::init();
185
TouchpadNavigationStyle ::init();
186
GestureNavigationStyle ::init();
187
OpenCascadeNavigationStyle ::init();
188
OpenSCADNavigationStyle ::init();
189
TinkerCADNavigationStyle ::init();
191
GLGraphicsItem ::init();
192
GLFlagWindow ::init();
194
SelectionObject ::init();
196
qRegisterMetaType<Base::Vector3f>("Base::Vector3f");
197
qRegisterMetaType<Base::Vector3d>("Base::Vector3d");
198
qRegisterMetaType<Base::Quantity>("Base::Quantity");
199
qRegisterMetaType<QList<Base::Quantity> >("Base::QuantityList");
203
storage = new SoGroup();
207
void Gui::SoFCDB::finish()
209
// Coin doesn't provide a mechanism to free static members of own data types.
210
// Hence, we need to define a static method e.g. 'finish()' for all new types
211
// to invoke the private member function 'atexit_cleanup()'.
212
SoFCColorBarBase ::finish();
213
SoFCColorBar ::finish();
214
SoFCColorLegend ::finish();
215
SoFCColorGradient ::finish();
216
SoFCBackgroundGradient ::finish();
217
SoFCBoundingBox ::finish();
218
SoFCSelection ::finish();
219
SoFCHighlightAction ::finish();
220
SoFCSelectionAction ::finish();
221
SoFCDocumentAction ::finish();
222
SoFCDocumentObjectAction ::finish();
223
SoFCEnableSelectionAction ::finish();
224
SoFCEnableHighlightAction ::finish();
225
SoFCSelectionColorAction ::finish();
226
SoUpdateVBOAction ::finish();
227
SoFCHighlightColorAction ::finish();
228
SoFCSeparator ::finish();
229
SoFCSelectionRoot ::finish();
230
SoFCPathAnnotation ::finish();
236
// buffer acrobatics for inventor ****************************************************
239
static std::vector<char> static_buffer;
242
buffer_realloc(void * /*bufptr*/, std::size_t size)
244
static_buffer.resize(size);
245
return static_buffer.data();
249
const std::string& Gui::SoFCDB::writeNodesToString(SoNode * root)
252
static_buffer.resize(1024);
253
out.setBuffer(static_buffer.data(), static_buffer.size(), buffer_realloc);
254
if (root && root->getTypeId().isDerivedFrom(SoVRMLParent::getClassTypeId()))
255
out.setHeaderString("#VRML V2.0 utf8");
257
SoWriteAction wa(&out);
260
static std::string cReturnString;
261
cReturnString = static_buffer.data();
262
return cReturnString;
265
SoNode* replaceSwitches(SoNodeList* children, SoGroup* parent)
271
for (int i=0; i<children->getLength(); i++) {
272
SoNode* node = (*children)[i];
273
if (node->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) {
274
if (node->getTypeId().isDerivedFrom(SoSwitch::getClassTypeId())) {
275
auto group = static_cast<SoSwitch*>(node);
276
int which = group->whichChild.getValue();
277
if (which == SO_SWITCH_NONE)
279
auto newParent = new SoGroup();
282
c.append(group->getChild(which));
285
// SO_SWITCH_INHERIT or SO_SWITCH_ALL
286
for (int i=0; i<group->getNumChildren(); i++)
287
c.append(group->getChild(i));
290
replaceSwitches(&c, newParent);
291
parent->addChild(newParent);
294
auto newParent = static_cast<SoGroup*>(node->getTypeId().createInstance());
295
replaceSwitches(node->getChildren(), newParent);
296
parent->addChild(newParent);
300
parent->addChild(node);
307
SoNode* replaceSwitchesInSceneGraph(SoNode* node)
309
if (node->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) {
310
return replaceSwitches(node->getChildren(), new SoSeparator);
316
SoNode* Gui::SoFCDB::replaceSwitches(SoNode* node)
318
return replaceSwitchesInSceneGraph(node);
321
void Gui::SoFCDB::writeToVRML(SoNode* node, std::string& buffer)
323
SoNode* noSwitches = replaceSwitchesInSceneGraph(node);
326
vrml2.setOverrideMode(true);
327
vrml2.apply(noSwitches);
328
SoToVRML2Action tovrml2;
329
tovrml2.apply(noSwitches);
330
SoVRMLGroup* vrmlRoot = tovrml2.getVRML2SceneGraph();
332
vrmlRoot->setInstancePrefix(SbString("o"));
334
buffer = SoFCDB::writeNodesToString(vrmlRoot);
335
vrmlRoot->unref(); // release the memory as soon as possible
337
// restore old settings
338
vrml2.setOverrideMode(false);
339
vrml2.apply(noSwitches);
343
bool Gui::SoFCDB::writeToVRML(SoNode* node, const char* filename, bool binary)
346
writeToVRML(node, buffer);
348
Base::FileInfo fi(filename);
350
// We want to write compressed VRML but Coin 2.4.3 doesn't do it even though
351
// SoOutput::getAvailableCompressionMethods() delivers a string list that
352
// contains 'GZIP'. setCompression() was called directly after opening the file,
353
// returned true and no error message appeared but anyway it didn't work.
354
// Strange is that reading GZIPped VRML files works.
355
// So, we do the compression on our own.
356
Base::ofstream str(fi, std::ios::out | std::ios::binary);
357
zipios::GZIPOutputStream gzip(str);
366
Base::ofstream str(fi, std::ios::out);
378
bool Gui::SoFCDB::writeToX3D(SoNode* node, const char* filename, bool binary)
381
writeToX3D(node, false, buffer);
383
Base::FileInfo fi(filename);
385
Base::ofstream str(fi, std::ios::out | std::ios::binary);
386
zipios::GZIPOutputStream gzip(str);
395
Base::ofstream str(fi, std::ios::out);
407
bool Gui::SoFCDB::writeToX3D(SoNode* node, bool exportViewpoints, std::string& buffer)
409
SoNode* noSwitches = replaceSwitchesInSceneGraph(node);
412
vrml2.setOverrideMode(true);
413
vrml2.apply(noSwitches);
414
SoToVRML2Action tovrml2;
415
tovrml2.apply(noSwitches);
416
SoVRMLGroup* vrmlRoot = tovrml2.getVRML2SceneGraph();
418
vrmlRoot->setInstancePrefix(SbString("o"));
421
// Search for SoVRMLIndexedFaceSet nodes and set creaseAngle to 0.5
424
sa.setType(SoVRMLShape::getClassTypeId());
425
sa.setInterest(SoSearchAction::ALL);
426
sa.setSearchingAll(true);
428
SoPathList& paths = sa.getPaths();
429
for (int i=0; i<paths.getLength(); i++) {
430
SoPath* path = paths[i];
431
auto shape = static_cast<SoVRMLShape*>(path->getTail());
432
SoNode* geom = shape->geometry.getValue();
433
if (geom && geom->getTypeId() == SoVRMLIndexedFaceSet::getClassTypeId()) {
434
SoNode* norm = static_cast<SoVRMLIndexedFaceSet*>(geom)->normal.getValue();
435
if (norm && norm->getTypeId() == SoVRMLNormal::getClassTypeId()) {
436
// if empty then nullify the normal field node
437
if (static_cast<SoVRMLNormal*>(norm)->vector.getNum() == 0)
438
static_cast<SoVRMLIndexedFaceSet*>(geom)->normal.setValue(nullptr);
441
static_cast<SoVRMLIndexedFaceSet*>(geom)->creaseAngle.setValue(0.5f);
445
sa.reset(); // clear the internal cache
448
std::stringstream out;
449
writeX3D(vrmlRoot, exportViewpoints, out);
452
vrmlRoot->unref(); // release the memory as soon as possible
454
// restore old settings
455
vrml2.setOverrideMode(false);
456
vrml2.apply(noSwitches);
462
void Gui::SoFCDB::writeX3DFields(SoNode* node, std::map<SoNode*, std::string>& nodeMap,
463
bool isRoot, int& numDEF, int spaces, std::ostream& out)
465
// remove the VRML prefix from the type name
466
std::string type(node->getTypeId().getName().getString());
467
type = type.substr(4);
469
out << Base::blanks(spaces) << "<" << type;
470
if (node->getRefCount() > 1 && !isRoot) {
471
SbName name = node->getName();
472
std::stringstream str;
473
if (name.getLength() == 0)
474
str << "o" << numDEF++;
476
str << name.getString();
478
nodeMap[node] = str.str();
479
out << " DEF=\"" << str.str() << "\"";
482
const SoFieldData* fielddata = node->getFieldData();
484
int numFieldNodes = 0;
486
// process non-SoSFNode and non-SoMFNode fields
487
for (int i=0; i<fielddata->getNumFields(); i++) {
488
SoField* field = fielddata->getField(node, i);
489
if (!field->isDefault()) {
490
if (!field->isOfType(SoSFNode::getClassTypeId()) &&
491
!field->isOfType(SoMFNode::getClassTypeId())) {
494
QByteArray ba(value.getString(), value.getLength());
495
ba.replace('\n', ' ');
496
if (field->isOfType(SoMField::getClassTypeId())) {
497
ba.replace('[', ' ');
498
ba.replace(']', ' ');
499
ba = ba.simplified();
502
out << '\n' << Base::blanks(spaces+2) << fielddata->getFieldName(i).getString() << "=\"" << ba.data() << "\" ";
510
if (numFieldNodes > 0) {
517
// process SoSFNode or SoMFNode fields
518
for (int i=0; i<fielddata->getNumFields(); i++) {
519
SoField* field = fielddata->getField(node, i);
520
if (!field->isDefault()) {
521
if (field->isOfType(SoSFNode::getClassTypeId())) {
522
auto sfNode = static_cast<SoSFNode*>(field);
523
writeX3DChild(sfNode->getValue(), nodeMap, numDEF, spaces+2, out);
525
else if (field->isOfType(SoMFNode::getClassTypeId())) {
526
auto mfNode = static_cast<SoMFNode*>(field);
527
for (int j=0; j<mfNode->getNum(); j++) {
528
writeX3DChild(mfNode->getNode(j), nodeMap, numDEF, spaces+2, out);
534
if (numFieldNodes > 0) {
535
out << Base::blanks(spaces) << "</" << type << ">\n";
540
void Gui::SoFCDB::writeX3DChild(SoNode* node, std::map<SoNode*, std::string>& nodeMap,
541
int& numDEF, int spaces, std::ostream& out)
546
// check if the node is already used
547
auto mapIt = nodeMap.find(node);
548
if (mapIt == nodeMap.end()) {
549
writeX3DFields(node, nodeMap, false, numDEF, spaces, out);
552
// remove the VRML prefix from the type name
553
std::string sftype(node->getTypeId().getName().getString());
554
sftype = sftype.substr(4);
555
out << Base::blanks(spaces) << "<" << sftype << " USE=\"" << mapIt->second << "\" />\n";
559
void Gui::SoFCDB::writeX3D(SoVRMLGroup* node, bool exportViewpoints, std::ostream& out)
561
out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
562
out << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
563
out << "<X3D profile=\"Immersive\" version=\"3.2\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema-instance\" "
564
"xsd:noNamespaceSchemaLocation=\"http://www.web3d.org/specifications/x3d-3.2.xsd\" width=\"1280px\" height=\"1024px\">\n";
566
" <meta name=\"generator\" content=\"FreeCAD\"/>\n"
567
" <meta name=\"author\" content=\"\"/>\n"
568
" <meta name=\"company\" content=\"\"/>\n"
571
std::map<SoNode*, std::string> nodeMap;
574
// compute a sensible view point
575
SoGetBoundingBoxAction bboxAction(SbViewportRegion(1280, 1024));
576
bboxAction.apply(node);
577
SbBox3f bbox = bboxAction.getBoundingBox();
579
bs.circumscribe(bbox);
580
const SbVec3f& cnt = bs.getCenter();
581
float dist = 2.4f * bs.getRadius();
582
float dist3 = 0.577350f * dist; // sqrt(1/3) * dist
584
if (exportViewpoints) {
585
auto viewpoint = [&out](const char* text, const SbVec3f& cnt,
586
const SbVec3f& pos, const SbRotation& rot) {
587
SbVec3f axis; float angle;
588
rot.getValue(axis, angle);
589
out << " <Viewpoint id=\"" << text
590
<< "\" centerOfRotation=\"" << cnt[0] << " " << cnt[1] << " " << cnt[2]
591
<< "\" position=\"" << pos[0] << " " << pos[1] << " " << pos[2]
592
<< "\" orientation=\"" << axis[0] << " " << axis[1] << " " << axis[2] << " " << angle
593
<< R"(" description="camera" fieldOfView="0.9">)"
597
viewpoint("Iso", cnt, SbVec3f(cnt[0] + dist3, cnt[1] - dist3, cnt[2] + dist3), Camera::rotation(Camera::Isometric));
598
viewpoint("Front", cnt, SbVec3f(cnt[0], cnt[1] - dist, cnt[2]), Camera::rotation(Camera::Front));
599
viewpoint("Back", cnt, SbVec3f(cnt[0], cnt[1] + dist, cnt[2]), Camera::rotation(Camera::Rear));
600
viewpoint("Right", cnt, SbVec3f(cnt[0] + dist, cnt[1], cnt[2]), Camera::rotation(Camera::Right));
601
viewpoint("Left", cnt, SbVec3f(cnt[0] - dist, cnt[1], cnt[2]), Camera::rotation(Camera::Left));
602
viewpoint("Top", cnt, SbVec3f(cnt[0], cnt[1], cnt[2] + dist), Camera::rotation(Camera::Top));
603
viewpoint("Bottom", cnt, SbVec3f(cnt[0], cnt[1], cnt[2] - dist), Camera::rotation(Camera::Bottom));
607
writeX3DFields(node, nodeMap, true, numDEF, 4, out);
608
out << " </Scene>\n";
612
bool Gui::SoFCDB::writeToX3DOM(SoNode* node, std::string& buffer)
615
if (!writeToX3D(node, true, x3d))
618
// remove the first two lines from the x3d output as this duplicates
619
// the xml and doctype header
620
std::size_t pos = x3d.find('\n');
621
pos = x3d.find('\n', pos + 1);
622
x3d = x3d.erase(0, pos + 1);
624
std::stringstream out;
625
out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
626
<< "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
627
out << "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
629
<< " <script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script>\n"
630
<< " <link rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'></link>\n"
633
auto onclick = [&out](const char* text) {
634
out << " <button onclick=\"document.getElementById('" << text << "').setAttribute('set_bind','true');\">" << text << "</button>\n";
654
bool Gui::SoFCDB::writeToFile(SoNode* node, const char* filename, bool binary)
657
Base::FileInfo fi(filename);
660
if (fi.hasExtension({"wrl", "vrml", "wrz"})) {
661
// If 'wrz' is set then force compression
662
if (fi.hasExtension("wrz"))
665
ret = SoFCDB::writeToVRML(node, filename, binary);
667
else if (fi.hasExtension({"x3d", "x3dz"})) {
668
// If 'x3dz' is set then force compression
669
if (fi.hasExtension("x3dz"))
672
ret = SoFCDB::writeToX3D(node, filename, binary);
674
else if (fi.hasExtension("xhtml")) {
676
if (SoFCDB::writeToX3DOM(node, buffer)) {
677
Base::ofstream str(Base::FileInfo(filename), std::ios::out);
686
else if (fi.hasExtension("iv")) {
687
// Write Inventor in ASCII
688
std::string buffer = SoFCDB::writeNodesToString(node);
689
Base::ofstream str(Base::FileInfo(filename), std::ios::out);
701
SoGroup* Gui::SoFCDB::getStorage()
703
assert(storage); //call init first.