1 /*****************************************************************************
2  *                                                                           *
3  *  Elmer, A Finite Element Software for Multiphysical Problems              *
4  *                                                                           *
5  *  Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland    *
6  *                                                                           *
7  *  This program is free software; you can redistribute it and/or            *
8  *  modify it under the terms of the GNU General Public License              *
9  *  as published by the Free Software Foundation; either version 2           *
10  *  of the License, or (at your option) any later version.                   *
11  *                                                                           *
12  *  This program is distributed in the hope that it will be useful,          *
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
15  *  GNU General Public License for more details.                             *
16  *                                                                           *
17  *  You should have received a copy of the GNU General Public License        *
18  *  along with this program (in file fem/GPL-2); if not, write to the        *
19  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,         *
20  *  Boston, MA 02110-1301, USA.                                              *
21  *                                                                           *
22  *****************************************************************************/
23 
24 /*****************************************************************************
25  *                                                                           *
26  *  ElmerGUI RenderArea                                                      *
27  *                                                                           *
28  *****************************************************************************
29  *                                                                           *
30  *  Authors: Mikko Lyly, Juha Ruokolainen and Peter Råback                   *
31  *  Email:   Juha.Ruokolainen@csc.fi                                         *
32  *  Web:     http://www.csc.fi/elmer                                         *
33  *  Address: CSC - IT Center for Science Ltd.                                *
34  *           Keilaranta 14                                                   *
35  *           02101 Espoo, Finland                                            *
36  *                                                                           *
37  *  Original Date: 15 Mar 2008                                               *
38  *                                                                           *
39  *****************************************************************************/
40 #include <QPainter>
41 #include <QPainterPath>
42 #include <QMouseEvent>
43 #include <QFile>
44 #include <QTextStream>
45 #include <QCoreApplication>
46 #include <QTableWidget>
47 #include <iostream>
48 #include <math.h>
49 #include "renderarea.h"
50 #include "curveeditor.h"
51 
52 using namespace std;
53 
RenderArea(QWidget * parent)54 RenderArea::RenderArea(QWidget *parent)
55   : QWidget(parent)
56 {
57   pointRadius = 3;
58   setAutoFillBackground(true);
59   setPalette(QPalette(Qt::white));
60 
61   QStringList args = QCoreApplication::arguments();
62 
63   if(args.count() > 1) {
64     readSlot(args.at(1));
65     fitSlot();
66   }
67 
68   reading = false;
69 
70   fitSlot();
71 }
72 
~RenderArea()73 RenderArea::~RenderArea()
74 {
75 }
76 
paintEvent(QPaintEvent *)77 void RenderArea::paintEvent(QPaintEvent * /* event */)
78 {
79   QPainter painter(this);
80   painter.setRenderHint(QPainter::Antialiasing);
81   this->viewport = painter.viewport();
82 
83   QPen pointPen;
84   pointPen.setWidth(1);
85   pointPen.setColor(Qt::black);
86   pointPen.setStyle(Qt::SolidLine);
87 
88   QPen splinePen;
89   splinePen.setWidth(1);
90   splinePen.setColor(Qt::blue);
91   splinePen.setStyle(Qt::SolidLine);
92 
93   QPen tangentPen;
94   tangentPen.setWidth(1);
95   tangentPen.setColor(Qt::red);
96   tangentPen.setStyle(Qt::SolidLine);
97 
98   QPen bodyTextPen;
99   bodyTextPen.setWidth(1);
100   bodyTextPen.setColor(Qt::green);
101   bodyTextPen.setStyle(Qt::SolidLine);
102 
103   // Draw splines:
104   //---------------
105   QPainterPath path;
106 
107   int j, n = 20;
108   double u;
109   QPointF p, q, t1, t2;
110 
111   QPointF offset(-pointRadius, pointRadius/2);
112 
113   for(int i = 0; i < splines.keys().size(); i++) {
114     int idx = splines.keys().at(i);
115     Spline s = splines.value(idx);
116 
117     if(!points.contains(s.p[0])) continue;
118     if(!points.contains(s.p[1])) continue;
119     if((s.np == 3) && !points.contains(s.p[2])) continue;
120 
121     QPointF p0 = points.value(s.p[0]);
122     QPointF p1 = points.value(s.p[1]);
123     QPointF p2 = points.value(s.p[2]);
124 
125     QPointF q0 = mapToViewport(p0);
126     QPointF q1 = mapToViewport(p1);
127     QPointF q2 = mapToViewport(p2);
128 
129     switch(s.np) {
130     case 2:
131       painter.setPen(splinePen);
132       path.moveTo(q0);
133 
134       // Draw linear segment:
135       //---------------------
136       if(drawSplines)
137 	path.lineTo(q1);
138 
139       // Draw spline number:
140       //---------------------
141       painter.setPen(pointPen);
142 
143       q = (q1 + q0)/2.0;
144 
145       if(drawSplineNumbers)
146 	painter.drawText(q + offset, QString::number(idx));
147 
148       // Draw material numbers:
149       //------------------------
150       t1 = q1 - q0;
151       t2.setX(-t1.y());
152       t2.setY(t1.x());
153       t2 /= sqrt(t2.x()*t2.x() + t2.y()*t2.y());
154       t2 *= 10;
155 
156       painter.setPen(bodyTextPen);
157 
158       if(drawMaterialNumbers) {
159 	painter.drawText(q + t2 + offset, QString::number(s.in));
160 	painter.drawText(q - t2 + offset, QString::number(s.out));
161       }
162 
163       break;
164 
165     case 3:
166       path.moveTo(q0);
167 
168       // Draw quadratic nurbs:
169       //-----------------------
170       for(j = 0; j <= n; j++) {
171 	u = double(j) / double(n);
172 	p = quadNurbs(u, q0, q1, q2);
173 
174 	if(drawSplines)
175 	  path.lineTo(p);
176       }
177 
178       // Draw spline number:
179       //---------------------
180       painter.setPen(pointPen);
181 
182       q = quadNurbs(0.5, q0, q1, q2);
183 
184       if(drawSplineNumbers)
185 	painter.drawText(q + offset, QString::number(idx));
186 
187       // Draw material numbers:
188       //------------------------
189       t1 = q2 - q0;
190       t2.setX(-t1.y());
191       t2.setY(t1.x());
192       t2 /= sqrt(t2.x()*t2.x() + t2.y()*t2.y());
193       t2 *= 10;
194 
195       painter.setPen(bodyTextPen);
196 
197       if(drawMaterialNumbers) {
198 	painter.drawText(q + t2 + offset, QString::number(s.in));
199 	painter.drawText(q - t2 + offset, QString::number(s.out));
200       }
201 
202       // Draw control tangents:
203       //------------------------
204       painter.setPen(tangentPen);
205 
206       if(drawTangents) {
207 	painter.drawLine(q0, q1);
208 	painter.drawLine(q1, q2);
209       }
210 
211       break;
212 
213     default:
214       break;
215     }
216   }
217 
218   // Draw spline path:
219   //-------------------
220   painter.setPen(splinePen);
221   painter.drawPath(path);
222 
223   // Draw points:
224   //--------------
225   QPointF offset2(pointRadius*1.5, pointRadius);
226 
227   painter.setPen(pointPen);
228   for(int i = 0; i < points.keys().size(); i++) {
229     int idx = points.keys().at(i);
230     if(idx < 1) continue;
231 
232     QPointF p = points.value(idx);
233     QPointF q = mapToViewport(p);
234 
235     if(drawPoints)
236       painter.drawEllipse(int(q.x()), int(q.y()), int(pointRadius), int(pointRadius));
237 
238     if(drawPointNumbers)
239       painter.drawText(q + offset2, QString::number(idx));
240   }
241 }
242 
wheelEvent(QWheelEvent * event)243 void RenderArea::wheelEvent(QWheelEvent *event)
244 {
245   double s = exp((double)(event->delta())*0.001);
246   double width = renderport.width();
247   double height = renderport.height();
248   renderport.setWidth(s*width);
249   renderport.setHeight(s*height);
250   renderport.translate(QPointF(0.5*(1-s)*width, 0.5*(1-s)*height));
251   lastPos = event->pos();
252   update();
253 }
254 
mousePressEvent(QMouseEvent * event)255 void RenderArea::mousePressEvent(QMouseEvent *event)
256 {
257   selectedPoint = -1;
258 
259   for(int i = 0; i < points.keys().size(); i++) {
260     int idx = points.keys().at(i);
261     QPointF p = points.value(idx);
262     QPointF q = mapToViewport(p);
263 
264     double d = (event->x() - q.x()) * (event->x() - q.x())
265              + (event->y() - q.y()) * (event->y() - q.y());
266 
267     if(d <= (pointRadius * pointRadius)) {
268       QString message = "Point " + QString::number(idx);
269       emit(statusMessage(message));
270       selectedPoint = idx;
271       break;
272     }
273   }
274 
275   lastPos = event->pos();
276 }
277 
mouseReleaseEvent(QMouseEvent * event)278 void RenderArea::mouseReleaseEvent(QMouseEvent *event)
279 {
280   selectedPoint = -1;
281 
282   lastPos = event->pos();
283 
284   emit(statusMessage("Ready"));
285 }
286 
mouseMoveEvent(QMouseEvent * event)287 void RenderArea::mouseMoveEvent(QMouseEvent *event)
288 {
289   QPointF p, q;
290   QString message;
291   double a, b, scale, dx, dy;
292 
293   switch(event->buttons()) {
294 
295   case Qt::LeftButton:
296     if(selectedPoint < 0)
297       return;
298 
299     // Move point:
300     //------------
301     p.setX(event->x());
302     p.setY(event->y());
303     q = mapToRenderport(p);
304     points.insert(selectedPoint, q);
305 
306     update();
307 
308     curveEditor->modifyPoint(selectedPoint, q.x(), q.y());
309 
310     message = QString::number(q.x()) + " "  + QString::number(q.y());
311 
312     emit(statusMessage(message));
313 
314     break;
315 
316   case Qt::RightButton:
317     a = renderport.height();
318     b = viewport.height();
319     scale = a/b;
320     dx = scale * (double(event->pos().x()) - double(lastPos.x()));
321     dy = scale * (double(event->pos().y()) - double(lastPos.y()));
322     p.setX(-dx);
323     p.setY(dy);
324 
325     // Pan:
326     //------
327     renderport.translate(p);
328     update();
329 
330     break;
331 
332   default:
333     break;
334   }
335 
336   lastPos = event->pos();
337 }
338 
mapToViewport(QPointF point) const339 QPointF RenderArea::mapToViewport(QPointF point) const
340 {
341   QPointF mapped;
342 
343   double h = renderport.height();
344   double x = renderport.x();
345   double y = renderport.y();
346   double xi = (point.x() - x) / h;
347   double eta = (point.y() - y) / h;
348 
349   double h0 = viewport.height();
350   double x0 = viewport.x();
351   double y0 = viewport.y();
352 
353   mapped.setX(x0 + xi * h0);
354   mapped.setY(y0 + h0 - eta * h0);
355 
356   return mapped;
357 }
358 
mapToRenderport(QPointF point) const359 QPointF RenderArea::mapToRenderport(QPointF point) const
360 {
361   QPointF mapped;
362 
363   double h = viewport.height();
364   double x = viewport.x();
365   double y = viewport.y();
366   double xi = (point.x() - x) / h;
367   double eta = 1.0 - (point.y() - y) / h;
368 
369   double h0 = renderport.height();
370   double x0 = renderport.x();
371   double y0 = renderport.y();
372 
373   mapped.setX(x0 + xi * h0);
374   mapped.setY(y0 + eta * h0);
375 
376   return mapped;
377 }
378 
fitSlot()379 void RenderArea::fitSlot()
380 {
381   qreal xmin = 9e9;
382   qreal xmax = -9e9;
383   qreal ymin = 9e9;
384   qreal ymax = -9e9;
385 
386   for(int i = 0; i < points.keys().size(); i++) {
387     int idx = points.keys().at(i);
388     QPointF p = points.value(idx);
389 
390     xmin = qMin(xmin, p.x());
391     xmax = qMax(xmax, p.x());
392     ymin = qMin(ymin, p.y());
393     ymax = qMax(ymax, p.y());
394   }
395 
396   if(points.keys().size() < 1) {
397     xmin = 0;
398     xmax = 1;
399     ymin = 0;
400     ymax = 1;
401   }
402 
403   double width = xmax - xmin;
404   double height = ymax - ymin;
405 
406   double oh = qMax(width, height) * 0.1;
407 
408   renderport.setRect(xmin-oh, ymin-oh, width+2*oh, height+2*oh);
409 
410   update();
411 }
412 
saveSlot(QString fileName)413 void RenderArea::saveSlot(QString fileName)
414 {
415   QFile file(fileName);
416 
417   if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
418     return;
419 
420   QTextStream out(&file);
421 
422   out << "splinecurves2dv2" << endl;
423   out << 1 << endl;
424   out << endl;
425 
426   out << "points" << endl;
427   for(int i = 0; i < points.keys().size(); i++) {
428     int idx = points.keys().at(i);
429     QPointF p = points.value(idx);
430     if(idx > 0)
431       out << idx << " " << p.x() << " " << p.y() << endl;
432   }
433 
434   out << endl;
435 
436   out << "segments" << endl;
437   for(int i = 0; i < splines.keys().size(); i++) {
438     int idx = splines.keys().at(i);
439     Spline s = splines.value(idx);
440     if((s.out > 0) || (s.in > 0)) {
441 	out << s.out << " " << s.in << " " << s.np;
442 	for(int j = 0; j < s.np; j++)
443 	  out << " " << s.p[j];
444 	out << " -bc=" << idx << endl;
445       }
446   }
447 
448   // Enumerate bodies:
449   //-------------------
450   bodies.clear();
451 
452   for(int i = 0; i < splines.keys().size(); i++) {
453     int idx = splines.keys().at(i);
454     Spline s = splines.value(idx);
455 
456     if(!bodies.contains(s.in))
457       bodies.push_back(s.in);
458 
459     if(!bodies.contains(s.out))
460       bodies.push_back(s.out);
461   }
462 
463   out << endl;
464   out << "materials" << endl;
465   for(int i = 0; i < bodies.size(); i++) {
466     if(bodies.at(i) == 0) continue;
467     out << bodies.at(i) << " material" << bodies.at(i);
468     out << " -maxh=0.1" << endl;
469   }
470 
471   file.close();
472 }
473 
readSlot(QString fileName)474 void RenderArea::readSlot(QString fileName)
475 {
476   QFile file(fileName);
477 
478   if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
479     return;
480 
481   points.clear();
482   splines.clear();
483   bodies.clear();
484   curveEditor->clearAll();
485 
486   reading = true;
487 
488   int mode = 0;
489   int index, i;
490   double x, y;
491   QPointF p;
492   Spline s;
493 
494   // Parse input file:
495   //-------------------
496   bool correctVersion = false;
497   int countSplines = 0;
498 
499   while(!file.atEnd()) {
500     QByteArray line = file.readLine();
501 
502     if(line.trimmed().isEmpty())
503       continue;
504 
505     if(line.trimmed().left(1) == "#")
506       continue;
507 
508     if(line.trimmed() == "splinecurves2dv2") {
509       correctVersion = true;
510       continue;
511     }
512 
513     if(line.trimmed() == "points") {
514       mode = 1;
515       continue;
516     }
517 
518     if(line.trimmed() == "segments") {
519       mode = 2;
520       continue;
521     }
522 
523     if(line.trimmed() == "materials") {
524       mode = 3;
525       continue;
526     }
527 
528     QTextStream stream(&line);
529 
530     switch(mode) {
531     case(1):
532       stream >> index >> x >> y;
533       p.setX(x); p.setY(y);
534       if(points.contains(index)) {
535 	QString message = "Consistency error. ";
536 	message += "Multiple point index " + QString::number(index);
537 	points.clear();
538 	splines.clear();
539 	bodies.clear();
540 #if WITH_QT5
541 	cout << message.toLatin1().data() << endl;
542 #else
543 	cout << message.toAscii().data() << endl;
544 #endif
545 	emit(statusMessage(message));
546 	return;
547       }
548       points.insert(index, p);
549       curveEditor->addPoint(index, x, y);
550       break;
551 
552     case(2):
553       stream >> s.out >> s.in >> s.np;
554       for(i = 0; i < s.np; i++)
555 	stream >> s.p[i];
556       splines.insert(++countSplines, s);
557       curveEditor->addCurve(s.in, s.out, s.np, s.p);
558       break;
559 
560     case(3):
561       break;
562 
563     default:
564       break;
565     }
566   }
567 
568   if(!correctVersion) {
569     QString message = "Unsupported format (splinecurve2dv2 is required)";
570     #if WITH_QT5
571     cout << message.toLatin1().data() << endl;
572     #else
573     cout << message.toAscii().data() << endl;
574     #endif
575     points.clear();
576     splines.clear();
577     curveEditor->clearAll();
578     emit(statusMessage(message));
579     reading = false;
580     return;
581   }
582 
583   // Enumerate bodies:
584   //-------------------
585   bodies.clear();
586 
587   for(int i = 0; i < splines.keys().size(); i++) {
588     int idx = splines.keys().at(i);
589     Spline s = splines.value(idx);
590 
591     if(!bodies.contains(s.in))
592       bodies.push_back(s.in);
593 
594     if(!bodies.contains(s.out))
595       bodies.push_back(s.out);
596   }
597 
598   // Check consistency:
599   //--------------------
600   for(int i = 0; i < splines.keys().size(); i++) {
601     int idx = splines.keys().at(i);
602     Spline s = splines.value(idx);
603 
604     for(int j = 0; j < s.np; j++) {
605       int p = s.p[j];
606 
607       if(!points.contains(p)) {
608 	QString message = "Consistency error. Spline ";
609 	message += QString::number(idx);
610 	message += " refers to point ";
611 	message += QString::number(p);
612 	message += " which does not exist.";
613 
614 	bodies.clear();
615 	points.clear();
616 	splines.clear();
617 	curveEditor->clearAll();
618 
619 	emit(statusMessage(message));
620 
621 	reading = false;
622 
623 	return;
624       }
625     }
626   }
627 
628   cout << "Points: " << points.count() << endl;
629   cout << "Splines: " << splines.count() << endl;
630   cout << "Bodies: " << bodies.count() - 1 << endl;
631 
632   emit(statusMessage("Ready"));
633 
634   reading = false;
635 }
636 
quadNurbs(double u,QPointF P0,QPointF P1,QPointF P2) const637 QPointF RenderArea::quadNurbs(double u, QPointF P0, QPointF P1, QPointF P2) const
638 {
639   QPointF result;
640 
641   double l0 = 1.0 - u;
642   double l1 = u;
643 
644   double q0 = l0 * l0;
645   double q1 = 2.0 * l0 * l1;
646   double q2 = l1 * l1;
647 
648   double w0 = 1.0;
649   double w1 = 1.0 / sqrt(2.0);
650   double w2 = 1.0;
651 
652   result = w0*q0*P0 + w1*q1*P1 + w2*q2*P2;
653 
654   result /= w0*q0 + w1*q1 + w2*q2;
655 
656   return result;
657 }
658 
drawPointsSlot(bool state)659 void RenderArea::drawPointsSlot(bool state)
660 {
661   drawPoints = state;
662   update();
663 }
664 
drawSplinesSlot(bool state)665 void RenderArea::drawSplinesSlot(bool state)
666 {
667   drawSplines = state;
668   update();
669 }
670 
drawTangentsSlot(bool state)671 void RenderArea::drawTangentsSlot(bool state)
672 {
673   drawTangents = state;
674   update();
675 }
676 
drawPointNumbersSlot(bool state)677 void RenderArea::drawPointNumbersSlot(bool state)
678 {
679   drawPointNumbers = state;
680   update();
681 }
682 
drawSplineNumbersSlot(bool state)683 void RenderArea::drawSplineNumbersSlot(bool state)
684 {
685   drawSplineNumbers = state;
686   update();
687 }
688 
drawMaterialNumbersSlot(bool state)689 void RenderArea::drawMaterialNumbersSlot(bool state)
690 {
691   drawMaterialNumbers = state;
692   update();
693 }
694 
setCurveEditor(CurveEditor * curveEditor)695 void RenderArea::setCurveEditor(CurveEditor *curveEditor)
696 {
697   this->curveEditor = curveEditor;
698 }
699 
modifyPoint(int idx,double x,double y)700 void RenderArea::modifyPoint(int idx, double x, double y)
701 {
702   if(reading) return;
703 
704   points.insert(idx, QPointF(x, y));
705 
706   update();
707 }
708 
modifyCurve(int idx,int in,int out,int np,int p0,int p1,int p2)709 void RenderArea::modifyCurve(int idx, int in, int out, int np, int p0, int p1, int p2)
710 {
711   if(reading) return;
712 
713   Spline s;
714   s.in = in;
715   s.out = out;
716   s.np = np;
717   s.p[0] = p0;
718   s.p[1] = p1;
719   s.p[2] = p2;
720 
721   splines.insert(idx+1, s);
722 
723   update();
724 }
725 
updatePoints(QTableWidget * table)726 void RenderArea::updatePoints(QTableWidget *table)
727 {
728   points.clear();
729 
730   for(int i = 0; i < table->rowCount(); i++) {
731     int idx = table->item(i, 0)->text().toInt();
732     double x = table->item(i, 1)->text().toDouble();
733     double y = table->item(i, 2)->text().toDouble();
734     points.insert(idx, QPointF(x, y));
735   }
736 
737   update();
738 
739 }
740 
updateCurves(QTableWidget * table)741 void RenderArea::updateCurves(QTableWidget *table)
742 {
743   Spline s;
744 
745   splines.clear();
746 
747   for(int i = 0; i < table->rowCount(); i++) {
748     s.in = table->item(i, 0)->text().toInt();
749     s.out = table->item(i, 1)->text().toInt();
750     s.np = table->item(i, 2)->text().toInt();
751 
752     if(s.np < 2) s.np = 2;
753     if(s.np > 3) s.np = 3;
754 
755     for(int j = 0; j < s.np; j++)
756       s.p[j] = table->item(i, 3+j)->text().toInt();
757 
758     splines.insert(i, s);
759   }
760 
761   update();
762 }
763