1425 lines
52 KiB
C++
1425 lines
52 KiB
C++
/****************************************************************************
|
|
* Copyright (c) 2018 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Library General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with this library; see the file COPYING.LIB. If not, *
|
|
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
|
* Suite 330, Boston, MA 02111-1307, USA *
|
|
* *
|
|
****************************************************************************/
|
|
|
|
#include "PreCompiled.h"
|
|
#if defined(__MINGW32__)
|
|
# define WNT // avoid conflict with GUID
|
|
#endif
|
|
#ifndef _PreComp_
|
|
# include <gp_Trsf.hxx>
|
|
# include <TopExp.hxx>
|
|
# include <TopExp_Explorer.hxx>
|
|
# include <Standard_Failure.hxx>
|
|
# include <Standard_Version.hxx>
|
|
# include <XCAFApp_Application.hxx>
|
|
# include <XCAFDoc_DocumentTool.hxx>
|
|
# include <XCAFDoc_ShapeTool.hxx>
|
|
# include <XCAFDoc_ColorTool.hxx>
|
|
# include <XCAFDoc_Location.hxx>
|
|
# include <XCAFDoc_GraphNode.hxx>
|
|
# include <TDF_Label.hxx>
|
|
# include <TDF_Tool.hxx>
|
|
# include <TDF_LabelSequence.hxx>
|
|
# include <TDF_ChildIterator.hxx>
|
|
# include <TDataStd_Name.hxx>
|
|
# include <Quantity_ColorRGBA.hxx>
|
|
# include <TopoDS_Iterator.hxx>
|
|
# include <Interface_Static.hxx>
|
|
# include <TDF_AttributeSequence.hxx>
|
|
# include <TopTools_MapOfShape.hxx>
|
|
#endif
|
|
|
|
#include <XCAFDoc_ShapeMapTool.hxx>
|
|
|
|
#include <boost/format.hpp>
|
|
#include <boost/regex.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <Base/Parameter.h>
|
|
#include <Base/Console.h>
|
|
#include <Base/FileInfo.h>
|
|
#include <App/Application.h>
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObjectPy.h>
|
|
#include <App/Part.h>
|
|
#include <App/Link.h>
|
|
#include <App/GroupExtension.h>
|
|
#include <Mod/Part/App/PartFeature.h>
|
|
#include <Mod/Part/App/FeatureCompound.h>
|
|
#include <Mod/Part/App/OCAF/ImportExportSettings.h>
|
|
#include <Mod/Part/App/ProgressIndicator.h>
|
|
#include <Mod/Part/App/ImportIges.h>
|
|
#include <Mod/Part/App/ImportStep.h>
|
|
#include <Mod/Part/App/Interface.h>
|
|
|
|
#include <App/DocumentObject.h>
|
|
#include <App/DocumentObjectGroup.h>
|
|
#include "ImportOCAF2.h"
|
|
|
|
#if OCC_VERSION_HEX >= 0x070500
|
|
// See https://dev.opencascade.org/content/occt-3d-viewer-becomes-srgb-aware
|
|
# define OCC_COLOR_SPACE Quantity_TOC_sRGB
|
|
#else
|
|
# define OCC_COLOR_SPACE Quantity_TOC_RGB
|
|
#endif
|
|
|
|
FC_LOG_LEVEL_INIT("Import",true,true)
|
|
|
|
using namespace Import;
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
static inline App::Color convertColor(const Quantity_ColorRGBA &c)
|
|
{
|
|
Standard_Real r, g, b;
|
|
c.GetRGB().Values(r, g, b, OCC_COLOR_SPACE);
|
|
return App::Color(static_cast<float>(r),
|
|
static_cast<float>(g),
|
|
static_cast<float>(b),
|
|
1.0f - static_cast<float>(c.Alpha()));
|
|
}
|
|
|
|
static inline Quantity_ColorRGBA convertColor(const App::Color &c)
|
|
{
|
|
return Quantity_ColorRGBA(Quantity_Color(c.r, c.g, c.b, OCC_COLOR_SPACE), 1.0f - c.a);
|
|
}
|
|
|
|
static inline std::ostream& operator<<(std::ostream& os, const Quantity_ColorRGBA &c) {
|
|
App::Color color = convertColor(c);
|
|
auto toHex = [](float v) {return boost::format("%02X") % static_cast<int>(v*255);};
|
|
return os << "#" << toHex(color.r) << toHex(color.g) << toHex(color.b) << toHex(color.a);
|
|
}
|
|
|
|
static std::string labelName(TDF_Label label) {
|
|
std::string txt;
|
|
Handle(TDataStd_Name) name;
|
|
if (!label.IsNull() && label.FindAttribute(TDataStd_Name::GetID(),name)) {
|
|
TCollection_ExtendedString extstr = name->Get();
|
|
char* str = new char[extstr.LengthOfCString()+1];
|
|
extstr.ToUTF8CString(str);
|
|
txt = str;
|
|
delete[] str;
|
|
boost::trim(txt);
|
|
}
|
|
return txt;
|
|
}
|
|
|
|
static void printLabel(TDF_Label label, Handle(XCAFDoc_ShapeTool) aShapeTool,
|
|
Handle(XCAFDoc_ColorTool) aColorTool, const char *msg = nullptr)
|
|
{
|
|
if(label.IsNull() || !FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
|
return;
|
|
if(!msg) msg = "Label: ";
|
|
TCollection_AsciiString entry;
|
|
TDF_Tool::Entry(label,entry);
|
|
std::ostringstream ss;
|
|
ss << msg << entry << ", " << labelName(label)
|
|
<< (aShapeTool->IsShape(label)?", shape":"")
|
|
<< (aShapeTool->IsTopLevel(label)?", topLevel":"")
|
|
<< (aShapeTool->IsFree(label)?", free":"")
|
|
<< (aShapeTool->IsAssembly(label)?", assembly":"")
|
|
<< (aShapeTool->IsSimpleShape(label)?", simple":"")
|
|
<< (aShapeTool->IsCompound(label)?", compound":"")
|
|
<< (aShapeTool->IsReference(label)?", reference":"")
|
|
<< (aShapeTool->IsComponent(label)?", component":"")
|
|
<< (aShapeTool->IsSubShape(label)?", subshape":"");
|
|
if(aShapeTool->IsSubShape(label)) {
|
|
auto shape = aShapeTool->GetShape(label);
|
|
if(!shape.IsNull())
|
|
ss << ", " << Part::TopoShape::shapeName(shape.ShapeType(),true);
|
|
}
|
|
if(aShapeTool->IsShape(label)) {
|
|
Quantity_ColorRGBA c;
|
|
if(aColorTool->GetColor(label,XCAFDoc_ColorGen,c))
|
|
ss << ", gc: " << c;
|
|
if(aColorTool->GetColor(label,XCAFDoc_ColorSurf,c))
|
|
ss << ", sc: " << c;
|
|
if(aColorTool->GetColor(label,XCAFDoc_ColorCurv,c))
|
|
ss << ", cc: " << c;
|
|
}
|
|
|
|
ss << std::endl;
|
|
Base::Console().NotifyLog(ss.str().c_str());
|
|
}
|
|
|
|
static void dumpLabels(TDF_Label label, Handle(XCAFDoc_ShapeTool) aShapeTool,
|
|
Handle(XCAFDoc_ColorTool) aColorTool, int depth=0)
|
|
{
|
|
std::string indent(depth*2,' ');
|
|
printLabel(label,aShapeTool,aColorTool,indent.c_str());
|
|
TDF_ChildIterator it;
|
|
for (it.Initialize(label); it.More(); it.Next())
|
|
dumpLabels(it.Value(),aShapeTool,aColorTool,depth+1);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
ImportOCAFOptions::ImportOCAFOptions()
|
|
{
|
|
defaultFaceColor.setPackedValue(0xCCCCCC00);
|
|
defaultFaceColor.a = 0;
|
|
|
|
defaultEdgeColor.setPackedValue(421075455UL);
|
|
defaultEdgeColor.a = 0;
|
|
}
|
|
|
|
ImportOCAF2::ImportOCAF2(Handle(TDocStd_Document) h, App::Document* d, const std::string& name)
|
|
: pDoc(h), pDocument(d), default_name(name), sequencer(nullptr)
|
|
{
|
|
aShapeTool = XCAFDoc_DocumentTool::ShapeTool (pDoc->Main());
|
|
aColorTool = XCAFDoc_DocumentTool::ColorTool(pDoc->Main());
|
|
|
|
if (d->isSaved()) {
|
|
Base::FileInfo fi(d->FileName.getValue());
|
|
filePath = fi.dirPath();
|
|
}
|
|
|
|
setUseLinkGroup(options.useLinkGroup);
|
|
}
|
|
|
|
ImportOCAF2::~ImportOCAF2()
|
|
{
|
|
}
|
|
|
|
ImportOCAFOptions ImportOCAF2::customImportOptions()
|
|
{
|
|
Part::OCAF::ImportExportSettings settings;
|
|
|
|
ImportOCAFOptions defaultOptions;
|
|
defaultOptions.merge = settings.getReadShapeCompoundMode();
|
|
defaultOptions.useLinkGroup = settings.getUseLinkGroup();
|
|
defaultOptions.useBaseName = settings.getUseBaseName();
|
|
defaultOptions.importHidden = settings.getImportHiddenObject();
|
|
defaultOptions.reduceObjects = settings.getReduceObjects();
|
|
defaultOptions.showProgress = settings.getShowProgress();
|
|
defaultOptions.expandCompound = settings.getExpandCompound();
|
|
defaultOptions.mode = static_cast<int>(settings.getImportMode());
|
|
|
|
auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
|
defaultOptions.defaultFaceColor.setPackedValue(hGrp->GetUnsigned("DefaultShapeColor", defaultOptions.defaultFaceColor.getPackedValue()));
|
|
defaultOptions.defaultFaceColor.a = 0;
|
|
|
|
defaultOptions.defaultEdgeColor.setPackedValue(hGrp->GetUnsigned("DefaultShapeLineColor", defaultOptions.defaultEdgeColor.getPackedValue()));
|
|
defaultOptions.defaultEdgeColor.a = 0;
|
|
|
|
return defaultOptions;
|
|
}
|
|
|
|
void ImportOCAF2::setImportOptions(ImportOCAFOptions opts)
|
|
{
|
|
options = opts;
|
|
setUseLinkGroup(options.useLinkGroup);
|
|
}
|
|
|
|
void ImportOCAF2::setUseLinkGroup(bool enable)
|
|
{
|
|
options.useLinkGroup = enable;
|
|
|
|
// Interface_Static::SetIVal("read.stepcaf.subshapes.name",1);
|
|
aShapeTool->SetAutoNaming(!enable);
|
|
}
|
|
|
|
void ImportOCAF2::setMode(int m)
|
|
{
|
|
if (m<0 || m >= ModeMax) {
|
|
FC_WARN("Invalid import mode " << m);
|
|
}
|
|
else {
|
|
options.mode = m;
|
|
}
|
|
|
|
if (options.mode != SingleDoc) {
|
|
if (pDocument->isSaved()) {
|
|
Base::FileInfo fi(pDocument->FileName.getValue());
|
|
filePath = fi.dirPath();
|
|
}
|
|
else {
|
|
FC_WARN("Disable multi-document mode because the input document is not saved.");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void setPlacement(App::PropertyPlacement *prop, const TopoDS_Shape &shape) {
|
|
prop->setValue(Base::Placement(
|
|
Part::TopoShape::convert(shape.Location().Transformation()))*prop->getValue());
|
|
}
|
|
|
|
std::string ImportOCAF2::getLabelName(TDF_Label label) {
|
|
std::string name;
|
|
if(label.IsNull())
|
|
return name;
|
|
if(!XCAFDoc_ShapeTool::IsReference(label))
|
|
return labelName(label);
|
|
if(!options.useBaseName)
|
|
name = labelName(label);
|
|
TDF_Label ref;
|
|
if(name.empty() && XCAFDoc_ShapeTool::GetReferredShape(label,ref))
|
|
name = labelName(ref);
|
|
return name;
|
|
}
|
|
|
|
void ImportOCAF2::setObjectName(Info &info, TDF_Label label) {
|
|
if(!info.obj)
|
|
return;
|
|
info.baseName = getLabelName(label);
|
|
if(!info.baseName.empty())
|
|
info.obj->Label.setValue(info.baseName.c_str());
|
|
else{
|
|
auto linked = info.obj->getLinkedObject(false);
|
|
if(!linked || linked==info.obj)
|
|
return;
|
|
info.obj->Label.setValue(linked->Label.getValue());
|
|
}
|
|
}
|
|
|
|
bool ImportOCAF2::getColor(const TopoDS_Shape &shape, Info &info, bool check, bool noDefault) {
|
|
bool ret = false;
|
|
Quantity_ColorRGBA aColor;
|
|
if(aColorTool->GetColor(shape, XCAFDoc_ColorSurf, aColor)) {
|
|
App::Color c = convertColor(aColor);
|
|
if(!check || info.faceColor!=c) {
|
|
info.faceColor = c;
|
|
info.hasFaceColor = true;
|
|
ret = true;
|
|
}
|
|
}
|
|
if(!noDefault && !info.hasFaceColor && aColorTool->GetColor(shape, XCAFDoc_ColorGen, aColor)) {
|
|
App::Color c = convertColor(aColor);
|
|
if(!check || info.faceColor!=c) {
|
|
info.faceColor = c;
|
|
info.hasFaceColor = true;
|
|
ret = true;
|
|
}
|
|
}
|
|
if(aColorTool->GetColor(shape, XCAFDoc_ColorCurv, aColor)) {
|
|
App::Color c = convertColor(aColor);
|
|
// Some STEP include a curve color with the same value of the face
|
|
// color. And this will look weird in FC. So for shape with face
|
|
// we'll ignore the curve color, if it is the same as the face color.
|
|
if((c!=info.faceColor || !TopExp_Explorer(shape,TopAbs_FACE).More()) &&
|
|
(!check || info.edgeColor!=c))
|
|
{
|
|
info.edgeColor = c;
|
|
info.hasEdgeColor = true;
|
|
ret = true;
|
|
}
|
|
}
|
|
if(!check) {
|
|
if(!info.hasFaceColor)
|
|
info.faceColor = options.defaultFaceColor;
|
|
if(!info.hasEdgeColor)
|
|
info.edgeColor = options.defaultEdgeColor;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
App::DocumentObject *ImportOCAF2::expandShape(
|
|
App::Document *doc, TDF_Label label, const TopoDS_Shape &shape)
|
|
{
|
|
if(shape.IsNull() || !TopExp_Explorer(shape,TopAbs_VERTEX).More())
|
|
return nullptr;
|
|
|
|
// When saved as compound, STEP file does not support instance sharing,
|
|
// meaning that even if the source compound may contain child shapes of
|
|
// shared instances, or multiple hierarchies, those information are lost
|
|
// when saved to STEP, everything become flat and duplicated. So the code
|
|
// below is not necessary.
|
|
#if 0
|
|
auto baseShape = shape.Located(TopLoc_Location());
|
|
auto it = myShapes.find(baseShape);
|
|
if(it!=myShapes.end()) {
|
|
auto link = static_cast<App::Link*>(doc->addObject("App::Link","Link"));
|
|
link->Visibility.setValue(false);
|
|
link->setLink(-1,it->second.obj);
|
|
setPlacement(&link->Placement,shape);
|
|
return link;
|
|
}
|
|
#endif
|
|
std::vector<App::DocumentObject*> objs;
|
|
|
|
if(shape.ShapeType() == TopAbs_COMPOUND) {
|
|
for(TopoDS_Iterator it(shape,0,0);it.More();it.Next()) {
|
|
TDF_Label childLabel;
|
|
if(!label.IsNull())
|
|
aShapeTool->FindSubShape(label,it.Value(),childLabel);
|
|
auto child = expandShape(doc,childLabel,it.Value());
|
|
if(child) {
|
|
objs.push_back(child);
|
|
Info info;
|
|
info.free = false;
|
|
info.obj = child;
|
|
myShapes.emplace(it.Value().Located(TopLoc_Location()),info);
|
|
}
|
|
}
|
|
if(objs.empty())
|
|
return nullptr;
|
|
auto compound = static_cast<Part::Compound2*>(doc->addObject("Part::Compound2","Compound"));
|
|
compound->Links.setValues(objs);
|
|
// compound->Visibility.setValue(false);
|
|
setPlacement(&compound->Placement,shape);
|
|
return compound;
|
|
}
|
|
Info info;
|
|
info.obj = nullptr;
|
|
createObject(doc,label,shape,info,false);
|
|
return info.obj;
|
|
}
|
|
|
|
bool ImportOCAF2::createObject(App::Document *doc, TDF_Label label,
|
|
const TopoDS_Shape &shape, Info &info, bool newDoc)
|
|
{
|
|
if(shape.IsNull() || !TopExp_Explorer(shape,TopAbs_VERTEX).More()) {
|
|
FC_WARN(labelName(label) << " has empty shape");
|
|
return false;
|
|
}
|
|
|
|
getColor(shape,info);
|
|
bool hasFaceColors = false;
|
|
bool hasEdgeColors = false;
|
|
|
|
Part::TopoShape tshape(shape);
|
|
std::vector<App::Color> faceColors;
|
|
std::vector<App::Color> edgeColors;
|
|
|
|
TDF_LabelSequence seq;
|
|
if(!label.IsNull() && aShapeTool->GetSubShapes(label,seq)) {
|
|
|
|
TopTools_IndexedMapOfShape faceMap,edgeMap;
|
|
TopExp::MapShapes(tshape.getShape(), TopAbs_FACE, faceMap);
|
|
TopExp::MapShapes(tshape.getShape(), TopAbs_EDGE, edgeMap);
|
|
|
|
faceColors.assign(faceMap.Extent(),info.faceColor);
|
|
edgeColors.assign(edgeMap.Extent(),info.edgeColor);
|
|
// Two passes to get sub shape colors. First pass, look for solid, and
|
|
// second pass look for face and edges. This allows lower level
|
|
// subshape to override color of higher level ones.
|
|
for(int j=0;j<2;++j) {
|
|
for(int i=1;i<=seq.Length();++i) {
|
|
TDF_Label l = seq.Value(i);
|
|
TopoDS_Shape subShape = aShapeTool->GetShape(l);
|
|
if(subShape.IsNull())
|
|
continue;
|
|
if(subShape.ShapeType()==TopAbs_FACE || subShape.ShapeType()==TopAbs_EDGE) {
|
|
if(j==0)
|
|
continue;
|
|
}else if(j!=0)
|
|
continue;
|
|
|
|
bool foundFaceColor=false,foundEdgeColor=false;
|
|
App::Color faceColor,edgeColor;
|
|
Quantity_ColorRGBA aColor;
|
|
if(aColorTool->GetColor(l, XCAFDoc_ColorSurf, aColor) ||
|
|
aColorTool->GetColor(l, XCAFDoc_ColorGen, aColor))
|
|
{
|
|
faceColor = convertColor(aColor);
|
|
foundFaceColor = true;
|
|
}
|
|
if(aColorTool->GetColor(l, XCAFDoc_ColorCurv, aColor)) {
|
|
edgeColor = convertColor(aColor);
|
|
foundEdgeColor = true;
|
|
if(j==0 && foundFaceColor && !faceColors.empty() && edgeColor==faceColor) {
|
|
// Do not set edge the same color as face
|
|
foundEdgeColor = false;
|
|
}
|
|
}
|
|
|
|
if(foundFaceColor) {
|
|
for(TopExp_Explorer exp(subShape,TopAbs_FACE);exp.More();exp.Next()) {
|
|
int idx = faceMap.FindIndex(exp.Current())-1;
|
|
if(idx>=0 && idx<(int)faceColors.size()) {
|
|
faceColors[idx] = faceColor;
|
|
hasFaceColors = true;
|
|
info.hasFaceColor = true;
|
|
}else
|
|
assert(0);
|
|
}
|
|
}
|
|
if(foundEdgeColor) {
|
|
for(TopExp_Explorer exp(subShape,TopAbs_EDGE);exp.More();exp.Next()) {
|
|
int idx = edgeMap.FindIndex(exp.Current())-1;
|
|
if(idx>=0 && idx<(int)edgeColors.size()) {
|
|
edgeColors[idx] = edgeColor;
|
|
hasEdgeColors = true;
|
|
info.hasEdgeColor = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Part::Feature *feature;
|
|
|
|
if(newDoc && (options.mode == ObjectPerDoc ||
|
|
options.mode == ObjectPerDir))
|
|
doc = getDocument(doc,label);
|
|
|
|
if(options.expandCompound &&
|
|
(tshape.countSubShapes(TopAbs_SOLID)>1 ||
|
|
(!tshape.countSubShapes(TopAbs_SOLID) && tshape.countSubShapes(TopAbs_SHELL)>1)))
|
|
{
|
|
feature = dynamic_cast<Part::Feature*>(expandShape(doc,label,shape));
|
|
assert(feature);
|
|
} else {
|
|
feature = static_cast<Part::Feature*>(doc->addObject("Part::Feature",tshape.shapeName().c_str()));
|
|
feature->Shape.setValue(shape);
|
|
// feature->Visibility.setValue(false);
|
|
}
|
|
applyFaceColors(feature,{info.faceColor});
|
|
applyEdgeColors(feature,{info.edgeColor});
|
|
if(hasFaceColors)
|
|
applyFaceColors(feature,faceColors);
|
|
if(hasEdgeColors)
|
|
applyEdgeColors(feature,edgeColors);
|
|
|
|
info.propPlacement = &feature->Placement;
|
|
info.obj = feature;
|
|
return true;
|
|
}
|
|
|
|
App::Document *ImportOCAF2::getDocument(App::Document *doc, TDF_Label label) {
|
|
if(filePath.empty() || options.mode==SingleDoc || options.merge)
|
|
return doc;
|
|
|
|
auto name = getLabelName(label);
|
|
if(name.empty())
|
|
return doc;
|
|
|
|
auto newDoc = App::GetApplication().newDocument(name.c_str(),name.c_str(),false);
|
|
std::ostringstream ss;
|
|
Base::FileInfo fi(doc->FileName.getValue());
|
|
std::string path = fi.dirPath();
|
|
if(options.mode == GroupPerDir || options.mode == ObjectPerDir) {
|
|
for(int i=0;i<1000;++i) {
|
|
ss.str("");
|
|
ss << path << '/' << fi.fileNamePure() << "_parts";
|
|
if(i>0)
|
|
ss << '_' << std::setfill('0') << std::setw(3) << i;
|
|
Base::FileInfo fi2(ss.str());
|
|
if(fi2.exists()) {
|
|
if(!fi2.isDir())
|
|
continue;
|
|
}else if(!fi2.createDirectory()) {
|
|
FC_WARN("Failed to create directory " << fi2.filePath());
|
|
break;
|
|
}
|
|
path = fi2.filePath();
|
|
break;
|
|
}
|
|
}
|
|
for(int i=0;i<1000;++i) {
|
|
ss.str("");
|
|
ss << path << '/' << newDoc->getName() << ".fcstd";
|
|
if(i>0)
|
|
ss << '_' << std::setfill('0') << std::setw(3) << i;
|
|
Base::FileInfo fi(ss.str());
|
|
if(!fi.exists()) {
|
|
if(!newDoc->saveAs(fi.filePath().c_str()))
|
|
break;
|
|
return newDoc;
|
|
}
|
|
}
|
|
|
|
FC_WARN("Cannot save document for part '" << name << "'");
|
|
return doc;
|
|
}
|
|
|
|
bool ImportOCAF2::createGroup(App::Document *doc, Info &info, const TopoDS_Shape &shape,
|
|
std::vector<App::DocumentObject*> &children,
|
|
const boost::dynamic_bitset<> &visibilities,
|
|
bool canReduce)
|
|
{
|
|
assert(children.size() == visibilities.size());
|
|
if(children.empty())
|
|
return false;
|
|
bool hasColor = getColor(shape,info,false,true);
|
|
if(canReduce && !hasColor && options.reduceObjects && children.size()==1 && visibilities[0]) {
|
|
info.obj = children.front();
|
|
info.free = true;
|
|
info.propPlacement = dynamic_cast<App::PropertyPlacement*>(info.obj->getPropertyByName("Placement"));
|
|
myCollapsedObjects.emplace(info.obj,info.propPlacement);
|
|
return true;
|
|
}
|
|
auto group = static_cast<App::LinkGroup*>(doc->addObject("App::LinkGroup","LinkGroup"));
|
|
for(auto &child : children) {
|
|
if(child->getDocument()!=doc) {
|
|
auto link = static_cast<App::Link*>(doc->addObject("App::Link","Link"));
|
|
link->Label.setValue(child->Label.getValue());
|
|
link->setLink(-1,child);
|
|
auto pla = Base::freecad_dynamic_cast<App::PropertyPlacement>(
|
|
child->getPropertyByName("Placement"));
|
|
if(pla)
|
|
link->Placement.setValue(pla->getValue());
|
|
child = link;
|
|
}
|
|
// child->Visibility.setValue(false);
|
|
}
|
|
// group->Visibility.setValue(false);
|
|
group->ElementList.setValues(children);
|
|
group->VisibilityList.setValue(visibilities);
|
|
info.obj = group;
|
|
info.propPlacement = &group->Placement;
|
|
if(getColor(shape,info,false,true)) {
|
|
if(info.hasFaceColor)
|
|
applyLinkColor(group,-1,info.faceColor);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
App::DocumentObject* ImportOCAF2::loadShapes()
|
|
{
|
|
if(!options.useLinkGroup) {
|
|
ImportLegacy legacy(*this);
|
|
legacy.setMerge(options.merge);
|
|
legacy.loadShapes();
|
|
return nullptr;
|
|
}
|
|
|
|
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
|
dumpLabels(pDoc->Main(),aShapeTool,aColorTool);
|
|
|
|
TDF_LabelSequence labels;
|
|
aShapeTool->GetShapes(labels);
|
|
Base::SequencerLauncher seq("Importing...",labels.Length());
|
|
FC_MSG("free shape count " << labels.Length());
|
|
sequencer = options.showProgress ? &seq : nullptr;
|
|
|
|
labels.Clear();
|
|
myShapes.clear();
|
|
myNames.clear();
|
|
myCollapsedObjects.clear();
|
|
|
|
std::vector<App::DocumentObject*> objs;
|
|
aShapeTool->GetFreeShapes (labels);
|
|
boost::dynamic_bitset<> vis;
|
|
int count = 0;
|
|
for (Standard_Integer i=1; i <= labels.Length(); i++ ) {
|
|
auto label = labels.Value(i);
|
|
if(!options.importHidden && !aColorTool->IsVisible(label))
|
|
continue;
|
|
++count;
|
|
}
|
|
for (Standard_Integer i=1; i <= labels.Length(); i++ ) {
|
|
auto label = labels.Value(i);
|
|
if(!options.importHidden && !aColorTool->IsVisible(label))
|
|
continue;
|
|
auto obj = loadShape(pDocument, label,
|
|
aShapeTool->GetShape(label), false, count>1);
|
|
if(obj) {
|
|
objs.push_back(obj);
|
|
vis.push_back(aColorTool->IsVisible(label));
|
|
}
|
|
}
|
|
App::DocumentObject *ret = nullptr;
|
|
if(objs.size()==1) {
|
|
ret = objs.front();
|
|
}else {
|
|
Info info;
|
|
if(createGroup(pDocument,info,TopoDS_Shape(),objs,vis))
|
|
ret = info.obj;
|
|
}
|
|
if(ret) {
|
|
// ret->Visibility.setValue(true);
|
|
ret->recomputeFeature(true);
|
|
}
|
|
if(options.merge && ret && !ret->isDerivedFrom(Part::Feature::getClassTypeId())) {
|
|
auto shape = Part::Feature::getTopoShape(ret);
|
|
auto feature = static_cast<Part::Feature*>(
|
|
pDocument->addObject("Part::Feature", "Feature"));
|
|
auto name = labelName(pDoc->Main());
|
|
feature->Label.setValue(name.empty()?default_name.c_str():name.c_str());
|
|
feature->Shape.setValue(shape);
|
|
applyFaceColors(feature,{});
|
|
|
|
std::vector<std::pair<App::Document*,std::string> > objNames;
|
|
for(auto obj : App::Document::getDependencyList(objs,App::Document::DepSort))
|
|
objNames.emplace_back(obj->getDocument(),obj->getNameInDocument());
|
|
for(auto &v : objNames)
|
|
v.first->removeObject(v.second.c_str());
|
|
ret = feature;
|
|
ret->recomputeFeature(true);
|
|
}
|
|
sequencer = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
void ImportOCAF2::getSHUOColors(TDF_Label label,
|
|
std::map<std::string,App::Color> &colors, bool appendFirst)
|
|
{
|
|
TDF_AttributeSequence seq;
|
|
if(label.IsNull() || !aShapeTool->GetAllComponentSHUO(label,seq))
|
|
return;
|
|
std::ostringstream ss;
|
|
for(int i=1;i<=seq.Length();++i) {
|
|
Handle(XCAFDoc_GraphNode) shuo = Handle(XCAFDoc_GraphNode)::DownCast(seq.Value(i));
|
|
if(shuo.IsNull())
|
|
continue;
|
|
|
|
TDF_Label slabel = shuo->Label();
|
|
|
|
// We only want to process the main shuo, i.e. those without upper_usage
|
|
TDF_LabelSequence uppers;
|
|
aShapeTool->GetSHUOUpperUsage(slabel, uppers);
|
|
if(uppers.Length())
|
|
continue;
|
|
|
|
// appendFirst tells us whether we shall append the object name of the first label
|
|
bool skipFirst = !appendFirst;
|
|
ss.str("");
|
|
while(1) {
|
|
if(skipFirst)
|
|
skipFirst = false;
|
|
else {
|
|
TDF_Label l = shuo->Label().Father();
|
|
auto it = myNames.find(l);
|
|
if(it == myNames.end()) {
|
|
FC_WARN("Failed to find object of label " << labelName(l));
|
|
ss.str("");
|
|
break;
|
|
}
|
|
if(!it->second.empty())
|
|
ss << it->second << '.';
|
|
}
|
|
if(!shuo->NbChildren())
|
|
break;
|
|
shuo = shuo->GetChild(1);
|
|
}
|
|
std::string subname = ss.str();
|
|
if(subname.empty())
|
|
continue;
|
|
if(!aColorTool->IsVisible(slabel)) {
|
|
subname += App::DocumentObject::hiddenMarker();
|
|
colors.emplace(subname,App::Color());
|
|
} else {
|
|
Quantity_ColorRGBA aColor;
|
|
if(aColorTool->GetColor(slabel, XCAFDoc_ColorSurf, aColor) ||
|
|
aColorTool->GetColor(slabel, XCAFDoc_ColorGen, aColor))
|
|
{
|
|
colors.emplace(subname,convertColor(aColor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
App::DocumentObject *ImportOCAF2::loadShape(App::Document *doc,
|
|
TDF_Label label, const TopoDS_Shape &shape, bool baseOnly, bool newDoc)
|
|
{
|
|
if(shape.IsNull())
|
|
return nullptr;
|
|
|
|
auto baseShape = shape.Located(TopLoc_Location());
|
|
auto it = myShapes.find(baseShape);
|
|
if(it == myShapes.end()) {
|
|
Info info;
|
|
auto baseLabel = aShapeTool->FindShape(baseShape);
|
|
if(sequencer && !baseLabel.IsNull() && aShapeTool->IsTopLevel(baseLabel))
|
|
sequencer->next(true);
|
|
bool res;
|
|
if(baseLabel.IsNull() || !aShapeTool->IsAssembly(baseLabel))
|
|
res = createObject(doc,baseLabel,baseShape,info,newDoc);
|
|
else
|
|
res = createAssembly(doc,baseLabel,baseShape,info,newDoc);
|
|
if(!res)
|
|
return nullptr;
|
|
setObjectName(info,baseLabel);
|
|
it = myShapes.emplace(baseShape,info).first;
|
|
}
|
|
if(baseOnly)
|
|
return it->second.obj;
|
|
|
|
std::map<std::string,App::Color> shuoColors;
|
|
if(!options.useLinkGroup)
|
|
getSHUOColors(label,shuoColors,false);
|
|
|
|
auto info = it->second;
|
|
getColor(shape,info,true);
|
|
|
|
if(shuoColors.empty() && info.free && doc==info.obj->getDocument()) {
|
|
it->second.free = false;
|
|
auto name = getLabelName(label);
|
|
if(info.faceColor!=it->second.faceColor ||
|
|
info.edgeColor!=it->second.edgeColor ||
|
|
(!name.empty() && !info.baseName.empty() && name!=info.baseName))
|
|
{
|
|
auto compound = static_cast<Part::Compound2*>(doc->addObject("Part::Compound2","Compound"));
|
|
compound->Links.setValue(info.obj);
|
|
// compound->Visibility.setValue(false);
|
|
info.propPlacement = &compound->Placement;
|
|
if(info.faceColor!=it->second.faceColor)
|
|
applyFaceColors(compound,{info.faceColor});
|
|
if(info.edgeColor!=it->second.edgeColor)
|
|
applyEdgeColors(compound,{info.edgeColor});
|
|
info.obj = compound;
|
|
setObjectName(info,label);
|
|
}
|
|
setPlacement(info.propPlacement,shape);
|
|
myNames.emplace(label,info.obj->getNameInDocument());
|
|
return info.obj;
|
|
}
|
|
|
|
auto link = static_cast<App::Link*>(doc->addObject("App::Link","Link"));
|
|
// link->Visibility.setValue(false);
|
|
link->setLink(-1,info.obj);
|
|
setPlacement(&link->Placement,shape);
|
|
info.obj = link;
|
|
setObjectName(info,label);
|
|
if(info.faceColor!=it->second.faceColor)
|
|
applyLinkColor(link,-1,info.faceColor);
|
|
|
|
myNames.emplace(label,link->getNameInDocument());
|
|
if(!shuoColors.empty())
|
|
applyElementColors(link,shuoColors);
|
|
return link;
|
|
}
|
|
|
|
struct ChildInfo {
|
|
std::vector<Base::Placement> plas;
|
|
boost::dynamic_bitset<> vis;
|
|
std::map<size_t,App::Color> colors;
|
|
std::vector<TDF_Label> labels;
|
|
TopoDS_Shape shape;
|
|
};
|
|
|
|
bool ImportOCAF2::createAssembly(App::Document *_doc,
|
|
TDF_Label label, const TopoDS_Shape &shape, Info &info, bool newDoc)
|
|
{
|
|
(void)label;
|
|
|
|
std::vector<App::DocumentObject*> children;
|
|
std::map<App::DocumentObject*, ChildInfo> childrenMap;
|
|
boost::dynamic_bitset<> visibilities;
|
|
std::map<std::string,App::Color> shuoColors;
|
|
|
|
auto doc = _doc;
|
|
if(newDoc)
|
|
doc = getDocument(_doc,label);
|
|
|
|
for(TopoDS_Iterator it(shape,0,0);it.More();it.Next()) {
|
|
TopoDS_Shape childShape = it.Value();
|
|
if(childShape.IsNull())
|
|
continue;
|
|
TDF_Label childLabel;
|
|
aShapeTool->Search(childShape,childLabel,Standard_True,Standard_True,Standard_False);
|
|
if(!childLabel.IsNull() && !options.importHidden && !aColorTool->IsVisible(childLabel))
|
|
continue;
|
|
auto obj = loadShape(doc, childLabel, childShape, options.reduceObjects);
|
|
if(!obj)
|
|
continue;
|
|
bool vis = true;
|
|
if(!childLabel.IsNull() && aShapeTool->IsComponent(childLabel))
|
|
vis = aColorTool->IsVisible(childLabel);
|
|
if(!options.reduceObjects) {
|
|
visibilities.push_back(vis);
|
|
children.push_back(obj);
|
|
getSHUOColors(childLabel,shuoColors,true);
|
|
continue;
|
|
}
|
|
|
|
auto &childInfo = childrenMap[obj];
|
|
if (childInfo.plas.empty()) {
|
|
children.push_back(obj);
|
|
visibilities.push_back(vis);
|
|
childInfo.shape = childShape;
|
|
}
|
|
|
|
childInfo.vis.push_back(vis);
|
|
childInfo.labels.push_back(childLabel);
|
|
childInfo.plas.emplace_back(Part::TopoShape::convert(childShape.Location().Transformation()));
|
|
Quantity_ColorRGBA aColor;
|
|
if (aColorTool->GetColor(childShape, XCAFDoc_ColorSurf, aColor)) {
|
|
childInfo.colors[childInfo.plas.size()-1] = convertColor(aColor);
|
|
}
|
|
}
|
|
assert(visibilities.size() == children.size());
|
|
|
|
if(children.empty()) {
|
|
if(doc!=_doc)
|
|
App::GetApplication().closeDocument(doc->getName());
|
|
return false;
|
|
}
|
|
|
|
if(options.reduceObjects) {
|
|
int i=-1;
|
|
for(auto &child : children) {
|
|
++i;
|
|
auto &childInfo = childrenMap[child];
|
|
if (childInfo.plas.size() == 1) {
|
|
child = loadShape(doc, childInfo.labels.front(), childInfo.shape);
|
|
getSHUOColors(childInfo.labels.front(), shuoColors, true);
|
|
continue;
|
|
}
|
|
|
|
visibilities[i] = true;
|
|
|
|
// Okay, we are creating a link array
|
|
auto link = static_cast<App::Link*>(doc->addObject("App::Link","Link"));
|
|
// link->Visibility.setValue(false);
|
|
link->setLink(-1,child);
|
|
link->ShowElement.setValue(false);
|
|
link->ElementCount.setValue(childInfo.plas.size());
|
|
auto it = myCollapsedObjects.find(child);
|
|
if(it!=myCollapsedObjects.end()) {
|
|
// child is a single component assembly that has been
|
|
// collapsed, so we have to honour its placement
|
|
for(auto &pla : childInfo.plas)
|
|
pla *= it->second->getValue();
|
|
}
|
|
link->PlacementList.setValue(childInfo.plas);
|
|
link->VisibilityList.setValue(childInfo.vis);
|
|
|
|
for(auto &v : childInfo.colors)
|
|
applyLinkColor(link,v.first,v.second);
|
|
|
|
int i=0;
|
|
std::string name = link->getNameInDocument();
|
|
name += '.';
|
|
for(auto childLabel : childInfo.labels) {
|
|
myNames.emplace(childLabel,name + std::to_string(i++));
|
|
getSHUOColors(childLabel,shuoColors,true);
|
|
}
|
|
|
|
child = link;
|
|
Info objInfo;
|
|
objInfo.obj = child;
|
|
setObjectName(objInfo, childInfo.labels.front());
|
|
}
|
|
}
|
|
|
|
if(children.empty())
|
|
return false;
|
|
|
|
if(!createGroup(doc,info,shape,children,visibilities,shuoColors.empty()))
|
|
return false;
|
|
if(!shuoColors.empty())
|
|
applyElementColors(info.obj,shuoColors);
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
ExportOCAFOptions::ExportOCAFOptions()
|
|
{
|
|
defaultColor.setPackedValue(0xCCCCCC00);
|
|
defaultColor.a = 0;
|
|
}
|
|
|
|
ExportOCAF2::ExportOCAF2(Handle(TDocStd_Document) h, GetShapeColorsFunc func)
|
|
: pDoc(h) , getShapeColors(func)
|
|
{
|
|
aShapeTool = XCAFDoc_DocumentTool::ShapeTool(pDoc->Main());
|
|
aColorTool = XCAFDoc_DocumentTool::ColorTool(pDoc->Main());
|
|
|
|
Part::Interface::writeStepAssembly(Part::Interface::Assembly::Auto);
|
|
}
|
|
|
|
/*!
|
|
* \brief ExportOCAF2::customExportOptions
|
|
* \return options from user settings
|
|
*/
|
|
ExportOCAFOptions ExportOCAF2::customExportOptions()
|
|
{
|
|
Part::OCAF::ImportExportSettings settings;
|
|
|
|
ExportOCAFOptions defaultOptions;
|
|
defaultOptions.exportHidden = settings.getExportHiddenObject();
|
|
defaultOptions.keepPlacement = settings.getExportKeepPlacement();
|
|
|
|
auto handle = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
|
defaultOptions.defaultColor.setPackedValue(handle->GetUnsigned("DefaultShapeColor", defaultOptions.defaultColor.getPackedValue()));
|
|
defaultOptions.defaultColor.a = 0;
|
|
|
|
return defaultOptions;
|
|
}
|
|
|
|
void ExportOCAF2::setName(TDF_Label label, App::DocumentObject *obj, const char *name) {
|
|
if(!name) {
|
|
if(!obj)
|
|
return;
|
|
name = obj->Label.getValue();
|
|
}
|
|
TDataStd_Name::Set(label, TCollection_ExtendedString(name, 1));
|
|
}
|
|
|
|
// Similar to XCAFDoc_ShapeTool::FindSHUO but return only main SHUO, i.e. SHUO
|
|
// with no upper_usage. It should not be necessary if we strictly export from
|
|
// bottom up, but let's make sure of it.
|
|
static Standard_Boolean FindSHUO (const TDF_LabelSequence& theLabels,
|
|
Handle(XCAFDoc_GraphNode)& theSHUOAttr)
|
|
{
|
|
assert(theLabels.Length()>1);
|
|
theSHUOAttr.Nullify();
|
|
TDF_AttributeSequence SHUOAttrs;
|
|
TDF_Label aCompLabel = theLabels.Value(1);
|
|
if (! ::XCAFDoc_ShapeTool::GetAllComponentSHUO( aCompLabel, SHUOAttrs ) )
|
|
return Standard_False;
|
|
for (Standard_Integer i = 1; i <= SHUOAttrs.Length(); i++) {
|
|
Handle(XCAFDoc_GraphNode) anSHUO = Handle(XCAFDoc_GraphNode)::DownCast(SHUOAttrs.Value(i));
|
|
TDF_LabelSequence aUpLabels;
|
|
// check for any upper_usage
|
|
::XCAFDoc_ShapeTool::GetSHUOUpperUsage( anSHUO->Label(), aUpLabels );
|
|
if ( aUpLabels.Length() > 0 )
|
|
continue; // reject if there is one
|
|
int j=2;
|
|
for ( ; anSHUO->NbChildren() ; ++j ) {
|
|
if ( j>theLabels.Length() ) {
|
|
j=0;
|
|
break;
|
|
}
|
|
anSHUO = anSHUO->GetChild( 1 );
|
|
if ( theLabels.Value(j)!=anSHUO->Label().Father() ) {
|
|
j=0;
|
|
break;
|
|
}
|
|
}
|
|
if( j!=theLabels.Length()+1 )
|
|
continue;
|
|
|
|
theSHUOAttr = Handle(XCAFDoc_GraphNode)::DownCast(SHUOAttrs.Value(i));
|
|
break;
|
|
}
|
|
return ( !theSHUOAttr.IsNull() );
|
|
}
|
|
|
|
TDF_Label ExportOCAF2::findComponent(const char *subname, TDF_Label label, TDF_LabelSequence &labels) {
|
|
const char *dot = strchr(subname,'.');
|
|
if(!dot) {
|
|
if(labels.Length()==1)
|
|
return labels.Value(1);
|
|
Handle(XCAFDoc_GraphNode) ret;
|
|
if(labels.Length() && (FindSHUO(labels,ret) || aShapeTool->SetSHUO(labels,ret)))
|
|
return ret->Label();
|
|
return TDF_Label();
|
|
}
|
|
TDF_LabelSequence components;
|
|
TDF_Label ref;
|
|
if(!aShapeTool->GetReferredShape(label,ref))
|
|
ref = label;
|
|
if(aShapeTool->GetComponents(ref,components)) {
|
|
for(int i=1;i<=components.Length();++i) {
|
|
auto component = components.Value(i);
|
|
if(std::isdigit((int)subname[0])) {
|
|
auto n = std::to_string(i-1)+".";
|
|
if(boost::starts_with(subname,n)) {
|
|
labels.Append(component);
|
|
return findComponent(subname+n.size(),component,labels);
|
|
}
|
|
}
|
|
auto it = myNames.find(component);
|
|
if(it == myNames.end())
|
|
continue;
|
|
for(auto &n : it->second) {
|
|
if(boost::starts_with(subname,n)) {
|
|
labels.Append(component);
|
|
return findComponent(subname+n.size(),component,labels);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TDF_Label();
|
|
}
|
|
|
|
void ExportOCAF2::setupObject(TDF_Label label, App::DocumentObject *obj,
|
|
const Part::TopoShape &shape, const std::string &prefix, const char *name, bool force)
|
|
{
|
|
setName(label,obj,name);
|
|
if(aShapeTool->IsComponent(label)) {
|
|
auto &names = myNames[label];
|
|
// The subname reference may contain several possible namings.
|
|
if(!name) {
|
|
// simple object internal name
|
|
names.push_back(prefix+obj->getNameInDocument()+".");
|
|
} else {
|
|
// name is not NULL in case this is a collapsed link array element.
|
|
// Collapsed means that the element is not an actual object, and
|
|
// 'obj' here is actually the parent. The given 'name' is in fact
|
|
// the element index
|
|
names.push_back(prefix + name + ".");
|
|
// In case the subname reference is created when the link array is
|
|
// previously expanded, the element object will be named as the
|
|
// parent object internal name + '_i<index>'
|
|
names.push_back(prefix + obj->getNameInDocument() + "_i" + name + ".");
|
|
}
|
|
// Finally, the subname reference allows to use the label for naming
|
|
// with preceding '$'
|
|
names.push_back(prefix + "$" + obj->Label.getValue() + ".");
|
|
}
|
|
|
|
if(!getShapeColors || (!force && !mySetups.emplace(obj,name?name:"").second))
|
|
return;
|
|
|
|
std::map<std::string, std::map<std::string,App::Color> > colors;
|
|
static std::string marker(App::DocumentObject::hiddenMarker()+"*");
|
|
static std::array<const char *,3> keys = {"Face*","Edge*",marker.c_str()};
|
|
std::string childName;
|
|
if(name) {
|
|
childName = name;
|
|
childName += '.';
|
|
}
|
|
for(auto key : keys) {
|
|
for(auto &v : getShapeColors(obj,key)) {
|
|
const char *subname = v.first.c_str();
|
|
if(name) {
|
|
if(!boost::starts_with(v.first,childName))
|
|
continue;
|
|
subname += childName.size();
|
|
}
|
|
const char *dot = strrchr(subname,'.');
|
|
if(!dot)
|
|
colors[""].emplace(subname,v.second);
|
|
else {
|
|
++dot;
|
|
colors[std::string(subname,dot-subname)].emplace(dot,v.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool warned = false;
|
|
|
|
for(auto &v : colors) {
|
|
TDF_Label nodeLabel = label;
|
|
if(!v.first.empty()) {
|
|
TDF_LabelSequence labels;
|
|
if(aShapeTool->IsComponent(label))
|
|
labels.Append(label);
|
|
nodeLabel = findComponent(v.first.c_str(),label,labels);
|
|
if(nodeLabel.IsNull()) {
|
|
FC_WARN("Failed to find component " << v.first);
|
|
continue;
|
|
}
|
|
}
|
|
for(auto &vv : v.second) {
|
|
if(vv.first == App::DocumentObject::hiddenMarker()) {
|
|
aColorTool->SetVisibility(nodeLabel,Standard_False);
|
|
continue;
|
|
}
|
|
const App::Color& c = vv.second;
|
|
Quantity_ColorRGBA color = convertColor(c);
|
|
auto colorType = vv.first[0]=='F'?XCAFDoc_ColorSurf:XCAFDoc_ColorCurv;
|
|
if(vv.first=="Face" || vv.first=="Edge") {
|
|
aColorTool->SetColor(nodeLabel, color, colorType);
|
|
continue;
|
|
}
|
|
|
|
if(nodeLabel!=label || aShapeTool->IsComponent(label)) {
|
|
// OCCT 7 seems to only support "Recommended practices for
|
|
// model styling and organization" version 1.2
|
|
// (https://www.cax-if.org/documents/rec_prac_styling_org_v12.pdf).
|
|
// The SHUO described in section 5.3 does not mention the
|
|
// capability of overriding context-depdendent element color,
|
|
// only whole shape color. Newer version of the same document
|
|
// (https://www.cax-if.org/documents/rec_prac_styling_org_v15.pdf)
|
|
// does support this, in section 5.1.
|
|
//
|
|
// The above observation is confirmed by further inspection of
|
|
// OCCT code, XCAFDoc_ShapeTool.cxx and STEPCAFControl_Writer.cxx.
|
|
if(!warned) {
|
|
warned = true;
|
|
FC_WARN("Current OCCT does not support element color override, for object "
|
|
<< obj->getFullName());
|
|
}
|
|
// continue;
|
|
}
|
|
|
|
auto subShape = shape.getSubShape(vv.first.c_str(),true);
|
|
if(subShape.IsNull()) {
|
|
FC_WARN("Failed to get subshape " << vv.first);
|
|
continue;
|
|
}
|
|
|
|
// The following code is copied from OCCT 7.3 and is a work around
|
|
// a bug in previous versions
|
|
Handle(XCAFDoc_ShapeMapTool) A;
|
|
if (!nodeLabel.FindAttribute(XCAFDoc_ShapeMapTool::GetID(), A)) {
|
|
TopoDS_Shape aShape = aShapeTool->GetShape(nodeLabel);
|
|
if (!aShape.IsNull()) {
|
|
A = XCAFDoc_ShapeMapTool::Set(nodeLabel);
|
|
A->SetShape(aShape);
|
|
}
|
|
}
|
|
|
|
TDF_Label subLabel = aShapeTool->AddSubShape(nodeLabel, subShape);
|
|
if(subLabel.IsNull()) {
|
|
FC_WARN("Failed to add subshape " << vv.first);
|
|
continue;
|
|
}
|
|
aColorTool->SetColor(subLabel, color, colorType);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExportOCAF2::exportObjects(std::vector<App::DocumentObject*> &objs, const char *name) {
|
|
if(objs.empty())
|
|
return;
|
|
myObjects.clear();
|
|
myNames.clear();
|
|
mySetups.clear();
|
|
if(objs.size()==1)
|
|
exportObject(objs.front(),nullptr,TDF_Label());
|
|
else {
|
|
auto label = aShapeTool->NewShape();
|
|
App::Document *doc = nullptr;
|
|
bool sameDoc = true;
|
|
for(auto obj : objs) {
|
|
if(doc)
|
|
sameDoc = sameDoc && doc==obj->getDocument();
|
|
else
|
|
doc = obj->getDocument();
|
|
exportObject(obj,nullptr,label);
|
|
}
|
|
|
|
if(!name && doc && sameDoc)
|
|
name = doc->getName();
|
|
setName(label,nullptr,name);
|
|
}
|
|
|
|
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
|
dumpLabels(pDoc->Main(),aShapeTool,aColorTool);
|
|
|
|
#if OCC_VERSION_HEX >= 0x070200
|
|
// Update is not performed automatically anymore: https://tracker.dev.opencascade.org/view.php?id=28055
|
|
aShapeTool->UpdateAssemblies();
|
|
#endif
|
|
}
|
|
|
|
TDF_Label ExportOCAF2::exportObject(App::DocumentObject* parentObj,
|
|
const char *sub, TDF_Label parent, const char *name)
|
|
{
|
|
App::DocumentObject *obj;
|
|
auto shape = Part::Feature::getTopoShape(parentObj,sub,false,nullptr,&obj,false,!sub);
|
|
if(!obj || shape.isNull()) {
|
|
if (obj)
|
|
FC_WARN(obj->getFullName() << " has null shape");
|
|
return TDF_Label();
|
|
}
|
|
|
|
//sub may contain more than one hierarchy, e.g. Assembly container may use
|
|
//getSubObjects to skip some hierarchy containing constraints and stuff
|
|
//when exporting. We search for extra '.', and set it as prefix if found.
|
|
//When setting SHUO's, we'll need this prefix for matching.
|
|
std::string prefix;
|
|
if(sub) {
|
|
auto len = strlen(sub);
|
|
if(len>1) {
|
|
--len;
|
|
// The prefix ends with the second last '.', so search for it.
|
|
for(int i=0;len!=0;--len) {
|
|
if(sub[len]=='.' && ++i==2) {
|
|
prefix = std::string(sub,len+1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TDF_Label label;
|
|
std::vector<App::DocumentObject *> links;
|
|
|
|
int depth = 0;
|
|
auto linked = obj;
|
|
auto linkedShape = shape;
|
|
while(1) {
|
|
auto s = Part::Feature::getTopoShape(linked);
|
|
if(s.isNull() || !s.getShape().IsPartner(shape.getShape()))
|
|
break;
|
|
linkedShape = s;
|
|
// Search using our own cache. We can't rely on ShapeTool::FindShape()
|
|
// in case this is an assembly. Because FindShape() search among its
|
|
// own computed shape, i.e. its own created compound, and thus will
|
|
// never match ours.
|
|
auto it = myObjects.find(linked);
|
|
if(it != myObjects.end()) {
|
|
for(auto l : links)
|
|
myObjects.emplace(l,it->second);
|
|
// Note: OCAF does not seem to support reference of references. We
|
|
// have to flaten all multi-level link without scales. In other
|
|
// word, all link will all be forced to refer to the same
|
|
// non-located shape
|
|
|
|
// retrieve OCAF computed shape, in case the current object returns
|
|
// a new shape every time Part::Feature::getTopoShape() is called.
|
|
auto baseShape = aShapeTool->GetShape(it->second);
|
|
shape.setShape(baseShape.Located(shape.getShape().Location()));
|
|
if(!parent.IsNull())
|
|
label = aShapeTool->AddComponent(parent,shape.getShape(),Standard_False);
|
|
else
|
|
label = aShapeTool->AddShape(shape.getShape(),Standard_False,Standard_False);
|
|
setupObject(label,name?parentObj:obj,shape,prefix,name);
|
|
return label;
|
|
}
|
|
auto next = linked->getLinkedObject(false,nullptr,false,depth++);
|
|
if(!next || linked==next)
|
|
break;
|
|
linked = next;
|
|
links.push_back(linked);
|
|
}
|
|
|
|
auto subs = obj->getSubObjects();
|
|
// subs empty means obj is not a container.
|
|
if(subs.empty()) {
|
|
|
|
if(!parent.IsNull()) {
|
|
// Search for non-located shape to see if we've stored the original shape before
|
|
if(!aShapeTool->FindShape(shape.getShape(),label)) {
|
|
auto baseShape = linkedShape;
|
|
auto linked = links.empty()?obj:links.back();
|
|
baseShape.setShape(baseShape.getShape().Located(TopLoc_Location()));
|
|
label = aShapeTool->NewShape();
|
|
aShapeTool->SetShape(label,baseShape.getShape());
|
|
setupObject(label,linked,baseShape,prefix);
|
|
}
|
|
|
|
label = aShapeTool->AddComponent(parent,shape.getShape(),Standard_False);
|
|
setupObject(label,name?parentObj:obj,shape,prefix,name);
|
|
|
|
}else{
|
|
// Here means we are exporting a single non-assembly object. We must
|
|
// not call setupObject() on a non-located baseshape like above,
|
|
// because OCCT does not respect shape style sharing when not
|
|
// exporting assembly
|
|
if(!options.keepPlacement || shape.getPlacement() == Base::Placement())
|
|
shape.setShape(shape.getShape().Located(TopLoc_Location()));
|
|
else {
|
|
Base::Matrix4D mat = shape.getTransform();
|
|
shape.setShape(shape.getShape().Located(TopLoc_Location()));
|
|
// Transform with copy to conceal the transformation
|
|
shape.transformShape(mat, true);
|
|
// Even if the shape has no transformation, TopoShape still sets
|
|
// a TopLoc_Location, so we need to clear it again.
|
|
shape.setShape(shape.getShape().Located(TopLoc_Location()));
|
|
}
|
|
label = aShapeTool->AddShape(shape.getShape(),Standard_False, Standard_False);
|
|
auto o = name?parentObj:obj;
|
|
if(o!=linked)
|
|
setupObject(label,linked,shape,prefix,nullptr,true);
|
|
setupObject(label,o,shape,prefix,name,true);
|
|
}
|
|
|
|
myObjects.emplace(obj, label);
|
|
for(auto link : links)
|
|
myObjects.emplace(link, label);
|
|
return label;
|
|
}
|
|
|
|
if(obj->getExtensionByType<App::LinkBaseExtension>(true)
|
|
|| obj->getExtensionByType<App::GeoFeatureGroupExtension>(true))
|
|
groupLinks.push_back(obj);
|
|
|
|
// Create a new assembly
|
|
label = aShapeTool->NewShape();
|
|
|
|
// check for link array
|
|
auto linkArray = obj->getLinkedObject(true)->getExtensionByType<App::LinkBaseExtension>(true);
|
|
if(linkArray && (linkArray->getShowElementValue() || !linkArray->getElementCountValue()))
|
|
linkArray = nullptr;
|
|
for(auto &subobj : subs) {
|
|
App::DocumentObject *parentGrp = nullptr;
|
|
std::string childName;
|
|
auto sobj = obj->resolve(subobj.c_str(),&parentGrp,&childName);
|
|
if(!sobj) {
|
|
FC_WARN("Cannot find object " << obj->getFullName() << '.' << subobj);
|
|
continue;
|
|
}
|
|
int vis = -1;
|
|
if(parentGrp) {
|
|
if(!groupLinks.empty()
|
|
&& parentGrp->getExtensionByType<App::GroupExtension>(true,false))
|
|
{
|
|
vis = groupLinks.back()->isElementVisible(childName.c_str());
|
|
}else
|
|
vis = parentGrp->isElementVisible(childName.c_str());
|
|
}
|
|
|
|
if(vis < 0)
|
|
vis = sobj->Visibility.getValue()?1:0;
|
|
|
|
if(!vis && !options.exportHidden)
|
|
continue;
|
|
|
|
TDF_Label childLabel = exportObject(obj,subobj.c_str(),label,linkArray?childName.c_str():nullptr);
|
|
if(childLabel.IsNull())
|
|
continue;
|
|
|
|
if(!vis) {
|
|
// Work around OCCT bug. If no color setting here, it will crash.
|
|
// The culprit is at STEPCAFControl_Writer::1093 as shown below
|
|
//
|
|
// surfColor = Styles.EncodeColor(Quantity_Color(1,1,1,OCC_COLOR_SPACE),DPDCs,ColRGBs);
|
|
// PSA = Styles.MakeColorPSA ( item, surfColor, curvColor, isComponent );
|
|
// if ( isComponent )
|
|
// setDefaultInstanceColor( override, PSA);
|
|
//
|
|
// Can be fixed with following
|
|
// if ( !override.IsNull() && isComponent )
|
|
// setDefaultInstanceColor( override, PSA);
|
|
//
|
|
auto childShape = aShapeTool->GetShape(childLabel);
|
|
Quantity_ColorRGBA col;
|
|
if(!aColorTool->GetInstanceColor(childShape,XCAFDoc_ColorGen,col) &&
|
|
!aColorTool->GetInstanceColor(childShape,XCAFDoc_ColorSurf,col) &&
|
|
!aColorTool->GetInstanceColor(childShape,XCAFDoc_ColorCurv,col))
|
|
{
|
|
auto &c = options.defaultColor;
|
|
aColorTool->SetColor(childLabel, convertColor(c), XCAFDoc_ColorGen);
|
|
FC_WARN(labelName(childLabel) << " set default color");
|
|
}
|
|
aColorTool->SetVisibility(childLabel,Standard_False);
|
|
}
|
|
}
|
|
|
|
if (!groupLinks.empty() && groupLinks.back()==obj)
|
|
groupLinks.pop_back();
|
|
|
|
// Finished adding components. Now retrieve the computed non-located shape
|
|
auto baseShape = shape;
|
|
baseShape.setShape(aShapeTool->GetShape(label));
|
|
|
|
myObjects.emplace(obj, label);
|
|
for(auto link : links)
|
|
myObjects.emplace(link, label);
|
|
|
|
if(!parent.IsNull() && !links.empty())
|
|
linked = links.back();
|
|
else
|
|
linked = obj;
|
|
setupObject(label,linked,baseShape,prefix);
|
|
|
|
if(!parent.IsNull()) {
|
|
// If we are a component, swap in the base shape but keep our location.
|
|
shape.setShape(baseShape.getShape().Located(shape.getShape().Location()));
|
|
label = aShapeTool->AddComponent(parent,label,shape.getShape().Location());
|
|
setupObject(label,name?parentObj:obj,shape,prefix,name);
|
|
}
|
|
return label;
|
|
}
|
|
|
|
bool ExportOCAF2::canFallback(std::vector<App::DocumentObject*> objs) {
|
|
for(size_t i=0;i<objs.size();++i) {
|
|
auto obj = objs[i];
|
|
if(!obj || !obj->getNameInDocument())
|
|
continue;
|
|
if(obj->getExtensionByType<App::LinkBaseExtension>(true))
|
|
return false;
|
|
for(auto &sub : obj->getSubObjects())
|
|
objs.push_back(obj->getSubObject(sub.c_str()));
|
|
}
|
|
return true;
|
|
}
|