// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * 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 #include #include #include #include #include #include #include "Builder3D.h" #include "Console.h" #include "Exception.h" #include "FileInfo.h" #include "Stream.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* VertexOrdering::toString() const { switch (ordering) { case Ordering::UnknownOrdering: return "UNKNOWN_ORDERING"; case Ordering::Clockwise: return "CLOCKWISE"; case Ordering::CounterClockwise: return "COUNTERCLOCKWISE"; } return "UNKNOWN_ORDERING"; } const char* ShapeType::toString() const { switch (type) { case Type::UnknownShapeType: return "UNKNOWN_SHAPE_TYPE"; case Type::Convex: return "SOLID"; } return "UNKNOWN_SHAPE_TYPE"; } 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"; } } // namespace Base // ----------------------------------------------------------------------------- LabelItem::LabelItem(std::string text) : text(std::move(text)) {} void LabelItem::write(InventorOutput& out) const { out.write("Label {\n"); out.write() << " label \"" << text << "\"\n"; out.write("}\n"); } // ----------------------------------------------------------------------------- InfoItem::InfoItem(std::string text) : text(std::move(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(std::vector points, DrawStyle drawStyle, const ColorRGB& rgb) : points {std::move(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::setVertexOrdering(VertexOrdering::Ordering value) { vertexOrdering.ordering = value; } void ShapeHintsItem::setShapeType(ShapeType::Type value) { shapeType.type = value; } void ShapeHintsItem::write(InventorOutput& out) const { out.write() << "ShapeHints {\n"; out.write() << " creaseAngle " << creaseAngle << '\n'; out.write() << " vertexOrdering " << vertexOrdering.toString() << '\n'; out.write() << " shapeType " << shapeType.toString() << '\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(std::vector points) : points(std::move(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(std::vector indices) : indices(std::move(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(std::vector indices) : indices(std::move(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(std::vector indices) : indices(std::move(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(std::vector vec) : vector(std::move(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(std::string string) : string(std::move(string)) {} void Text2Item::write(InventorOutput& out) const { out.write() << "Text2 { string \"" << string << "\" " << "}\n"; } // ----------------------------------------------------------------------------- // NOLINTNEXTLINE 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& str) : result(str) { addHeader(); } InventorBuilder::~InventorBuilder() = default; void InventorBuilder::addHeader() { result << "#Inventor V2.1 ascii \n\n"; } 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() : result {} , builder {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( "Builder3D", result.str(), Base::LogStyle::Log, Base::IntendedRecipient::Developer, Base::ContentType::Untranslatable ); } } /** * 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(); } void Builder3D::addNode(const NodeItem& item) { builder.addNode(item); } void Builder3D::beginSeparator() { builder.beginSeparator(); } void Builder3D::endSeparator() { builder.endSeparator(); } // ----------------------------------------------------------------------------- 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& face) { if (face.p1 < 0 || face.p1 >= value) { return false; } if (face.p2 < 0 || face.p2 >= value) { return false; } if (face.p3 < 0 || face.p3 >= value) { return false; } return true; }; return std::all_of(faces.cbegin(), faces.cend(), [&inRange](const Face& face) { return inRange(face); }); } namespace Base { BaseExport Vector3f stringToVector(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; } BaseExport std::string vectorToString(Vector3f vec) { return fmt::format("({},{},{})", vec.x, vec.y, vec.z); } } // namespace Base