14 * |
14 * |
15 * You should have received a copy of the GNU General Public License |
15 * You should have received a copy of the GNU General Public License |
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 // TODO: I believe the multi-threading is unnecessary here. Get rid of it. |
|
20 |
|
21 #include <QDir> |
19 #include <QDir> |
22 #include <QThread> |
|
23 #include <QRegExp> |
20 #include <QRegExp> |
24 #include <QFileDialog> |
21 #include <QFileDialog> |
25 #include "document.h" |
22 #include "document.h" |
26 #include "gui.h" |
23 #include "gui.h" |
27 #include "primitives.h" |
24 #include "primitives.h" |
28 #include "ui_makeprim.h" |
25 #include "ui_makeprim.h" |
29 #include "misc.h" |
26 #include "misc.h" |
30 #include "colors.h" |
27 #include "colors.h" |
31 #include "moc_primitives.cpp" |
28 #include "moc_primitives.cpp" |
32 |
29 |
33 QList<PrimitiveCategory> g_PrimitiveCategories; |
30 QList<PrimitiveCategory*> g_PrimitiveCategories; |
34 QList<Primitive> g_primitives; |
31 QList<Primitive> g_primitives; |
35 static PrimitiveLister* g_activePrimLister = null; |
32 static PrimitiveLister* g_activePrimLister = null; |
36 static bool g_primListerMutex = false; |
33 PrimitiveCategory* g_unmatched = null; |
37 static const str g_Other = PrimitiveLister::tr ("Other"); |
34 |
|
35 extern_cfg (String, ld_defaultname); |
|
36 extern_cfg (String, ld_defaultuser); |
|
37 extern_cfg (Int, ld_defaultlicense); |
38 |
38 |
39 static const str g_radialNameRoots[] = |
39 static const str g_radialNameRoots[] = |
40 { "edge", |
40 { "edge", |
41 "cyli", |
41 "cyli", |
42 "disc", |
42 "disc", |
43 "ndis", |
43 "ndis", |
44 "ring", |
44 "ring", |
45 "con" |
45 "con" |
46 }; |
46 }; |
47 |
47 |
48 static void populateCategories(); |
48 PrimitiveLister* getPrimitiveLister() |
49 static void loadPrimitiveCatgories(); |
49 { return g_activePrimLister; |
|
50 } |
50 |
51 |
51 // ============================================================================= |
52 // ============================================================================= |
52 // ----------------------------------------------------------------------------- |
53 // ----------------------------------------------------------------------------- |
53 void loadPrimitives() |
54 void loadPrimitives() |
54 { log ("Loading primitives...\n"); |
55 { log ("Loading primitives...\n"); |
55 loadPrimitiveCatgories(); |
56 PrimitiveCategory::loadCategories(); |
56 |
57 |
57 // Try to load prims.cfg |
58 // Try to load prims.cfg |
58 File conf (Config::filepath ("prims.cfg"), File::Read); |
59 File conf (Config::filepath ("prims.cfg"), File::Read); |
59 |
60 |
60 if (!conf) |
61 if (!conf) |
61 { // No prims.cfg, build it |
62 { // No prims.cfg, build it |
62 PrimitiveLister::start(); |
63 PrimitiveLister::start(); |
63 } |
64 } |
64 else |
65 else |
65 { // Read primitives from prims.cfg |
66 { // Read primitives from prims.cfg |
66 for (str line : conf) |
67 for (str line : conf) |
67 { int space = line.indexOf (" "); |
68 { int space = line.indexOf (" "); |
68 |
69 |
69 if (space == -1) |
70 if (space == -1) |
70 continue; |
71 continue; |
71 |
72 |
127 { info.title.remove (0, 1); // remove 0 |
138 { info.title.remove (0, 1); // remove 0 |
128 info.title = info.title.simplified(); |
139 info.title = info.title.simplified(); |
129 } |
140 } |
130 |
141 |
131 m_prims << info; |
142 m_prims << info; |
132 emit update (++i); |
143 } |
133 } |
144 |
134 |
145 if (m_i == m_files.size()) |
135 // Save to a config file |
146 { // Done with primitives, now save to a config file |
136 File conf (Config::filepath ("prims.cfg"), File::Write); |
147 File conf (Config::filepath ("prims.cfg"), File::Write); |
137 |
148 |
138 for (Primitive & info : m_prims) |
149 for (Primitive& info : m_prims) |
139 fprint (conf, "%1 %2\n", info.name, info.title); |
150 fprint (conf, "%1 %2\n", info.name, info.title); |
140 |
151 |
141 conf.close(); |
152 conf.close(); |
142 |
153 |
143 g_primListerMutex = true; |
154 g_primitives = m_prims; |
144 g_primitives = m_prims; |
155 PrimitiveCategory::populateCategories(); |
145 populateCategories(); |
156 log ("%1 primitives listed", g_primitives.size()); |
146 g_primListerMutex = false; |
157 g_activePrimLister = null; |
147 g_activePrimLister = null; |
158 emit workDone(); |
148 emit workDone(); |
159 deleteLater(); |
|
160 } |
|
161 else |
|
162 { // Defer to event loop, pick up the work later |
|
163 emit update (m_i); |
|
164 QMetaObject::invokeMethod (this, "work", Qt::QueuedConnection); |
|
165 } |
149 } |
166 } |
150 |
167 |
151 // ============================================================================= |
168 // ============================================================================= |
152 // ----------------------------------------------------------------------------- |
169 // ----------------------------------------------------------------------------- |
153 void PrimitiveLister::start() |
170 void PrimitiveLister::start() |
154 { if (g_activePrimLister) |
171 { if (g_activePrimLister) |
155 return; |
172 return; |
156 |
173 |
157 PrimitiveLister* lister = new PrimitiveLister; |
174 PrimitiveLister* lister = new PrimitiveLister; |
158 QThread* listerThread = new QThread; |
175 /* |
159 lister->moveToThread (listerThread); |
|
160 connect (lister, SIGNAL (starting (int)), g_win, SLOT (primitiveLoaderStart (int))); |
176 connect (lister, SIGNAL (starting (int)), g_win, SLOT (primitiveLoaderStart (int))); |
161 connect (lister, SIGNAL (update (int)), g_win, SLOT (primitiveLoaderUpdate (int))); |
177 connect (lister, SIGNAL (update (int)), g_win, SLOT (primitiveLoaderUpdate (int))); |
162 connect (lister, SIGNAL (workDone()), g_win, SLOT (primitiveLoaderEnd())); |
178 connect (lister, SIGNAL (workDone()), g_win, SLOT (primitiveLoaderEnd())); |
163 connect (listerThread, SIGNAL (started()), lister, SLOT (work())); |
179 */ |
164 connect (listerThread, SIGNAL (finished()), lister, SLOT (deleteLater())); |
180 lister->work(); |
165 listerThread->start(); |
181 } |
166 } |
182 |
167 |
183 // ============================================================================= |
168 // ============================================================================= |
184 // ----------------------------------------------------------------------------- |
169 // ----------------------------------------------------------------------------- |
185 PrimitiveCategory::PrimitiveCategory (str name, QObject* parent) : |
170 static PrimitiveCategory* findCategory (str name) |
186 QObject (parent), |
171 { for (PrimitiveCategory & cat : g_PrimitiveCategories) |
187 m_Name (name) {} |
172 if (cat.getName() == name) |
188 |
173 return &cat; |
189 // ============================================================================= |
174 |
190 // ----------------------------------------------------------------------------- |
175 return null; |
191 void PrimitiveCategory::populateCategories() |
176 } |
192 { for (PrimitiveCategory* cat : g_PrimitiveCategories) |
177 |
193 cat->prims.clear(); |
178 // ============================================================================= |
194 |
179 // ----------------------------------------------------------------------------- |
195 |
180 static void populateCategories() |
196 for (Primitive& prim : g_primitives) |
181 { for (PrimitiveCategory & cat : g_PrimitiveCategories) |
|
182 cat.prims.clear(); |
|
183 |
|
184 PrimitiveCategory* unmatched = findCategory (g_Other); |
|
185 |
|
186 if (!unmatched) |
|
187 { // Shouldn't happen.. but catch it anyway. |
|
188 PrimitiveCategory cat; |
|
189 cat.setName (g_Other); |
|
190 g_PrimitiveCategories << cat; |
|
191 unmatched = &g_PrimitiveCategories.last(); |
|
192 } |
|
193 |
|
194 for (Primitive & prim : g_primitives) |
|
195 { bool matched = false; |
197 { bool matched = false; |
196 prim.cat = null; |
198 prim.cat = null; |
197 |
199 |
198 // Go over the categories and their regexes, if and when there's a match, |
200 // Go over the categories and their regexes, if and when there's a match, |
199 // the primitive's category is set to the category the regex beloings to. |
201 // the primitive's category is set to the category the regex beloings to. |
200 for (PrimitiveCategory& cat : g_PrimitiveCategories) |
202 for (PrimitiveCategory* cat : g_PrimitiveCategories) |
201 { for (PrimitiveCategory::RegexEntry& entry : cat.regexes) |
203 { for (RegexEntry& entry : cat->regexes) |
202 { switch (entry.type) |
204 { switch (entry.type) |
203 { case PrimitiveCategory::Filename: |
205 { case EFilenameRegex: |
204 // f-regex, check against filename |
206 { // f-regex, check against filename |
205 matched = entry.regex.exactMatch (prim.name); |
207 matched = entry.regex.exactMatch (prim.name); |
206 break; |
208 } break; |
207 |
209 |
208 case PrimitiveCategory::Title: |
210 case ETitleRegex: |
209 // t-regex, check against title |
211 { // t-regex, check against title |
210 matched = entry.regex.exactMatch (prim.title); |
212 matched = entry.regex.exactMatch (prim.title); |
211 break; |
213 } break; |
212 } |
214 } |
213 |
215 |
214 if (matched) |
216 if (matched) |
215 { prim.cat = &cat; |
217 { prim.cat = cat; |
216 break; |
218 break; |
217 } |
219 } |
218 } |
220 } |
219 |
221 |
220 // Drop out if a category was decided on. |
222 // Drop out if a category was decided on. |
221 if (prim.cat) |
223 if (prim.cat != null) |
222 break; |
224 break; |
223 } |
225 } |
224 |
226 |
225 // If there was a match, add the primitive to the category. |
227 // If there was a match, add the primitive to the category. |
226 // Otherwise, add it to the list of unmatched primitives. |
228 // Otherwise, add it to the list of unmatched primitives. |
227 if (prim.cat) |
229 if (prim.cat != null) |
228 prim.cat->prims << prim; |
230 prim.cat->prims << prim; |
229 else |
231 else |
230 unmatched->prims << prim; |
232 g_unmatched->prims << prim; |
231 } |
233 } |
232 } |
234 } |
233 |
235 |
234 // ============================================================================= |
236 // ============================================================================= |
235 // ----------------------------------------------------------------------------- |
237 // ----------------------------------------------------------------------------- |
236 static void loadPrimitiveCatgories() |
238 void PrimitiveCategory::loadCategories() |
237 { g_PrimitiveCategories.clear(); |
239 { for (PrimitiveCategory* cat : g_PrimitiveCategories) |
|
240 delete cat; |
|
241 |
|
242 g_PrimitiveCategories.clear(); |
238 File f (Config::dirpath() + "primregexps.cfg", File::Read); |
243 File f (Config::dirpath() + "primregexps.cfg", File::Read); |
239 |
244 |
240 if (!f) |
245 if (!f) |
241 f.open (":/data/primitive-categories.cfg", File::Read); |
246 f.open (":/data/primitive-categories.cfg", File::Read); |
242 |
247 |
243 if (!f) |
248 if (!f) |
244 critical (QObject::tr ("Failed to open primitive categories!")); |
249 { critical (QObject::tr ("Failed to open primitive categories!")); |
|
250 return; |
|
251 } |
245 |
252 |
246 if (f) |
253 if (f) |
247 { PrimitiveCategory cat; |
254 { PrimitiveCategory* cat = null; |
248 |
255 |
249 for (str line : f) |
256 for (str line : f) |
250 { int colon; |
257 { int colon; |
251 |
258 |
252 if (line.length() == 0 || line[0] == '#') |
259 if (line.length() == 0 || line[0] == '#') |
253 continue; |
260 continue; |
254 |
261 |
255 if ( (colon = line.indexOf (":")) == -1) |
262 if ((colon = line.indexOf (":")) == -1) |
256 { if (cat.regexes.size() > 0) |
263 { if (cat && cat->isValidToInclude()) |
257 g_PrimitiveCategories << cat; |
264 g_PrimitiveCategories << cat; |
258 |
265 |
259 cat.regexes.clear(); |
266 cat = new PrimitiveCategory (line); |
260 cat.prims.clear(); |
267 } |
261 cat.setName (line); |
268 elif (cat != null) |
|
269 { str cmd = line.left (colon); |
|
270 ERegexType type = EFilenameRegex; |
|
271 |
|
272 if (cmd == "f") |
|
273 type = EFilenameRegex; |
|
274 elif (cmd == "t") |
|
275 type = ETitleRegex; |
|
276 else |
|
277 { log (tr ("Warning: unknown command \"%1\" on line \"%2\""), cmd, line); |
|
278 continue; |
|
279 } |
|
280 |
|
281 QRegExp regex (line.mid (colon + 1)); |
|
282 RegexEntry entry = { regex, type }; |
|
283 cat->regexes << entry; |
262 } |
284 } |
263 else |
285 else |
264 { str cmd = line.left (colon); |
286 log ("Warning: Rules given before the first category name"); |
265 PrimitiveCategory::Type type = PrimitiveCategory::Filename; |
|
266 |
|
267 if (cmd == "f") |
|
268 type = PrimitiveCategory::Filename; |
|
269 |
|
270 elif (cmd == "t") |
|
271 type = PrimitiveCategory::Title; |
|
272 else |
|
273 continue; |
|
274 |
|
275 QRegExp regex (line.mid (colon + 1)); |
|
276 PrimitiveCategory::RegexEntry entry = { regex, type }; |
|
277 cat.regexes << entry; |
|
278 } |
|
279 } |
287 } |
280 |
288 |
281 if (cat.regexes.size() > 0) |
289 if (cat->isValidToInclude()) |
282 g_PrimitiveCategories << cat; |
290 g_PrimitiveCategories << cat; |
283 } |
291 } |
284 |
292 |
285 // Add a category for unmatched primitives |
293 // Add a category for unmatched primitives. |
286 PrimitiveCategory cat; |
294 // Note: if this function is called the second time, g_unmatched has been |
287 cat.setName (g_Other); |
295 // deleted at the beginning of the function and is dangling at this point. |
288 g_PrimitiveCategories << cat; |
296 g_unmatched = new PrimitiveCategory (tr ("Other")); |
|
297 g_PrimitiveCategories << g_unmatched; |
|
298 } |
|
299 |
|
300 // ============================================================================= |
|
301 // ----------------------------------------------------------------------------- |
|
302 bool PrimitiveCategory::isValidToInclude() |
|
303 { if (regexes.size() == 0) |
|
304 { log (tr ("Warning: category \"%1\" left without patterns"), getName()); |
|
305 deleteLater(); |
|
306 return false; |
|
307 } |
|
308 |
|
309 return true; |
289 } |
310 } |
290 |
311 |
291 // ============================================================================= |
312 // ============================================================================= |
292 // ----------------------------------------------------------------------------- |
313 // ----------------------------------------------------------------------------- |
293 bool isPrimitiveLoaderBusy() |
314 bool isPrimitiveLoaderBusy() |
294 { return g_primListerMutex; |
315 { return g_activePrimLister != null; |
295 } |
316 } |
296 |
317 |
297 // ============================================================================= |
318 // ============================================================================= |
298 // ----------------------------------------------------------------------------- |
319 // ----------------------------------------------------------------------------- |
299 static double radialPoint (int i, int divs, double (*func) (double)) |
320 static double radialPoint (int i, int divs, double (*func) (double)) |
300 { return (*func) ( (i * 2 * pi) / divs); |
321 { return (*func) ((i * 2 * pi) / divs); |
301 } |
322 } |
302 |
323 |
303 // ============================================================================= |
324 // ============================================================================= |
304 // ----------------------------------------------------------------------------- |
325 // ----------------------------------------------------------------------------- |
305 void makeCircle (int segs, int divs, double radius, QList<QLineF>& lines) |
326 void makeCircle (int segs, int divs, double radius, QList<QLineF>& lines) |
306 { for (int i = 0; i < segs; ++i) |
327 { for (int i = 0; i < segs; ++i) |
307 { double x0 = radius * radialPoint (i, divs, cos), |
328 { double x0 = radius * radialPoint (i, divs, cos), |
308 x1 = radius * radialPoint (i + 1, divs, cos), |
329 x1 = radius * radialPoint (i + 1, divs, cos), |
309 z0 = radius * radialPoint (i, divs, sin), |
330 z0 = radius * radialPoint (i, divs, sin), |
310 z1 = radius * radialPoint (i + 1, divs, sin); |
331 z1 = radius * radialPoint (i + 1, divs, sin); |
311 |
332 |
312 lines << QLineF (QPointF (x0, z0), QPointF (x1, z1)); |
333 lines << QLineF (QPointF (x0, z0), QPointF (x1, z1)); |
313 } |
334 } |
314 } |
335 } |
315 |
336 |