Sun, 09 Apr 2023 13:28:36 +0300
Move some recent file handling to MainWindow
/* * LDForge: LDraw parts authoring CAD * Copyright (C) 2013 - 2020 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 <QRegularExpression> #include <QIODevice> #include "src/ldrawalgorithm.h" #include "src/model.h" #include "src/parser.h" #define NUMBER_REGEX R"(([+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))))" #define SPACE_REGEX R"(\s+)" #define VEC3_REGEX NUMBER_REGEX SPACE_REGEX NUMBER_REGEX SPACE_REGEX NUMBER_REGEX #define TWO_VECTORS VEC3_REGEX SPACE_REGEX VEC3_REGEX #define THREE_VECTORS TWO_VECTORS SPACE_REGEX VEC3_REGEX #define FOUR_VECTORS THREE_VECTORS SPACE_REGEX VEC3_REGEX static const auto& exprs() { static const struct { QRegularExpression subfileRe{QStringLiteral( R"(^\s*(1)\s+(\d+)\s+)" FOUR_VECTORS SPACE_REGEX R"(([^ ]+)\s*$)" )}; QRegularExpression edgeRe{QStringLiteral( R"(^\s*(2)\s+(\d+)\s+)" TWO_VECTORS R"(\s*$)" )}; QRegularExpression triangleRe{QStringLiteral( R"(^\s*(3)\s+(\d+)\s+)" THREE_VECTORS R"(\s*$)" )}; QRegularExpression quadRe{QStringLiteral( R"(^\s*(4)\s+(\d+)\s+)" FOUR_VECTORS R"(\s*$)" )}; QRegularExpression cedgeRe{QStringLiteral( R"(^\s*(5)\s+(\d+)\s+)" FOUR_VECTORS R"(\s*$)" )}; #if 0 QRegularExpression bfcRe{QStringLiteral( R"(^\s*(0) (BFC (?:CERTIFY CCW|CERTIFY CW|NOCERTIFY|INVERTNEXT|CLIP|NOCLIP))\s*$)" )}; #endif QRegularExpression commentRe{QStringLiteral( R"(^\s*(0)\s+(.+)$)" )}; } result; return result; } template<Attribute Attrib, typename T, Attribute... Attribs> QString attrib(const LineType<T, Attribs...>& parsed) { const int index = attribIndex<LineType<T, Attribs...>, Attrib>; const TextRange& range = parsed.positions[index]; const QString content = parsed.content; return parsed.content.mid(range.start, range.length); } template<Attribute X, Attribute Y, Attribute Z, typename T, Attribute... Attribs> glm::vec3 vectorAttrib(const LineType<T, Attribs...>& parsed) { return glm::vec3{ attrib<X>(parsed).toFloat(), attrib<Y>(parsed).toFloat(), attrib<Z>(parsed).toFloat(), }; } template<typename T> ColorIndex colorAttrib(T* parsed) { return ColorIndex{attrib<Attribute::Color>(*parsed).toInt()}; } opt<ParsedLine> parse(const QString& line) { const auto tryRe = [&line](const QRegularExpression& re){ opt<QRegularExpressionMatch> result = re.match(line); if (not result->hasMatch()) { result.reset(); } return result; }; opt<ParsedLine> result; //! \brief Put value into result, add matched ranges to it and return a pointer const auto init = [&]<typename T>(T&& value, const QRegularExpressionMatch& match){ result = value; T* parsed = &std::get<T>(*result); for (int i = 0; i < countof(T::attributes); ++i) { parsed->positions[i] = { .start = match.capturedStart(i + 1), .length = match.capturedLength(i + 1), }; } parsed->content = match.captured(0); return parsed; }; if (auto line1Match = tryRe(exprs().commentRe)) { LineType0* const parsed = init(LineType0{}, *line1Match); parsed->value = Comment{attrib<Attribute::Text>(*parsed)}; } if (auto line1Match = tryRe(exprs().subfileRe)) { LineType1* const parsed = init(LineType1{}, *line1Match); parsed->value = { SubfileReference{ .name = attrib<Attribute::Name>(*parsed), .transformation = glm::mat4{ glm::vec4{vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), 0}, glm::vec4{vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed), 0}, glm::vec4{vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed), 0}, glm::vec4{vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), 1} }, }, colorAttrib(parsed), }; } else if (auto line2Match = tryRe(exprs().edgeRe)) { LineType2* const parsed = init(LineType2{}, *line2Match); parsed->value = { LineSegment{ .p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), .p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), }, colorAttrib(parsed), }; } else if (auto line3Match = tryRe(exprs().triangleRe)) { LineType3* const parsed = init(LineType3{}, *line3Match); parsed->value = { Triangle{ .p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), .p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), .p3 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed), }, colorAttrib(parsed), }; } else if (auto line4Match = tryRe(exprs().quadRe)) { LineType4* const parsed = init(LineType4{}, *line4Match); parsed->value = { Quadrilateral{ .p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), .p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), .p3 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed), .p4 = vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed), }, colorAttrib(parsed), }; } else if (auto line5Match = tryRe(exprs().cedgeRe)) { LineType5* const parsed = init(LineType5{}, *line5Match); parsed->value = { ConditionalEdge{ .p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), .p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), .c1 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed), .c2 = vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed), }, colorAttrib(parsed), }; } return result; } #if 0 static QRegExp re{R"((?:(\d+)\\)?(\d+)-(\d)+([a-z]+)\.dat)"}; if (re.exactMatch(name)) { const auto p = std::find(std::begin(circularPrimitiveStems), std::end(circularPrimitiveStems), re.cap(4)); const unsigned int divisions = (re.cap(1).isEmpty()) ? 16 : re.cap(1).toUInt(); const unsigned int segments = re.cap(2).toUInt() * divisions / re.cap(3).toUInt(); if (p != std::end(circularPrimitiveStems)) { const auto type = static_cast<CircularPrimitive::Type>(p - std::begin(circularPrimitiveStems)); return Colored<CircularPrimitive>{ CircularPrimitive{ .type = type, .fraction = {segments, divisions}, .transformation = transform, }, color, }; } } #endif