SymbolEditor  1.3.0
 All Classes Files Functions Variables Enumerations Enumerator Friends Pages
Editor.cpp
Go to the documentation of this file.
1 /********************************************************************************
2  * Copyright (C) 2011 by Stephen Allewell *
3  * sallewell@users.sourceforge.net *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  ********************************************************************************/
10 
11 
145 #include "Editor.h"
146 
147 #include <QAction>
148 #include <QMouseEvent>
149 #include <QPainter>
150 
151 #include <KCharSelect>
152 
153 #include <math.h>
154 
155 #include "SymbolEditor.h"
156 
157 
158 const int pointsRequired[] = {1, 1, 3, 2, 2};
169 Editor::Editor(QWidget *parent)
170  : QWidget(parent),
171  m_index(0),
172  m_charSelect(0)
173 {
174  readSettings();
175 
176  setMouseTracking(true);
177  setFocusPolicy(Qt::StrongFocus);
178 
179  m_topEdge = QLineF(0.0, 0.0, 1.0, 0.0);
180  m_bottomEdge = QLineF(0.0, 1.0, 1.0, 1.0);
181  m_leftEdge = QLineF(0.0, 0.0, 0.0, 1.0);
182  m_rightEdge = QLineF(1.0, 0, 1.0, 1.0);
183 
184  m_angles << 0 << 45 << 90 << 135 << 180 << 225 << 270 << 315;
185 }
186 
187 
193 {
194  delete m_charSelect;
195 }
196 
197 
204 QPair<qint16, Symbol> Editor::symbol()
205 {
207  return QPair<qint16, Symbol>(m_index, m_symbol);
208 }
209 
210 
217 void Editor::setSymbol(const QPair<qint16, Symbol> &pair)
218 {
219  m_undoStack.clear();
220  m_index = pair.first;
221  m_symbol = pair.second;
224  update();
225 }
226 
227 
237 QPainterPath Editor::moveTo(const QPointF &to)
238 {
239  QPainterPath path = m_painterPath;
240  m_painterPath.moveTo(to);
242  update();
243  return path;
244 }
245 
246 
256 QPainterPath Editor::lineTo(const QPointF &to)
257 {
258  QPainterPath path = m_painterPath;
259  m_painterPath.lineTo(to);
261  update();
262  return path;
263 }
264 
265 
277 QPainterPath Editor::cubicTo(const QPointF &control1, const QPointF &control2, const QPointF &to)
278 {
279  QPainterPath path = m_painterPath;
280  m_painterPath.cubicTo(control1, control2, to);
282  update();
283  return path;
284 }
285 
286 
297 QPainterPath Editor::addRectangle(const QPointF &from, const QPointF &to)
298 {
299  QPainterPath path = m_painterPath;
300  m_painterPath.addRect(QRectF(from, to));
302  update();
303  return path;
304 }
305 
306 
317 QPainterPath Editor::addEllipse(const QPointF &from, const QPointF &to)
318 {
319  QPainterPath path = m_painterPath;
320  m_painterPath.addEllipse(QRectF(from, to));
322  update();
323  return path;
324 }
325 
326 
332 void Editor::removeLast(const QPainterPath& path)
333 {
334  m_painterPath = path;
336  update();
337 }
338 
339 
346 void Editor::movePoint(int index, const QPointF &to)
347 {
348  m_points[index] = to;
350  update();
351 }
352 
353 
358 {
359  for (int i = 0 ; i < m_points.count() ; ++i) {
360  QPointF p = m_points[i];
361  m_points[i] = QPointF(p.y(), 1.0 - p.x());
362  }
363 
364  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
365  QPointF p = m_activePoints[i];
366  m_activePoints[i] = QPointF(p.y(), 1.0 - p.x());
367  }
368 
370  update();
371 }
372 
373 
378 {
379  for (int i = 0 ; i < m_points.count() ; ++i) {
380  QPointF p = m_points[i];
381  m_points[i] = QPointF(1.0 - p.y(), p.x());
382  }
383 
384  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
385  QPointF p = m_activePoints[i];
386  m_activePoints[i] = QPointF(1.0 - p.y(), p.x());
387  }
388 
390  update();
391 }
392 
393 
398 {
399  for (int i = 0 ; i < m_points.count() ; ++i) {
400  QPointF p = m_points[i];
401  p.setX(1.0 - p.x());
402  m_points[i] = p;
403  }
404 
405  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
406  QPointF p = m_activePoints[i];
407  p.setX(1.0 - p.x());
408  m_activePoints[i] = p;
409  }
410 
412  update();
413 }
414 
415 
420 {
421  for (int i = 0 ; i < m_points.count() ; ++i) {
422  QPointF p = m_points[i];
423  p.setY(1.0 - p.y());
424  m_points[i] = p;
425  }
426 
427  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
428  QPointF p = m_activePoints[i];
429  p.setY(1.0 - p.y());
430  m_activePoints[i] = p;
431  }
432 
434  update();
435 }
436 
437 
443 void Editor::setFilled(bool filled)
444 {
445  m_symbol.setFilled(filled);
446  update();
447 }
448 
449 
455 void Editor::setFillRule(Qt::FillRule rule)
456 {
457  m_painterPath.setFillRule(rule);
458  update();
459 }
460 
461 
467 void Editor::setCapStyle(Qt::PenCapStyle capStyle)
468 {
469  m_symbol.setCapStyle(capStyle);
470  update();
471 }
472 
473 
479 void Editor::setJoinStyle(Qt::PenJoinStyle joinStyle)
480 {
481  m_symbol.setJoinStyle(joinStyle);
482  update();
483 }
484 
485 
493 void Editor::setLineWidth(double width)
494 {
495  width = round(width * 100) / 100;
496  m_symbol.setLineWidth(width);
497  emit minLineWidth(width == 0.01);
498  emit maxLineWidth(width == 1.00);
499  update();
500 }
501 
502 
510 QPainterPath Editor::setPath(const QPainterPath &path)
511 {
512  QPainterPath originalPath = m_painterPath;
513  m_painterPath = path;
515  update();
516 
517  return originalPath;
518 }
519 
520 
526 {
527  m_undoStack.clear();
528  m_points.clear();
529  m_activePoints.clear();
530  m_elements.clear();
531  m_index = 0;
532  m_symbol = Symbol();
534  update();
535 }
536 
537 
543 QUndoStack *Editor::undoStack()
544 {
545  return &m_undoStack;
546 }
547 
548 
558 void Editor::selectTool(QAction *action)
559 {
560  m_activePoints.clear();
561  m_toolMode = static_cast<Editor::ToolMode>(action->data().toInt());
562 
563  if (m_toolMode == Editor::Character) {
564  if (m_charSelect == 0) {
565  m_charSelect = new KCharSelect(0, 0);
566  connect(m_charSelect, SIGNAL(charSelected(QChar)), this, SLOT(charSelected(QChar)));
567  }
568 
569  m_charSelect->show();
570  } else if (m_charSelect) {
571  m_charSelect->hide();
572  }
573 }
574 
575 
582 void Editor::charSelected(const QChar &character)
583 {
584  QPainterPath path;
585  QFont font = m_charSelect->currentFont();
586  path.addText(0.0, 0.0, font, QString(character));
587 
588  // scale the path to fit the bounding rectangle to fit in a rectangle 0,0-1,1 maintaining
589  // aspect ratio.
590  QRectF boundingRect = path.boundingRect();
591 
592  double scale = double(m_gridElements - m_borderSize - m_borderSize) / double(std::max(boundingRect.width(), boundingRect.height()) * m_gridElements);
593  QTransform transform = QTransform::fromTranslate(-boundingRect.center().x(), -boundingRect.center().y()) * QTransform::fromScale(scale, scale) * QTransform::fromTranslate(0.5, 0.5);
594  path = transform.map(path);
595 
596  m_undoStack.push(new AddCharacterCommand(this, path));
597 }
598 
599 
605 void Editor::enableSnap(bool enabled)
606 {
607  m_snap = enabled;
608 }
609 
610 
616 void Editor::selectFilled(bool filled)
617 {
618  m_undoStack.push(new ChangeFilledCommand(this, m_symbol.filled(), filled));
619 }
620 
621 
628 void Editor::selectFillRule(QAction *action)
629 {
630  Qt::FillRule fillRule = static_cast<Qt::FillRule>(action->data().toInt());
631  m_undoStack.push(new ChangeFillRuleCommand(this, m_symbol.path().fillRule(), fillRule));
632 }
633 
634 
641 void Editor::selectCapStyle(QAction *action)
642 {
643  Qt::PenCapStyle capStyle = static_cast<Qt::PenCapStyle>(action->data().toInt());
644  m_undoStack.push(new ChangeCapStyleCommand(this, m_symbol.capStyle(), capStyle));
645 }
646 
647 
654 void Editor::selectJoinStyle(QAction *action)
655 {
656  Qt::PenJoinStyle joinStyle = static_cast<Qt::PenJoinStyle>(action->data().toInt());
657  m_undoStack.push(new ChangeJoinStyleCommand(this, m_symbol.joinStyle(), joinStyle));
658 }
659 
660 
665 {
667 }
668 
669 
674 {
676 }
677 
678 
683 {
684  m_undoStack.push(new RotateLeftCommand(this));
685 }
686 
687 
692 {
693  m_undoStack.push(new RotateRightCommand(this));
694 }
695 
696 
701 {
702  m_undoStack.push(new FlipHorizontalCommand(this));
703 }
704 
705 
710 {
711  m_undoStack.push(new FlipVerticalCommand(this));
712 }
713 
714 
719 {
721 }
722 
723 
730 {
731  m_gridElements = Configuration::editor_GridElements();
732  m_elementSize = Configuration::editor_ElementSize();
733  m_elementGrouping = Configuration::editor_ElementGrouping();
734  m_pointSize = Configuration::editor_PointSize();
735  m_snapThreshold = 1.0 / m_gridElements * Configuration::editor_SnapThreshold();
736  m_borderSize = Configuration::editor_BorderSize();
737  m_preferredSizeColor = Configuration::editor_PreferredSizeColor();
738  m_guideLineColor = Configuration::editor_GuideLineColor();
739 
741  resize(m_size + 1, m_size + 1);
742  setMinimumSize(size());
743 }
744 
745 
753 void Editor::mousePressEvent(QMouseEvent *event)
754 {
755  QPointF p = snapPoint(event->pos());
756 
757  if (event->buttons() & Qt::LeftButton) {
758  m_start = m_tracking = p;
759  m_rubberBand = QRectF();
760 
761  if (node(p)) {
762  m_dragging = true;
764  } else {
765  addPoint(p);
766  }
767  }
768 
769  update();
770 }
771 
772 
788 void Editor::mouseMoveEvent(QMouseEvent *event)
789 {
790  if (!m_guideLines.isEmpty()) {
791  m_guideLines.clear();
792  m_guideCircles.clear();
793  update();
794  }
795 
796  QPointF p = snapPoint(event->pos());
797 
798  if (event->buttons() & Qt::LeftButton) {
799  if (m_tracking != p) {
800  m_tracking = p;
801 
802  if (m_dragging) {
803  if (m_dragPointIndex.first) {
804  m_points[m_dragPointIndex.second] = p;
806  } else {
807  m_activePoints[m_dragPointIndex.second] = p;
808  }
809  } else if (m_toolMode == Rectangle || m_toolMode == Ellipse) {
810  m_rubberBand = QRectF(m_start, m_tracking).normalized();
811  }
812 
813  update();
814  }
815  } else {
816  if (node(p)) {
817  setCursor(Qt::SizeAllCursor);
818  } else {
819  setCursor(Qt::ArrowCursor);
820  }
821  }
822 
823  if (constructGuides(p)) {
824  update();
825  }
826 }
827 
828 
841 void Editor::mouseReleaseEvent(QMouseEvent *event)
842 {
843  QPointF p = snapPoint(event->pos());
844 
845  if (m_dragging) {
846  if ((p != m_start) && (m_dragPointIndex.first)) {
847  m_undoStack.push(new MovePointCommand(this, m_dragPointIndex.second, m_start, p));
848  }
849 
850  m_dragging = false;
851  } else if (m_toolMode == Rectangle || m_toolMode == Ellipse) {
852  m_rubberBand = QRectF();
853 
854  if (p != m_start) {
855  addPoint(p);
856  }
857  }
858 
859  update();
860 }
861 
862 
873 void Editor::addPoint(const QPointF &point)
874 {
875  m_activePoints.append(point);
876 
877  if (m_activePoints.count() == pointsRequired[m_toolMode]) {
878  switch (m_toolMode) {
879  case MoveTo:
880  m_undoStack.push(new MoveToCommand(this, m_activePoints.at(0)));
881  break;
882 
883  case LineTo:
884  m_undoStack.push(new LineToCommand(this, m_activePoints.at(0)));
885  break;
886 
887  case CubicTo:
888  m_undoStack.push(new CubicToCommand(this, m_activePoints.at(0), m_activePoints.at(1), m_activePoints.at(2)));
889  break;
890 
891  case Rectangle:
892  m_undoStack.push(new RectangleCommand(this, m_activePoints.at(0), m_activePoints.at(1)));
893  break;
894 
895  case Ellipse:
896  m_undoStack.push(new EllipseCommand(this, m_activePoints.at(0), m_activePoints.at(1)));
897  break;
898  }
899 
900  m_activePoints.clear();
901  }
902 
903  update();
904 }
905 
906 
919 void Editor::paintEvent(QPaintEvent *event)
920 {
921  // initialise the painter
922  QPainter p(this);
923  p.setRenderHint(QPainter::Antialiasing, true);
924  p.fillRect(event->rect(), Qt::white);
925 
926  // draw vertical grid
927  for (int x = 0 ; x < m_gridElements + 1 ; ++x) {
928  if (x % m_elementGrouping) {
929  p.setPen(Qt::lightGray);
930  } else {
931  p.setPen(Qt::darkGray);
932  }
933 
934  p.drawLine(x * m_elementSize, 0, x * m_elementSize, m_size + 1);
935  }
936 
937  // draw horizontal grid
938  for (int y = 0 ; y < m_gridElements + 1 ; ++y) {
939  if (y % m_elementGrouping) {
940  p.setPen(Qt::lightGray);
941  } else {
942  p.setPen(Qt::darkGray);
943  }
944 
945  p.drawLine(0, y * m_elementSize, m_size + 1, y * m_elementSize);
946  }
947 
948  // define a rectangle for the points
949  QRect dot(0, 0, m_pointSize, m_pointSize);
950 
951  // create a dashed pen for use with curve references and a wide pen for the shape
952  QPen dashedPen(Qt::DashLine);
953 
954  // draw all the points as a circle
955  p.setBrush(Qt::SolidPattern);
956 
957  for (int i = 0 ; i < m_points.count() ; ++i) {
958  QPoint s = toScreen(m_points.at(i));
959  dot.moveCenter(s);
960  p.drawEllipse(dot);
961  }
962 
963  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
964  QPoint s = toScreen(m_activePoints.at(i));
965  dot.moveCenter(s);
966  p.drawEllipse(dot);
967  }
968 
969  // iterate through the elements and for each curve element draw the reference lines with a dashed pen
970  for (int i = 0, j = 0 ; i < m_elements.count() ; ++i) {
971  QPainterPath::ElementType element = m_elements[i];
972  QPoint s;
973  QPoint e;
974  QPoint c1;
975  QPoint c2;
976 
977  switch (element) {
978  case QPainterPath::MoveToElement:
979  // increment to next pointer
980  ++j;
981  break;
982 
983  case QPainterPath::LineToElement:
984  // increment to next pointer
985  j++;
986  break;
987 
988  case QPainterPath::CurveToElement:
989  p.setPen(dashedPen);
990  s = toScreen(m_points.at(j - 1));
991  c1 = toScreen(m_points.at(j++));
992  c2 = toScreen(m_points.at(j++));
993  e = toScreen(m_points.at(j++));
994  p.drawLine(s, c1);
995  p.drawLine(c1, c2);
996  p.drawLine(c2, e);
997  break;
998  }
999  }
1000 
1001  // draw the rubber band rectangle or the active points for the current command
1002  if (m_rubberBand.isValid()) {
1003  p.setBrush(Qt::NoBrush);
1004 
1005  if (m_toolMode == Rectangle) {
1006  p.drawRect(QRect(toScreen(m_rubberBand.topLeft()), toScreen(m_rubberBand.bottomRight())));
1007  } else {
1008  p.drawEllipse(QRect(toScreen(m_rubberBand.topLeft()), toScreen(m_rubberBand.bottomRight())));
1009  }
1010  } else if (m_activePoints.count() && m_toolMode != Rectangle && m_toolMode != Ellipse) {
1011  p.setPen(dashedPen);
1012  QPoint s;
1013 
1014  if (m_points.count()) {
1015  s = toScreen(m_points.last());
1016  }
1017 
1018  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
1019  QPoint e = toScreen(m_activePoints[i]);
1020  p.drawLine(s, e);
1021  s = e;
1022  }
1023  }
1024 
1025  // draw a rectangle representing the preferred symbol size allowing for some white space
1026  int border = m_elementSize * m_borderSize;
1027  QRect preferredSizeRect = QRect(0, 0, m_size, m_size).adjusted(border, border, -border, -border);
1028  QColor preferredSizeColor(m_preferredSizeColor);
1029  preferredSizeColor.setAlpha(128);
1030  p.setPen(preferredSizeColor);
1031  p.setBrush(Qt::NoBrush);
1032  p.drawRect(preferredSizeRect);
1033 
1034  // scale the painter and draw the path
1035  p.scale(m_size, m_size);
1036  QColor c(Qt::black);
1037  c.setAlpha(128);
1038  QPen pathPen(c);
1039  pathPen.setWidthF(m_symbol.filled() ? 0.0 : m_symbol.lineWidth());
1040  pathPen.setCapStyle(m_symbol.capStyle());
1041  pathPen.setJoinStyle(m_symbol.joinStyle());
1042  p.setPen(pathPen);
1043  QBrush pathFill(m_symbol.filled() ? Qt::SolidPattern : Qt::NoBrush);
1044  pathFill.setColor(c);
1045  p.setBrush(pathFill);
1046  p.drawPath(m_painterPath);
1047 
1048  // draw the guidelines
1049  QColor guideLineColor(m_guideLineColor);
1050  guideLineColor.setAlpha(128);
1051  QPen guideLinePen(guideLineColor);
1052  p.setPen(guideLinePen);
1053  p.setBrush(Qt::NoBrush);
1054  QRectF snapRect(0, 0, 0.03, 0.03);
1055 
1056  foreach (const QPointF & snapPoint, m_snapPoints) {
1057  snapRect.moveCenter(snapPoint);
1058  p.drawRect(snapRect);
1059  }
1060 
1061  foreach (const QLineF & guideLine, m_guideLines) {
1062  p.drawLine(guideLine);
1063  }
1064 
1065  foreach (qreal guideCircle, m_guideCircles) {
1066  p.drawEllipse(QPointF(0.5, 0.5), guideCircle, guideCircle);
1067  }
1068 }
1069 
1070 
1076 void Editor::keyPressEvent(QKeyEvent *event)
1077 {
1078  switch (event->key()) {
1079  case Qt::Key_Escape:
1080  m_activePoints.clear();
1081  update();
1082  break;
1083 
1084  default:
1085  QWidget::keyPressEvent(event);
1086  break;
1087  }
1088 }
1089 
1090 
1101 QPointF Editor::toSymbol(const QPoint &point) const
1102 {
1103  double sx = static_cast<double>(point.x()) / m_size;
1104  double sy = static_cast<double>(point.y()) / m_size;
1105  return QPointF(sx, sy);
1106 }
1107 
1108 
1121 QPointF Editor::snapPoint(const QPoint &point) const
1122 {
1123  QPair<bool, QPointF> snap = snapToGuide(toSymbol(point));
1124 
1125  if (!snap.first) {
1126  snap = snapToGrid(point);
1127  }
1128 
1129  return snap.second;
1130 }
1131 
1132 
1143 QPair<bool, QPointF> Editor::snapToGrid(const QPoint &point) const
1144 {
1145  QPair<bool, QPointF> snap(false, toSymbol(point));
1146 
1147  if (m_snap) {
1148  double sx = round(static_cast<double>(point.x()) * m_gridElements / (m_size)) / m_gridElements;
1149  double sy = round(static_cast<double>(point.y()) * m_gridElements / (m_size)) / m_gridElements;
1150  snap.first = true;
1151  snap.second = QPointF(sx, sy);
1152  }
1153 
1154  return snap;
1155 }
1156 
1157 
1168 QPair<bool, QPointF> Editor::snapToGuide(const QPointF &point) const
1169 {
1170  QPair<bool, QPointF> snap(false, point);
1171 
1172  if (m_snap) {
1173  foreach (const QPointF & p, m_snapPoints) {
1174  if ((point - p).manhattanLength() < m_snapThreshold) {
1175  snap.first = true;
1176  snap.second = p;
1177  break;
1178  }
1179  }
1180  }
1181 
1182  return snap;
1183 }
1184 
1185 
1195 QPoint Editor::toScreen(const QPointF &point) const
1196 {
1197  int sx = floor(point.x() * m_size);
1198  int sy = floor(point.y() * m_size);
1199  return QPoint(sx, sy);
1200 }
1201 
1202 
1211 bool Editor::node(const QPointF &point) const
1212 {
1213  bool found = false;
1214 
1215  for (int i = 0 ; i < m_points.count() ; ++i) {
1216  QPointF distance = point - m_points[i];
1217 
1218  if (distance.manhattanLength() < m_snapThreshold) {
1219  found = true;
1220  }
1221  }
1222 
1223  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
1224  QPointF distance = point - m_activePoints[i];
1225 
1226  if (distance.manhattanLength() < m_snapThreshold) {
1227  found = true;
1228  }
1229  }
1230 
1231  return found;
1232 }
1233 
1234 
1244 QPair<bool, int> Editor::nodeUnderCursor(const QPointF &point) const
1245 {
1246  for (int i = 0 ; i < m_points.count() ; ++i) {
1247  QPointF distance = point - m_points[i];
1248 
1249  if (distance.manhattanLength() < m_snapThreshold) {
1250  return QPair<bool, int>(true, i);
1251  }
1252  }
1253 
1254  for (int i = 0 ; i < m_activePoints.count() ; ++i) {
1255  QPointF distance = point - m_activePoints[i];
1256 
1257  if (distance.manhattanLength() < m_snapThreshold) {
1258  return QPair<bool, int>(false, i);
1259  }
1260  }
1261 
1262  return QPair<bool, int>(false, -1);
1263 }
1264 
1265 
1272 {
1273  m_points.clear();
1274  m_activePoints.clear();
1275  m_elements.clear();
1276 
1277  for (int i = 0 ; i < m_painterPath.elementCount() ; ++i) {
1278  QPainterPath::Element e = m_painterPath.elementAt(i);
1279 
1280  switch (e.type) {
1281  case QPainterPath::MoveToElement:
1282  m_elements.append(e.type);
1283  m_points.append(QPointF(e));
1284  break;
1285 
1286  case QPainterPath::LineToElement:
1287  m_elements.append(e.type);
1288  m_points.append(QPointF(e));
1289  break;
1290 
1291  case QPainterPath::CurveToElement:
1292  m_elements.append(e.type);
1293  m_points.append(QPointF(e));
1294  break;
1295 
1296  case QPainterPath::CurveToDataElement:
1297  m_points.append(QPointF(e));
1298  break;
1299  }
1300  }
1301 }
1302 
1303 
1310 {
1311  QPainterPath path;;
1312 
1313  for (int i = 0, j = 0 ; i < m_elements.count() ; ++i) {
1314  QPainterPath::ElementType e = m_elements[i];
1315 
1316  switch (e) {
1317  case QPainterPath::MoveToElement:
1318  path.moveTo(m_points[j++]);
1319  break;
1320 
1321  case QPainterPath::LineToElement:
1322  path.lineTo(m_points[j++]);
1323  break;
1324 
1325  case QPainterPath::CurveToElement:
1326  path.cubicTo(m_points[j], m_points[j + 1], m_points[j + 2]);
1327  j += 3;
1328  break;
1329  }
1330  }
1331 
1332  path.setFillRule(m_painterPath.fillRule());
1333  m_painterPath = path;
1334 }
1335 
1336 
1347 bool Editor::constructGuides(const QPointF &to)
1348 {
1349  foreach (const QPointF & from, m_points) {
1350  constructLineGuides(from, to);
1351  constructCircleGuides(from, to);
1352  }
1353 
1354  foreach (const QPointF & from, m_activePoints) {
1355  constructLineGuides(from, to);
1356  constructCircleGuides(from, to);
1357  }
1358 
1359  return !m_guideLines.isEmpty();
1360 }
1361 
1362 
1372 void Editor::constructLineGuides(const QPointF &from, const QPointF &to)
1373 {
1374  if (from != to) {
1375  QLineF line(from, to);
1376 
1377  if (m_angles.contains(line.angle())) {
1378  m_guideLines.append(projected(line));
1379  }
1380  }
1381 }
1382 
1383 
1395 void Editor::constructCircleGuides(const QPointF &from, const QPointF &to)
1396 {
1397  QVector<QPointF> intersections;
1398  intersections << QPointF(1.0 - from.x(), from.y()) << QPointF(from.x(), 1.0 - from.y()) << QPointF(1.0 - from.x(), 1.0 - from.y());
1399  bool circles = false;
1400 
1401  foreach (const QPointF & i, intersections) {
1402  if (i == from) { // point is on center line
1403  break;
1404  }
1405 
1406  if (i == to) {
1407  m_guideLines.append(projected(QLineF(i, to)));
1408  circles = true;
1409  }
1410  }
1411 
1412  if (circles) {
1413  m_guideCircles.append(sqrt(pow(0.5 - to.x(), 2) + pow(0.5 - to.y(), 2)));
1414  }
1415 }
1416 
1417 
1430 QLineF Editor::projected(const QLineF &line) const
1431 {
1432  QPointF intersectTop;
1433  QPointF intersectBottom;
1434  QPointF intersectLeft;
1435  QPointF intersectRight;
1436 
1437  QLineF::IntersectType t;
1438  QLineF::IntersectType b;
1439  QLineF::IntersectType l;
1440  QLineF::IntersectType r;
1441 
1442  t = line.intersect(m_topEdge, &intersectTop);
1443  b = line.intersect(m_bottomEdge, &intersectBottom);
1444  l = line.intersect(m_leftEdge, &intersectLeft);
1445  r = line.intersect(m_rightEdge, &intersectRight);
1446 
1447  if (t == 0) { // horizontal line
1448  return QLineF(intersectLeft, intersectRight);
1449  }
1450 
1451  if (l == 0) { // vertical line
1452  return QLineF(intersectTop, intersectBottom);
1453  }
1454 
1455  if (intersectTop == QPointF(0.0, 0.0) || intersectTop == QPointF(1.0, 0.0)) { // diagonal line at the corners
1456  return QLineF(intersectTop, intersectBottom);
1457  }
1458 
1459  if (intersectTop.x() > 0.0 && intersectTop.x() < 1.0) {
1460  if (intersectLeft.y() > 0.0 && intersectLeft.y() < 1.0) {
1461  return QLineF(intersectTop, intersectLeft);
1462  } else {
1463  return QLineF(intersectTop, intersectRight);
1464  }
1465  } else {
1466  if (intersectLeft.y() > 0.0 && intersectLeft.y() < 1.0) {
1467  return QLineF(intersectBottom, intersectLeft);
1468  } else {
1469  return QLineF(intersectBottom, intersectRight);
1470  }
1471  }
1472 }
1473