OdbDesignLib
OdbDesign ODB++ Parsing Library
 
Loading...
Searching...
No Matches
FeaturesFile.cpp
1#include "FeaturesFile.h"
2#include "ArchiveExtractor.h"
3#include <fstream>
4#include "Logger.h"
5#include "../invalid_odb_error.h"
6#include "str_utils.h"
7#include "../parse_error.h"
8#include "SymbolName.h"
9#include "equals_within.h"
10#include "macros.h"
11
12#include <algorithm>
13#include <cctype>
14
15namespace Odb::Lib::FileModel::Design
16{
17 namespace
18 {
19 void apply_default_units_to_symbols(const std::string &rawUnits, SymbolName::Vector &symbols)
20 {
21 const auto inferred = Odb::Lib::FileModel::Design::inferred_unit_type_from_features_units(rawUnits);
22 if (inferred == UnitType::None)
23 return;
24 for (const auto &sym : symbols)
25 {
26 if (sym)
27 {
28 sym->ApplyDefaultUnitTypeIfNone(inferred);
29 }
30 }
31 }
32 }
33
34 FeaturesFile::FeaturesFile()
35 : m_path(""), m_directory(""), m_numFeatures(0), m_id(static_cast<unsigned>(-1))
36 {
37 }
38
39 FeaturesFile::~FeaturesFile()
40 {
41 m_featureRecords.clear();
42 m_attributeNames.clear();
43 m_attributeTextValues.clear();
44 m_symbolNamesByName.clear();
45 m_symbolNames.clear();
46 }
47
48 bool FeaturesFile::Parse(std::filesystem::path directory, const std::string &alternateFilename /*= ""*/)
49 {
50 std::ifstream featuresFile;
51 int lineNumber = 0;
52 std::string line;
53
54 try
55 {
56 m_directory = directory;
57
58 loginfo("checking for extraction...");
59
60 std::vector<std::string> filenames;
61 if (alternateFilename.empty())
62 {
63 std::copy(std::begin(FEATURES_FILENAMES), std::end(FEATURES_FILENAMES), std::back_inserter(filenames));
64 }
65 else
66 {
67 filenames.push_back(alternateFilename);
68 }
69
70 std::filesystem::path featuresFilePath;
71 for (const auto &featuresFilename : filenames)
72 {
73 loginfo("trying features file: [" + featuresFilename + "]...");
74
75 featuresFilePath = Utils::ArchiveExtractor::getUncompressedFilePath(m_directory, featuresFilename);
76 if (exists(featuresFilePath) && is_regular_file(featuresFilePath))
77 {
78 loginfo("found features file: [" + featuresFilePath.string() + "]");
79 break;
80 }
81 }
82
83 m_path = featuresFilePath;
84
85 loginfo("any extraction complete, parsing data...");
86
87 if (!std::filesystem::exists(m_path))
88 {
89 auto message = "features file does not exist: [" + m_path.string() + "]";
90 throw invalid_odb_error(message.c_str());
91 }
92 else if (!std::filesystem::is_regular_file(m_path))
93 {
94 auto message = "features is not a file: [" + m_path.string() + "]";
95 throw invalid_odb_error(message.c_str());
96 }
97
98 featuresFile.open(m_path.string(), std::ios::in);
99 if (!featuresFile.is_open())
100 {
101 auto message = "unable to open features file: [" + m_path.string() + "]";
102 throw invalid_odb_error(message.c_str());
103 }
104
105 std::shared_ptr<FeatureRecord> pCurrentFeatureRecord;
106 std::shared_ptr<ContourPolygon> pCurrentContourPolygon;
107
108 while (std::getline(featuresFile, line))
109 {
110 lineNumber++;
111
112 // trim whitespace from beginning and end of line
113 Utils::str_trim(line);
114 if (!line.empty())
115 {
116 std::stringstream lineStream(line);
117 // char firstChar = line[0];
118
119 if (line.find(COMMENT_TOKEN) == 0)
120 {
121 // comment line
122 }
123 else if (line.find(UNITS_TOKEN) == 0)
124 {
125 // units line
126 std::string token;
127 if (!std::getline(lineStream, token, '='))
128 {
129 throw_parse_error(m_path, line, token, lineNumber);
130 }
131 else if (!std::getline(lineStream, token, '='))
132 {
133 throw_parse_error(m_path, line, token, lineNumber);
134 }
135
136 m_units = token;
137 apply_default_units_to_symbols(m_units, m_symbolNames);
138 }
139 else if (line.find("U") == 0)
140 {
141 std::string token;
142 if (!std::getline(lineStream, token, ' '))
143 {
144 throw_parse_error(m_path, line, token, lineNumber);
145 }
146 else if (!std::getline(lineStream, token, ' '))
147 {
148 throw_parse_error(m_path, line, token, lineNumber);
149 }
150
151 m_units = token;
152 apply_default_units_to_symbols(m_units, m_symbolNames);
153 }
154 else if (line.find(ID_TOKEN) == 0)
155 {
156 std::string token;
157 if (!std::getline(lineStream, token, '='))
158 {
159 throw_parse_error(m_path, line, token, lineNumber);
160 }
161 else if (!std::getline(lineStream, token, '='))
162 {
163 throw_parse_error(m_path, line, token, lineNumber);
164 }
165 m_id = std::stoul(token);
166 }
167 else if (line.find(NUM_FEATURES_TOKEN) == 0)
168 {
169 // component record line
170 std::string token;
171
172 if (!(lineStream >> token))
173 {
174 throw_parse_error(m_path, line, token, lineNumber);
175 }
176
177 if (token != NUM_FEATURES_TOKEN)
178 {
179 throw_parse_error(m_path, line, token, lineNumber);
180 }
181
182 if (!(lineStream >> m_numFeatures))
183 {
184 throw_parse_error(m_path, line, token, lineNumber);
185 }
186 }
187 else if (line.find(ATTRIBUTE_NAME_TOKEN) == 0)
188 {
189 // component attribute name line
190 std::string token;
191 // TODO: continue on failing line parse, to make a less strict/more robust parser (make a flag: enum ParseStrictness { strict, lax })
192 if (!std::getline(lineStream, token, ' '))
193 {
194 throw_parse_error(m_path, line, token, lineNumber);
195 }
196 else if (!std::getline(lineStream, token, ' '))
197 {
198 throw_parse_error(m_path, line, token, lineNumber);
199 }
200 m_attributeNames.push_back(token);
201 }
202 else if (line.find(ATTRIBUTE_VALUE_TOKEN) == 0)
203 {
204 // component attribute text string values
205 std::string token;
206 if (!std::getline(lineStream, token, ' '))
207 {
208 throw_parse_error(m_path, line, token, lineNumber);
209 }
210 else if (!std::getline(lineStream >> std::ws, token))
211 {
212 throw_parse_error(m_path, line, token, lineNumber);
213 }
214 m_attributeTextValues.push_back(token);
215 }
216 else if (line.find(SYMBOL_NAME_TOKEN) == 0)
217 {
218 // component attribute text string values
219 auto pSymbolName = std::make_shared<SymbolName>();
220 if (!pSymbolName->SymbolName::Parse(m_path, line, lineNumber))
221 {
222 throw_parse_error(m_path, line, "", lineNumber);
223 }
224 pSymbolName->ApplyDefaultUnitTypeIfNone(Odb::Lib::FileModel::Design::inferred_unit_type_from_features_units(m_units));
225 m_symbolNamesByName[pSymbolName->GetName()] = pSymbolName;
226 m_symbolNames.push_back(pSymbolName);
227 }
228 else if (line.find(FeatureRecord::LINE_TOKEN) == 0)
229 {
230 std::string token;
231 if (!(lineStream >> token) || token != FeatureRecord::LINE_TOKEN)
232 {
233 throw_parse_error(m_path, line, token, lineNumber);
234 }
235
236 auto pFeatureRecord = std::make_shared<FeatureRecord>();
237 pFeatureRecord->type = FeatureRecord::Type::Line;
238
239 if (!(lineStream >> pFeatureRecord->xs))
240 {
241 throw_parse_error(m_path, line, token, lineNumber);
242 }
243
244 if (!(lineStream >> pFeatureRecord->ys))
245 {
246 throw_parse_error(m_path, line, token, lineNumber);
247 }
248
249 if (!(lineStream >> pFeatureRecord->xe))
250 {
251 throw_parse_error(m_path, line, token, lineNumber);
252 }
253
254 if (!(lineStream >> pFeatureRecord->ye))
255 {
256 throw_parse_error(m_path, line, token, lineNumber);
257 }
258
259 if (!(lineStream >> pFeatureRecord->sym_num))
260 {
261 throw_parse_error(m_path, line, token, lineNumber);
262 }
263
264 char polarity;
265 if (!(lineStream >> polarity))
266 {
267 throw_parse_error(m_path, line, token, lineNumber);
268 }
269 switch (polarity)
270 {
271 case 'P':
272 pFeatureRecord->polarity = Polarity::Positive;
273 break;
274 case 'N':
275 pFeatureRecord->polarity = Polarity::Negative;
276 break;
277 default:
278 throw_parse_error(m_path, line, token, lineNumber);
279 }
280
281 if (!(lineStream >> pFeatureRecord->dcode))
282 {
283 throw_parse_error(m_path, line, token, lineNumber);
284 }
285
286 std::string attrIdString;
287 lineStream >> attrIdString;
288
289 if (!pFeatureRecord->ParseAttributeLookupTable(attrIdString))
290 {
291 throw_parse_error(m_path, line, token, lineNumber);
292 }
293
294 m_featureRecords.push_back(pFeatureRecord);
295 }
296 else if (line.find(FeatureRecord::PAD_TOKEN) == 0)
297 {
298 std::string token;
299 if (!(lineStream >> token) || token != FeatureRecord::PAD_TOKEN)
300 {
301 throw_parse_error(m_path, line, token, lineNumber);
302 }
303
304 auto pFeatureRecord = std::make_shared<FeatureRecord>();
305 pFeatureRecord->type = FeatureRecord::Type::Pad;
306
307 if (!(lineStream >> pFeatureRecord->x))
308 {
309 throw_parse_error(m_path, line, token, lineNumber);
310 }
311
312 if (!(lineStream >> pFeatureRecord->y))
313 {
314 throw_parse_error(m_path, line, token, lineNumber);
315 }
316
317 if (!(lineStream >> pFeatureRecord->apt_def_symbol_num))
318 {
319 throw_parse_error(m_path, line, token, lineNumber);
320 }
321 if (pFeatureRecord->apt_def_symbol_num == -1)
322 {
323 if (!(lineStream >> pFeatureRecord->apt_def_resize_factor))
324 {
325 throw_parse_error(m_path, line, token, lineNumber);
326 }
327 }
328
329 // Contract: Pads reference a symbol by FeatureRecord.sym_num.
330 // In ODB++ pad records this comes from apt_def_symbol_num; keep sym_num unset when apt_def_symbol_num == -1.
331 if (pFeatureRecord->apt_def_symbol_num >= 0 && pFeatureRecord->sym_num == -1)
332 {
333 pFeatureRecord->sym_num = pFeatureRecord->apt_def_symbol_num;
334 }
335
336 char polarity;
337 if (!(lineStream >> polarity))
338 {
339 throw_parse_error(m_path, line, token, lineNumber);
340 }
341 switch (polarity)
342 {
343 case 'P':
344 pFeatureRecord->polarity = Polarity::Positive;
345 break;
346 case 'N':
347 pFeatureRecord->polarity = Polarity::Negative;
348 break;
349 default:
350 throw_parse_error(m_path, line, token, lineNumber);
351 }
352
353 if (!(lineStream >> pFeatureRecord->dcode))
354 {
355 throw_parse_error(m_path, line, token, lineNumber);
356 }
357
358 if (!(lineStream >> pFeatureRecord->orient_def))
359 {
360 throw_parse_error(m_path, line, token, lineNumber);
361 }
362
363 std::string attrIdString;
364 lineStream >> attrIdString;
365
366 if (!pFeatureRecord->ParseAttributeLookupTable(attrIdString))
367 {
368 throw_parse_error(m_path, line, token, lineNumber);
369 }
370
371 m_featureRecords.push_back(pFeatureRecord);
372 }
373 else if (line.find(FeatureRecord::TEXT_TOKEN) == 0)
374 {
375 std::string token;
376 if (!(lineStream >> token) || token != FeatureRecord::TEXT_TOKEN)
377 {
378 throw_parse_error(m_path, line, token, lineNumber);
379 }
380
381 auto pFeatureRecord = std::make_shared<FeatureRecord>();
382 pFeatureRecord->type = FeatureRecord::Type::Text;
383
384 if (!(lineStream >> pFeatureRecord->x))
385 {
386 throw_parse_error(m_path, line, token, lineNumber);
387 }
388
389 if (!(lineStream >> pFeatureRecord->y))
390 {
391 throw_parse_error(m_path, line, token, lineNumber);
392 }
393
394 if (!(lineStream >> pFeatureRecord->font))
395 {
396 throw_parse_error(m_path, line, token, lineNumber);
397 }
398
399 char polarity;
400 if (!(lineStream >> polarity))
401 {
402 throw_parse_error(m_path, line, token, lineNumber);
403 }
404 switch (polarity)
405 {
406 case 'P':
407 pFeatureRecord->polarity = Polarity::Positive;
408 break;
409 case 'N':
410 pFeatureRecord->polarity = Polarity::Negative;
411 break;
412 default:
413 throw_parse_error(m_path, line, token, lineNumber);
414 }
415
416 if (!(lineStream >> pFeatureRecord->orient_def))
417 {
418 throw_parse_error(m_path, line, token, lineNumber);
419 }
420
421 if (pFeatureRecord->orient_def == 8 ||
422 pFeatureRecord->orient_def == 9)
423 {
424 if (!(lineStream >> pFeatureRecord->orient_def_rotation))
425 {
426 throw_parse_error(m_path, line, token, lineNumber);
427 }
428 }
429
430 if (!(lineStream >> pFeatureRecord->xsize))
431 {
432 throw_parse_error(m_path, line, token, lineNumber);
433 }
434
435 if (!(lineStream >> pFeatureRecord->ysize))
436 {
437 throw_parse_error(m_path, line, token, lineNumber);
438 }
439
440 if (!(lineStream >> pFeatureRecord->width_factor))
441 {
442 throw_parse_error(m_path, line, token, lineNumber);
443 }
444
445 if (!(lineStream >> std::quoted(pFeatureRecord->text, '\'')))
446 {
447 throw_parse_error(m_path, line, token, lineNumber);
448 }
449
450 if (!(lineStream >> pFeatureRecord->version))
451 {
452 throw_parse_error(m_path, line, token, lineNumber);
453 }
454
455 std::string attrIdString;
456 lineStream >> attrIdString;
457
458 if (!pFeatureRecord->ParseAttributeLookupTable(attrIdString))
459 {
460 throw_parse_error(m_path, line, token, lineNumber);
461 }
462
463 m_featureRecords.push_back(pFeatureRecord);
464 }
465 else if (line.find(FeatureRecord::ARC_TOKEN) == 0)
466 {
467 std::string token;
468 if (!(lineStream >> token) || token != FeatureRecord::ARC_TOKEN)
469 {
470 throw_parse_error(m_path, line, token, lineNumber);
471 }
472
473 auto pFeatureRecord = std::make_shared<FeatureRecord>();
474 pFeatureRecord->type = FeatureRecord::Type::Arc;
475
476 if (!(lineStream >> pFeatureRecord->xs))
477 {
478 throw_parse_error(m_path, line, token, lineNumber);
479 }
480
481 if (!(lineStream >> pFeatureRecord->ys))
482 {
483 throw_parse_error(m_path, line, token, lineNumber);
484 }
485
486 if (!(lineStream >> pFeatureRecord->xe))
487 {
488 throw_parse_error(m_path, line, token, lineNumber);
489 }
490
491 if (!(lineStream >> pFeatureRecord->ye))
492 {
493 throw_parse_error(m_path, line, token, lineNumber);
494 }
495
496 if (!(lineStream >> pFeatureRecord->xc))
497 {
498 throw_parse_error(m_path, line, token, lineNumber);
499 }
500
501 if (!(lineStream >> pFeatureRecord->yc))
502 {
503 throw_parse_error(m_path, line, token, lineNumber);
504 }
505
506 if (!(lineStream >> pFeatureRecord->sym_num))
507 {
508 throw_parse_error(m_path, line, token, lineNumber);
509 }
510
511 char polarity;
512 if (!(lineStream >> polarity))
513 {
514 throw_parse_error(m_path, line, token, lineNumber);
515 }
516 switch (polarity)
517 {
518 case 'P':
519 pFeatureRecord->polarity = Polarity::Positive;
520 break;
521 case 'N':
522 pFeatureRecord->polarity = Polarity::Negative;
523 break;
524 default:
525 throw_parse_error(m_path, line, token, lineNumber);
526 }
527
528 if (!(lineStream >> pFeatureRecord->dcode))
529 {
530 throw_parse_error(m_path, line, token, lineNumber);
531 }
532
533 char cw;
534 if (!(lineStream >> cw))
535 {
536 throw_parse_error(m_path, line, token, lineNumber);
537 }
538 switch (cw)
539 {
540 case 'Y':
541 pFeatureRecord->cw = true;
542 break;
543 case 'N':
544 pFeatureRecord->cw = false;
545 break;
546 default:
547 throw_parse_error(m_path, line, token, lineNumber);
548 }
549
550 std::string attrIdString;
551 lineStream >> attrIdString;
552
553 if (!pFeatureRecord->ParseAttributeLookupTable(attrIdString))
554 {
555 throw_parse_error(m_path, line, token, lineNumber);
556 }
557
558 m_featureRecords.push_back(pFeatureRecord);
559 }
560 else if (line.find(FeatureRecord::BARCODE_TOKEN) == 0)
561 {
562 std::string token;
563 if (!(lineStream >> token) || token != FeatureRecord::BARCODE_TOKEN)
564 {
565 throw_parse_error(m_path, line, token, lineNumber);
566 }
567
568 auto pFeatureRecord = std::make_shared<FeatureRecord>();
569 pFeatureRecord->type = FeatureRecord::Type::Barcode;
570
571 // TODO: barcode feature record type
572
573 // std::string attrIdString;
574 // lineStream >> attrIdString;
575
576 // if (!pFeatureRecord->ParseAttributeLookupTable(attrIdString))
577 //{
578 // throw_parse_error(m_path, line, token, lineNumber);
579 // }
580
581 m_featureRecords.push_back(pFeatureRecord);
582 }
583 else if (line.find(FeatureRecord::SURFACE_START_TOKEN) == 0 &&
584 line.size() > 1 && line[1] == ' ')
585 {
586 std::string token;
587 if (!(lineStream >> token) || token != FeatureRecord::SURFACE_START_TOKEN)
588 {
589 throw_parse_error(m_path, line, token, lineNumber);
590 }
591
592 pCurrentFeatureRecord = std::make_shared<FeatureRecord>();
593 pCurrentFeatureRecord->type = FeatureRecord::Type::Surface;
594
595 char polarity;
596 if (!(lineStream >> polarity))
597 {
598 throw_parse_error(m_path, line, token, lineNumber);
599 }
600 switch (polarity)
601 {
602 case 'P':
603 pCurrentFeatureRecord->polarity = Polarity::Positive;
604 break;
605 case 'N':
606 pCurrentFeatureRecord->polarity = Polarity::Negative;
607 break;
608 default:
609 throw_parse_error(m_path, line, token, lineNumber);
610 }
611
612 if (!(lineStream >> pCurrentFeatureRecord->dcode))
613 {
614 throw_parse_error(m_path, line, token, lineNumber);
615 }
616
617 std::string attrIdString;
618 lineStream >> attrIdString;
619
620 if (!pCurrentFeatureRecord->ParseAttributeLookupTable(attrIdString))
621 {
622 throw_parse_error(m_path, line, token, lineNumber);
623 }
624 }
625 else if (line.find(FeatureRecord::SURFACE_END_TOKEN) == 0)
626 {
627 std::string token;
628 if (!(lineStream >> token) || token != FeatureRecord::SURFACE_END_TOKEN)
629 {
630 throw_parse_error(m_path, line, token, lineNumber);
631 }
632
633 if (pCurrentFeatureRecord != nullptr)
634 {
635 m_featureRecords.push_back(pCurrentFeatureRecord);
636 pCurrentFeatureRecord.reset();
637 }
638 else
639 {
640 throw_parse_error(m_path, line, token, lineNumber);
641 }
642 }
643 else if (line.find(ContourPolygon::BEGIN_RECORD_TOKEN) == 0)
644 {
645 std::string token;
646 if (!(lineStream >> token))
647 {
648 throw_parse_error(m_path, line, token, lineNumber);
649 }
650
651 if (token != ContourPolygon::BEGIN_RECORD_TOKEN)
652 {
653 throw_parse_error(m_path, line, token, lineNumber);
654 }
655
656 pCurrentContourPolygon = std::make_shared<ContourPolygon>();
657
658 if (!(lineStream >> pCurrentContourPolygon->xStart))
659 {
660 throw_parse_error(m_path, line, token, lineNumber);
661 }
662
663 if (!(lineStream >> pCurrentContourPolygon->yStart))
664 {
665 throw_parse_error(m_path, line, token, lineNumber);
666 }
667
668 if (!(lineStream >> token))
669 {
670 throw_parse_error(m_path, line, token, lineNumber);
671 }
672
673 if (token == ContourPolygon::ISLAND_TYPE_TOKEN)
674 {
675 pCurrentContourPolygon->type = ContourPolygon::Type::Island;
676 }
677 else if (token == ContourPolygon::HOLE_TYPE_TOKEN)
678 {
679 pCurrentContourPolygon->type = ContourPolygon::Type::Hole;
680 }
681 else
682 {
683 throw_parse_error(m_path, line, token, lineNumber);
684 }
685 }
686 else if (line.find(ContourPolygon::END_RECORD_TOKEN) == 0)
687 {
688 std::string token;
689 if (!(lineStream >> token))
690 {
691 throw_parse_error(m_path, line, token, lineNumber);
692 }
693
694 if (token != ContourPolygon::END_RECORD_TOKEN)
695 {
696 throw_parse_error(m_path, line, token, lineNumber);
697 }
698
699 if (pCurrentFeatureRecord != nullptr)
700 {
701 pCurrentFeatureRecord->m_contourPolygons.push_back(pCurrentContourPolygon);
702 pCurrentContourPolygon.reset();
703 }
704 else
705 {
706 throw_parse_error(m_path, line, token, lineNumber);
707 }
708 }
709 else if (line.find(ContourPolygon::PolygonPart::ARC_RECORD_TOKEN) == 0)
710 {
711 std::string token;
712 if (!(lineStream >> token))
713 {
714 throw_parse_error(m_path, line, token, lineNumber);
715 }
716
717 if (token != ContourPolygon::PolygonPart::ARC_RECORD_TOKEN)
718 {
719 throw_parse_error(m_path, line, token, lineNumber);
720 }
721
722 auto pPolygonPart = std::make_shared<ContourPolygon::PolygonPart>();
723 pPolygonPart->type = ContourPolygon::PolygonPart::Type::Arc;
724
725 if (!(lineStream >> pPolygonPart->endX))
726 {
727 throw_parse_error(m_path, line, token, lineNumber);
728 }
729
730 if (!(lineStream >> pPolygonPart->endY))
731 {
732 throw_parse_error(m_path, line, token, lineNumber);
733 }
734
735 if (!(lineStream >> pPolygonPart->xCenter))
736 {
737 throw_parse_error(m_path, line, token, lineNumber);
738 }
739
740 if (!(lineStream >> pPolygonPart->yCenter))
741 {
742 throw_parse_error(m_path, line, token, lineNumber);
743 }
744
745 if (!(lineStream >> token))
746 {
747 throw_parse_error(m_path, line, token, lineNumber);
748 }
749
750 if (token == "y" || token == "Y")
751 {
752 pPolygonPart->isClockwise = true;
753 }
754 else if (token == "n" || token == "N")
755 {
756 pPolygonPart->isClockwise = false;
757 }
758 else
759 {
760 throw_parse_error(m_path, line, token, lineNumber);
761 }
762
763 if (pCurrentContourPolygon != nullptr)
764 {
765 pCurrentContourPolygon->m_polygonParts.push_back(pPolygonPart);
766 }
767 else
768 {
769 throw_parse_error(m_path, line, token, lineNumber);
770 }
771 }
772 else if (line.find(ContourPolygon::PolygonPart::SEGMENT_RECORD_TOKEN) == 0)
773 {
774 std::string token;
775 if (!(lineStream >> token))
776 {
777 throw_parse_error(m_path, line, token, lineNumber);
778 }
779
780 if (token != ContourPolygon::PolygonPart::SEGMENT_RECORD_TOKEN)
781 {
782 throw_parse_error(m_path, line, token, lineNumber);
783 }
784
785 auto pPolygonPart = std::make_shared<ContourPolygon::PolygonPart>();
786 pPolygonPart->type = ContourPolygon::PolygonPart::Type::Segment;
787
788 if (!(lineStream >> pPolygonPart->endX))
789 {
790 throw_parse_error(m_path, line, token, lineNumber);
791 }
792
793 if (!(lineStream >> pPolygonPart->endY))
794 {
795 throw_parse_error(m_path, line, token, lineNumber);
796 }
797
798 if (pCurrentContourPolygon != nullptr)
799 {
800 pCurrentContourPolygon->m_polygonParts.push_back(pPolygonPart);
801 }
802 else
803 {
804 throw_parse_error(m_path, line, token, lineNumber);
805 }
806 }
807 else
808 {
809 // unrecognized record line
810 parse_info pi(m_path, line, lineNumber);
811 logwarn(pi.toString("unrecognized record line in features file:"));
812 }
813 }
814 }
815
816 // do we have a current feature record? (finish up the last record in the file- i.e. ran out of file)
817 if (pCurrentFeatureRecord != nullptr)
818 {
819 // finish it up and add it to the list
820 m_featureRecords.push_back(pCurrentFeatureRecord);
821 pCurrentFeatureRecord.reset();
822 }
823
824 featuresFile.close();
825 }
826 catch (parse_error &pe)
827 {
828 auto m = pe.toString("Parse Error:");
829 logerror(m);
830 featuresFile.close();
831 throw pe;
832 }
833 catch (std::exception &e)
834 {
835 parse_info pi(m_path, line, lineNumber);
836 const auto m = pi.toString();
837 logexception_msg(e, m);
838 featuresFile.close();
839 throw e;
840 }
841
842 return true;
843 }
844
845 std::string FeaturesFile::GetUnits() const
846 {
847 return m_units;
848 }
849
850 std::filesystem::path FeaturesFile::GetPath()
851 {
852 return m_path;
853 }
854
855 std::filesystem::path FeaturesFile::GetDirectory()
856 {
857 return m_directory;
858 }
859
860 int FeaturesFile::GetNumFeatures() const
861 {
862 return m_numFeatures;
863 }
864
865 unsigned int FeaturesFile::GetId() const
866 {
867 return m_id;
868 }
869
870 const SymbolName::StringMap &FeaturesFile::GetSymbolNamesByName() const
871 {
872 return m_symbolNamesByName;
873 }
874
875 const SymbolName::Vector &FeaturesFile::GetSymbolNames() const
876 {
877 return m_symbolNames;
878 }
879
880 const FeaturesFile::FeatureRecord::Vector &FeaturesFile::GetFeatureRecords() const
881 {
882 return m_featureRecords;
883 }
884
885 std::unique_ptr<Odb::Lib::Protobuf::FeaturesFile> FeaturesFile::to_protobuf() const
886 {
887 std::unique_ptr<Odb::Lib::Protobuf::FeaturesFile> pFeaturesFileMessage(new Odb::Lib::Protobuf::FeaturesFile);
888 pFeaturesFileMessage->set_id(m_id);
889 pFeaturesFileMessage->set_numfeatures(m_numFeatures);
890 pFeaturesFileMessage->set_units(m_units);
891 for (const auto &pFeatureRecord : m_featureRecords)
892 {
893 pFeaturesFileMessage->add_featurerecords()->CopyFrom(*pFeatureRecord->to_protobuf());
894 }
895 for (const auto &kvSymbolName : m_symbolNamesByName)
896 {
897 (*pFeaturesFileMessage->mutable_symbolnamesbyname())[kvSymbolName.first] = *kvSymbolName.second->to_protobuf();
898 }
899 for (const auto &symbolName : m_symbolNames)
900 {
901 pFeaturesFileMessage->add_symbolnames()->CopyFrom(*symbolName->to_protobuf());
902 }
903 return pFeaturesFileMessage;
904 }
905
906 void FeaturesFile::from_protobuf(const Odb::Lib::Protobuf::FeaturesFile &message)
907 {
908 m_id = message.id();
909 m_numFeatures = message.numfeatures();
910 m_units = message.units();
911 for (const auto &featureRecordMessage : message.featurerecords())
912 {
913 std::shared_ptr<FeatureRecord> pFeatureRecord(new FeatureRecord);
914 pFeatureRecord->from_protobuf(featureRecordMessage);
915 m_featureRecords.push_back(pFeatureRecord);
916 }
917 if (message.symbolnames().size() > 0)
918 {
919 // create both collections from symbolNames
920 for (const auto &symbolNameMessage : message.symbolnames())
921 {
922 auto pSymbolName = std::make_shared<SymbolName>();
923 pSymbolName->from_protobuf(symbolNameMessage);
924 m_symbolNames.push_back(pSymbolName);
925 m_symbolNamesByName[pSymbolName->GetName()] = pSymbolName;
926 }
927 }
928 else if (message.symbolnamesbyname().size() > 0)
929 {
930 // create both collections from symbolsByName
931 for (const auto &kvSymbolNameMessage : message.symbolnamesbyname())
932 {
933 auto pSymbolName = std::make_shared<SymbolName>();
934 pSymbolName->from_protobuf(kvSymbolNameMessage.second);
935 m_symbolNamesByName[kvSymbolNameMessage.first] = pSymbolName;
936 m_symbolNames.push_back(pSymbolName);
937 }
938 }
939 }
940
941 bool FeaturesFile::Save(std::ostream &os)
942 {
943 return true;
944 }
945
946 FeaturesFile::FeatureRecord::~FeatureRecord()
947 {
948 m_contourPolygons.clear();
949 }
950
951 const ContourPolygon::Vector &FeaturesFile::FeatureRecord::GetContourPolygons() const
952 {
953 return m_contourPolygons;
954 }
955
956 std::unique_ptr<Odb::Lib::Protobuf::FeaturesFile::FeatureRecord> Odb::Lib::FileModel::Design::FeaturesFile::FeatureRecord::to_protobuf() const
957 {
958 std::unique_ptr<Odb::Lib::Protobuf::FeaturesFile::FeatureRecord> pFeatureRecordMessage(new Odb::Lib::Protobuf::FeaturesFile::FeatureRecord);
959 pFeatureRecordMessage->set_apt_def_resize_factor(apt_def_resize_factor);
960 pFeatureRecordMessage->set_xc(xc);
961 pFeatureRecordMessage->set_yc(yc);
962 pFeatureRecordMessage->set_cw(cw);
963 pFeatureRecordMessage->set_font(font);
964 pFeatureRecordMessage->set_xsize(xsize);
965 pFeatureRecordMessage->set_ysize(ysize);
966 pFeatureRecordMessage->set_width_factor(width_factor);
967 pFeatureRecordMessage->set_text(text);
968 pFeatureRecordMessage->set_version(version);
969 if (sym_num >= 0)
970 {
971 pFeatureRecordMessage->set_sym_num(sym_num);
972 }
973 pFeatureRecordMessage->set_polarity(static_cast<Odb::Lib::Protobuf::Polarity>(polarity));
974 pFeatureRecordMessage->set_dcode(dcode);
975 pFeatureRecordMessage->set_id(id);
976 pFeatureRecordMessage->set_orient_def(orient_def);
977 pFeatureRecordMessage->set_orient_def_rotation(orient_def_rotation);
978 pFeatureRecordMessage->set_type(static_cast<Odb::Lib::Protobuf::FeaturesFile::FeatureRecord::Type>(type));
979 pFeatureRecordMessage->set_xs(xs);
980 pFeatureRecordMessage->set_ys(ys);
981 pFeatureRecordMessage->set_xe(xe);
982 pFeatureRecordMessage->set_ye(ye);
983 pFeatureRecordMessage->set_x(x);
984 pFeatureRecordMessage->set_y(y);
985 if (apt_def_symbol_num >= 0)
986 {
987 pFeatureRecordMessage->set_apt_def_symbol_num(apt_def_symbol_num);
988 }
989 for (const auto &pContourPolygon : m_contourPolygons)
990 {
991 pFeatureRecordMessage->add_contourpolygons()->CopyFrom(*pContourPolygon->to_protobuf());
992 }
993 for (const auto &kvAttributeAssignment : m_attributeLookupTable)
994 {
995 (*pFeatureRecordMessage->mutable_attributelookuptable())[kvAttributeAssignment.first] = kvAttributeAssignment.second;
996 }
997 return pFeatureRecordMessage;
998 }
999
1000 void Odb::Lib::FileModel::Design::FeaturesFile::FeatureRecord::from_protobuf(const Odb::Lib::Protobuf::FeaturesFile::FeatureRecord &message)
1001 {
1002 apt_def_resize_factor = message.apt_def_resize_factor();
1003 xc = message.xc();
1004 yc = message.yc();
1005 cw = message.cw();
1006 font = message.font();
1007 xsize = message.xsize();
1008 ysize = message.ysize();
1009 width_factor = message.width_factor();
1010 text = message.text();
1011 version = message.version();
1012 sym_num = message.has_sym_num() ? message.sym_num() : -1;
1013 polarity = static_cast<Polarity>(message.polarity());
1014 dcode = message.dcode();
1015 id = message.id();
1016 orient_def = message.orient_def();
1017 orient_def_rotation = message.orient_def_rotation();
1018 type = static_cast<Type>(message.type());
1019 xs = message.xs();
1020 ys = message.ys();
1021 xe = message.xe();
1022 ye = message.ye();
1023 x = message.x();
1024 y = message.y();
1025 apt_def_symbol_num = message.has_apt_def_symbol_num() ? message.apt_def_symbol_num() : -1;
1026 for (const auto &contourPolygonMessage : message.contourpolygons())
1027 {
1028 std::shared_ptr<ContourPolygon> pContourPolygon(new ContourPolygon);
1029 pContourPolygon->from_protobuf(contourPolygonMessage);
1030 m_contourPolygons.push_back(pContourPolygon);
1031 }
1032 for (const auto &kvAttributeAssignment : message.attributelookuptable())
1033 {
1034 m_attributeLookupTable[kvAttributeAssignment.first] = kvAttributeAssignment.second;
1035 }
1036 }
1037
1038 SymbolName::Vector collect_symbols(const FeaturesFile &featuresFile)
1039 {
1040 const auto &vec = featuresFile.GetSymbolNames();
1041 if (!vec.empty())
1042 return vec;
1043
1044 SymbolName::Vector collected;
1045 for (const auto &kv : featuresFile.GetSymbolNamesByName())
1046 {
1047 collected.push_back(kv.second);
1048 }
1049 return collected;
1050 }
1051
1052 UnitType inferred_unit_type_from_features_units(const std::string &rawUnits)
1053 {
1054 if (rawUnits.empty())
1055 return UnitType::None;
1056 std::string u = rawUnits;
1057 std::transform(u.begin(), u.end(), u.begin(), [](unsigned char c)
1058 { return static_cast<char>(std::tolower(c)); });
1059 if (u == "mm" || u == "millimeter" || u == "millimeters" || u == "micron" || u == "microns" || u == "um" || u == "µm")
1060 {
1061 return UnitType::Metric;
1062 }
1063 if (u == "inch" || u == "inches" || u == "in" || u == "mil" || u == "mils")
1064 {
1065 return UnitType::Imperial;
1066 }
1067 return UnitType::None;
1068 }
1069}