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