1 /****************************************************************************
2 **  Copyright (c) 2019, Adel Kara Slimane <adel.ks@zegrapher.com>
3 **
4 **  This file is part of ZeGrapher's source code.
5 **
6 **  ZeGrapher is free software: you may copy, redistribute and/or modify it
7 **  under the terms of the GNU General Public License as published by the
8 **  Free Software Foundation, either version 3 of the License, or (at your
9 **  option) any later version.
10 **
11 **  This file is distributed in the hope that it will be useful, but
12 **  WITHOUT ANY WARRANTY; without even the implied warranty of
13 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 **  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.  If not, see <http://www.gnu.org/licenses/>.
18 **
19 ****************************************************************************/
20 
21 #include "GraphDraw/mainview.h"
22 
MainView(Information * info)23 MainView::MainView(Information *info): BaseGraphDraw(info)
24 {
25     orientation = QPageLayout::Landscape;
26     moveType = NOTHING;
27     setMouseTracking(true);
28     sizeSettings.scalingFactor = 1;
29 
30     minRelSize = RELATIVE_MIN_SIZE;
31 
32     screenDPI = qGuiApp->primaryScreen()->physicalDotsPerInch();
33 
34     relFigRect.setHeight(1);
35     relFigRect.setWidth(1);
36     relFigRect.moveTopLeft(QPointF(0, 0));
37 
38     connect(information, SIGNAL(graphSizeSettingsChanged()), this, SLOT(onSizeSettingsChange()));
39     connect(information, SIGNAL(graphZoomSettingsChanged()), this, SLOT(onZoomSettingsChange()));
40 }
41 
onSizeUnitChange()42 void MainView::onSizeUnitChange()
43 {
44     updateTargetSupportSizePx();
45 }
46 
updateWidgetSize()47 void MainView::updateWidgetSize()
48 {
49     sizeSettings = information->getGraphSizeSettings();
50     zoomSettings = information->getGraphZoomSettings();
51 
52     if(sizeSettings.sizingType == ZeSizeSettings::FITWINDOW or zoomSettings.zoomingType == ZeZoomSettings::FITSHEET)
53     {
54         resize(parentWidget()->size());
55     }
56     else
57     {
58         if(zoomSettings.zoom > 1)
59             resize((QSizeF(sizeSettings.pxSheetSize) * zoomSettings.zoom).toSize());
60         else resize(sizeSettings.pxSheetSize);
61     }
62 
63     emit widgetResized();
64 }
65 
updateTargetSupportSizePx()66 void MainView::updateTargetSupportSizePx()
67 {
68     sizeSettings = information->getGraphSizeSettings();
69 
70     if(sizeSettings.sizingType == ZeSizeSettings::FITWINDOW)
71     {
72         sizeSettings.pxSheetSize = size();
73         sizeSettings.cmSheetSize = sizeSettings.pxSheetSize / screenDPI * CM_PER_INCH;
74 
75         information->setGraphSizeSettings(sizeSettings);
76     }
77 
78     if(sizeSettings.sizeUnit == ZeSizeSettings::CENTIMETER)
79     {
80         // TODO: determine orientation from the sheet size: whether or not sizeSettings.cmSheetSize height > width
81         QSizeF sheetSizeMm(sizeSettings.cmSheetSize.width() * 10, sizeSettings.cmSheetSize.height() * 10);
82         QPageLayout layout(QPageSize(sheetSizeMm, QPageSize::Millimeter), orientation, QMarginsF(), QPageLayout::Millimeter);
83         layout.setOrientation(orientation);
84         targetSupportSizePixels = layout.fullRectPixels(int(screenDPI)).size();
85 
86         if(fabs(layout.fullRect().width() / layout.fullRect().height() - sheetSizeMm.width() / sheetSizeMm.height()) > 0.01)
87             targetSupportSizePixels.transpose();
88     }
89     else
90     {
91         targetSupportSizePixels = sizeSettings.pxSheetSize;
92     }
93 }
94 
getTargetSupportSizePixels()95 QSize MainView::getTargetSupportSizePixels()
96 {
97     return targetSupportSizePixels;
98 }
99 
getMinFigureRelativeSize()100 double MainView::getMinFigureRelativeSize()
101 {
102     return minRelSize;
103 }
104 
exportPDF(QString fileName,SheetSizeType sizeType)105 void MainView::exportPDF(QString fileName, SheetSizeType sizeType)
106 {
107     QPdfWriter *pdfWriter = new QPdfWriter(fileName);
108 
109     pdfWriter->setCreator(QString("ZeGrapher ") + SOFTWARE_VERSION_STR);
110     pdfWriter->setTitle(tr("Exported graph"));
111 
112     int targetResolution = int(screenDPI / sizeSettings.scalingFactor);
113 
114     pdfWriter->setResolution(targetResolution);
115 
116     QPageLayout layout;
117     layout.setUnits(QPageLayout::Millimeter);
118 
119     QSizeF sheetSizeMm(sizeSettings.cmSheetSize.width() * 10, sizeSettings.cmSheetSize.height() * 10);
120     layout.setPageSize(QPageSize(sheetSizeMm, QPageSize::Millimeter, "", QPageSize::FuzzyOrientationMatch));
121 
122     if(sizeType == SheetSizeType::NORMALISED)
123         layout.setOrientation(orientation);
124     else layout.setOrientation(QPageLayout::Portrait);
125 
126     pdfWriter->setPageLayout(layout);
127 
128     painter.begin(pdfWriter);
129 
130     if(information->getGraphSettings().estheticSettings.backgroundColor != QColor(Qt::white))
131     {
132         painter.setBrush(QBrush(information->getGraphSettings().estheticSettings.backgroundColor));
133         painter.drawRect(painter.viewport());
134     }
135 
136     figureRectScaled = getFigureRect(painter.viewport());
137 
138     painter.translate(figureRectScaled.topLeft());
139 
140     paint();
141 
142     painter.end();
143 
144     delete pdfWriter;
145 }
146 
exportSVG(QString fileName)147 void MainView::exportSVG(QString fileName)
148 {
149     QSvgGenerator svgGenerator;
150     svgGenerator.setFileName(fileName);
151 
152     svgGenerator.setTitle(tr("Exported graph"));
153     svgGenerator.setDescription(tr("Created with ZeGrapher ") + SOFTWARE_VERSION_STR);
154 
155     double targetResolution = screenDPI / sizeSettings.scalingFactor;
156 
157     svgGenerator.setResolution(int(targetResolution));
158 
159     QSize sizePx(int(sizeSettings.cmSheetSize.width() * 0.393701 * targetResolution), int(sizeSettings.cmSheetSize.height() * 0.393701 * targetResolution));
160 
161     svgGenerator.setSize(sizePx);
162     svgGenerator.setViewBox(QRect(QPoint(0, 0), sizePx));
163 
164     painter.begin(&svgGenerator);
165 
166     if(information->getGraphSettings().estheticSettings.backgroundColor != QColor(Qt::white))
167     {
168         painter.setBrush(QBrush(information->getGraphSettings().estheticSettings.backgroundColor));
169         painter.drawRect(painter.viewport());
170     }
171 
172     figureRectScaled = getFigureRect(painter.viewport());
173 
174     painter.translate(figureRectScaled.topLeft());
175 
176     paint();
177 
178     painter.end();
179 }
180 
181 
paintEvent(QPaintEvent * event)182 void MainView::paintEvent(QPaintEvent *event)
183 {
184     Q_UNUSED(event);
185 
186     if(currentSize != size())
187     {
188         currentSize = size();
189         updateWidgetSize();
190         onSizeSettingsChange();
191     }
192 
193     painter.begin(this);
194 
195     drawSupport();
196     drawFigureRect();
197 
198     assignMouseRects();
199 
200     drawGraph();
201 
202     painter.end();
203 }
204 
assignMouseRects()205 void MainView::assignMouseRects()
206 {
207     QPoint topLeftTranslation;
208     topLeftTranslation.setX(-8);
209     topLeftTranslation.setY(-8);
210 
211     QPoint bottomRightTranslation;
212     bottomRightTranslation.setX(8);
213     bottomRightTranslation.setY(8);
214 
215 
216     topLeft.setTopLeft(figureRect.topLeft() + topLeftTranslation);
217     topLeft.setBottomRight(figureRect.topLeft() + bottomRightTranslation);
218 
219     topRight.setTopLeft(figureRect.topRight() + topLeftTranslation);
220     topRight.setBottomRight(figureRect.topRight() + bottomRightTranslation);
221 
222     bottomLeft.setTopLeft(figureRect.bottomLeft() + topLeftTranslation);
223     bottomLeft.setBottomRight(figureRect.bottomLeft() + bottomRightTranslation);
224 
225     bottomRight.setTopLeft(figureRect.bottomRight() + topLeftTranslation);
226     bottomRight.setBottomRight(figureRect.bottomRight() + bottomRightTranslation);
227 
228     top.setTopLeft(topLeft.topRight());
229     top.setBottomRight(topRight.bottomLeft());
230 
231     bottom.setTopLeft(bottomLeft.topRight());
232     bottom.setBottomRight(bottomRight.bottomLeft());
233 
234     left.setTopLeft(topLeft.bottomLeft());
235     left.setBottomRight(bottomLeft.topRight());
236 
237     right.setTopLeft(topRight.bottomLeft());
238     right.setBottomRight(bottomRight.topRight());
239 }
240 
getDrawableRect(const QRect & refSupportRect)241 QRect MainView::getDrawableRect(const QRect &refSupportRect)
242 {   // gives the drawable rect in the given support, in the support's coordinates
243     // the drawable rect is the support's rect minus the margins
244 
245     int pxMargins = 0;
246 
247     if(information->getGraphSizeSettings().sizeUnit == ZeSizeSettings::CENTIMETER)
248     {
249         pxMargins = int(sizeSettings.cmMargins / CM_PER_INCH * screenDPI);
250     }
251     else
252     {
253         pxMargins = sizeSettings.pxMargins;
254     }
255 
256     QRect drawableRect;
257     drawableRect.setWidth(refSupportRect.width() - 2 * pxMargins);
258     drawableRect.setHeight(refSupportRect.height() - 2 * pxMargins);
259     drawableRect.moveTopLeft(QPoint(pxMargins, pxMargins));
260 
261     return drawableRect;
262 }
263 
getFigureRect(const QRect & refSupportRect)264 QRect MainView::getFigureRect(const QRect &refSupportRect)
265 {
266     QRect rect;
267     QPoint graphRectTopLeft;
268 
269     graphRectTopLeft.setX(int(relFigRect.topLeft().x() * double(refSupportRect.width())));
270     graphRectTopLeft.setY(int(relFigRect.topLeft().y() * double(refSupportRect.height())));
271 
272     rect.setWidth(int(relFigRect.width() * refSupportRect.width()));
273     rect.setHeight(int(relFigRect.height() * refSupportRect.height()));
274     rect.moveTopLeft(refSupportRect.topLeft() + graphRectTopLeft);
275 
276     return rect;
277 }
278 
supportRectFromViewRect(QRect viewRect)279 QRect MainView::supportRectFromViewRect(QRect viewRect)
280 {
281     QRect rect;
282     zoomSettings = information->getGraphZoomSettings();
283     sizeSettings = information->getGraphSizeSettings();
284 
285     if(sizeSettings.sizingType == ZeSizeSettings::FITWINDOW)
286     {
287         rect = viewRect;
288     }
289     else if(zoomSettings.zoomingType == ZeZoomSettings::FITSHEET)
290     {
291         double ratio, targetRatio;
292         ratio = double(viewRect.height()) / double(viewRect.width());
293 
294         if(sizeSettings.sizeUnit == ZeSizeSettings::CENTIMETER)
295             targetRatio = sizeSettings.cmSheetSize.height() / sizeSettings.cmSheetSize.width();
296         else targetRatio = double(sizeSettings.pxSheetSize.height()) / double(sizeSettings.pxSheetSize.width());
297 
298         if(ratio <= targetRatio)
299         {
300             rect.setHeight(viewRect.height());
301             rect.setWidth(int(double(viewRect.height()) / targetRatio));
302 
303             rect.moveTopLeft(QPoint((viewRect.width() - rect.width())/2, 0));
304         }
305         else
306         {
307             rect.setHeight(int(double(viewRect.width()) * targetRatio));
308             rect.setWidth(viewRect.width());
309 
310             rect.moveTopLeft(QPoint(0, (viewRect.height() - rect.height())/2));
311         }
312     }
313     else
314     {
315         rect.setSize(sizeSettings.pxSheetSize);
316         rect.translate(viewRect.center() - rect.center());
317     }
318 
319     return rect;
320 }
321 
drawSupport()322 void MainView::drawSupport()
323 { // draws the sheet on an untransformed view
324     painter.setBrush(QBrush(information->getGraphSettings().estheticSettings.backgroundColor));
325 
326     supportRect = supportRectFromViewRect(painter.viewport());
327 
328     painter.drawRect(supportRect);
329 }
330 
drawFigureRect()331 void MainView::drawFigureRect()
332 {
333     figureRect = getFigureRect(supportRect);
334 
335     painter.setBrush(Qt::NoBrush);
336     pen.setStyle(Qt::DashLine);
337     pen.setWidth(1);
338     pen.setColor(information->getAxesSettings().x.color);
339     painter.setPen(pen);
340     painter.drawRect(figureRect);
341 
342     pen.setStyle(Qt::SolidLine);
343     painter.setPen(pen);
344 }
345 
setGraphRange(GraphRange range)346 void MainView::setGraphRange(GraphRange range)
347 {
348 
349 }
350 
scaleView(const QRect & refSheetRect)351 void MainView::scaleView(const QRect &refSheetRect)
352 {
353     zoomSettings = information->getGraphZoomSettings();
354 
355     if(zoomSettings.zoomingType == ZeZoomSettings::FITSHEET)
356     {
357         double newZoom;
358 
359         if(information->getGraphSizeSettings().sizeUnit == ZeSizeSettings::CENTIMETER)
360         {
361             newZoom = double(refSheetRect.width()) / double(targetSupportSizePixels.width());
362         }
363         else
364         {
365             newZoom = double(refSheetRect.width()) / double(sizeSettings.pxSheetSize.width());
366         }
367 
368         if(fabs(newZoom - zoomSettings.zoom) > 0.001)
369         {
370             zoomSettings.zoom = newZoom;
371 
372             information->setGraphZoomSettings(zoomSettings);
373         }
374     }
375 
376     double totalScaleFactor = zoomSettings.zoom * sizeSettings.scalingFactor;
377 
378     painter.scale(totalScaleFactor, totalScaleFactor);
379 }
380 
drawGraph()381 void MainView::drawGraph()
382 {
383     scaleView(supportRect);
384 
385     sheetRectScaled = painter.worldTransform().inverted().mapRect(supportRect);
386 
387     figureRectScaled = getFigureRect(sheetRectScaled);
388 
389     painter.translate(figureRectScaled.topLeft());
390 
391     paint();
392 }
393 
mousePressEvent(QMouseEvent * event)394 void MainView::mousePressEvent(QMouseEvent *event)
395 {
396     if(topLeft.contains(event->pos()))
397         moveType = TOPLEFT_CORNER;
398     else if(topRight.contains(event->pos()))
399         moveType = TOPRIGHT_CORNER;
400     else if(top.contains(event->pos()))
401         moveType = TOP_SIDE;
402     else if(bottomLeft.contains(event->pos()))
403         moveType = BOTTOMLEFT_CORNER;
404     else if(bottomRight.contains(event->pos()))
405         moveType = BOTTOMRIGHT_CORNER;
406     else if(bottom.contains(event->pos()))
407         moveType = BOTTOM_SIDE;
408     else if(left.contains(event->pos()))
409         moveType = LEFT_SIDE;
410     else if(right.contains(event->pos()))
411         moveType = RIGHT_SIDE;
412     else if(figureRect.contains(event->pos()))
413         moveType = ALL;
414     else moveType = NOTHING;
415 
416     if(moveType != NOTHING)
417         lastMousePos = event->pos();
418 }
419 
mouseMoveEvent(QMouseEvent * event)420 void MainView::mouseMoveEvent(QMouseEvent *event)
421 {
422     if(moveType == NOTHING)
423     {
424         if(topLeft.contains(event->pos()) || bottomRight.contains(event->pos()))
425             setCursor(Qt::SizeFDiagCursor);
426         else if(topRight.contains(event->pos()) || bottomLeft.contains(event->pos()))
427             setCursor(Qt::SizeBDiagCursor);
428         else if(top.contains(event->pos()) || bottom.contains(event->pos()))
429             setCursor(Qt::SizeVerCursor);
430         else if(left.contains(event->pos()) || right.contains(event->pos()))
431             setCursor(Qt::SizeHorCursor);
432         else if(figureRect.contains(event->pos()))
433             setCursor(Qt::SizeAllCursor);
434         else setCursor(Qt::ArrowCursor);
435 
436         lastMousePos = event->pos();
437     }
438     else
439     {
440         QPoint dr = event->pos() - lastMousePos;
441         lastMousePos = event->pos();
442         auto zoomSettings = information->getGraphZoomSettings();
443         switch(moveType)
444         {
445         case TOPLEFT_CORNER:
446             figureRect.setTopLeft(figureRect.topLeft() + dr);
447             break;
448         case TOPRIGHT_CORNER:
449             figureRect.setTopRight(figureRect.topRight() + dr);
450             break;
451         case BOTTOMLEFT_CORNER:
452             figureRect.setBottomLeft(figureRect.bottomLeft() + dr);
453             break;
454         case BOTTOMRIGHT_CORNER:
455             figureRect.setBottomRight(figureRect.bottomRight() + dr);
456             break;
457         case LEFT_SIDE:
458             figureRect.setLeft(figureRect.left() + dr.x());
459             break;
460         case TOP_SIDE:
461             figureRect.setTop(figureRect.top() + dr.y());
462             break;
463         case RIGHT_SIDE:
464             figureRect.setRight(figureRect.right() + dr.x());
465             break;
466         case BOTTOM_SIDE:
467             figureRect.setBottom(figureRect.bottom() + dr.y());
468             break;
469         case ALL:
470             figureRect.translate(dr);
471             break;
472         case NOTHING:
473             break;
474         }
475 
476 
477         QPointF pt = figureRect.topLeft() - supportRect.topLeft();
478         pt.setX(pt.x() / double(supportRect.width()));
479         pt.setY(pt.y() / double(supportRect.height()));
480 
481         relFigRect.setWidth(double(figureRect.width()) / double(supportRect.width()));
482         relFigRect.setHeight(double(figureRect.height()) / double(supportRect.height()));
483         relFigRect.moveTopLeft(pt);
484 
485         constrainFigureRectRel();
486         updateFigureSize();
487         update();
488     }
489 
490 }
491 
onSizeSettingsChange()492 void MainView::onSizeSettingsChange()
493 {
494 
495     updateTargetSupportSizePx();
496 
497     // Add function here that calcualtes the needed widget size
498 
499     sizeSettings = information->getGraphSizeSettings();
500 
501     if(sizeSettings.sizeUnit == ZeSizeSettings::CENTIMETER)
502     {
503         relFigRect.setWidth(sizeSettings.cmFigureSize.width() / (sizeSettings.cmSheetSize.width()));
504         relFigRect.setHeight(sizeSettings.cmFigureSize.height() / (sizeSettings.cmSheetSize.height()));
505     }
506     else
507     {
508         relFigRect.setWidth(double(sizeSettings.pxFigureSize.width()) / double(sizeSettings.pxSheetSize.width()));
509         relFigRect.setHeight(double(sizeSettings.pxFigureSize.height()) / (sizeSettings.pxSheetSize.height() ));
510     }
511 
512     constrainFigureRectRel();
513     updateFigureSize();
514 
515     update();
516 }
517 
onZoomSettingsChange()518 void MainView::onZoomSettingsChange()
519 {
520     update();
521 }
522 
updateFigureSize()523 void MainView::updateFigureSize()
524 {
525     sizeSettings.cmFigureSize.setWidth(relFigRect.width() * sizeSettings.cmSheetSize.width());
526     sizeSettings.cmFigureSize.setHeight(relFigRect.height() * sizeSettings.cmSheetSize.height());
527 
528     sizeSettings.pxFigureSize.setWidth(int(relFigRect.width() * double(sizeSettings.pxSheetSize.width())));
529     sizeSettings.pxFigureSize.setHeight(int(relFigRect.height() * double(sizeSettings.pxSheetSize.height())));
530 
531     disconnect(information, SIGNAL(graphSizeSettingsChanged()), this, SLOT(onSizeSettingsChange()));
532     information->setGraphSizeSettings(sizeSettings);
533     connect(information, SIGNAL(graphSizeSettingsChanged()), this, SLOT(onSizeSettingsChange()));
534 }
535 
constrainFigureRectRel()536 void MainView::constrainFigureRectRel()
537 {
538     if(sizeSettings.sizeUnit == ZeSizeSettings::CENTIMETER)
539     {
540         relMargins.setWidth(sizeSettings.cmMargins / sizeSettings.cmSheetSize.width());
541         relMargins.setHeight(sizeSettings.cmMargins / sizeSettings.cmSheetSize.height());
542     }
543     else
544     {
545         relMargins.setWidth(double(sizeSettings.pxMargins) / double(sizeSettings.pxSheetSize.width()));
546         relMargins.setHeight(double(sizeSettings.pxMargins) / double(sizeSettings.pxSheetSize.height()));
547     }
548 
549     if(relFigRect.width() > 1 - 2*relMargins.width())
550         relFigRect.setWidth(1 - 2*relMargins.width());
551 
552     if(relFigRect.height() > 1 - 2*relMargins.height())
553         relFigRect.setHeight(1 - 2*relMargins.height());
554     //------------------------------
555 
556     if(relFigRect.width() < minRelSize)
557         relFigRect.setWidth(minRelSize);
558 
559     if(relFigRect.height() < minRelSize)
560         relFigRect.setHeight(minRelSize);
561     //------------------------------
562 
563     if(relFigRect.left() < relMargins.width())
564         relFigRect.moveLeft(relMargins.width());
565 
566     if(relFigRect.right() > 1 - relMargins.width())
567         relFigRect.moveRight(1 - relMargins.width());
568     //------------------------------
569 
570     if(relFigRect.bottom() > 1 - relMargins.height())
571         relFigRect.moveBottom(1 - relMargins.height());
572 
573     if(relFigRect.top() < relMargins.height())
574         relFigRect.moveTop(relMargins.height());
575 }
576 
mouseReleaseEvent(QMouseEvent * event)577 void MainView::mouseReleaseEvent(QMouseEvent *event)
578 {
579     Q_UNUSED(event);
580     setCursor(Qt::ArrowCursor);
581     moveType = NOTHING;
582 }
583 
wheelEvent(QWheelEvent * event)584 void MainView::wheelEvent(QWheelEvent *event)
585 {
586     Q_UNUSED(event);
587 }
588