Sun, 22 Sep 2019 11:51:41 +0300
Added lots of code
--- a/.hgignore Sat Aug 24 14:44:42 2019 +0300 +++ b/.hgignore Sun Sep 22 11:51:41 2019 +0300 @@ -1,3 +1,4 @@ syntax:glob CMakeLists.txt.user __pycache__ +*.orig
--- a/CMakeLists.txt Sat Aug 24 14:44:42 2019 +0300 +++ b/CMakeLists.txt Sun Sep 22 11:51:41 2019 +0300 @@ -18,19 +18,42 @@ # set_source_files_properties (${CMAKE_BINARY_DIR}/configuration.cpp PROPERTIES GENERATED TRUE) # set_property(SOURCE configuration.cpp PROPERTY SKIP_AUTOMOC ON) set (LDFORGE_SOURCES + src/documentmanager.cpp src/main.cpp src/mainwindow.cpp - src/modelobject.cpp - src/uuid.cpp + src/model.cpp + src/modeleditcontext.cpp + src/parser.cpp + src/uuid.cpp src/version.cpp + src/vertex.cpp + src/objecttypes/comment.cpp + src/objecttypes/conditionaledge.cpp + src/objecttypes/edge.cpp + src/objecttypes/errorline.cpp + src/objecttypes/modelobject.cpp + src/objecttypes/polygon.cpp + src/objecttypes/subfilereference.cpp ) set (LDFORGE_HEADERS + src/basics.h + src/colors.h + src/documentmanager.h src/main.h src/mainwindow.h - src/mainwindow.h - src/modelobject.h - src/uuid.h + src/model.h + src/modeleditcontext.h + src/parser.h + src/uuid.h src/version.h + src/vertex.h + src/objecttypes/comment.h + src/objecttypes/conditionaledge.h + src/objecttypes/edge.h + src/objecttypes/errorline.h + src/objecttypes/modelobject.h + src/objecttypes/polygon.h + src/objecttypes/subfilereference.h ) set (LDFORGE_FORMS src/mainwindow.ui @@ -50,8 +73,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG") endif() - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=all -Wextra") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-implicit-fallthrough -Wno-noexcept-type") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-implicit-fallthrough") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-noexcept-type") endif() # qt5_add_resources (LDFORGE_QRC ${LDFORGE_RESOURCES}) qt5_wrap_ui (LDFORGE_FORMS_HEADERS ${LDFORGE_FORMS})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/basics.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,88 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2019 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once +#include <cstdio> +#include <cstdlib> +#include <cmath> +#include <QMatrix4x4> +#include <QObject> +#include <QPointF> +#include <QSet> +#include <QString> +#include <QStringList> +#include <QVariant> +#include <QVector> +#include <QVector3D> + +using GLRotationMatrix = QMatrix4x4; + +enum Axis +{ + X, + Y, + Z +}; + +enum Winding +{ + NoWinding, + CounterClockwise, + Clockwise, +}; + +/* + * Special operator definition that implements the XOR operator for windings. + * However, if either winding is NoWinding, then this function returns NoWinding. + */ +inline Winding operator^(Winding one, Winding other) +{ + if (one == NoWinding or other == NoWinding) + return NoWinding; + else + return static_cast<Winding>(static_cast<int>(one) ^ static_cast<int>(other)); +} + +inline Winding& operator^=(Winding& one, Winding other) +{ + one = one ^ other; + return one; +} + +template<typename T, std::size_t N> +constexpr std::size_t countof(T(&)[N]) +{ + return N; +} + +static constexpr long double pi = M_PIl; + +// http://stackoverflow.com/a/18204188/3629665 +template<typename T> +inline auto rotl10(T x) + -> std::enable_if_t<std::is_arithmetic_v<T>, T> +{ + return (x << 10) | ((x >> 22) & 0x000000ff); +} + +template<typename T> +inline auto rotl20(T x) + -> std::enable_if_t<std::is_arithmetic_v<T>, T> +{ + return (x << 20) | ((x >> 12) & 0x000000ff); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/colors.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,19 @@ +#pragma once +#include "main.h" + +struct Color +{ + qint32 index; +}; + +namespace colors +{ + static constexpr Color black {0}; + static constexpr Color blue {1}; + static constexpr Color green {2}; + static constexpr Color red {4}; + static constexpr Color yellow {14}; + static constexpr Color white {15}; + static constexpr Color main {16}; + static constexpr Color edge {24}; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/documentmanager.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,7 @@ +#include "documentmanager.h" + +Model* DocumentManager::newModel() +{ + openModels.emplace_back(); + return openModels.back().get(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/documentmanager.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,16 @@ +#pragma once +#include "main.h" +#include "model.h" + +class DocumentManager +{ +public: + DocumentManager() = default; + DocumentManager(const DocumentManager&) = delete; + DocumentManager(DocumentManager&&) = default; + DocumentManager& operator=(const DocumentManager&) = delete; + DocumentManager& operator=(DocumentManager&&) = default; + Model* newModel(); +private: + std::vector<std::unique_ptr<Model>> openModels; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/header.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,48 @@ +#pragma once +#include <QDate> +#include "main.h" + +struct LDHeader +{ + struct HistoryEntry + { + QDate date; + QString author; + QString description; + }; + enum FileType + { + NoHeader, + Part, + Subpart, + Shortcut, + Primitive, + Primitive_8, + Primitive_48, + Configuration, + } type = NoHeader; + enum Qualifier + { + Alias = 1 << 0, + Physical_Color = 1 << 1, + Flexible_Section = 1 << 2, + }; + QFlags<Qualifier> qualfiers; + QString description; + QString name; + QString author; + QString category; + QString cmdline; + QString help; + QString keywords; + QVector<HistoryEntry> history; + enum + { + UnspecifiedLicense, + CaLicense, + NonCaLicense + } license = UnspecifiedLicense; + static decltype(license) defaultLicense(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<LDHeader::Qualifier>)
--- a/src/main.cpp Sat Aug 24 14:44:42 2019 +0300 +++ b/src/main.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -1,12 +1,11 @@ #include <QApplication> #include "main.h" #include "mainwindow.h" -#include <QTextStream> int main(int argc, char *argv[]) { - QApplication app{argc, argv}; - MainWindow mainwindow; - mainwindow.show(); - return app.exec(); + QApplication app{argc, argv}; + MainWindow mainwindow; + mainwindow.show(); + return app.exec(); }
--- a/src/main.h Sat Aug 24 14:44:42 2019 +0300 +++ b/src/main.h Sun Sep 22 11:51:41 2019 +0300 @@ -2,5 +2,5 @@ #include <QString> #include <QVector> #include <QSet> +#include "basics.h" -static constexpr long double pi = 3.14159265358979323846264338327950288419716939937510L;
--- a/src/mainwindow.cpp Sat Aug 24 14:44:42 2019 +0300 +++ b/src/mainwindow.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -1,13 +1,14 @@ #include "mainwindow.h" #include "ui_mainwindow.h" #include "version.h" +#include <QQuaternion> MainWindow::MainWindow(QWidget *parent) : - QMainWindow{parent}, - ui{*new Ui_MainWindow} + QMainWindow{parent}, + ui{std::make_unique<Ui_MainWindow>()} { - ui.setupUi(this); - connect(ui.actionQuit, &QAction::triggered, this, &QMainWindow::close); + ui->setupUi(this); + connect(ui->actionQuit, &QAction::triggered, this, &QMainWindow::close); QString title = ::appName; title += " "; title += fullVersionString(); @@ -16,5 +17,4 @@ MainWindow::~MainWindow() { - delete &this->ui; }
--- a/src/mainwindow.h Sat Aug 24 14:44:42 2019 +0300 +++ b/src/mainwindow.h Sun Sep 22 11:51:41 2019 +0300 @@ -1,12 +1,14 @@ #pragma once #include <QMainWindow> +#include <memory> +#include <vector> class MainWindow : public QMainWindow { - Q_OBJECT + Q_OBJECT public: - MainWindow(QWidget *parent = nullptr); - ~MainWindow(); + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); private: - class Ui_MainWindow &ui; + std::unique_ptr<class Ui_MainWindow> ui; };
--- a/src/mainwindow.ui Sat Aug 24 14:44:42 2019 +0300 +++ b/src/mainwindow.ui Sun Sep 22 11:51:41 2019 +0300 @@ -13,7 +13,13 @@ <property name="windowTitle"> <string>MainWindow</string> </property> - <widget class="QWidget" name="centralwidget"/> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabs"/> + </item> + </layout> + </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> @@ -27,6 +33,9 @@ <property name="title"> <string>File</string> </property> + <addaction name="actionNewFile"/> + <addaction name="actionOpen"/> + <addaction name="separator"/> <addaction name="actionQuit"/> </widget> <addaction name="menuFile"/> @@ -37,6 +46,22 @@ <string>Quit</string> </property> </action> + <action name="actionOpen"> + <property name="text"> + <string>Open…</string> + </property> + <property name="shortcut"> + <string>Ctrl+O</string> + </property> + </action> + <action name="actionNewFile"> + <property name="text"> + <string>New File</string> + </property> + <property name="shortcut"> + <string>Ctrl+N</string> + </property> + </action> </widget> <resources/> <connections/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/model.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,17 @@ +#include "model.h" +#include "modeleditcontext.h" + +Model::Model() +{ + +} + +int Model::size() const +{ + return this->body.size(); +} + +Model::EditContext Model::edit() +{ + return {*this}; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/model.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,40 @@ +#pragma once +#include <memory> +#include "main.h" +#include "header.h" +#include "objecttypes/modelobject.h" + +class Model : public QObject +{ + Q_OBJECT +public: + class EditContext; + Model(); + Model(const Model &) = delete; + int size() const; + EditContext edit(); +private: + template<typename T, typename... Args> + T* append(Args&&... args); + template<typename T, typename... Args> + T* insert(int position, Args&&... args); + bool modified = false; + QString path; + using ModelObjectPointer = std::unique_ptr<modelobjects::BaseObject>; + LDHeader header; + std::vector<ModelObjectPointer> body; +}; + +template<typename T, typename... Args> +T* Model::append(Args&&... args) +{ + this->body.push_back(std::make_unique<T>(args...)); + return static_cast<T*>(this->body.back().get()); +} + +template<typename T, typename... Args> +T* Model::insert(int position, Args&&... args) +{ + this->body.insert(position, std::make_unique<T>(args...)); + return this->body[position]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/modeleditcontext.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,14 @@ +#include "modeleditcontext.h" + +Model::EditContext::EditContext(Model& model) : + model{model} +{ +} + +void Model::EditContext::setObjectProperty( + modelobjects::BaseObject* object, + modelobjects::Property property, + const QVariant& value) +{ + object->setProperty(property, value); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/modeleditcontext.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,32 @@ +#pragma once +#include "model.h" +#include "objecttypes/modelobject.h" + +class Model::EditContext +{ +public: + template<typename T, typename... Args> + modelobjects::BaseObject* append(Args&&... args); + template<typename T, typename... Args> + modelobjects::BaseObject* insert(int position, Args&&... args); + void setObjectProperty( + modelobjects::BaseObject* object, + modelobjects::Property property, + const QVariant &value); +private: + EditContext(Model& model); + friend class Model; + Model& model; +}; + +template<typename T, typename... Args> +modelobjects::BaseObject* Model::EditContext::append(Args&&... args) +{ + return this->model.append<T>(args...); +} + +template<typename T, typename... Args> +modelobjects::BaseObject* Model::EditContext::insert(int position, Args&&... args) +{ + return this->model.insert<T>(position, args...); +}
--- a/src/modelobject.cpp Sat Aug 24 14:44:42 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -#include "modelobject.h" - -static Uuid &getUuidForNewObject() -{ - static Uuid running_uuid {0, 0}; - incrementUuid(running_uuid); - return running_uuid; -} - -ModelObject::ModelObject() : - id {getUuidForNewObject()} -{ -} - -ModelObject::~ModelObject() -{ -} - - -Comment::Comment(QStringView text) : - ModelObject{}, - storedText{text} {}
--- a/src/modelobject.h Sat Aug 24 14:44:42 2019 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -#pragma once -#include <QString> -#include <QStringView> -#include "uuid.h" - -class ModelObject -{ -public: - ModelObject(); - virtual ~ModelObject(); - - const Uuid id; - virtual void toString(QTextStream &out) = 0; -private: - -}; - -class Comment : public ModelObject -{ - Comment(QStringView text); - -private: - QStringView storedText; -};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/comment.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,29 @@ +#include "comment.h" + +modelobjects::Comment::Comment(QStringView text) : + BaseObject{}, + storedText{text.toString()} {} + +QVariant modelobjects::Comment::getProperty(Property property) const +{ + switch (property) + { + case Property::Text: + return storedText; + default: + return BaseObject::getProperty(property); + } +} + +auto modelobjects::Comment::setProperty(Property property, const QVariant& value) + -> SetPropertyResult +{ + switch (property) + { + case Property::Text: + storedText = value.toString(); + return SetPropertyResult::Success; + default: + return BaseObject::setProperty(property, value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/comment.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,20 @@ +#pragma once +#include "modelobject.h" + +namespace modelobjects +{ + class Comment; +} + +class modelobjects::Comment : public BaseObject +{ +public: + Comment() = default; + Comment(QStringView text); + QVariant getProperty(Property property) const override; + SetPropertyResult setProperty( + Property property, + const QVariant& value) override; +private: + QString storedText = ""; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/conditionaledge.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,42 @@ +#include "conditionaledge.h" + +modelobjects::ConditionalEdge::ConditionalEdge( + const Vertex& point_1, + const Vertex& point_2, + const Vertex& controlPoint_1, + const Vertex& controlPoint_2, + const Color color_index) : + Edge{point_1, point_2, color_index}, + controlPoint_1{controlPoint_1}, + controlPoint_2{controlPoint_2} +{ +} + +QVariant modelobjects::ConditionalEdge::getProperty(Property property) const +{ + switch (property) + { + case Property::ControlPoint1: + return controlPoint_1; + case Property::ControlPoint2: + return controlPoint_2; + default: + return Edge::getProperty(property); + } +} + +auto modelobjects::ConditionalEdge::setProperty( + Property property, + const QVariant& value) + -> SetPropertyResult +{ + switch (property) + { + case Property::ControlPoint1: + controlPoint_1 = value.value<Vertex>(); + case Property::ControlPoint2: + controlPoint_2 = value.value<Vertex>(); + default: + return Edge::setProperty(property, value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/conditionaledge.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,26 @@ +#pragma once +#include "edge.h" + +namespace modelobjects +{ + class ConditionalEdge; +} + +class modelobjects::ConditionalEdge : public Edge +{ +public: + ConditionalEdge() = default; + ConditionalEdge( + const Vertex& point_1, + const Vertex& point_2, + const Vertex& controlPoint_1, + const Vertex& controlPoint_2, + const Color color_index = colors::edge); + QVariant getProperty(Property property) const override; + SetPropertyResult setProperty( + Property property, + const QVariant& value) override; +private: + Vertex controlPoint_1 = {}; + Vertex controlPoint_2 = {}; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/edge.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,38 @@ +#include "edge.h" + +modelobjects::Edge::Edge( + const Vertex& point_1, + const Vertex& point_2, + const Color color_index) : + ColoredBaseObject{color_index}, + point_1{point_1}, + point_2{point_2} {} + +QVariant modelobjects::Edge::getProperty(Property property) const +{ + switch (property) + { + case Property::Point1: + return point_1; + case Property::Point2: + return point_2; + default: + return BaseClass::getProperty(property); + } +} + +auto modelobjects::Edge::setProperty(Property property, const QVariant& value) + -> SetPropertyResult +{ + switch (property) + { + case Property::Point1: + point_1 = value.value<Vertex>(); + return SetPropertyResult::Success; + case Property::Point2: + point_2 = value.value<Vertex>(); + return SetPropertyResult::Success; + default: + return BaseClass::setProperty(property, value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/edge.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,23 @@ +#pragma once +#include "objecttypes/modelobject.h" + +namespace modelobjects +{ + class Edge; +} + +class modelobjects::Edge : public ColoredBaseObject +{ +public: + using BaseClass = ColoredBaseObject; + Edge() = default; + Edge(const Vertex& point_1, const Vertex& point_2, + const Color color_index = colors::edge); + QVariant getProperty(Property property) const override; + SetPropertyResult setProperty( + Property property, + const QVariant& value) override; +private: + Vertex point_1 = {}; + Vertex point_2 = {}; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/errorline.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,32 @@ +#include "errorline.h" + +modelobjects::ErrorLine::ErrorLine(QStringView text) : + text{text.toString()} +{ +} + +QVariant modelobjects::ErrorLine::getProperty(Property property) const +{ + switch (property) + { + case Property::Text: + return text; + default: + return BaseObject::getProperty(property); + } +} + +auto modelobjects::ErrorLine::setProperty( + Property property, + const QVariant& value) + -> SetPropertyResult +{ + switch (property) + { + case Property::Text: + text = value.toString(); + return SetPropertyResult::Success; + default: + return BaseObject::setProperty(property, value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/errorline.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,19 @@ +#pragma once +#include "modelobject.h" + +namespace modelobjects +{ + class ErrorLine; +} + +class modelobjects::ErrorLine : public BaseObject +{ +public: + ErrorLine(QStringView text = u""); + QVariant getProperty(Property property) const override; + SetPropertyResult setProperty( + Property property, + const QVariant& value) override; +private: + QString text; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/modelobject.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,81 @@ +#include "modelobject.h" + +static Uuid &getUuidForNewObject() +{ + static Uuid running_uuid {0, 0}; + incrementUuid(running_uuid); + return running_uuid; +} + +modelobjects::BaseObject::BaseObject() : + id {getUuidForNewObject()} +{ +} + +modelobjects::BaseObject::~BaseObject() +{ +} + +bool modelobjects::BaseObject::hasColor() const +{ + return false; +} + +QVariant modelobjects::BaseObject::getProperty(Property id) const +{ + Q_UNUSED(id); + return {}; +} + +auto modelobjects::BaseObject::setProperty(Property id, const QVariant& value) + -> SetPropertyResult +{ + Q_UNUSED(id) + Q_UNUSED(value) + return SetPropertyResult::PropertyNotHandled; +} + +modelobjects::ColoredBaseObject::ColoredBaseObject(const Color color_index) : + color_index{color_index} +{ +} + +bool modelobjects::ColoredBaseObject::hasColor() const +{ + return true; +} + +QVariant modelobjects::ColoredBaseObject::getProperty(Property id) const +{ + switch (id) + { + case Property::Color: + return color_index.index; + default: + return BaseObject::getProperty(id); + } +} + +auto modelobjects::ColoredBaseObject::setProperty(Property id, const QVariant& value) + -> SetPropertyResult +{ + switch (id) + { + case Property::Color: + { + bool ok; + const int value_int = value.toInt(&ok); + if (ok) + { + color_index.index = value_int; + return SetPropertyResult::Success; + } + else + { + return SetPropertyResult::InvalidValue; + } + } + default: + return BaseObject::setProperty(id, value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/modelobject.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,61 @@ +#pragma once +#include <QPointF> +#include <QString> +#include <QStringView> +#include "main.h" +#include "colors.h" +#include "uuid.h" +#include "vertex.h" + +namespace modelobjects +{ + enum class Property; + class BaseObject; + class ColoredBaseObject; +} + +enum class modelobjects::Property +{ + Color, + Text, + Point1, + Point2, + Point3, + Point4, + ControlPoint1, + ControlPoint2, + Position, + Transformation, + ReferenceName, + IsInverted, +}; + +class modelobjects::BaseObject +{ +public: + enum class SetPropertyResult + { + Success = 0, + PropertyNotHandled, + InvalidValue + }; + BaseObject(); + BaseObject(const BaseObject&) = delete; + virtual ~BaseObject(); + const Uuid id; + //virtual void toString(QTextStream &out) = 0; + virtual bool hasColor() const; + virtual QVariant getProperty(Property id) const; + virtual SetPropertyResult setProperty(Property id, const QVariant& value); +}; + +class modelobjects::ColoredBaseObject : public BaseObject +{ +public: + ColoredBaseObject(const Color color_index = colors::main); + bool hasColor() const override final; + QVariant getProperty(Property id) const override; + SetPropertyResult setProperty(Property id, const QVariant& value) override; +private: + Color color_index = colors::main; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/polygon.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,97 @@ +#include "polygon.h" + +modelobjects::Triangle::Triangle( + const Vertex& point_1, + const Vertex& point_2, + const Vertex& point_3, + Color color_index) : + ColoredBaseObject{color_index}, + points{point_1, point_2, point_3} +{ +} + +QVariant modelobjects::Triangle::getProperty(const Property id) const +{ + switch (id) + { + case Property::Point1: + return points[0]; + case Property::Point2: + return points[1]; + case Property::Point3: + return points[2]; + default: + return ColoredBaseObject::getProperty(id); + } +} + +auto modelobjects::Triangle::setProperty(Property id, const QVariant& value) + -> SetPropertyResult +{ + switch (id) + { + case Property::Point1: + points[0] = value.value<Vertex>(); + return SetPropertyResult::Success; + case Property::Point2: + points[1] = value.value<Vertex>(); + return SetPropertyResult::Success; + case Property::Point3: + points[2] = value.value<Vertex>(); + return SetPropertyResult::Success; + default: + return ColoredBaseObject::setProperty(id, value); + } +} + +modelobjects::Quadrilateral::Quadrilateral( + const Vertex& point_1, + const Vertex& point_2, + const Vertex& point_3, + const Vertex& point_4, + Color color_index) : + ColoredBaseObject{color_index}, + points{point_1, point_2, point_3, point_4} +{ +} + +QVariant modelobjects::Quadrilateral::getProperty(const Property id) const +{ + switch (id) + { + case Property::Point1: + return points[0]; + case Property::Point2: + return points[1]; + case Property::Point3: + return points[2]; + case Property::Point4: + return points[3]; + default: + return ColoredBaseObject::getProperty(id); + } +} + +auto modelobjects::Quadrilateral::setProperty( + const Property id, + const QVariant& value) + -> SetPropertyResult +{ + switch (id) + { + case Property::Point1: + points[0] = value.value<Vertex>(); + return SetPropertyResult::Success; + case Property::Point2: + points[1] = value.value<Vertex>(); + return SetPropertyResult::Success; + case Property::Point3: + points[2] = value.value<Vertex>(); + return SetPropertyResult::Success; + case Property::Point4: + points[3] = value.value<Vertex>(); + return SetPropertyResult::Success; + default: + return ColoredBaseObject::setProperty(id, value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/polygon.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,39 @@ +#include <array> +#include "modelobject.h" + +namespace modelobjects +{ + class Triangle; + class Quadrilateral; +} + +class modelobjects::Triangle : public ColoredBaseObject +{ +public: + Triangle() = default; + Triangle( + const Vertex &point_1, + const Vertex &point_2, + const Vertex &point_3, + Color color_index = colors::main); + QVariant getProperty(Property id) const override; + SetPropertyResult setProperty(Property id, const QVariant& value) override; +private: + Vertex points[3] = {{}}; +}; + +class modelobjects::Quadrilateral : public ColoredBaseObject +{ +public: + Quadrilateral() = default; + Quadrilateral( + const Vertex &point_1, + const Vertex &point_2, + const Vertex &point_3, + const Vertex &point_4, + Color color_index = colors::main); + QVariant getProperty(Property id) const override; + SetPropertyResult setProperty(Property id, const QVariant& value) override; +private: + Vertex points[4] = {{}}; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/subfilereference.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,16 @@ +#include "subfilereference.h" + +QVariant modelobjects::SubfileReference::getProperty(Property property) const +{ + switch (property) + { + case Property::Position: + return this->position; + case Property::Transformation: + return QVariant::fromValue(this->transformation); + case Property::ReferenceName: + return this->referenceName; + default: + return ColoredBaseObject::getProperty(property); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/objecttypes/subfilereference.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,18 @@ +#pragma once +#include "modelobject.h" + +namespace modelobjects +{ + class SubfileReference; +} + +class modelobjects::SubfileReference : ColoredBaseObject +{ +public: + SubfileReference() = default; + QVariant getProperty(Property property) const override; +private: + Vertex position; + QMatrix3x3 transformation; + QString referenceName; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,270 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2018 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "model.h" +#include "parser.h" +#include "objecttypes/comment.h" +#include "objecttypes/conditionaledge.h" +#include "objecttypes/edge.h" +#include "objecttypes/errorline.h" +#include "objecttypes/modelobject.h" +#include "objecttypes/polygon.h" +#include "objecttypes/subfilereference.h" + +/* + * Constructs an LDraw parser + */ +Parser::Parser(QIODevice& device, QObject* parent) : + QObject {parent}, + device {device} {} + +/* + * Reads a single line from the device. + */ +QString Parser::readLine() +{ + return QString::fromUtf8(this->device.readLine()).trimmed(); +} + +const QMap<QString, decltype(LDHeader::type)> Parser::typeStrings { + {"Part", LDHeader::Part}, + {"Subpart", LDHeader::Subpart}, + {"Shortcut", LDHeader::Shortcut}, + {"Primitive", LDHeader::Primitive}, + {"8_Primitive", LDHeader::Primitive_8}, + {"48_Primitive", LDHeader::Primitive_48}, + {"Configuration", LDHeader::Configuration}, +}; + +/* + * Parses a single line of the header. + * Possible parse results: + * · ParseSuccess: the header line was parsed successfully. + * · ParseFailure: the header line was parsed incorrectly and needs to be handled otherwise. + * · StopParsing: the line does not belong in the header and header parsing needs to stop. + */ +Parser::HeaderParseResult Parser::parseHeaderLine( + LDHeader& header, + Winding& winding, + const QString& line +) { + if (line.isEmpty()) + { + return ParseSuccess; + } + else if (not line.startsWith("0") or line.startsWith("0 //")) + { + return StopParsing; + } + else if (line.startsWith("0 !LDRAW_ORG ")) + { + QStringList tokens = line + .mid(strlen("0 !LDRAW_ORG ")) + .split(" ", QString::SkipEmptyParts); + + if (not tokens.isEmpty()) + { + QString partTypeString = tokens[0]; + // Anything that enters LDForge becomes unofficial in any case if saved. + // Therefore we don't need to give the Unofficial type any special + // consideration. + if (partTypeString.startsWith("Unofficial_")) + partTypeString = partTypeString.mid(strlen("Unofficial_")); + header.type = Parser::typeStrings.value(partTypeString, LDHeader::Part); + header.qualfiers = 0; + if (tokens.contains("Alias")) + header.qualfiers |= LDHeader::Alias; + if (tokens.contains("Physical_Color")) + header.qualfiers |= LDHeader::Physical_Color; + if (tokens.contains("Flexible_Section")) + header.qualfiers |= LDHeader::Flexible_Section; + return ParseSuccess; + } + else + { + return ParseFailure; + } + } + else if (line == "0 BFC CERTIFY CCW") + { + winding = CounterClockwise; + return ParseSuccess; + } + else if (line == "0 BFC CERTIFY CW") + { + winding = Clockwise; + return ParseSuccess; + } + else if (line == "0 BFC NOCERTIFY") + { + winding = NoWinding; + return ParseSuccess; + } + else if (line.startsWith("0 !HISTORY ")) + { + static const QRegExp historyRegexp { + R"(0 !HISTORY\s+(\d{4}-\d{2}-\d{2})\s+)" + R"((\{[^}]+|\[[^]]+)[\]}]\s+(.+))" + }; + if (historyRegexp.exactMatch(line)) + { + QString dateString = historyRegexp.capturedTexts().value(1); + QString authorWithPrefix = historyRegexp.capturedTexts().value(2); + QString description = historyRegexp.capturedTexts().value(3); + LDHeader::HistoryEntry historyEntry; + historyEntry.date = QDate::fromString(dateString, Qt::ISODate); + historyEntry.description = description; + + if (authorWithPrefix[0] == '{') + historyEntry.author = authorWithPrefix + "}"; + else + historyEntry.author = authorWithPrefix.mid(1); + + header.history.append(historyEntry); + return ParseSuccess; + } + else + { + return ParseFailure; + } + } + else if (line.startsWith("0 Author: ")) + { + header.author = line.mid(strlen("0 Author: ")); + return ParseSuccess; + } + else if (line.startsWith("0 Name: ")) + { + header.name = line.mid(strlen("0 Name: ")); + return ParseSuccess; + } + else if (line.startsWith("0 !HELP ")) + { + if (not header.help.isEmpty()) + header.help += "\n"; + header.help += line.mid(strlen("0 !HELP ")); + return ParseSuccess; + } + else if (line.startsWith("0 !KEYWORDS ")) + { + if (not header.keywords.isEmpty()) + header.keywords += "\n"; + header.keywords += line.mid(strlen("0 !KEYWORDS ")); + return ParseSuccess; + } + else if (line.startsWith("0 !CATEGORY ")) + { + header.category = line.mid(strlen("0 !CATEGORY ")); + return ParseSuccess; + } + else if (line.startsWith("0 !CMDLINE ")) + { + header.cmdline = line.mid(strlen("0 !CMDLINE ")); + return ParseSuccess; + } + else if (line.startsWith("0 !LICENSE Redistributable under CCAL version 2.0")) + { + header.license = LDHeader::CaLicense; + return ParseSuccess; + } + else if (line.startsWith("0 !LICENSE Not redistributable")) + { + header.license = LDHeader::NonCaLicense; + return ParseSuccess; + } + else + { + return ParseFailure; + } +} + +/* + * Parses the header from the device given at construction and returns + * the resulting header structure. + */ +LDHeader Parser::parseHeader(Winding& winding) +{ + LDHeader header = {}; + + if (not this->device.atEnd()) + { + // Parse the description + QString descriptionLine = this->readLine(); + if (descriptionLine.startsWith("0 ")) + { + header.description = descriptionLine.mid(strlen("0 ")).trimmed(); + + // Parse the rest of the header + while (not this->device.atEnd()) + { + const QString& line = this->readLine(); + auto result = parseHeaderLine(header, winding, line); + + if (result == ParseFailure) + { + // Failed to parse this header line, add it as a comment into the body later. + this->bag.append(line); + } + else if (result == StopParsing) + { + // Header parsing stops, add this line to the body. + this->bag.append(line); + break; + } + } + } + else + { + this->bag.append(descriptionLine); + } + } + + return header; +} + +/** + * @brief Parses the model body into the given model. + * @param editor Handle to model edit context + */ +void Parser::parseBody(Model::EditContext& editor) +{ + bool invertNext = false; + while (not this->device.atEnd()) + this->bag.append(this->readLine()); + for (const QString& line : this->bag) + { + if (line == "0 BFC INVERTNEXT" or line == "0 BFC CERTIFY INVERTNEXT") + { + invertNext = true; + continue; + } + modelobjects::BaseObject* object = parseFromString(editor, line); + if (invertNext) + { + editor.setObjectProperty(object, modelobjects::Property::IsInverted, true); + } + invertNext = false; + } +} + +modelobjects::BaseObject* Parser::parseFromString( + Model::EditContext& editor, + const QString& line) +{ + return editor.append<modelobjects::Comment>(line); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,43 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2018 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once +#include "main.h" +#include "objecttypes/modelobject.h" +#include "model.h" +#include "modeleditcontext.h" +#include "header.h" + +class Parser : public QObject +{ + Q_OBJECT +public: + enum { EndOfModel = -1 }; + Parser(QIODevice& device, QObject* parent = nullptr); + LDHeader parseHeader(Winding& winding); + void parseBody(Model::EditContext& editor); + static modelobjects::BaseObject* parseFromString(Model::EditContext& editor, + const QString& line); + static const QMap<QString, decltype(LDHeader::type)> typeStrings; +private: + enum HeaderParseResult {ParseSuccess, ParseFailure, StopParsing}; + QString readLine(); + HeaderParseResult parseHeaderLine(LDHeader& header, Winding& winding, const QString& line); + QIODevice& device; + QStringList bag; +};
--- a/src/version.h Sat Aug 24 14:44:42 2019 +0300 +++ b/src/version.h Sun Sep 22 11:51:41 2019 +0300 @@ -1,6 +1,6 @@ /* * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2018 Teemu Piippo + * Copyright (C) 2019 Teemu Piippo * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vertex.cpp Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,215 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright(C) 2013 - 2018 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "main.h" +#include "vertex.h" + +/* +void Vertex::transform(const Matrix& matrix, const Vertex& pos) +{ + double x2 = (matrix(0, 0) * x) + (matrix(0, 1) * y) + (matrix(0, 2) * z) + pos.x; + double y2 = (matrix(1, 0) * x) + (matrix(1, 1) * y) + (matrix(1, 2) * z) + pos.y; + double z2 = (matrix(2, 0) * x) + (matrix(2, 1) * y) + (matrix(2, 2) * z) + pos.z; + this->x = x2; + this->y = y2; + this->z = z2; +} +*/ + +double& Vertex::operator[](Axis axis) +{ + switch (axis) + { + case X: + return this->x; + case Y: + return this->y; + case Z: + return this->z; + default: + throw std::runtime_error("Non-axis given to Vertex::operator[]"); + } +} + +double Vertex::operator[](Axis axis) const +{ + switch (axis) + { + case X: + return this->x; + case Y: + return this->y; + case Z: + return this->z; + default: + return 0.0; + } +} + +void Vertex::setCoordinate(Axis axis, qreal value) +{ + (*this)[axis] = value; +} + +Vertex VertexFromVector(const QVector3D& vector) +{ + return {vector.x(), vector.y(), vector.z()}; +} + +Vertex Vertex::operator*(qreal scalar) const +{ + return {this->x * scalar, this->y * scalar, this->z * scalar}; +} + +Vertex& Vertex::operator+=(const QVector3D& other) +{ + this->x += other.x(); + this->y += other.y(); + this->z += other.z(); + return *this; +} + +Vertex Vertex::operator+(const QVector3D& other) const +{ + Vertex result(*this); + result += other; + return result; +} + + +QVector3D vertexToVector(const Vertex& vertex) +{ + return { + static_cast<float>(vertex.x), + static_cast<float>(vertex.y), + static_cast<float>(vertex.z) + }; +} + +Vertex Vertex::operator-(const QVector3D& vector) const +{ + Vertex result = *this; + result -= vector; + return result; +} + +Vertex& Vertex::operator-=(const QVector3D& vector) +{ + this->x -= vector.x(); + this->y -= vector.y(); + this->z -= vector.z(); + return *this; +} + +QVector3D Vertex::operator-(const Vertex& other) const +{ + return { + static_cast<float>(this->x - other.x), + static_cast<float>(this->y - other.y), + static_cast<float>(this->z - other.z) + }; +} + +Vertex& Vertex::operator*=(qreal scalar) +{ + x *= scalar; + y *= scalar; + z *= scalar; + return *this; +} + +bool Vertex::operator==(const Vertex& other) const +{ + return this->x == other.x and this->y == other.y and this->z == other.z; +} + +bool Vertex::operator!=(const Vertex& other) const +{ + return not(*this == other); +} + +Vertex::operator QVariant() const +{ + return QVariant::fromValue<Vertex>(*this); +} + +bool Vertex::operator<(const Vertex& other) const +{ + if (not qFuzzyCompare(this->x, other.x)) + return this->x < other.x; + else if (not qFuzzyCompare(this->y, other.y)) + return this->y < other.y; + else + return this->z < other.z; +} + +/* + * Transforms this vertex with a tranformation matrix and returns the result. + */ +Vertex Vertex::transformed(const GLRotationMatrix& matrix) const +{ + return { + matrix(0, 0) * this->x + + matrix(0, 1) * this->y + + matrix(0, 2) * this->z, + matrix(1, 0) * this->x + + matrix(1, 1) * this->y + + matrix(1, 2) * this->z, + matrix(2, 0) * this->x + + matrix(2, 1) * this->y + + matrix(2, 2) * this->z, + }; +} + +/* + * Returns the distance from one vertex to another. + */ +qreal distance(const Vertex& one, const Vertex& other) +{ + return (one - other).length(); +} + +/* + * Returns a vertex with all coordinates inverted. + */ +Vertex operator-(const Vertex& vertex) +{ + return {-vertex.x, -vertex.y, -vertex.z}; +} + +/* + * Inserts this vertex into a data stream. This is needed for vertices to be + * stored in QSettings. + */ +QDataStream& operator<<(QDataStream& out, const Vertex& vertex) +{ + return out << vertex.x << vertex.y << vertex.z; +} + +/* + * Takes a vertex from a data stream. + */ +QDataStream& operator>>(QDataStream& in, Vertex& vertex) +{ + return in >> vertex.x >> vertex.y >> vertex.z; +} + +unsigned int qHash(const Vertex& key) +{ + return qHash(key.x) ^ rotl10(qHash(key.y)) ^ rotl20(qHash(key.z)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vertex.h Sun Sep 22 11:51:41 2019 +0300 @@ -0,0 +1,59 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2019 Teemu Piippo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once +#include <functional> +#include <QVector3D> +#include "basics.h" + +struct Vertex +{ + qreal x; + qreal y; + qreal z; + // void transform(const class Matrix& matrix, const Vertex& pos); + Vertex transformed(const GLRotationMatrix& matrix) const; + void setCoordinate(Axis ax, qreal value); + Vertex& operator+=(const QVector3D& other); + Vertex operator+(const QVector3D& other) const; + QVector3D operator-(const Vertex& other) const; + Vertex operator-(const QVector3D& vector) const; + Vertex& operator-=(const QVector3D& vector); + Vertex& operator*=(qreal scalar); + Vertex operator*(qreal scalar) const; + bool operator<(const Vertex& other) const; + double& operator[](Axis ax); + double operator[](Axis ax) const; + bool operator==(const Vertex& other) const; + bool operator!=(const Vertex& other) const; + operator QVariant() const; +}; + +inline Vertex operator*(qreal scalar, const Vertex& vertex) +{ + return vertex * scalar; +} + +Q_DECLARE_METATYPE(Vertex) +qreal distance(const Vertex& one, const Vertex& other); +Vertex vertexFromVector(const QVector3D& vector); +QVector3D vertexToVector(const Vertex &vertex); +unsigned int qHash(const Vertex& key); +Vertex operator-(const Vertex& vertex); +QDataStream& operator<<(QDataStream& out, const Vertex& vertex); +QDataStream& operator>>(QDataStream& in, Vertex& vertex);