Skip to content

Commit

Permalink
Fix for X11 zoom bug. Only fixes lines (not polygons), and if you
Browse files Browse the repository at this point in the history
zoom in enough things will still behave oddly (due to over/underflow of
doubles rather than 16 bit ints). Work in progress. Polygons next.


git-svn-id: http://svn.osgeo.org/qgis/trunk@3000 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
g_j_m committed Mar 22, 2005
1 parent fcde577 commit 6f2828c
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 28 deletions.
202 changes: 202 additions & 0 deletions src/qgsmaptopixel.cpp
Expand Up @@ -75,3 +75,205 @@ QString QgsMapToPixel::showParameters()
return rep;

}

void QgsMapToPixel::trimLine(const QgsPoint& from, const QgsPoint& to,
QgsPoint& tFrom, QgsPoint& tTo)
{
// The limits for X11 screen coordinates. Actual value is 32767, but
// we allow a little bit if space of rounding errors in the
// calculations below.
static const int minX = -32760;
static const int maxX = 32760;
static const int minY = -32760;
static const int maxY = 32760;
static const double SMALL_NUM = 1e-6;

// One thing in our favour is that if this function is called we
// already know that the line intersects the region that is visible
// on the screen, so there's no need to check for that intersection.

// To determine the intersection between a line given by the points
// A and B, and the line given by the points C and D, calculate
//
// (Ay - Cy)(Dx - Cx) - (Ax - Cx)(Dy - Cy)
// r = -----------------------------------------
// (Bx - Ax)(Dy - Cy) - (By - Ay)(Dx - Cx)
//
// (Ay - Cy)(Bx - Ax) - (Ax - Cx)(By - Ay)
// s = -----------------------------------------
// (Bx - Ax)(Dy - Cy) - (By - Ay)(Dx - Cx)
//
// If the demoninator is 0, the lines are parallel, and don't
// intersect, and can be left untrimmed
// If the numerator is 0 too, the lines are collinear and can be
// left untrimmed.
//
// Note that the demoninator is the same for r and s.
//
// If the two points that define the line segment are actually at
// the same place, the denominator will be zero and the points will
// be left alone as per parallel lines. This shouldn't happen anyway
// because if the line is just a point, and it's within the visible
// window on screen, the coordinates will be within the limits and
// the first if() test below will fail and not attempt any trimming.
//
// If 0 >= r <= 1 and 0 >= s <= 1 then the line segments intersect,
// and the intersection point P is given by:
//
// P = A + r*(B-A)
//

// Once we have adjusted both the to and from points, there is no
// point in doing further checks, so return from the function
// if that occurs.
bool toDone = false, fromDone = false;

// Check for the need to trim first
if (from.x() < minX || from.x() > maxX || to.x() < minX || to.x() > maxX ||
from.y() < minY || from.y() > maxY || to.y() < minY || to.y() > maxY)
{
tFrom = from;
tTo = to;

// Check the top boundary
double r_n = (from.y() - minY) * (maxX - minX);
double d = - (to.y() - from.y()) * (maxX - minX);
double s_n = (from.y() - minY) * (to.x() - from.x())
- (from.x() - minX) * (to.y() - from.y());

if (fabs(d) > SMALL_NUM && fabs(r_n) > SMALL_NUM != 0.0)
{
double r_nOverd = r_n / d;
double s_nOverd = s_n / d;
if (r_nOverd >= 0.0 && r_nOverd <= 1.0 &&
s_nOverd >= 0.0 && s_nOverd <= 1.0)
{
// intersects the top line. Trim back.
// work out which end to trim
if (from.y() <= minY) // trim from
{
fromDone = true;
tFrom.setX(from.x() + r_nOverd*(to.x() - from.x()));
tFrom.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
else // trim to
{
toDone = true;
tTo.setX(from.x() + r_nOverd*(to.x() - from.x()));
tTo.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
}
}

// the right border
r_n = -(from.x() - maxX) * (maxY - minY);
d = (to.x() - from.x()) * (maxY - minY);
s_n = (from.y() - minY) * (to.x() - from.x())
- (from.x() - maxX) * (to.y() - from.y());

if (fabs(d) > SMALL_NUM && fabs(r_n) > SMALL_NUM != 0.0)
{
double r_nOverd = r_n / d;
double s_nOverd = s_n / d;
if (r_nOverd >= 0.0 && r_nOverd <= 1.0 &&
s_nOverd >= 0.0 && s_nOverd <= 1.0)
{
// intersects the bottom line. Trim back.
// work out which end to trim
if (from.x() >= maxX) // trim from
{
fromDone = true;
tFrom.setX(from.x() + r_nOverd*(to.x() - from.x()));
tFrom.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
else // trim to
{
toDone = true;
tTo.setX(from.x() + r_nOverd*(to.x() - from.x()));
tTo.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
}
}

// Done both ends of the line, so leave.
if (toDone && fromDone)
return;

// the left border
r_n = - (from.x() - minX) * (maxY - minY);
d = (to.x() - from.x()) * (maxY - minY);
s_n = (from.y() - minY) * (to.x() - from.x())
- (from.x() - minX) * (to.y() - from.y());

if (fabs(d) > SMALL_NUM && fabs(r_n) > SMALL_NUM != 0.0)
{
double r_nOverd = r_n / d;
double s_nOverd = s_n / d;
if (r_nOverd >= 0.0 && r_nOverd <= 1.0 &&
s_nOverd >= 0.0 && s_nOverd <= 1.0)
{
// intersects the left line. Trim back.
// work out which end to trim
if (from.x() <= minX) // trim from
{
fromDone = true;
tFrom.setX(from.x() + r_nOverd*(to.x() - from.x()));
tFrom.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
else // trim to
{
toDone = true;
tTo.setX(from.x() + r_nOverd*(to.x() - from.x()));
tTo.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
}
}

// Done both ends of the line, so leave.
if (toDone && fromDone)
return;

// the bottom border
r_n = (from.y() - maxY) * (maxX - minX);
d = - (to.y() - from.y()) * (maxX - minX);
s_n = (from.y() - maxY) * (to.x() - from.x())
- (from.x() - minX) * (to.y() - from.y());

if (fabs(d) > SMALL_NUM && fabs(r_n) > SMALL_NUM != 0.0)
{
double r_nOverd = r_n/d;
double s_nOverd = s_n/d;
if (r_nOverd >= 0.0 && r_nOverd <= 1.0 &&
s_nOverd >= 0.0 && s_nOverd <= 1.0)
{
// intersects the bottom line. Trim back.
// work out which end to trim
if (from.y() >= maxY) // trim from
{
fromDone = true;
tFrom.setX(from.x() + r_nOverd*(to.x() - from.x()));
tFrom.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
else // trim to
{
toDone = true;
tTo.setX(from.x() + r_nOverd*(to.x() - from.x()));
tTo.setY(from.y() + r_nOverd*(to.y() - from.y()));
}
}
}
}
else
{
// The entire line is visible on screen, so do nothing.
tFrom = from;
tTo = to;
}
/*
// Too verbose for QGISDEBUG, but handy sometimes.
std::cerr << "Point 1 trimmed from " << from.x() << ", " << from.y()
<< " to " << tFrom.x() << ", " << tFrom.y() << '\n'
<< "Point 2 trimmed from " << to.x() << ", " << to.y()
<< " to " << tTo.x() << ", " << tTo.y() << "\n\n";
*/
}
8 changes: 7 additions & 1 deletion src/qgsmaptopixel.h
Expand Up @@ -84,6 +84,9 @@ class QgsMapToPixel{
void setParameters(double mupp, double xmin, double ymin, double ymax);
//! String representation of the parameters used in the transform
QString showParameters();

static void trimLine(const QgsPoint& from, const QgsPoint& to,
QgsPoint& tFrom, QgsPoint& tTo);
private:
double mapUnitsPerPixel;
double yMax;
Expand Down Expand Up @@ -119,6 +122,7 @@ inline QgsPoint QgsMapToPixel::transform(QgsPoint p)
double dx = (p.x() - xMin) / mapUnitsPerPixel;
double dy = yMax - ((p.y() - yMin)) / mapUnitsPerPixel;
// double dy = (yMax - (p.y() - yMin))/mapUnitsPerPixel;
//std::cerr << "Point to pixel...X : " << p.x() << "-->" << dx << ", Y: " << p.y() << " -->" << dy << std::endl;
return QgsPoint(dx, dy);
}

Expand All @@ -127,11 +131,13 @@ inline void QgsMapToPixel::transform(QgsPoint* p)
float x = ((p->x()-xMin)/mapUnitsPerPixel);
float y = (yMax-((p->y() - yMin)) / mapUnitsPerPixel);
#ifdef QGISDEBUG
//std::cout << "Point to pixel...X : " << p->x() << "-->" << x << ", Y: " << p->y() << " -->" << y << std::endl;
//std::cerr << "Point to pixel...X : " << p->x() << "-->" << x << ", Y: " << p->y() << " -->" << y << std::endl;
#endif
p->setX(x);
p->setY(y);

}



#endif // QGSMAPTOPIXEL
76 changes: 49 additions & 27 deletions src/qgsvectorlayer.cpp
Expand Up @@ -2206,6 +2206,8 @@ void QgsVectorLayer::drawFeature(QPainter* p, QgsFeature* fet, QgsMapToPixel * t
double *y;
int wkbType;
QgsPoint pt,myProjectedPoint;
QgsPoint ptFrom, ptTo;
QgsPoint trimmedFrom, trimmedTo;

QPen pen;

Expand Down Expand Up @@ -2283,24 +2285,34 @@ void QgsVectorLayer::drawFeature(QPainter* p, QgsFeature* fet, QgsMapToPixel * t
if (projectionsEnabledFlag)
{
//reproject the point to the map coordinate system
try {
myProjectedPoint=mCoordinateTransform->transform(pt);
}
catch (QgsCsException &e)
{
qDebug( "Transform error caught in %s line %d:\n%s", __FILE__, __LINE__, e.what());
}
//transform from projected coordinate system to pixel position on map canvas
theMapToPixelTransform->transform(&myProjectedPoint);
try
{
ptTo=mCoordinateTransform->transform(pt);
}
catch (QgsCsException &e)
{
qDebug( "Transform error caught in %s line %d:\n%s",
__FILE__, __LINE__, e.what());
}
//transform from projected coordinate system to pixel
// position on map canvas
theMapToPixelTransform->transform(&ptTo);
}
else
{
myProjectedPoint=theMapToPixelTransform->transform(pt);
ptTo=theMapToPixelTransform->transform(pt);
}
if (idx == 0)
p->moveTo(static_cast<int>(myProjectedPoint.x()), static_cast<int>(myProjectedPoint.y()));
else
p->lineTo(static_cast<int>(myProjectedPoint.x()), static_cast<int>(myProjectedPoint.y()));
ptFrom = ptTo;
else
{
QgsMapToPixel::trimLine(ptFrom, ptTo, trimmedFrom, trimmedTo);
p->drawLine(static_cast<int>(trimmedFrom.x()),
static_cast<int>(trimmedFrom.y()),
static_cast<int>(trimmedTo.x()),
static_cast<int>(trimmedTo.y()));
ptFrom = ptTo;
}
}
break;
}
Expand Down Expand Up @@ -2332,24 +2344,34 @@ void QgsVectorLayer::drawFeature(QPainter* p, QgsFeature* fet, QgsMapToPixel * t
if (projectionsEnabledFlag)
{
//reproject the point to the map coordinate system
try {
myProjectedPoint=mCoordinateTransform->transform(pt);
}
catch (QgsCsException &e)
{
qDebug( "Transform error caught in %s line %d:\n%s", __FILE__, __LINE__, e.what());
}
//transform from projected coordinate system to pixel position on map canvas
theMapToPixelTransform->transform(&myProjectedPoint);
try
{
ptTo=mCoordinateTransform->transform(pt);
}
catch (QgsCsException &e)
{
qDebug( "Transform error caught in %s line %d:\n%s",
__FILE__, __LINE__, e.what());
}
//transform from projected coordinate system to pixel
// position on map canvas
theMapToPixelTransform->transform(&ptTo);
}
else
{
myProjectedPoint=theMapToPixelTransform->transform(pt);
ptTo=theMapToPixelTransform->transform(pt);
}
if (idx == 0)
p->moveTo(static_cast<int>(myProjectedPoint.x()), static_cast<int>(myProjectedPoint.y()));
else
p->lineTo(static_cast<int>(myProjectedPoint.x()), static_cast<int>(myProjectedPoint.y()));
if (idx == 0)
ptFrom = ptTo;
else
{
QgsMapToPixel::trimLine(ptFrom, ptTo, trimmedFrom, trimmedTo);
p->drawLine(static_cast<int>(trimmedFrom.x()),
static_cast<int>(trimmedFrom.y()),
static_cast<int>(trimmedTo.x()),
static_cast<int>(trimmedTo.y()));
ptFrom = ptTo;
}
}
}
break;
Expand Down

0 comments on commit 6f2828c

Please sign in to comment.