00001
00002
00003
00004
00005
00006
00007 #include "Document.h"
00008 #include "DocumentModelCoords.h"
00009 #include "DocumentModelGridDisplay.h"
00010 #include "EngaugeAssert.h"
00011 #include "EnumsToQt.h"
00012 #include "GraphicsArcItem.h"
00013 #include "GridLineFactory.h"
00014 #include "GridLineLimiter.h"
00015 #include "GridLines.h"
00016 #include "GridLineStyle.h"
00017 #include "Logger.h"
00018 #include "MainWindowModel.h"
00019 #include <QGraphicsScene>
00020 #include <qmath.h>
00021 #include <QTextStream>
00022 #include "QtToString.h"
00023 #include "Transformation.h"
00024
00025 const int Z_VALUE_IN_FRONT = 100;
00026
00027
00028 const double CHECKER_OPACITY = 0.6;
00029
00030 const double PI = 3.1415926535;
00031 const double TWO_PI = 2.0 * PI;
00032 const double DEGREES_TO_RADIANS = PI / 180.0;
00033 const double RADIANS_TO_TICS = 5760 / TWO_PI;
00034
00035 GridLineFactory::GridLineFactory(QGraphicsScene &scene,
00036 const DocumentModelCoords &modelCoords) :
00037 m_scene (scene),
00038 m_pointRadius (0.0),
00039 m_modelCoords (modelCoords),
00040 m_isChecker (false)
00041 {
00042 LOG4CPP_DEBUG_S ((*mainCat)) << "GridLineFactory::GridLineFactory";
00043 }
00044
00045 GridLineFactory::GridLineFactory(QGraphicsScene &scene,
00046 int pointRadius,
00047 const QList<Point> &pointsToIsolate,
00048 const DocumentModelCoords &modelCoords) :
00049 m_scene (scene),
00050 m_pointRadius (pointRadius),
00051 m_pointsToIsolate (pointsToIsolate),
00052 m_modelCoords (modelCoords),
00053 m_isChecker (true)
00054 {
00055 LOG4CPP_DEBUG_S ((*mainCat)) << "GridLineFactory::GridLineFactory"
00056 << " pointRadius=" << pointRadius
00057 << " pointsToIsolate=" << pointsToIsolate.count();
00058 }
00059
00060 void GridLineFactory::bindItemToScene(QGraphicsItem *item) const
00061 {
00062 LOG4CPP_DEBUG_S ((*mainCat)) << "GridLineFactory::bindItemToScene";
00063
00064 item->setOpacity (CHECKER_OPACITY);
00065 item->setZValue (Z_VALUE_IN_FRONT);
00066 if (m_isChecker) {
00067 item->setToolTip (QObject::tr ("Axes checker. If this does not align with the axes, then the axes points should be checked"));
00068 }
00069
00070 m_scene.addItem (item);
00071 }
00072
00073 GridLine *GridLineFactory::createGridLine (double xFrom,
00074 double yFrom,
00075 double xTo,
00076 double yTo,
00077 const Transformation &transformation)
00078 {
00079 LOG4CPP_DEBUG_S ((*mainCat)) << "GridLineFactory::createGridLine"
00080 << " xFrom=" << xFrom
00081 << " yFrom=" << yFrom
00082 << " xTo=" << xTo
00083 << " yTo=" << yTo;
00084
00085 GridLine *gridLine = new GridLine ();
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096 const int NUM_STEPS = 1000;
00097
00098 bool stateSegmentIsActive = false;
00099 QPointF posStartScreen (0, 0);
00100
00101
00102 for (int i = 0; i <= NUM_STEPS; i++) {
00103
00104 double s = (double) i / (double) NUM_STEPS;
00105
00106
00107 double xGraph = (1.0 - s) * xFrom + s * xTo;
00108 double yGraph = (1.0 - s) * yFrom + s * yTo;
00109
00110
00111 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
00112 xGraph = qExp ((1.0 - s) * qLn (xFrom) + s * qLn (xTo));
00113 }
00114 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
00115 yGraph = qExp ((1.0 - s) * qLn (yFrom) + s * qLn (yTo));
00116 }
00117
00118 QPointF pointScreen;
00119 transformation.transformRawGraphToScreen (QPointF (xGraph, yGraph),
00120 pointScreen);
00121
00122 double distanceToNearestPoint = minScreenDistanceFromPoints (pointScreen);
00123 if ((distanceToNearestPoint < m_pointRadius) ||
00124 (i == NUM_STEPS)) {
00125
00126
00127 if (stateSegmentIsActive) {
00128
00129
00130 finishActiveGridLine (posStartScreen,
00131 pointScreen,
00132 yFrom,
00133 yTo,
00134 transformation,
00135 *gridLine);
00136 stateSegmentIsActive = false;
00137
00138 }
00139 } else {
00140
00141
00142 if (!stateSegmentIsActive) {
00143
00144
00145 stateSegmentIsActive = true;
00146 posStartScreen = pointScreen;
00147
00148 }
00149 }
00150 }
00151
00152 return gridLine;
00153 }
00154
00155 void GridLineFactory::createGridLinesForEvenlySpacedGrid (const DocumentModelGridDisplay &modelGridDisplay,
00156 const Document &document,
00157 const MainWindowModel &modelMainWindow,
00158 const Transformation &transformation,
00159 GridLines &gridLines)
00160 {
00161
00162
00163
00164 if (transformation.transformIsDefined() &&
00165 modelGridDisplay.stable()) {
00166
00167 double startX = modelGridDisplay.startX ();
00168 double startY = modelGridDisplay.startY ();
00169 double stepX = modelGridDisplay.stepX ();
00170 double stepY = modelGridDisplay.stepY ();
00171 double stopX = modelGridDisplay.stopX ();
00172 double stopY = modelGridDisplay.stopY ();
00173
00174
00175 GridLineLimiter gridLineLimiter;
00176 gridLineLimiter.limitForXTheta (document,
00177 transformation,
00178 m_modelCoords,
00179 modelMainWindow,
00180 modelGridDisplay,
00181 startX,
00182 stepX);
00183 gridLineLimiter.limitForYRadius (document,
00184 transformation,
00185 m_modelCoords,
00186 modelMainWindow,
00187 modelGridDisplay,
00188 startY,
00189 stepY);
00190
00191
00192 bool isLinearX = (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LINEAR);
00193 bool isLinearY = (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR);
00194 if (stepX > (isLinearX ? 0 : 1) &&
00195 stepY > (isLinearY ? 0 : 1) &&
00196 (isLinearX || (startX > 0)) &&
00197 (isLinearY || (startY > 0))) {
00198
00199 QColor color (ColorPaletteToQColor (modelGridDisplay.paletteColor()));
00200 QPen pen (QPen (color,
00201 GRID_LINE_WIDTH,
00202 GRID_LINE_STYLE));
00203
00204 for (double x = startX; x <= stopX; (isLinearX ? x += stepX : x *= stepX)) {
00205
00206 GridLine *gridLine = createGridLine (x, startY, x, stopY, transformation);
00207 gridLine->setPen (pen);
00208 gridLines.add (gridLine);
00209 }
00210
00211 for (double y = startY; y <= stopY; (isLinearY ? y += stepY : y *= stepY)) {
00212
00213 GridLine *gridLine = createGridLine (startX, y, stopX, y, transformation);
00214 gridLine->setPen (pen);
00215 gridLines.add (gridLine);
00216 }
00217 }
00218 }
00219 }
00220
00221 void GridLineFactory::createTransformAlign (const Transformation &transformation,
00222 double radiusLinearCartesian,
00223 const QPointF &posOriginScreen,
00224 QTransform &transformAlign,
00225 double &ellipseXAxis,
00226 double &ellipseYAxis) const
00227 {
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239 QPointF posXRadiusY0Graph (radiusLinearCartesian, 0), posX0YRadiusGraph (0, radiusLinearCartesian);
00240 QPointF posXRadiusY0Screen, posX0YRadiusScreen;
00241 transformation.transformLinearCartesianGraphToScreen (posXRadiusY0Graph,
00242 posXRadiusY0Screen);
00243 transformation.transformLinearCartesianGraphToScreen (posX0YRadiusGraph,
00244 posX0YRadiusScreen);
00245
00246
00247 QPointF deltaXRadiusY0 = posXRadiusY0Screen - posOriginScreen;
00248 QPointF deltaX0YRadius = posX0YRadiusScreen - posOriginScreen;
00249 ellipseXAxis = qSqrt (deltaXRadiusY0.x () * deltaXRadiusY0.x () +
00250 deltaXRadiusY0.y () * deltaXRadiusY0.y ());
00251 ellipseYAxis = qSqrt (deltaX0YRadius.x () * deltaX0YRadius.x () +
00252 deltaX0YRadius.y () * deltaX0YRadius.y ());
00253
00254
00255 QPointF posXRadiusY0AlignedScreen (posOriginScreen.x() + ellipseXAxis, posOriginScreen.y());
00256 QPointF posX0YRadiusAlignedScreen (posOriginScreen.x(), posOriginScreen.y() - ellipseYAxis);
00257
00258 transformAlign = Transformation::calculateTransformFromLinearCartesianPoints (posOriginScreen,
00259 posXRadiusY0Screen,
00260 posX0YRadiusScreen,
00261 posOriginScreen,
00262 posXRadiusY0AlignedScreen,
00263 posX0YRadiusAlignedScreen);
00264
00265 LOG4CPP_INFO_S ((*mainCat)) << "GridLineFactory::createTransformAlign"
00266 << " transformation=" << QTransformToString (transformation.transformMatrix()).toLatin1().data() << endl
00267 << " radiusLinearCartesian=" << radiusLinearCartesian
00268 << " posXRadiusY0Screen=" << QPointFToString (posXRadiusY0Screen).toLatin1().data()
00269 << " posX0YRadiusScreen=" << QPointFToString (posX0YRadiusScreen).toLatin1().data()
00270 << " ellipseXAxis=" << ellipseXAxis
00271 << " ellipseYAxis=" << ellipseYAxis
00272 << " posXRadiusY0AlignedScreen=" << QPointFToString (posXRadiusY0AlignedScreen).toLatin1().data()
00273 << " posX0YRadiusAlignedScreen=" << QPointFToString (posX0YRadiusAlignedScreen).toLatin1().data()
00274 << " transformAlign=" << QTransformToString (transformAlign).toLatin1().data();
00275 }
00276
00277 QGraphicsItem *GridLineFactory::ellipseItem (const Transformation &transformation,
00278 double radiusLinearCartesian,
00279 const QPointF &posStartScreen,
00280 const QPointF &posEndScreen) const
00281 {
00282
00283
00284 QPointF posStartGraph, posEndGraph;
00285
00286 transformation.transformScreenToRawGraph (posStartScreen,
00287 posStartGraph);
00288 transformation.transformScreenToRawGraph (posEndScreen,
00289 posEndGraph);
00290
00291
00292 double angleStart = posStartGraph.x() * DEGREES_TO_RADIANS;
00293 double angleEnd = posEndGraph.x() * DEGREES_TO_RADIANS;
00294 if (angleEnd < angleStart) {
00295 angleEnd += TWO_PI;
00296 }
00297 double angleSpan = angleEnd - angleStart;
00298
00299
00300 QPointF posOriginGraph (0, 0), posOriginScreen;
00301 transformation.transformLinearCartesianGraphToScreen (posOriginGraph,
00302 posOriginScreen);
00303
00304 LOG4CPP_INFO_S ((*mainCat)) << "GridLineFactory::ellipseItem"
00305 << " radiusLinearCartesian=" << radiusLinearCartesian
00306 << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data()
00307 << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data()
00308 << " posOriginScreen=" << QPointFToString (posOriginScreen).toLatin1().data()
00309 << " angleStart=" << angleStart / DEGREES_TO_RADIANS
00310 << " angleEnd=" << angleEnd / DEGREES_TO_RADIANS
00311 << " transformation=" << transformation;
00312
00313
00314
00315
00316 double ellipseXAxis, ellipseYAxis;
00317 QTransform transformAlign;
00318 createTransformAlign (transformation,
00319 radiusLinearCartesian,
00320 posOriginScreen,
00321 transformAlign,
00322 ellipseXAxis,
00323 ellipseYAxis);
00324
00325
00326 QRectF boundingRect (-1.0 * ellipseXAxis + posOriginScreen.x(),
00327 -1.0 * ellipseYAxis + posOriginScreen.y(),
00328 2 * ellipseXAxis,
00329 2 * ellipseYAxis);
00330 GraphicsArcItem *item = new GraphicsArcItem (boundingRect);
00331 item->setStartAngle (angleStart * RADIANS_TO_TICS);
00332 item->setSpanAngle (angleSpan * RADIANS_TO_TICS);
00333
00334 item->setTransform (transformAlign.transposed ().inverted ());
00335
00336 return item;
00337 }
00338
00339 void GridLineFactory::finishActiveGridLine (const QPointF &posStartScreen,
00340 const QPointF &posEndScreen,
00341 double yFrom,
00342 double yTo,
00343 const Transformation &transformation,
00344 GridLine &gridLine) const
00345 {
00346 LOG4CPP_DEBUG_S ((*mainCat)) << "GridLineFactory::finishActiveGridLine"
00347 << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data()
00348 << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data()
00349 << " yFrom=" << yFrom
00350 << " yTo=" << yTo;
00351
00352 QGraphicsItem *item;
00353 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
00354 (yFrom == yTo)) {
00355
00356
00357 double radiusLinearCartesian = yFrom;
00358 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
00359 radiusLinearCartesian = transformation.logToLinearRadius(yFrom,
00360 m_modelCoords.originRadius());
00361 } else {
00362 radiusLinearCartesian -= m_modelCoords.originRadius();
00363 }
00364
00365
00366 item = ellipseItem (transformation,
00367 radiusLinearCartesian,
00368 posStartScreen,
00369 posEndScreen);
00370
00371 } else {
00372
00373
00374 item = lineItem (posStartScreen,
00375 posEndScreen);
00376 }
00377
00378 gridLine.add (item);
00379 bindItemToScene (item);
00380 }
00381
00382 QGraphicsItem *GridLineFactory::lineItem (const QPointF &posStartScreen,
00383 const QPointF &posEndScreen) const
00384 {
00385 LOG4CPP_DEBUG_S ((*mainCat)) << "GridLineFactory::lineItem"
00386 << " posStartScreen=" << QPointFToString (posStartScreen).toLatin1().data()
00387 << " posEndScreen=" << QPointFToString (posEndScreen).toLatin1().data();
00388
00389 return new QGraphicsLineItem (QLineF (posStartScreen,
00390 posEndScreen));
00391 }
00392
00393 double GridLineFactory::minScreenDistanceFromPoints (const QPointF &posScreen)
00394 {
00395 double minDistance = 0;
00396 for (int i = 0; i < m_pointsToIsolate.count (); i++) {
00397 const Point &pointCenter = m_pointsToIsolate.at (i);
00398
00399 double dx = posScreen.x() - pointCenter.posScreen().x();
00400 double dy = posScreen.y() - pointCenter.posScreen().y();
00401
00402 double distance = qSqrt (dx * dx + dy * dy);
00403 if (i == 0 || distance < minDistance) {
00404 minDistance = distance;
00405 }
00406 }
00407
00408 return minDistance;
00409 }