QDebug operator<<(QDebug dbg, const QDoubleVector3D &vector) { QDebugStateSaver saver(dbg); dbg.nospace() << "QDoubleVector3D(" << vector.x() << ", " << vector.y() << ", " << vector.z() << ')'; return dbg; }
double QDoubleVector3D::distanceToLine (const QDoubleVector3D &point, const QDoubleVector3D &direction) const { if (direction.isNull()) return (*this - point).length(); QDoubleVector3D p = point + dotProduct(*this - point, direction) * direction; return (*this - p).length(); }
void QGeoCameraTilesPrivate::appendZIntersects(const QDoubleVector3D &start, const QDoubleVector3D &end, double z, QVector<QDoubleVector3D> &results) const { if (start.z() == end.z()) { if (start.z() == z) { results.append(start); results.append(end); } } else { double f = (start.z() - z) / (start.z() - end.z()); if ((0 <= f) && (f <= 1.0)) { results.append((1 - f) * start + f * end); } } }
QPair<Polygon, Polygon> QGeoCameraTilesPrivate::clipFootprintToMap(const Polygon &footprint) const { bool clipX0 = false; bool clipX1 = false; bool clipY0 = false; bool clipY1 = false; double side = 1.0 * sideLength_; typedef Polygon::const_iterator const_iter; const_iter i = footprint.constBegin(); const_iter end = footprint.constEnd(); for (; i != end; ++i) { QDoubleVector3D p = *i; if ((p.x() < 0.0) || (qFuzzyIsNull(p.x()))) clipX0 = true; if ((side < p.x()) || (qFuzzyCompare(side, p.x()))) clipX1 = true; if (p.y() < 0.0) clipY0 = true; if (side < p.y()) clipY1 = true; } Polygon results = footprint; if (clipY0) { results = splitPolygonAtAxisValue(results, 1, 0.0).second; } if (clipY1) { results = splitPolygonAtAxisValue(results, 1, side).first; } if (clipX0) { if (clipX1) { results = splitPolygonAtAxisValue(results, 0, 0.0).second; results = splitPolygonAtAxisValue(results, 0, side).first; return QPair<Polygon, Polygon>(results, Polygon()); } else { QPair<Polygon, Polygon> pair = splitPolygonAtAxisValue(results, 0, 0.0); if (pair.first.isEmpty()) { // if we touched the line but didn't cross it... for (int i = 0; i < pair.second.size(); ++i) { if (qFuzzyIsNull(pair.second.at(i).x())) pair.first.append(pair.second.at(i)); } if (pair.first.size() == 2) { double y0 = pair.first[0].y(); double y1 = pair.first[1].y(); pair.first.clear(); pair.first.append(QDoubleVector3D(side, y0, 0.0)); pair.first.append(QDoubleVector3D(side - 0.001, y0, 0.0)); pair.first.append(QDoubleVector3D(side - 0.001, y1, 0.0)); pair.first.append(QDoubleVector3D(side, y1, 0.0)); } else if (pair.first.size() == 1) { // FIXME this is trickier // - touching at one point on the tile boundary // - probably need to build a triangular polygon across the edge // - don't want to add another y tile if we can help it // - initial version doesn't care double y = pair.first.at(0).y(); pair.first.clear(); pair.first.append(QDoubleVector3D(side - 0.001, y, 0.0)); pair.first.append(QDoubleVector3D(side, y + 0.001, 0.0)); pair.first.append(QDoubleVector3D(side, y - 0.001, 0.0)); } } else { for (int i = 0; i < pair.first.size(); ++i) { pair.first[i].setX(pair.first.at(i).x() + side); } } return pair; } } else { if (clipX1) { QPair<Polygon, Polygon> pair = splitPolygonAtAxisValue(results, 0, side); if (pair.second.isEmpty()) { // if we touched the line but didn't cross it... for (int i = 0; i < pair.first.size(); ++i) { if (qFuzzyCompare(side, pair.first.at(i).x())) pair.second.append(pair.first.at(i)); } if (pair.second.size() == 2) { double y0 = pair.second[0].y(); double y1 = pair.second[1].y(); pair.second.clear(); pair.second.append(QDoubleVector3D(0, y0, 0.0)); pair.second.append(QDoubleVector3D(0.001, y0, 0.0)); pair.second.append(QDoubleVector3D(0.001, y1, 0.0)); pair.second.append(QDoubleVector3D(0, y1, 0.0)); } else if (pair.second.size() == 1) { // FIXME this is trickier // - touching at one point on the tile boundary // - probably need to build a triangular polygon across the edge // - don't want to add another y tile if we can help it // - initial version doesn't care double y = pair.second.at(0).y(); pair.second.clear(); pair.second.append(QDoubleVector3D(0.001, y, 0.0)); pair.second.append(QDoubleVector3D(0.0, y - 0.001, 0.0)); pair.second.append(QDoubleVector3D(0.0, y + 0.001, 0.0)); } } else { for (int i = 0; i < pair.second.size(); ++i) { pair.second[i].setX(pair.second.at(i).x() - side); } } return pair; } else { return QPair<Polygon, Polygon>(results, Polygon()); } } }
// Returns the intersection of the plane of the map and the camera frustum as a right handed polygon Polygon QGeoCameraTilesPrivate::frustumFootprint(const Frustum &frustum) const { Polygon points; points.reserve(24); appendZIntersects(frustum.topLeftNear, frustum.topLeftFar, 0.0, points); appendZIntersects(frustum.topRightNear, frustum.topRightFar, 0.0, points); appendZIntersects(frustum.bottomLeftNear, frustum.bottomLeftFar, 0.0, points); appendZIntersects(frustum.bottomRightNear, frustum.bottomRightFar, 0.0, points); appendZIntersects(frustum.topLeftNear, frustum.bottomLeftNear, 0.0, points); appendZIntersects(frustum.bottomLeftNear, frustum.bottomRightNear, 0.0, points); appendZIntersects(frustum.bottomRightNear, frustum.topRightNear, 0.0, points); appendZIntersects(frustum.topRightNear, frustum.topLeftNear, 0.0, points); appendZIntersects(frustum.topLeftFar, frustum.bottomLeftFar, 0.0, points); appendZIntersects(frustum.bottomLeftFar, frustum.bottomRightFar, 0.0, points); appendZIntersects(frustum.bottomRightFar, frustum.topRightFar, 0.0, points); appendZIntersects(frustum.topRightFar, frustum.topLeftFar, 0.0, points); if (points.isEmpty()) return points; // sort points into a right handed polygon LengthSorter sorter; // - initial sort to remove duplicates sorter.base = points.first(); qSort(points.begin(), points.end(), sorter); for (int i = points.size() - 1; i > 0; --i) { if (points.at(i) == points.at(i - 1)) points.remove(i); } // - proper sort // - start with the first point, put it in the sorted part of the list // - add the nearest unsorted point to the last sorted point to the end // of the sorted points Polygon::iterator i; for (i = points.begin(); i != points.end(); ++i) { sorter.base = *i; if (i + 1 != points.end()) qSort(i + 1, points.end(), sorter); } // - determine if what we have is right handed int size = points.size(); if (size >= 3) { QDoubleVector3D normal = QDoubleVector3D::normal(points.at(1) - points.at(0), points.at(2) - points.at(1)); // - if not, reverse the list if (normal.z() < 0.0) { int halfSize = size / 2; for (int i = 0; i < halfSize; ++i) { QDoubleVector3D spare = points.at(i); points[i] = points[size - 1 - i]; points[size - 1 - i] = spare; } } } return points; }
Frustum QGeoCameraTilesPrivate::frustum(double fieldOfViewGradient) const { QDoubleVector3D center = sideLength_ * QGeoProjection::coordToMercator(camera_.center()); center.setZ(0.0); double f = qMin(screenSize_.width(), screenSize_.height()) / (1.0 * tileSize_); double z = std::pow(2.0, camera_.zoomLevel() - intZoomLevel_); double altitude = f / (2.0 * z); QDoubleVector3D eye = center; eye.setZ(altitude); QDoubleVector3D view = eye - center; QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0)); QDoubleVector3D up = QDoubleVector3D::normal(side, view); double nearPlane = sideLength_ / (1.0 * tileSize_ * (1 << maxZoom_)); double farPlane = 3.0; double aspectRatio = 1.0 * screenSize_.width() / screenSize_.height(); double hn = 0.0; double wn = 0.0; double hf = 0.0; double wf = 0.0; // fixes field of view at 45 degrees // this assumes that viewSize = 2*nearPlane x 2*nearPlane if (aspectRatio > 1.0) { hn = 2 * fieldOfViewGradient * nearPlane; wn = hn * aspectRatio; hf = 2 * fieldOfViewGradient * farPlane; wf = hf * aspectRatio; } else { wn = 2 * fieldOfViewGradient * nearPlane; hn = wn / aspectRatio; wf = 2 * fieldOfViewGradient * farPlane; hf = wf / aspectRatio; } QDoubleVector3D d = center - eye; d.normalize(); up.normalize(); QDoubleVector3D right = QDoubleVector3D::normal(d, up); QDoubleVector3D cf = eye + d * farPlane; QDoubleVector3D cn = eye + d * nearPlane; Frustum frustum; frustum.topLeftFar = cf + (up * hf / 2) - (right * wf / 2); frustum.topRightFar = cf + (up * hf / 2) + (right * wf / 2); frustum.bottomLeftFar = cf - (up * hf / 2) - (right * wf / 2); frustum.bottomRightFar = cf - (up * hf / 2) + (right * wf / 2); frustum.topLeftNear = cn + (up * hn / 2) - (right * wn / 2); frustum.topRightNear = cn + (up * hn / 2) + (right * wn / 2); frustum.bottomLeftNear = cn - (up * hn / 2) - (right * wn / 2); frustum.bottomRightNear = cn - (up * hn / 2) + (right * wn / 2); return frustum; }