/*************************************************************************** * Copyright (c) 2011 Jürgen Riegel * * * * 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" #ifndef _PreComp_ # include # include # include # include # include # include # include # include # include # include #endif #include "Builder3D.h" #include "Console.h" #include "Exception.h" #include "FileInfo.h" #include "Matrix.h" #include "Stream.h" #include "Tools.h" using namespace Base; constexpr float valueMinLegal {-1.0F}; constexpr float valueMaxLegal {1.0F}; ColorRGB::ColorRGB() : Rgb{1.0F, 1.0F, 1.0F} { } ColorRGB::ColorRGB(float red, float green, float blue) : Rgb{valueInRange(red), valueInRange(green), valueInRange(blue)} { } float ColorRGB::valueInRange(float value) { return std::clamp(value, valueMinLegal, valueMaxLegal); } const char* DrawStyle::styleAsString() const { switch (style) { case Style::Filled: return "FILLED"; case Style::Lines: return "LINES"; case Style::Points: return "POINTS"; case Style::Invisible: return "INVISIBLE"; } return "FILLED"; } std::string DrawStyle::patternAsString() const { std::stringstream str; str << "0x" << std::hex << linePattern; return str.str(); } const char* BindingElement::bindingAsString() const { switch (value) { case Binding::PerPart: return "PER_PART"; case Binding::PerPartIndexed: return "PER_PART_INDEXED"; case Binding::PerFace: return "PER_FACE"; case Binding::PerFaceIndexed: return "PER_FACE_INDEXED"; case Binding::PerVertex: return "PER_VERTEX"; case Binding::PerVertexIndexed: return "PER_VERTEX_INDEXED"; default: return "OVERALL"; } } const char* PolygonOffset::styleAsString() const { switch (style) { case Style::Filled: return "FILLED"; case Style::Lines: return "LINES"; case Style::Points: return "POINTS"; } return "FILLED"; } // ----------------------------------------------------------------------------- InventorOutput::InventorOutput(std::ostream& result, Indentation& indent) : result(result) , indent(indent) { } std::ostream& InventorOutput::stream() { return result; } std::ostream& InventorOutput::write() { result << indent; return result; } std::ostream& InventorOutput::write(const char* str) { result << indent << str; return result; } std::ostream& InventorOutput::write(const std::string& str) { result << indent << str; return result; } std::ostream& InventorOutput::writeLine() { result << indent << '\n'; return result; } std::ostream& InventorOutput::writeLine(const char* str) { result << indent << str << '\n'; return result; } std::ostream& InventorOutput::writeLine(const std::string& str) { result << indent << str << '\n'; return result; } void InventorOutput::increaseIndent() { indent.increaseIndent(); } void InventorOutput::decreaseIndent() { indent.decreaseIndent(); } // ----------------------------------------------------------------------------- namespace Base { template struct field_traits { }; template <> struct field_traits { using field_type = float; static std::ostream& write(std::ostream& out, const field_type& item) { out << item; return out; } }; template <> struct field_traits { using field_type = Vector3f; static std::ostream& write(std::ostream& out, const field_type& item) { out << item.x << " " << item.y << " " << item.z; return out; } }; template <> struct field_traits { using field_type = ColorRGB; static std::ostream& write(std::ostream& out, const field_type& item) { out << item.red() << " " << item.green() << " " << item.blue(); return out; } }; /** * Writes a field type to a stream. * @author Werner Mayer */ class InventorFieldWriter { public: template void write(const char* fieldName, const std::vector& fieldData, InventorOutput& out) const; }; template void InventorFieldWriter::write(const char* fieldName, const std::vector& fieldData, InventorOutput& out) const { if (fieldData.empty()) return; if (fieldData.size() == 1) { out.write() << fieldName << " "; field_traits::write(out.stream(), fieldData[0]) << '\n'; } else { out.write() << fieldName << " [\n"; out.increaseIndent(); for (auto it : fieldData) { out.write(); field_traits::write(out.stream(), it) << '\n'; } out.decreaseIndent(); out.write() << "]\n"; } } template<> void InventorFieldWriter::write(const char* fieldName, const std::vector& fieldData, InventorOutput& out) const { if (fieldData.empty()) return; out.write() << fieldName << " [\n"; out.increaseIndent(); std::size_t last_index{fieldData.size()}; std::size_t index{}; for (auto it : fieldData) { if (index % 8 == 0) out.write(); if (index < last_index) out.stream() << it << ", "; else out.stream() << it << " ] \n"; if (++index % 8 == 0) out.stream() << '\n'; } out.decreaseIndent(); out.write() << "]\n"; } } // ----------------------------------------------------------------------------- LabelItem::LabelItem(const std::string& text) : text(text) { } void LabelItem::write(InventorOutput& out) const { out.write("Label {\n"); out.write() << " label \"" << text << "\"\n"; out.write("}\n"); } // ----------------------------------------------------------------------------- InfoItem::InfoItem(const std::string& text) : text(text) { } void InfoItem::write(InventorOutput& out) const { out.write("Info {\n"); out.write() << " string \"" << text << "\"\n"; out.write("}\n"); } // ----------------------------------------------------------------------------- BaseColorItem::BaseColorItem(const ColorRGB& rgb) : rgb(rgb) { } void BaseColorItem::write(InventorOutput& out) const { out.write("BaseColor {\n"); out.write() << " rgb " << rgb.red() << " " << rgb.green() << " " << rgb.blue() << '\n'; out.write("}\n"); } // ----------------------------------------------------------------------------- PointItem::PointItem(const Base::Vector3f& point, DrawStyle drawStyle, const ColorRGB& rgb) : point(point) , drawStyle(drawStyle) , rgb(rgb) { } void PointItem::write(InventorOutput& out) const { out.write() << "Separator { \n"; out.write() << " Material { \n"; out.write() << " diffuseColor " << rgb.red() << " "<< rgb.green() << " "<< rgb.blue() << '\n'; out.write() << " }\n"; out.write() << " MaterialBinding { value PER_PART }\n"; out.write() << " DrawStyle { pointSize " << drawStyle.pointSize << "}\n"; out.write() << " Coordinate3 {\n"; out.write() << " point [ " << point.x << " " << point.y << " " << point.z << "]\n"; out.write() << " }\n"; out.write() << " PointSet { }\n"; out.write() <<"}\n"; } // ----------------------------------------------------------------------------- LineItem::LineItem(const Base::Line3f& line, DrawStyle drawStyle, const ColorRGB& rgb) : line(line) , drawStyle(drawStyle) , rgb(rgb) { } void LineItem::write(InventorOutput& out) const { std::string pattern = drawStyle.patternAsString(); out.write(" Separator { \n"); out.write() << " Material { diffuseColor " << rgb.red() << " "<< rgb.green() << " "<< rgb.blue() << "} \n"; out.write() << " DrawStyle { lineWidth " << drawStyle.lineWidth << " linePattern " << pattern << " } \n"; out.write() << " Coordinate3 { \n"; out.write() << " point [ "; out.write() << line.p1.x << " " << line.p1.y << " " << line.p1.z << ","; out.write() << line.p2.x << " " << line.p2.y << " " << line.p2.z; out.write() << " ] \n"; out.write() << " } \n"; out.write() << " LineSet { } \n"; out.write() << " } \n"; } // ----------------------------------------------------------------------------- MultiLineItem::MultiLineItem(const std::vector& points, DrawStyle drawStyle, const ColorRGB& rgb) : points{points} , drawStyle{drawStyle} , rgb{rgb} { } void MultiLineItem::write(InventorOutput& out) const { std::string pattern = drawStyle.patternAsString(); out.write() << "Separator {\n"; out.write() << " Material { diffuseColor " << rgb.red() << " "<< rgb.green() << " "<< rgb.blue() << "}\n"; out.write() << " DrawStyle { lineWidth " << drawStyle.lineWidth << " linePattern " << pattern << " }\n"; out.write() << " Coordinate3 {\n"; InventorFieldWriter writer; writer.write("point", points, out); out.write() << " }\n"; out.write() << " LineSet {\n"; out.write() << " numVertices [ -1 ]\n"; out.write() << " }\n"; out.write() << "}\n"; } // ----------------------------------------------------------------------------- ArrowItem::ArrowItem(const Base::Line3f& line, DrawStyle drawStyle, const ColorRGB& rgb) : line(line) , drawStyle(drawStyle) , rgb(rgb) { } void ArrowItem::write(InventorOutput& out) const { float length = line.Length(); float coneLength = length / 10.0F; float coneRadius = coneLength / 2.0F; float sf1 = length - coneLength; float sf2 = length - coneLength/2.0F; Vector3f dir = line.GetDirection(); dir.Normalize(); dir.Scale(sf1, sf1, sf1); Vector3f pt2s = line.p1 + dir; dir.Normalize(); dir.Scale(sf2, sf2, sf2); Vector3f cpt = line.p1 + dir; Vector3f rot = Vector3f(0.0f, 1.0f, 0.0f) % dir; rot.Normalize(); float angle = Vector3f(0.0f, 1.0f, 0.0f).GetAngle(dir); out.write() << "Separator {\n"; out.write() << " Material { diffuseColor " << rgb.red() << " "<< rgb.green() << " "<< rgb.blue() << "}\n"; out.write() << " DrawStyle { lineWidth " << drawStyle.lineWidth << " }\n"; out.write() << " Coordinate3 {\n"; out.write() << " point [ "; out.write() << line.p1.x << " " << line.p1.y << " " << line.p1.z << ", "; out.write() << pt2s.x << " " << pt2s.y << " " << pt2s.z; out.write() << " ]\n"; out.write() << " }\n"; out.write() << " LineSet { }\n"; out.write() << " Transform { \n"; out.write() << " translation " << cpt.x << " " << cpt.y << " " << cpt.z << '\n'; out.write() << " rotation " << rot.x << " " << rot.y << " " << rot.z << " " << angle << '\n'; out.write() << " }\n"; out.write() << " Cone { bottomRadius " << coneRadius << " height " << coneLength << "} \n"; out.write() << "}\n"; } // ----------------------------------------------------------------------------- BoundingBoxItem::BoundingBoxItem(const Vector3f& pt1, const Vector3f& pt2, DrawStyle drawStyle, const ColorRGB& rgb) : pt1{pt1} , pt2{pt2} , drawStyle{drawStyle} , rgb{rgb} { } void BoundingBoxItem::write(InventorOutput& out) const { std::vector points(8); points[0].Set(pt1.x, pt1.y, pt1.z); points[1].Set(pt1.x, pt1.y, pt2.z); points[2].Set(pt1.x, pt2.y, pt1.z); points[3].Set(pt1.x, pt2.y, pt2.z); points[4].Set(pt2.x, pt1.y, pt1.z); points[5].Set(pt2.x, pt1.y, pt2.z); points[6].Set(pt2.x, pt2.y, pt1.z); points[7].Set(pt2.x, pt2.y, pt2.z); std::vector lineset = { 0, 2, 6, 4, 0, -1, 1, 5, 7, 3, 1, -1, 7, 6, 2, 3, 7, -1, 3, 2, 0, 1, 3, -1, 5, 1, 0, 4, 5, -1 }; out.write() << "Separator {\n"; out.write() << " Material { diffuseColor " << rgb.red() << " "<< rgb.green() << " "<< rgb.blue() << "}\n"; out.write() << " DrawStyle { lineWidth " << drawStyle.lineWidth << "}\n"; Coordinate3Item coords{points}; out.increaseIndent(); coords.write(out); out.decreaseIndent(); IndexedLineSetItem indexed{lineset}; out.increaseIndent(); indexed.write(out); out.decreaseIndent(); out.write() << "}\n"; } // ----------------------------------------------------------------------------- void MaterialItem::setAmbientColor(const std::vector& rgb) { ambientColor = rgb; } void MaterialItem::setDiffuseColor(const std::vector& rgb) { diffuseColor = rgb; } void MaterialItem::setSpecularColor(const std::vector& rgb) { specularColor = rgb; } void MaterialItem::setEmissiveColor(const std::vector& rgb) { emissiveColor = rgb; } void MaterialItem::setShininess(const std::vector& value) { shininess = value; } void MaterialItem::setTransparency(const std::vector& value) { transparency = value; } void MaterialItem::write(InventorOutput& out) const { beginMaterial(out); writeAmbientColor(out); writeDiffuseColor(out); writeSpecularColor(out); writeEmissiveColor(out); writeShininess(out); writeTransparency(out); endMaterial(out); } void MaterialItem::beginMaterial(InventorOutput& out) const { out.writeLine("Material {"); out.increaseIndent(); } void MaterialItem::endMaterial(InventorOutput& out) const { out.decreaseIndent(); out.writeLine("}"); } void MaterialItem::writeAmbientColor(InventorOutput& out) const { InventorFieldWriter writer; writer.write("ambientColor", ambientColor, out); } void MaterialItem::writeDiffuseColor(InventorOutput& out) const { InventorFieldWriter writer; writer.write("diffuseColor", diffuseColor, out); } void MaterialItem::writeSpecularColor(InventorOutput& out) const { InventorFieldWriter writer; writer.write("specularColor", specularColor, out); } void MaterialItem::writeEmissiveColor(InventorOutput& out) const { InventorFieldWriter writer; writer.write("emissiveColor", emissiveColor, out); } void MaterialItem::writeShininess(InventorOutput& out) const { InventorFieldWriter writer; writer.write("shininess", shininess, out); } void MaterialItem::writeTransparency(InventorOutput& out) const { InventorFieldWriter writer; writer.write("transparency", transparency, out); } // ----------------------------------------------------------------------------- MaterialBindingItem::MaterialBindingItem(BindingElement::Binding bind) { value.value = bind; } void MaterialBindingItem::setValue(BindingElement::Binding bind) { value.value = bind; } void MaterialBindingItem::write(InventorOutput& out) const { out.write() << "MaterialBinding { value " << value.bindingAsString() << " } \n"; } // ----------------------------------------------------------------------------- DrawStyleItem::DrawStyleItem(DrawStyle value) : style{value} { } void DrawStyleItem::setValue(DrawStyle value) { style = value; } void DrawStyleItem::write(InventorOutput& out) const { out.write() << "DrawStyle {\n"; out.write() << " style " << style.styleAsString() << '\n'; out.write() << " pointSize " << style.pointSize << '\n'; out.write() << " lineWidth " << style.lineWidth << '\n'; out.write() << " linePattern " << style.patternAsString() << '\n'; out.write() << "}\n"; } // ----------------------------------------------------------------------------- ShapeHintsItem::ShapeHintsItem(float creaseAngle) : creaseAngle(creaseAngle) { } void ShapeHintsItem::write(InventorOutput& out) const { out.write() << "ShapeHints {\n"; out.write() << " creaseAngle " << creaseAngle << '\n'; out.write() << "}\n"; } // ----------------------------------------------------------------------------- void PolygonOffsetItem::setValue(PolygonOffset value) { offset = value; } void PolygonOffsetItem::write(InventorOutput& out) const { out.write() << "PolygonOffset {\n"; out.write() << " factor " << offset.factor << '\n'; out.write() << " units " << offset.units << '\n'; out.write() << " styles " << offset.styleAsString() << '\n'; out.write() << " on " << (offset.on ? "TRUE" : "FALSE") << '\n'; out.write() << "}\n"; } // ----------------------------------------------------------------------------- Coordinate3Item::Coordinate3Item(const std::vector& points) : points(points) { } void Coordinate3Item::write(InventorOutput& out) const { beginPoint(out); InventorFieldWriter writer; writer.write("point", points, out); endPoint(out); } void Coordinate3Item::beginPoint(InventorOutput& out) const { out.writeLine("Coordinate3 {"); out.increaseIndent(); } void Coordinate3Item::endPoint(InventorOutput& out) const { out.decreaseIndent(); out.writeLine("}"); } // ----------------------------------------------------------------------------- void PointSetItem::write(InventorOutput& out) const { out.writeLine("PointSet { }"); } // ----------------------------------------------------------------------------- void LineSetItem::write(InventorOutput& out) const { out.writeLine("LineSet { }"); } // ----------------------------------------------------------------------------- FaceSetItem::FaceSetItem(const std::vector& indices) : indices(indices) { } void FaceSetItem::write(InventorOutput &out) const { out.write() << "FaceSet {\n"; out.increaseIndent(); InventorFieldWriter writer; writer.write("numVertices", indices, out); out.decreaseIndent(); out.write() << "}\n"; } // ----------------------------------------------------------------------------- IndexedLineSetItem::IndexedLineSetItem(const std::vector& indices) : indices(indices) { } void IndexedLineSetItem::write(InventorOutput &out) const { out.write() << "IndexedLineSet {\n"; out.increaseIndent(); InventorFieldWriter writer; writer.write("coordIndex", indices, out); out.decreaseIndent(); out.write() << "}\n"; } // ----------------------------------------------------------------------------- IndexedFaceSetItem::IndexedFaceSetItem(const std::vector& indices) : indices(indices) { } void IndexedFaceSetItem::write(InventorOutput &out) const { out.write() << "IndexedFaceSet {\n"; out.increaseIndent(); InventorFieldWriter writer; writer.write("coordIndex", indices, out); out.decreaseIndent(); out.write() << "}\n"; } // ----------------------------------------------------------------------------- NormalItem::NormalItem(const std::vector& vec) : vector(vec) { } void NormalItem::write(InventorOutput& out) const { beginNormal(out); InventorFieldWriter writer; writer.write("vector", vector, out); endNormal(out); } void NormalItem::beginNormal(InventorOutput& out) const { out.writeLine("Normal {"); out.increaseIndent(); } void NormalItem::endNormal(InventorOutput& out) const { out.decreaseIndent(); out.writeLine("}"); } // ----------------------------------------------------------------------------- void NormalBindingItem::setValue(BindingElement::Binding bind) { value.value = bind; } void NormalBindingItem::write(InventorOutput& out) const { out.write() << "NormalBinding { value " << value.bindingAsString() << " }\n"; } // ----------------------------------------------------------------------------- void CylinderItem::setRadius(float value) { radius = value; } void CylinderItem::setHeight(float value) { height = value; } void CylinderItem::write(InventorOutput& out) const { out.write() << "Cylinder {\n"; out.write() << " radius " << radius << "\n"; out.write() << " height " << height << "\n"; out.write() << " parts (SIDES | TOP | BOTTOM)\n"; out.write() << "}\n"; } // ----------------------------------------------------------------------------- void ConeItem::setBottomRadius(float value) { bottomRadius = value; } void ConeItem::setHeight(float value) { height = value; } void ConeItem::write(InventorOutput& out) const { out.write() << "Cone { bottomRadius " << bottomRadius << " height " << height << " }\n"; } // ----------------------------------------------------------------------------- void SphereItem::setRadius(float value) { radius = value; } void SphereItem::write(InventorOutput& out) const { out.write() << "Sphere { radius " << radius << " }\n"; } // ----------------------------------------------------------------------------- void NurbsSurfaceItem::setControlPoints(int numU, int numV) { numUControlPoints = numU; numVControlPoints = numV; } void NurbsSurfaceItem::setKnotVector(const std::vector& uKnots, const std::vector& vKnots) { uKnotVector = uKnots; vKnotVector = vKnots; } void NurbsSurfaceItem::write(InventorOutput& out) const { out.write() << "NurbsSurface {\n"; out.write() << " numUControlPoints " << numUControlPoints << '\n'; out.write() << " numVControlPoints " << numVControlPoints << '\n'; out.increaseIndent(); InventorFieldWriter writer; writer.write("uKnotVector", uKnotVector, out); writer.write("vKnotVector", vKnotVector, out); out.decreaseIndent(); out.write() << "}\n"; } // ----------------------------------------------------------------------------- Text2Item::Text2Item(const std::string& string) : string(string) { } void Text2Item::write(InventorOutput& out) const { out.write() << "Text2 { string \"" << string << "\" " << "}\n"; } // ----------------------------------------------------------------------------- TransformItem::TransformItem(const Base::Placement& placement) : placement(placement) { } TransformItem::TransformItem(const Matrix4D& transform) { placement.fromMatrix(transform); } void TransformItem::write(InventorOutput& out) const { Base::Vector3d translation = placement.getPosition(); Base::Vector3d rotationaxis; double angle{}; placement.getRotation().getValue(rotationaxis, angle); out.write() << "Transform {\n"; out.write() << " translation " << translation.x << " " << translation.y << " " << translation.z << '\n'; out.write() << " rotation " << rotationaxis.x << " " << rotationaxis.y << " " << rotationaxis.z << " " << angle << '\n'; out.write() << "}" << '\n'; } // ----------------------------------------------------------------------------- InventorBuilder::InventorBuilder(std::ostream& output) : result(output) { result << "#Inventor V2.1 ascii \n\n"; } InventorBuilder:: ~InventorBuilder() { } void InventorBuilder::increaseIndent() { indent.increaseIndent(); } void InventorBuilder::decreaseIndent() { indent.decreaseIndent(); } void InventorBuilder::addNode(const NodeItem& node) { InventorOutput out(result, indent); node.write(out); } void InventorBuilder::beginSeparator() { result << indent << "Separator { \n"; increaseIndent(); } void InventorBuilder::endSeparator() { decreaseIndent(); result << indent << "}\n"; } // ----------------------------------------------------------------------------- /** * A constructor. * A more elaborate description of the constructor. */ Builder3D::Builder3D() : InventorBuilder(result) { } /** * A destructor. * A more elaborate description of the destructor. */ Builder3D::~Builder3D() = default; void Builder3D::clear () { // under gcc stringstream::str() returns a copy not a reference #if defined(_MSC_VER) result.str().clear(); #endif result.clear(); } /** * Save the resulting inventor 3D representation to the Console().Log() facility. * In DEBUG mode the Gui (if running) will trigger on that and show the representation in * the active Viewer/Document. It shows only one representation on time. If you need to * show more then one representation use saveToFile() instead. * @see saveToFile() */ void Builder3D::saveToLog() { ILogger* obs = Base::Console().Get("StatusBar"); if (obs){ obs->SendLog(result.str().c_str(), Base::LogStyle::Log); } } /** * Save the resulting inventor 3D representation to a file. Ending should be *.iv. * That enables you to show the result in a Inventor Viewer or in FreeCAD by: * /code * Gui.document().addAnnotation("Debug","MyFile.iv") * /endcode * @see saveToFile() */ void Builder3D::saveToFile(const char* FileName) { Base::FileInfo fi(FileName); Base::ofstream file(fi); if (!file) { throw FileException("Cannot open file"); } file << result.str(); } // ----------------------------------------------------------------------------- template std::vector InventorLoader::readData(const char* fieldName) const { std::vector fieldValues; std::string str; // search for 'fieldName' and '[' bool found = false; while (std::getline(inp, str)) { std::string::size_type point = str.find(fieldName); std::string::size_type open = str.find("["); if (point != std::string::npos && open > point) { str = str.substr(open); found = true; break; } } if (!found) return {}; do { boost::char_separator sep(" ,"); boost::tokenizer > tokens(str, sep); std::vector token_results; token_results.assign(tokens.begin(),tokens.end()); for (const auto& it : token_results) { try { T value = boost::lexical_cast(it); fieldValues.emplace_back(value); } catch (const boost::bad_lexical_cast&) { } } // search for ']' to finish the reading if (str.find("]") != std::string::npos) break; } while (std::getline(inp, str)); return fieldValues; } std::vector InventorLoader::convert(const std::vector& data) const { if (data.size() % 3 != 0) throw std::string("Reading failed"); std::size_t len = data.size() / 3; std::vector points; points.reserve(len); for (std::size_t i = 0; i < len; i++) { float x = data[3 * i]; float y = data[3 * i + 1]; float z = data[3 * i + 2]; points.emplace_back(x, y, z); } return points; } std::vector InventorLoader::convert(const std::vector& data) const { std::vector faces; faces.reserve(data.size()); int32_t coordIndex = 0; for (const auto it : data) { if (it == 3) { faces.emplace_back(coordIndex, coordIndex+1, coordIndex+2); } else if (it == 4) { faces.emplace_back(coordIndex, coordIndex+1, coordIndex+2); faces.emplace_back(coordIndex, coordIndex+2, coordIndex+3); } coordIndex += it; } return faces; } std::vector> InventorLoader::split(const std::vector& data) { std::vector> splitdata; std::vector::const_iterator begin = data.cbegin(); std::vector::const_iterator it = begin; while ((it = std::find(begin, data.cend(), -1)) != data.cend()) { splitdata.emplace_back(begin, it); begin = it; std::advance(begin, 1); } return splitdata; } std::vector InventorLoader::convert(const std::vector>& coordIndex) const { std::vector faces; faces.reserve(coordIndex.size()); for (const auto& it : coordIndex) { if (it.size() == 3) { faces.emplace_back(it[0], it[1], it[2]); } else if (it.size() == 4) { faces.emplace_back(it[0], it[1], it[2]); faces.emplace_back(it[0], it[2], it[3]); } } return faces; } void InventorLoader::readNormals() { auto data = readData("vector"); vector = convert(data); } void InventorLoader::readCoords() { auto data = readData("point"); points = convert(data); } void InventorLoader::readIndexedFaceSet() { auto data = readData("coordIndex"); faces = convert(split(data)); } void InventorLoader::readFaceSet() { auto data = readData("numVertices"); faces = convert(data); isnonindexed = true; } bool InventorLoader::read() { if (!inp || inp.bad()) return false; std::string line; // Verify it's an Inventor 2.1 file std::getline(inp, line); if (line.find("#Inventor V2.1 ascii") == std::string::npos) return false; while (std::getline(inp, line)) { // read the normals if they are defined if (line.find("Normal {") != std::string::npos) { readNormals(); } else if (line.find("Coordinate3 {") != std::string::npos) { readCoords(); } else if (line.find("IndexedFaceSet {") != std::string::npos) { readIndexedFaceSet(); break; } else if (line.find("FaceSet {") != std::string::npos) { readFaceSet(); break; } } return true; } bool InventorLoader::isValid() const { int32_t value{static_cast(points.size())}; auto inRange = [value](const Face& f) { if (f.p1 < 0 || f.p1 >= value) return false; if (f.p2 < 0 || f.p2 >= value) return false; if (f.p3 < 0 || f.p3 >= value) return false; return true; }; for (auto it : faces) { if (!inRange(it)) return false; } return true; } namespace Base { BaseExport Vector3f to_vector(std::string str) { std::string_view view = str; if (!boost::starts_with(view, "(") || !boost::ends_with(str, ")")) throw std::runtime_error("string is not a tuple"); view.remove_prefix(1); view.remove_suffix(1); str = view; boost::char_separator sep(" ,"); boost::tokenizer > tokens(str, sep); std::vector token_results; token_results.assign(tokens.begin(), tokens.end()); if (token_results.size() != 3) throw std::runtime_error("not a tuple of three floats"); Base::Vector3f vec; vec.x = boost::lexical_cast(token_results.at(0)); vec.y = boost::lexical_cast(token_results.at(1)); vec.z = boost::lexical_cast(token_results.at(2)); return vec; } }