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