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