16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 */ |
17 */ |
18 |
18 |
19 #include "model.h" |
19 #include "model.h" |
20 #include "parser.h" |
20 #include "parser.h" |
21 #include "linetypes/conditionaledge.h" |
21 #include "ldrawalgorithm.h" |
22 #include "linetypes/edge.h" |
|
23 #include "linetypes/errorline.h" |
|
24 #include "linetypes/metacommand.h" |
|
25 #include "linetypes/object.h" |
|
26 #include "linetypes/quadrilateral.h" |
|
27 #include "linetypes/subfilereference.h" |
|
28 #include "linetypes/triangle.h" |
|
29 |
22 |
30 struct BodyParseError |
23 struct BodyParseError |
31 { |
24 { |
32 QString message; |
25 QString message; |
33 }; |
26 }; |
138 } |
132 } |
139 } |
133 } |
140 return result; |
134 return result; |
141 } |
135 } |
142 |
136 |
143 static std::unique_ptr<ldraw::Object> parseType0Line( |
137 static Comment parseType0Line(const QString& line) |
144 const QString& line, |
138 { |
145 const QStringList& tokens) |
139 return {line.mid(1).trimmed()}; |
146 { |
140 } |
147 Q_UNUSED(tokens) |
141 |
148 return std::make_unique<ldraw::MetaCommand>(line.mid(1).trimmed()); |
142 static Colored<SubfileReference> parseType1Line(const QStringList& tokens) |
149 } |
143 { |
150 |
|
151 static std::unique_ptr<ldraw::SubfileReference> parseType1Line( |
|
152 const QString& line, |
|
153 const QStringList& tokens) |
|
154 { |
|
155 Q_UNUSED(line) |
|
156 constexpr int colorPosition = 1; |
144 constexpr int colorPosition = 1; |
157 constexpr int positionPosition = 2; // 2..4 |
145 constexpr int positionPosition = 2; // 2..4 |
158 constexpr int transformPosition = 5; // 5..13 |
146 constexpr int transformPosition = 5; // 5..13 |
159 constexpr int namePosition = 14; |
147 constexpr int namePosition = 14; |
160 if (tokens.size() != 15) |
148 if (tokens.size() != 15) |
162 throw BodyParseError{"wrong amount of tokens in a type-1 line"}; |
150 throw BodyParseError{"wrong amount of tokens in a type-1 line"}; |
163 } |
151 } |
164 const ldraw::Color color = colorFromString(tokens[colorPosition]); |
152 const ldraw::Color color = colorFromString(tokens[colorPosition]); |
165 const glm::mat4 transform = matrixFromStrings(tokens, transformPosition, positionPosition); |
153 const glm::mat4 transform = matrixFromStrings(tokens, transformPosition, positionPosition); |
166 const QString& name = tokens[namePosition]; |
154 const QString& name = tokens[namePosition]; |
167 return std::make_unique<ldraw::SubfileReference>(transform, name, color); |
155 return Colored<SubfileReference>{ |
168 } |
156 { |
169 |
157 .name = name, |
170 template<typename T, int NumVertices> |
158 .transformation = transform, |
171 static std::unique_ptr<T> parsePolygon( |
159 }, |
172 const QString& line, |
160 color, |
173 const QStringList& tokens) |
161 }; |
174 { |
162 } |
175 Q_UNUSED(line) |
163 |
|
164 template<int NumVertices> |
|
165 static auto parsePolygon(const QStringList& tokens) |
|
166 { |
176 constexpr int colorPosition = 1; |
167 constexpr int colorPosition = 1; |
177 auto vertexPosition = [](int n) { return 2 + 3*n; }; |
168 auto vertexPosition = [](int n) { return 2 + 3*n; }; |
178 if (tokens.size() != 2 + 3 * NumVertices) |
169 if (tokens.size() != 2 + 3 * NumVertices) |
179 { |
170 { |
180 throw BodyParseError{"wrong amount of tokens"}; |
171 throw BodyParseError{"wrong amount of tokens"}; |
183 std::array<glm::vec3, NumVertices> vertices; |
174 std::array<glm::vec3, NumVertices> vertices; |
184 for (int i = 0; i < NumVertices; i += 1) |
175 for (int i = 0; i < NumVertices; i += 1) |
185 { |
176 { |
186 vertices[unsigned_cast(i)] = vertexFromStrings(tokens, vertexPosition(i)); |
177 vertices[unsigned_cast(i)] = vertexFromStrings(tokens, vertexPosition(i)); |
187 } |
178 } |
188 return std::make_unique<T>(vertices, color); |
179 return std::make_pair(vertices, color); |
189 } |
180 } |
190 |
181 |
191 std::unique_ptr<ldraw::Object> Parser::parseFromString(QString line) |
182 ModelElement parseLDrawLine(QString line) |
192 { |
183 { |
193 line = line.trimmed(); |
184 line = line.trimmed(); |
194 try |
185 try |
195 { |
186 { |
196 const QStringList tokens = line.split(QRegExp{R"(\s+)"}); |
187 const QStringList tokens = line.split(QRegExp{R"(\s+)"}); |
197 if (tokens.empty() or tokens == QStringList{{""}}) |
188 if (tokens.empty() or tokens == QStringList{{""}}) |
198 { |
189 { |
199 return std::make_unique<ldraw::Empty>(); |
190 return Empty{}; |
200 } |
191 } |
201 bool ok_code; |
192 bool ok_code; |
202 const int code = tokens[0].toInt(&ok_code); |
193 const int code = tokens[0].toInt(&ok_code); |
203 if (not ok_code) |
194 if (not ok_code) |
204 { |
195 { |
205 throw BodyParseError{"line type was not an integer"}; |
196 throw BodyParseError{"line type was not an integer"}; |
206 } |
197 } |
207 switch (code) |
198 switch (code) |
208 { |
199 { |
209 case 0: |
200 case 0: |
210 return parseType0Line(line, tokens); |
201 return parseType0Line(line); |
211 case 1: |
202 case 1: |
212 return parseType1Line(line, tokens); |
203 return parseType1Line(tokens); |
213 case 2: |
204 case 2: |
214 return parsePolygon<ldraw::Edge, 2>(line, tokens); |
205 { |
|
206 const auto pair = parsePolygon<2>(tokens); |
|
207 return Colored<LineSegment>{{pair.first[0], pair.first[1]}, pair.second}; |
|
208 } |
215 case 3: |
209 case 3: |
216 return parsePolygon<ldraw::Triangle, 3>(line, tokens); |
210 { |
|
211 const auto pair = parsePolygon<3>(tokens); |
|
212 return Colored<Triangle>{{pair.first[0], pair.first[1], pair.first[2]}, pair.second |
|
213 }; |
|
214 } |
217 case 4: |
215 case 4: |
218 return parsePolygon<ldraw::Quadrilateral, 4>(line, tokens); |
216 { |
|
217 const auto pair = parsePolygon<4>(tokens); |
|
218 const Quadrilateral quad{pair.first[0], pair.first[1], pair.first[2], pair.first[3]}; |
|
219 return Colored<Quadrilateral>{quad, pair.second}; |
|
220 } |
219 case 5: |
221 case 5: |
220 return parsePolygon<ldraw::ConditionalEdge, 4>(line, tokens); |
222 { |
|
223 const auto pair = parsePolygon<4>(tokens); |
|
224 const ConditionalEdge cedge{pair.first[0], pair.first[1], pair.first[2], pair.first[3]}; |
|
225 return Colored<ConditionalEdge>{cedge, pair.second}; |
|
226 } |
221 default: |
227 default: |
222 throw BodyParseError{utility::format("bad line type '%1'", code)}; |
228 throw BodyParseError{utility::format("bad line type '%1'", code)}; |
223 } |
229 } |
224 } |
230 } |
225 catch(const BodyParseError& error) |
231 catch(const BodyParseError& error) |
226 { |
232 { |
227 return std::make_unique<ldraw::ErrorLine>(line, error.message); |
233 return ParseError{line}; |
228 } |
234 } |
229 } |
235 } |