Sun, 16 Jun 2013 00:10:11 +0300
Added support for regex-based primitive categorization
changelog.txt | file | annotate | diff | comparison | revisions | |
data/primitive-categories.cfg | file | annotate | diff | comparison | revisions | |
ldforge.qrc | file | annotate | diff | comparison | revisions | |
mkqrc.sh | file | annotate | diff | comparison | revisions | |
src/addObjectDialog.cpp | file | annotate | diff | comparison | revisions | |
src/file.cpp | file | annotate | diff | comparison | revisions | |
src/primitives.cpp | file | annotate | diff | comparison | revisions | |
src/primitives.h | file | annotate | diff | comparison | revisions | |
src/types.h | file | annotate | diff | comparison | revisions |
--- a/changelog.txt Sat Jun 15 19:14:42 2013 +0300 +++ b/changelog.txt Sun Jun 16 00:10:11 2013 +0300 @@ -6,7 +6,8 @@ - Added ability to snap to pre-existing vertices while drawing. - When drawing, drawn vertices now display coordinate labels. - Replaced parts list in subfile item list with a primitive listing. Listing is generated either if - it's not cached (prims.cfg in configuration directory) or on user demand. + it's not cached (prims.cfg in configuration directory) or on user demand. Primitives can be categorised + with the use of a regex-based configuration file, with a valid default which should cater to most users. - Added an export to file action, moved it + insert from to File menu - Parts are now zoomed to fit properly, making the initial view of the part clearer. - Replace coords: allow replacing all coords regardless of original value, plus relative moving (offset)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/primitive-categories.cfg Sun Jun 16 00:10:11 2013 +0300 @@ -0,0 +1,103 @@ +# This file determines the rules based on which primitives are grouped into +# categories. +# +# Categories are defined as lines with their names, followed by lines +# containing rules. Rules are regular expressions prefixed with either: +# - f: for filename matching, or +# - t: for file title matching. +# +# Primitives are matched by the regular expressions in order as they are +# defined, the first category whose regex matches the primitive's filename +# gets the primitive. +# +# Any primitive which does not match any of the regexes default into an +# "Other" category. Do not define this category explicitly! +# +# All lines starting with # are comments and are ignored. + +Rings +f:[0-9]+\-[0-9]+ring[0-9]\.dat +f:[0-9]+\-[0-9]+rin[0-9]+\.dat +f:[0-9]+\-[0-9]+ri[0-9]+\.dat + +Cones +f:[0-9]+\-[0-9]+cone[0-9]\.dat +f:[0-9]+\-[0-9]+con[0-9]+\.dat +f:[0-9]+\-[0-9]+ri[0-9]+\.dat + +Circles +f:[0-9]+\-[0-9]+edge\.dat + +Cylinders +f:[0-9]+\-[0-9]+cyli[0-9]*\.dat + +Discs +f:[0-9]+\-[0-9]+disc\.dat + +Disc Negatives +f:[0-9]+\-[0-9]+ndis\.dat + +Open/Closed Cylinders +f:[0-9]+\-[0-9]+cylo\.dat +f:[0-9]+\-[0-9]+cylc\.dat +f:[0-9]+\-[0-9]+cylc2\.dat + +Sloped Cylinders +f:[0-9]+\-[0-9]+cyls\.dat +f:[0-9]+\-[0-9]+cys2\.dat +f:[0-9]+\-[0-9]+cylse\.dat + +Chords +f:[0-9]+\-[0-9]+chrd\.dat +f:[0-9]+\-[0-9]+chr\.dat + +Spheres +f:[0-9]+\-[0-9]+sphe\.dat + +Torii (Inside) +f:t[0-9]+i[0-9]+\.dat + +Torii (Outside) +f:r[0-9]+o[0-9]+\.dat +f:t[0-9]+o[0-9]+\.dat + +Torii (Tube) +f:t[0-9]+q[0-9]+\.dat + +Rings (48) +f:48\\[0-9]+\-[0-9]+ring[0-9]\.dat +f:48\\[0-9]+\-[0-9]+rin[0-9]+\.dat +f:48\\[0-9]+\-[0-9]+ri[0-9]+\.dat + +Cones (48) +f:48\\[0-9]+\-[0-9]+cone[0-9]\.dat +f:48\\[0-9]+\-[0-9]+con[0-9]+\.dat +f:48\\[0-9]+\-[0-9]+ri[0-9]+\.dat + +Other (48) +f:48\\.*\.dat + +Rectangles +f:rect.*\.dat + +Boxes +f:box.*\.dat + +Studs +f:stud.*\.dat + +Studs (Fast-Draw) +f:stu2.*\.dat + +Stud Groups +f:stug.*\.dat + +Gear Teeth +f:tooth.*\.dat +f:tootb.*\.dat + +Technic +f:axle.*\.dat +f:axl[0-9e]ho[le0-9]+\.dat +f:peghol.*\.dat +t:Technic .* \ No newline at end of file
--- a/ldforge.qrc Sat Jun 15 19:14:42 2013 +0300 +++ b/ldforge.qrc Sun Jun 16 00:10:11 2013 +0300 @@ -100,6 +100,7 @@ <file>./icons/visibility.png</file> <file>./icons/wireframe.png</file> <file>./icons/ytruder.png</file> + <file>data/primitive-categories.cfg</file> <file>LICENSE</file> <file>LICENSE.icons</file> </qresource>
--- a/mkqrc.sh Sat Jun 15 19:14:42 2013 +0300 +++ b/mkqrc.sh Sun Jun 16 00:10:11 2013 +0300 @@ -1,7 +1,7 @@ #!/bin/bash QRCFILE=ldforge.qrc -FILES=$(echo ./icons/*.* LICENSE LICENSE.icons) +FILES=$(echo ./icons/*.* data/*.* LICENSE LICENSE.icons) printf "" > $QRCFILE
--- a/src/addObjectDialog.cpp Sat Jun 15 19:14:42 2013 +0300 +++ b/src/addObjectDialog.cpp Sun Jun 16 00:10:11 2013 +0300 @@ -96,17 +96,22 @@ coordCount = 3; tw_subfileList = new QTreeWidget (); - SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, null); - parentItem->setText (0, "Primitives"); - QList<QTreeWidgetItem*> subfileItems; + tw_subfileList->setHeaderLabel ("Primitives"); - for (Primitive& info : g_Primitives) { - SubfileListItem* item = new SubfileListItem (parentItem, &info); - item->setText (0, fmt ("%1 - %2", info.name, info.title)); - subfileItems << item; + for (PrimitiveCategory& cat : g_PrimitiveCategories) { + SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, null); + parentItem->setText (0, cat.name ()); + QList<QTreeWidgetItem*> subfileItems; + + for (Primitive& prim : cat.prims) { + SubfileListItem* item = new SubfileListItem (parentItem, &prim); + item->setText (0, fmt ("%1 - %2", prim.name, prim.title)); + subfileItems << item; + } + + tw_subfileList->addTopLevelItem (parentItem); } - tw_subfileList->addTopLevelItem (parentItem); connect (tw_subfileList, SIGNAL (itemSelectionChanged ()), this, SLOT (slot_subfileTypeChanged ())); lb_subfileName = new QLabel ("File:"); le_subfileName = new QLineEdit;
--- a/src/file.cpp Sat Jun 15 19:14:42 2013 +0300 +++ b/src/file.cpp Sun Jun 16 00:10:11 2013 +0300 @@ -38,7 +38,6 @@ cfg (str, io_recentfiles, ""); static bool g_loadingMainFile = false; -bool g_primListerMutex = false; // ============================================================================= namespace LDPaths {
--- a/src/primitives.cpp Sat Jun 15 19:14:42 2013 +0300 +++ b/src/primitives.cpp Sun Jun 16 00:10:11 2013 +0300 @@ -1,11 +1,46 @@ #include <QDir> #include <QThread> +#include <QRegExp> #include "file.h" #include "gui.h" #include "primitives.h" -PrimitiveLister* g_activePrimLister = null; -vector<Primitive> g_Primitives; +vector<PrimitiveCategory> g_PrimitiveCategories; +static PrimitiveLister* g_activePrimLister = null; +static bool g_primListerMutex = false; +vector<Primitive> g_primitives; + +static void populateCategories (); +static void loadPrimitiveCatgories (); + +// ============================================================================= +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// ============================================================================= +void loadPrimitives () { + print ("Loading primitives...\n"); + + loadPrimitiveCatgories (); + + // Try to load prims.cfg + File conf (config::dirpath () + "prims.cfg", File::Read); + if (!conf) { + // No prims.cfg, build it + PrimitiveLister::start (); + } else { + for (str line : conf) { + int space = line.indexOf (" "); + if (space == -1) + continue; + + Primitive info; + info.name = line.left (space); + info.title = line.mid (space + 1); + g_primitives << info; + } + } + + populateCategories (); +} // ============================================================================= // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -23,6 +58,9 @@ } } +// ============================================================================= +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// ============================================================================= void PrimitiveLister::work () { g_activePrimLister = this; m_prims.clear (); @@ -54,6 +92,9 @@ info.title = info.title.simplified (); } + // Figure which category to use + info.cat = null; + m_prims << info; emit update (++i); } @@ -66,13 +107,15 @@ conf.close (); g_primListerMutex = true; - g_Primitives = m_prims; + g_primitives = m_prims; g_primListerMutex = false; - g_activePrimLister = null; emit workDone (); } +// ============================================================================= +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// ============================================================================= void PrimitiveLister::start () { if (g_activePrimLister) return; @@ -88,25 +131,122 @@ listerThread->start (); } -void loadPrimitives () { - g_Primitives.clear (); +static PrimitiveCategory* findCategory (str name) { + for (PrimitiveCategory& cat : g_PrimitiveCategories) + if (cat.name () == name) + return &cat; - // Try to load prims.cfg - File conf (config::dirpath () + "prims.cfg", File::Read); - if (!conf) { - // No prims.cfg, build it - PrimitiveLister::start (); - return; + return null; +} + +// ============================================================================= +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// ============================================================================= +static void populateCategories () { + for (PrimitiveCategory& cat : g_PrimitiveCategories) + cat.prims.clear (); + + PrimitiveCategory* unmatched = findCategory ("Other"); + + if (!unmatched) { + // Shouldn't happen.. but catch it anyway. + print ("No `Other` category found! Creating one...\n"); + PrimitiveCategory cat; + cat.setName ("Other"); + unmatched = &(g_PrimitiveCategories << cat); } - for (str line : conf) { - int space = line.indexOf (" "); - if (space == -1) - continue; + for (Primitive& prim : g_primitives) { + bool matched = false; + + // Go over the categories and their regexes, if and when there's a match, + // the primitive's category is set to the category the regex beloings to. + for (PrimitiveCategory& cat : g_PrimitiveCategories) { + for (PrimitiveCategory::RegexEntry& entry : cat.regexes) { + switch (entry.type) { + case PrimitiveCategory::Filename: + // f-regex, check against filename + matched = entry.regex.exactMatch (prim.name); + break; + + case PrimitiveCategory::Title: + // t-regex, check against title + matched = entry.regex.exactMatch (prim.title); + break; + } + + if (matched) { + prim.cat = &cat; + break; + } + } + + // Drop out if a category was decided on. + if (prim.cat) + break; + } - Primitive info; - info.name = line.left (space); - info.title = line.mid (space + 1); - g_Primitives << info; + // If there was a match, add the primitive to the category. + // Otherwise, add it to the list of unmatched primitives. + if (prim.cat) + prim.cat->prims << prim; + else + unmatched->prims << prim; } +} + +// ============================================================================= +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// ============================================================================= +static void loadPrimitiveCatgories () { + g_PrimitiveCategories.clear (); + + File f (config::dirpath () + "primregexps.cfg", File::Read); + + if (!f) + f.open (":/data/primitive-categories.cfg", File::Read); + + if (!f) + critical ("Failed to open primitive categories!"); + + if (f) { + PrimitiveCategory cat; + + for (str line : f) { + int colon; + + if (line.length () == 0 || line[0] == '#') + continue; + + if ((colon = line.indexOf (":")) == -1) { + if (cat.regexes.size () > 0) + g_PrimitiveCategories << cat; + + cat.regexes.clear (); + cat.prims.clear (); + cat.setName (line); + } else { + str cmd = line.left (colon); + + PrimitiveCategory::Type type = PrimitiveCategory::Filename; + if (cmd == "f") { + type = PrimitiveCategory::Filename; + } else if (cmd == "t") { + type = PrimitiveCategory::Title; + } + + QRegExp regex (line.mid (colon + 1)); + PrimitiveCategory::RegexEntry entry = { regex, type }; + cat.regexes << entry; + } + } + + if (cat.regexes.size () > 0) + g_PrimitiveCategories << cat; + } + + // Add a category for unmatched primitives + PrimitiveCategory cat; + cat.setName ("Other"); + g_PrimitiveCategories << cat; } \ No newline at end of file
--- a/src/primitives.h Sat Jun 15 19:14:42 2013 +0300 +++ b/src/primitives.h Sun Jun 16 00:10:11 2013 +0300 @@ -21,9 +21,34 @@ #include "common.h" #include "types.h" +#include <QRegExp> +class PrimitiveCategory; struct Primitive { str name, title; + PrimitiveCategory* cat; +}; + +class PrimitiveCategory { + PROPERTY (str, name, setName) + +public: + enum Type { + Filename, + Title + }; + + struct RegexEntry { + QRegExp regex; + Type type; + }; + + typedef vector<RegexEntry>::it it; + typedef vector<RegexEntry>::c_it c_it; + + vector<RegexEntry> regexes; + vector<Primitive> prims; + static vector<Primitive> uncat; }; // ============================================================================= @@ -52,9 +77,7 @@ vector<Primitive> m_prims; }; -extern vector<Primitive> g_Primitives; -extern PrimitiveLister* g_activePrimLister; -extern bool g_primListerMutex; +extern vector<PrimitiveCategory> g_PrimitiveCategories; void loadPrimitives ();
--- a/src/types.h Sat Jun 15 19:14:42 2013 +0300 +++ b/src/types.h Sun Jun 16 00:10:11 2013 +0300 @@ -52,6 +52,7 @@ typedef uint64_t uint64; template<class T> using initlist = std::initializer_list<T>; +template<class T, class R> using pair = std::pair<T, R>; using std::size_t; enum Axis { X, Y, Z }; @@ -187,8 +188,9 @@ m_vect.erase (m_vect.begin () + pos); } - void push_back (const T& value) { + T& push_back (const T& value) { m_vect.push_back (value); + return m_vect[m_vect.size () - 1]; } void push_back (const vector<T>& vals) { @@ -205,14 +207,12 @@ return true; } - vector<T>& operator<< (const T& value) { - push_back (value); - return *this; + T& operator<< (const T& value) { + return push_back (value); } - vector<T>& operator<< (const vector<T>& vals) { + void operator<< (const vector<T>& vals) { push_back (vals); - return *this; } bool operator>> (T& value) {