"Professional CMake" book suggest the following: "Targets should build successfully with or without compiler support for precompiled headers. It should be considered an optimization, not a requirement. In particular, do not explicitly include a precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically generated precompile header on the compiler command line instead. This is more portable across the major compilers and is likely to be easier to maintain. It will also avoid warnings being generated from certain code checking tools like iwyu (include what you use)." Therefore, removed the "#include <PreCompiled.h>" from sources, also there is no need for the "#ifdef _PreComp_" anymore
1287 lines
34 KiB
C++
1287 lines
34 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* 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 <algorithm>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/tokenizer.hpp>
|
|
|
|
#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<class type>
|
|
struct field_traits
|
|
{
|
|
};
|
|
|
|
template<>
|
|
struct field_traits<float>
|
|
{
|
|
using field_type = float;
|
|
static std::ostream& write(std::ostream& out, const field_type& item)
|
|
{
|
|
out << item;
|
|
return out;
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct field_traits<Vector3f>
|
|
{
|
|
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<ColorRGB>
|
|
{
|
|
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<typename T>
|
|
void write(const char* fieldName, const std::vector<T>& fieldData, InventorOutput& out) const;
|
|
};
|
|
|
|
template<typename T>
|
|
void InventorFieldWriter::write(const char* fieldName,
|
|
const std::vector<T>& fieldData,
|
|
InventorOutput& out) const
|
|
{
|
|
if (fieldData.empty()) {
|
|
return;
|
|
}
|
|
|
|
if (fieldData.size() == 1) {
|
|
out.write() << fieldName << " ";
|
|
field_traits<T>::write(out.stream(), fieldData[0]) << '\n';
|
|
}
|
|
else {
|
|
out.write() << fieldName << " [\n";
|
|
out.increaseIndent();
|
|
for (auto it : fieldData) {
|
|
out.write();
|
|
field_traits<T>::write(out.stream(), it) << '\n';
|
|
}
|
|
out.decreaseIndent();
|
|
out.write() << "]\n";
|
|
}
|
|
}
|
|
|
|
template<>
|
|
void InventorFieldWriter::write<int>(const char* fieldName,
|
|
const std::vector<int>& 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<Vector3f> 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<Vector3f>("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<Base::Vector3f> 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<int> 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<ColorRGB>& rgb)
|
|
{
|
|
ambientColor = rgb;
|
|
}
|
|
|
|
void MaterialItem::setDiffuseColor(const std::vector<ColorRGB>& rgb)
|
|
{
|
|
diffuseColor = rgb;
|
|
}
|
|
|
|
void MaterialItem::setSpecularColor(const std::vector<ColorRGB>& rgb)
|
|
{
|
|
specularColor = rgb;
|
|
}
|
|
|
|
void MaterialItem::setEmissiveColor(const std::vector<ColorRGB>& rgb)
|
|
{
|
|
emissiveColor = rgb;
|
|
}
|
|
|
|
void MaterialItem::setShininess(const std::vector<float>& value)
|
|
{
|
|
shininess = value;
|
|
}
|
|
|
|
void MaterialItem::setTransparency(const std::vector<float>& 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<ColorRGB>("ambientColor", ambientColor, out);
|
|
}
|
|
|
|
void MaterialItem::writeDiffuseColor(InventorOutput& out) const
|
|
{
|
|
InventorFieldWriter writer;
|
|
writer.write<ColorRGB>("diffuseColor", diffuseColor, out);
|
|
}
|
|
|
|
void MaterialItem::writeSpecularColor(InventorOutput& out) const
|
|
{
|
|
InventorFieldWriter writer;
|
|
writer.write<ColorRGB>("specularColor", specularColor, out);
|
|
}
|
|
|
|
void MaterialItem::writeEmissiveColor(InventorOutput& out) const
|
|
{
|
|
InventorFieldWriter writer;
|
|
writer.write<ColorRGB>("emissiveColor", emissiveColor, out);
|
|
}
|
|
|
|
void MaterialItem::writeShininess(InventorOutput& out) const
|
|
{
|
|
InventorFieldWriter writer;
|
|
writer.write<float>("shininess", shininess, out);
|
|
}
|
|
|
|
void MaterialItem::writeTransparency(InventorOutput& out) const
|
|
{
|
|
InventorFieldWriter writer;
|
|
writer.write<float>("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<Vector3f> points)
|
|
: points(std::move(points))
|
|
{}
|
|
|
|
void Coordinate3Item::write(InventorOutput& out) const
|
|
{
|
|
beginPoint(out);
|
|
InventorFieldWriter writer;
|
|
writer.write<Vector3f>("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<int> indices)
|
|
: indices(std::move(indices))
|
|
{}
|
|
|
|
void FaceSetItem::write(InventorOutput& out) const
|
|
{
|
|
out.write() << "FaceSet {\n";
|
|
out.increaseIndent();
|
|
InventorFieldWriter writer;
|
|
writer.write<int>("numVertices", indices, out);
|
|
out.decreaseIndent();
|
|
out.write() << "}\n";
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
IndexedLineSetItem::IndexedLineSetItem(std::vector<int> indices)
|
|
: indices(std::move(indices))
|
|
{}
|
|
|
|
void IndexedLineSetItem::write(InventorOutput& out) const
|
|
{
|
|
out.write() << "IndexedLineSet {\n";
|
|
out.increaseIndent();
|
|
InventorFieldWriter writer;
|
|
writer.write<int>("coordIndex", indices, out);
|
|
out.decreaseIndent();
|
|
out.write() << "}\n";
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
IndexedFaceSetItem::IndexedFaceSetItem(std::vector<int> indices)
|
|
: indices(std::move(indices))
|
|
{}
|
|
|
|
void IndexedFaceSetItem::write(InventorOutput& out) const
|
|
{
|
|
out.write() << "IndexedFaceSet {\n";
|
|
out.increaseIndent();
|
|
InventorFieldWriter writer;
|
|
writer.write<int>("coordIndex", indices, out);
|
|
out.decreaseIndent();
|
|
out.write() << "}\n";
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
NormalItem::NormalItem(std::vector<Base::Vector3f> vec)
|
|
: vector(std::move(vec))
|
|
{}
|
|
|
|
void NormalItem::write(InventorOutput& out) const
|
|
{
|
|
beginNormal(out);
|
|
InventorFieldWriter writer;
|
|
writer.write<Vector3f>("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<float>& uKnots,
|
|
const std::vector<float>& 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<float>("uKnotVector", uKnotVector, out);
|
|
writer.write<float>("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<typename T>
|
|
std::vector<T> InventorLoader::readData(const char* fieldName) const
|
|
{
|
|
std::vector<T> 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<char> sep(" ,");
|
|
boost::tokenizer<boost::char_separator<char>> tokens(str, sep);
|
|
std::vector<std::string> token_results;
|
|
token_results.assign(tokens.begin(), tokens.end());
|
|
|
|
for (const auto& it : token_results) {
|
|
try {
|
|
T value = boost::lexical_cast<T>(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<Vector3f> InventorLoader::convert(const std::vector<float>& data) const
|
|
{
|
|
if (data.size() % 3 != 0) {
|
|
throw std::string("Reading failed");
|
|
}
|
|
|
|
std::size_t len = data.size() / 3;
|
|
std::vector<Vector3f> 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::Face> InventorLoader::convert(const std::vector<int32_t>& data) const
|
|
{
|
|
std::vector<Face> 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<std::vector<int32_t>> InventorLoader::split(const std::vector<int32_t>& data)
|
|
{
|
|
std::vector<std::vector<int32_t>> splitdata;
|
|
std::vector<int32_t>::const_iterator begin = data.cbegin();
|
|
std::vector<int32_t>::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::Face>
|
|
InventorLoader::convert(const std::vector<std::vector<int32_t>>& coordIndex) const
|
|
{
|
|
std::vector<Face> 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<float>("vector");
|
|
vector = convert(data);
|
|
}
|
|
|
|
void InventorLoader::readCoords()
|
|
{
|
|
auto data = readData<float>("point");
|
|
points = convert(data);
|
|
}
|
|
|
|
void InventorLoader::readIndexedFaceSet()
|
|
{
|
|
auto data = readData<int32_t>("coordIndex");
|
|
faces = convert(split(data));
|
|
}
|
|
|
|
void InventorLoader::readFaceSet()
|
|
{
|
|
auto data = readData<int32_t>("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<int32_t>(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<char> sep(" ,");
|
|
boost::tokenizer<boost::char_separator<char>> tokens(str, sep);
|
|
std::vector<std::string> 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<float>(token_results.at(0));
|
|
vec.y = boost::lexical_cast<float>(token_results.at(1));
|
|
vec.z = boost::lexical_cast<float>(token_results.at(2));
|
|
|
|
return vec;
|
|
}
|
|
|
|
BaseExport std::string vectorToString(Vector3f vec)
|
|
{
|
|
return fmt::format("({},{},{})", vec.x, vec.y, vec.z);
|
|
}
|
|
|
|
} // namespace Base
|