Path: blob/devel/ElmerGUI/Application/twod/renderarea.cpp
3203 views
/*****************************************************************************1* *2* Elmer, A Finite Element Software for Multiphysical Problems *3* *4* Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland *5* *6* This program is free software; you can redistribute it and/or *7* modify it under the terms of the GNU General Public License *8* as published by the Free Software Foundation; either version 2 *9* of the License, or (at your option) any later version. *10* *11* This program is distributed in the hope that it will be useful, *12* but WITHOUT ANY WARRANTY; without even the implied warranty of *13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *14* GNU General Public License for more details. *15* *16* You should have received a copy of the GNU General Public License *17* along with this program (in file fem/GPL-2); if not, write to the *18* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *19* Boston, MA 02110-1301, USA. *20* *21*****************************************************************************/2223/*****************************************************************************24* *25* ElmerGUI RenderArea *26* *27*****************************************************************************28* *29* Authors: Mikko Lyly, Juha Ruokolainen and Peter RÃ¥back *30* Email: [email protected] *31* Web: http://www.csc.fi/elmer *32* Address: CSC - IT Center for Science Ltd. *33* Keilaranta 14 *34* 02101 Espoo, Finland *35* *36* Original Date: 15 Mar 2008 *37* *38*****************************************************************************/39#include <QPainter>40#include <QPainterPath>41#include <QMouseEvent>42#include <QFile>43#include <QTextStream>44#include <QCoreApplication>45#include <QTableWidget>46#include <iostream>47#include <math.h>48#include "renderarea.h"49#include "curveeditor.h"5051#if WITH_QT652#define endl Qt::endl53#endif5455using namespace std;5657RenderArea::RenderArea(QWidget *parent)58: QWidget(parent)59{60pointRadius = 3;61setAutoFillBackground(true);62setPalette(QPalette(Qt::white));6364QStringList args = QCoreApplication::arguments();6566if(args.count() > 1) {67readSlot(args.at(1));68fitSlot();69}7071reading = false;7273fitSlot();74}7576RenderArea::~RenderArea()77{78}7980void RenderArea::paintEvent(QPaintEvent * /* event */)81{82QPainter painter(this);83painter.setRenderHint(QPainter::Antialiasing);84this->viewport = painter.viewport();8586QPen pointPen;87pointPen.setWidth(1);88pointPen.setColor(Qt::black);89pointPen.setStyle(Qt::SolidLine);9091QPen splinePen;92splinePen.setWidth(1);93splinePen.setColor(Qt::blue);94splinePen.setStyle(Qt::SolidLine);9596QPen tangentPen;97tangentPen.setWidth(1);98tangentPen.setColor(Qt::red);99tangentPen.setStyle(Qt::SolidLine);100101QPen bodyTextPen;102bodyTextPen.setWidth(1);103bodyTextPen.setColor(Qt::green);104bodyTextPen.setStyle(Qt::SolidLine);105106// Draw splines:107//---------------108QPainterPath path;109110int j, n = 20;111double u;112QPointF p, q, t1, t2;113114QPointF offset(-pointRadius, pointRadius/2);115116for(int i = 0; i < splines.keys().size(); i++) {117int idx = splines.keys().at(i);118Spline s = splines.value(idx);119120if(!points.contains(s.p[0])) continue;121if(!points.contains(s.p[1])) continue;122if((s.np == 3) && !points.contains(s.p[2])) continue;123124QPointF p0 = points.value(s.p[0]);125QPointF p1 = points.value(s.p[1]);126QPointF p2 = points.value(s.p[2]);127128QPointF q0 = mapToViewport(p0);129QPointF q1 = mapToViewport(p1);130QPointF q2 = mapToViewport(p2);131132switch(s.np) {133case 2:134painter.setPen(splinePen);135path.moveTo(q0);136137// Draw linear segment:138//---------------------139if(drawSplines)140path.lineTo(q1);141142// Draw spline number:143//---------------------144painter.setPen(pointPen);145146q = (q1 + q0)/2.0;147148if(drawSplineNumbers)149painter.drawText(q + offset, QString::number(idx));150151// Draw material numbers:152//------------------------153t1 = q1 - q0;154t2.setX(-t1.y());155t2.setY(t1.x());156t2 /= sqrt(t2.x()*t2.x() + t2.y()*t2.y());157t2 *= 10;158159painter.setPen(bodyTextPen);160161if(drawMaterialNumbers) {162painter.drawText(q + t2 + offset, QString::number(s.in));163painter.drawText(q - t2 + offset, QString::number(s.out));164}165166break;167168case 3:169path.moveTo(q0);170171// Draw quadratic nurbs:172//-----------------------173for(j = 0; j <= n; j++) {174u = double(j) / double(n);175p = quadNurbs(u, q0, q1, q2);176177if(drawSplines)178path.lineTo(p);179}180181// Draw spline number:182//---------------------183painter.setPen(pointPen);184185q = quadNurbs(0.5, q0, q1, q2);186187if(drawSplineNumbers)188painter.drawText(q + offset, QString::number(idx));189190// Draw material numbers:191//------------------------192t1 = q2 - q0;193t2.setX(-t1.y());194t2.setY(t1.x());195t2 /= sqrt(t2.x()*t2.x() + t2.y()*t2.y());196t2 *= 10;197198painter.setPen(bodyTextPen);199200if(drawMaterialNumbers) {201painter.drawText(q + t2 + offset, QString::number(s.in));202painter.drawText(q - t2 + offset, QString::number(s.out));203}204205// Draw control tangents:206//------------------------207painter.setPen(tangentPen);208209if(drawTangents) {210painter.drawLine(q0, q1);211painter.drawLine(q1, q2);212}213214break;215216default:217break;218}219}220221// Draw spline path:222//-------------------223painter.setPen(splinePen);224painter.drawPath(path);225226// Draw points:227//--------------228QPointF offset2(pointRadius*1.5, pointRadius);229230painter.setPen(pointPen);231for(int i = 0; i < points.keys().size(); i++) {232int idx = points.keys().at(i);233if(idx < 1) continue;234235QPointF p = points.value(idx);236QPointF q = mapToViewport(p);237238if(drawPoints)239painter.drawEllipse(int(q.x()), int(q.y()), int(pointRadius), int(pointRadius));240241if(drawPointNumbers)242painter.drawText(q + offset2, QString::number(idx));243}244}245246void RenderArea::wheelEvent(QWheelEvent *event)247{248#if WITH_QT6249double s = exp((double)(event->angleDelta().y())*0.001);250#else251double s = exp((double)(event->delta())*0.001);252#endif253double width = renderport.width();254double height = renderport.height();255renderport.setWidth(s*width);256renderport.setHeight(s*height);257renderport.translate(QPointF(0.5*(1-s)*width, 0.5*(1-s)*height));258#if WITH_QT6259lastPos = event->position().toPoint();260#else261lastPos = event->pos();262#endif263update();264}265266void RenderArea::mousePressEvent(QMouseEvent *event)267{268selectedPoint = -1;269270for(int i = 0; i < points.keys().size(); i++) {271int idx = points.keys().at(i);272QPointF p = points.value(idx);273QPointF q = mapToViewport(p);274275#if WITH_QT6276double d = (event->position().x() - q.x()) * (event->position().x() - q.x())277+ (event->position().y() - q.y()) * (event->position().y() - q.y());278#else279double d = (event->x() - q.x()) * (event->x() - q.x())280+ (event->y() - q.y()) * (event->y() - q.y());281#endif282283if(d <= (pointRadius * pointRadius)) {284QString message = "Point " + QString::number(idx);285emit(statusMessage(message));286selectedPoint = idx;287break;288}289}290291lastPos = event->pos();292}293294void RenderArea::mouseReleaseEvent(QMouseEvent *event)295{296selectedPoint = -1;297298#if WITH_QT6299lastPos = event->position().toPoint();300#else301lastPos = event->pos();302#endif303304emit(statusMessage("Ready"));305}306307void RenderArea::mouseMoveEvent(QMouseEvent *event)308{309QPointF p, q;310QString message;311double a, b, scale, dx, dy;312313switch(event->buttons()) {314315case Qt::LeftButton:316if(selectedPoint < 0)317return;318319// Move point:320//------------321#if WITH_QT6322p = event->position();323#else324p.setX(event->x());325p.setY(event->y());326#endif327q = mapToRenderport(p);328points.insert(selectedPoint, q);329330update();331332curveEditor->modifyPoint(selectedPoint, q.x(), q.y());333334message = QString::number(q.x()) + " " + QString::number(q.y());335336emit(statusMessage(message));337338break;339340case Qt::RightButton:341a = renderport.height();342b = viewport.height();343scale = a/b;344#if WITH_QT6345dx = scale * (double(event->position().x()) - double(lastPos.x()));346dy = scale * (double(event->position().y()) - double(lastPos.y()));347#else348dx = scale * (double(event->pos().x()) - double(lastPos.x()));349dy = scale * (double(event->pos().y()) - double(lastPos.y()));350#endif351p.setX(-dx);352p.setY(dy);353354// Pan:355//------356renderport.translate(p);357update();358359break;360361default:362break;363}364365lastPos = event->pos();366}367368QPointF RenderArea::mapToViewport(QPointF point) const369{370QPointF mapped;371372double h = renderport.height();373double x = renderport.x();374double y = renderport.y();375double xi = (point.x() - x) / h;376double eta = (point.y() - y) / h;377378double h0 = viewport.height();379double x0 = viewport.x();380double y0 = viewport.y();381382mapped.setX(x0 + xi * h0);383mapped.setY(y0 + h0 - eta * h0);384385return mapped;386}387388QPointF RenderArea::mapToRenderport(QPointF point) const389{390QPointF mapped;391392double h = viewport.height();393double x = viewport.x();394double y = viewport.y();395double xi = (point.x() - x) / h;396double eta = 1.0 - (point.y() - y) / h;397398double h0 = renderport.height();399double x0 = renderport.x();400double y0 = renderport.y();401402mapped.setX(x0 + xi * h0);403mapped.setY(y0 + eta * h0);404405return mapped;406}407408void RenderArea::fitSlot()409{410qreal xmin = 9e9;411qreal xmax = -9e9;412qreal ymin = 9e9;413qreal ymax = -9e9;414415for(int i = 0; i < points.keys().size(); i++) {416int idx = points.keys().at(i);417QPointF p = points.value(idx);418419xmin = qMin(xmin, p.x());420xmax = qMax(xmax, p.x());421ymin = qMin(ymin, p.y());422ymax = qMax(ymax, p.y());423}424425if(points.keys().size() < 1) {426xmin = 0;427xmax = 1;428ymin = 0;429ymax = 1;430}431432double width = xmax - xmin;433double height = ymax - ymin;434435double oh = qMax(width, height) * 0.1;436437renderport.setRect(xmin-oh, ymin-oh, width+2*oh, height+2*oh);438439update();440}441442void RenderArea::saveSlot(QString fileName)443{444QFile file(fileName);445446if(!file.open(QIODevice::WriteOnly | QIODevice::Text))447return;448449QTextStream out(&file);450451out << "splinecurves2dv2" << endl;452out << 1 << endl;453out << endl;454455out << "points" << endl;456for(int i = 0; i < points.keys().size(); i++) {457int idx = points.keys().at(i);458QPointF p = points.value(idx);459if(idx > 0)460out << idx << " " << p.x() << " " << p.y() << endl;461}462463out << endl;464465out << "segments" << endl;466for(int i = 0; i < splines.keys().size(); i++) {467int idx = splines.keys().at(i);468Spline s = splines.value(idx);469if((s.out > 0) || (s.in > 0)) {470out << s.out << " " << s.in << " " << s.np;471for(int j = 0; j < s.np; j++)472out << " " << s.p[j];473out << " -bc=" << idx << endl;474}475}476477// Enumerate bodies:478//-------------------479bodies.clear();480481for(int i = 0; i < splines.keys().size(); i++) {482int idx = splines.keys().at(i);483Spline s = splines.value(idx);484485if(!bodies.contains(s.in))486bodies.push_back(s.in);487488if(!bodies.contains(s.out))489bodies.push_back(s.out);490}491492out << endl;493out << "materials" << endl;494for(int i = 0; i < bodies.size(); i++) {495if(bodies.at(i) == 0) continue;496out << bodies.at(i) << " material" << bodies.at(i);497out << " -maxh=0.1" << endl;498}499500file.close();501}502503void RenderArea::readSlot(QString fileName)504{505QFile file(fileName);506507if(!file.open(QIODevice::ReadOnly | QIODevice::Text))508return;509510points.clear();511splines.clear();512bodies.clear();513curveEditor->clearAll();514515reading = true;516517int mode = 0;518int index, i;519double x, y;520QPointF p;521Spline s;522523// Parse input file:524//-------------------525bool correctVersion = false;526int countSplines = 0;527528while(!file.atEnd()) {529QByteArray line = file.readLine();530531if(line.trimmed().isEmpty())532continue;533534if(line.trimmed().left(1) == "#")535continue;536537if(line.trimmed() == "splinecurves2dv2") {538correctVersion = true;539continue;540}541542if(line.trimmed() == "points") {543mode = 1;544continue;545}546547if(line.trimmed() == "segments") {548mode = 2;549continue;550}551552if(line.trimmed() == "materials") {553mode = 3;554continue;555}556557QTextStream stream(&line);558559switch(mode) {560case(1):561stream >> index >> x >> y;562p.setX(x); p.setY(y);563if(points.contains(index)) {564QString message = "Consistency error. ";565message += "Multiple point index " + QString::number(index);566points.clear();567splines.clear();568bodies.clear();569#if WITH_QT5 || WITH_QT6570cout << message.toLatin1().data() << endl;571#else572cout << message.toAscii().data() << endl;573#endif574emit(statusMessage(message));575return;576}577points.insert(index, p);578curveEditor->addPoint(index, x, y);579break;580581case(2):582stream >> s.out >> s.in >> s.np;583for(i = 0; i < s.np; i++)584stream >> s.p[i];585splines.insert(++countSplines, s);586curveEditor->addCurve(s.in, s.out, s.np, s.p);587break;588589case(3):590break;591592default:593break;594}595}596597if(!correctVersion) {598QString message = "Unsupported format (splinecurve2dv2 is required)";599#if WITH_QT5 || WITH_QT6600cout << message.toLatin1().data() << endl;601#else602cout << message.toAscii().data() << endl;603#endif604points.clear();605splines.clear();606curveEditor->clearAll();607emit(statusMessage(message));608reading = false;609return;610}611612// Enumerate bodies:613//-------------------614bodies.clear();615616for(int i = 0; i < splines.keys().size(); i++) {617int idx = splines.keys().at(i);618Spline s = splines.value(idx);619620if(!bodies.contains(s.in))621bodies.push_back(s.in);622623if(!bodies.contains(s.out))624bodies.push_back(s.out);625}626627// Check consistency:628//--------------------629for(int i = 0; i < splines.keys().size(); i++) {630int idx = splines.keys().at(i);631Spline s = splines.value(idx);632633for(int j = 0; j < s.np; j++) {634int p = s.p[j];635636if(!points.contains(p)) {637QString message = "Consistency error. Spline ";638message += QString::number(idx);639message += " refers to point ";640message += QString::number(p);641message += " which does not exist.";642643bodies.clear();644points.clear();645splines.clear();646curveEditor->clearAll();647648emit(statusMessage(message));649650reading = false;651652return;653}654}655}656657cout << "Points: " << points.count() << endl;658cout << "Splines: " << splines.count() << endl;659cout << "Bodies: " << bodies.count() - 1 << endl;660661emit(statusMessage("Ready"));662663reading = false;664}665666QPointF RenderArea::quadNurbs(double u, QPointF P0, QPointF P1, QPointF P2) const667{668QPointF result;669670double l0 = 1.0 - u;671double l1 = u;672673double q0 = l0 * l0;674double q1 = 2.0 * l0 * l1;675double q2 = l1 * l1;676677double w0 = 1.0;678double w1 = 1.0 / sqrt(2.0);679double w2 = 1.0;680681result = w0*q0*P0 + w1*q1*P1 + w2*q2*P2;682683result /= w0*q0 + w1*q1 + w2*q2;684685return result;686}687688void RenderArea::drawPointsSlot(bool state)689{690drawPoints = state;691update();692}693694void RenderArea::drawSplinesSlot(bool state)695{696drawSplines = state;697update();698}699700void RenderArea::drawTangentsSlot(bool state)701{702drawTangents = state;703update();704}705706void RenderArea::drawPointNumbersSlot(bool state)707{708drawPointNumbers = state;709update();710}711712void RenderArea::drawSplineNumbersSlot(bool state)713{714drawSplineNumbers = state;715update();716}717718void RenderArea::drawMaterialNumbersSlot(bool state)719{720drawMaterialNumbers = state;721update();722}723724void RenderArea::setCurveEditor(CurveEditor *curveEditor)725{726this->curveEditor = curveEditor;727}728729void RenderArea::modifyPoint(int idx, double x, double y)730{731if(reading) return;732733points.insert(idx, QPointF(x, y));734735update();736}737738void RenderArea::modifyCurve(int idx, int in, int out, int np, int p0, int p1, int p2)739{740if(reading) return;741742Spline s;743s.in = in;744s.out = out;745s.np = np;746s.p[0] = p0;747s.p[1] = p1;748s.p[2] = p2;749750splines.insert(idx+1, s);751752update();753}754755void RenderArea::updatePoints(QTableWidget *table)756{757points.clear();758759for(int i = 0; i < table->rowCount(); i++) {760int idx = table->item(i, 0)->text().toInt();761double x = table->item(i, 1)->text().toDouble();762double y = table->item(i, 2)->text().toDouble();763points.insert(idx, QPointF(x, y));764}765766update();767768}769770void RenderArea::updateCurves(QTableWidget *table)771{772Spline s;773774splines.clear();775776for(int i = 0; i < table->rowCount(); i++) {777s.in = table->item(i, 0)->text().toInt();778s.out = table->item(i, 1)->text().toInt();779s.np = table->item(i, 2)->text().toInt();780781if(s.np < 2) s.np = 2;782if(s.np > 3) s.np = 3;783784for(int j = 0; j < s.np; j++)785s.p[j] = table->item(i, 3+j)->text().toInt();786787splines.insert(i, s);788}789790update();791}792793794