Improvement of snappper

This commit is contained in:
Jakub Melka 2020-02-26 19:06:32 +01:00
parent a27216e752
commit 46c267e537
6 changed files with 248 additions and 37 deletions

View File

@ -884,6 +884,36 @@ PDFInteger PDFDrawWidgetProxy::getPageUnderPoint(QPoint point, QPointF* pagePoin
return -1;
}
PDFWidgetSnapshot PDFDrawWidgetProxy::getSnapshot() const
{
PDFWidgetSnapshot snapshot;
// Get the viewport
QRect viewport = getWidget()->rect();
// Iterate trough pages, place them and test, if they intersects with rectangle
for (const LayoutItem& item : m_layout.items)
{
// The offsets m_horizontalOffset and m_verticalOffset are offsets to the
// topleft point of the block. But block maybe doesn't start at (0, 0),
// so we must also use translation from the block beginning.
QRect placedRect = item.pageRect.translated(m_horizontalOffset - m_layout.blockRect.left(), m_verticalOffset - m_layout.blockRect.top());
if (placedRect.intersects(viewport))
{
const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(item.pageIndex);
PDFWidgetSnapshot::SnapshotItem snapshotItem;
snapshotItem.rect = placedRect;
snapshotItem.pageIndex = item.pageIndex;
snapshotItem.compiledPage = m_compiler->getCompiledPage(item.pageIndex, false);
snapshotItem.pageToDeviceMatrix = createPagePointToDevicePointMatrix(page, placedRect);
snapshot.items.emplace_back(qMove(snapshotItem));
}
}
return snapshot;
}
QRect PDFDrawWidgetProxy::getPagesIntersectingRectBoundingBox(QRect rect) const
{
QRect resultRect;

View File

@ -185,6 +185,21 @@ private:
PDFFontCache m_fontCache;
};
/// Snapshot for current widget viewable items.
struct PDFWidgetSnapshot
{
struct SnapshotItem
{
PDFInteger pageIndex = -1; ///< Index of page
QRectF rect; ///< Page rectangle on viewport
QMatrix pageToDeviceMatrix; ///< Transforms page coordinates to widget coordinates
const PDFPrecompiledPage* compiledPage = nullptr; ///< Compiled page (can be nullptr)
};
using SnapshotItems = std::vector<SnapshotItem>;
SnapshotItems items;
};
/// This is a proxy class to draw space controller using widget. We have two spaces, pixel space
/// (on the controlled widget) and device space (device is draw space controller).
class PDFFORQTLIBSHARED_EXPORT PDFDrawWidgetProxy : public QObject
@ -350,6 +365,17 @@ public:
/// Returns current paper color
QColor getPaperColor();
/// Transforms pixels to device space
/// \param pixel Value in pixels
PDFReal transformPixelToDeviceSpace(PDFReal pixel) const { return pixel * m_pixelToDeviceSpaceUnit; }
/// Transforms value in device space to pixel value
/// \param deviceSpaceValue Value in device space
PDFReal transformDeviceSpaceToPixel(PDFReal deviceSpaceValue) const { return deviceSpaceValue * m_deviceSpaceUnitToPixel; }
/// Returns snapshot of current view area
PDFWidgetSnapshot getSnapshot() const;
signals:
void drawSpaceChanged();

View File

@ -18,6 +18,7 @@
#include "pdfsnapper.h"
#include "pdfcompiler.h"
#include "pdfwidgetutils.h"
#include "pdfdrawspacecontroller.h"
#include <QPainter>
@ -74,28 +75,25 @@ PDFSnapper::PDFSnapper()
}
void PDFSnapper::drawSnapPoints(QPainter* painter, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, const QMatrix& pagePointToDevicePointMatrix) const
void PDFSnapper::drawSnapPoints(QPainter* painter) const
{
if (m_currentPage != -1 && m_currentPage != pageIndex)
{
// We are drawing only snap points, which are on current page
return;
}
Q_ASSERT(painter);
Q_ASSERT(compiledPage);
const PDFSnapInfo* snapInfo = (m_currentPage != pageIndex) ? compiledPage->getSnapInfo() : &m_currentPageSnapInfo;
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
QPen pen = painter->pen();
pen.setCapStyle(Qt::RoundCap);
pen.setWidth(PDFWidgetUtils::scaleDPI_x(painter->device(), 10));
pen.setWidth(m_snapPointPixelSize);
for (const PDFSnapInfo::SnapPoint& snapPoint : snapInfo->getSnapPoints())
for (const ViewportSnapPoint& snapPoint : m_snapPoints)
{
if (!isSnappingAllowed(snapPoint.pageIndex))
{
// We are drawing only snap points, which are on current page
continue;
}
QColor color = pen.color();
QColor newColor = color;
switch (snapPoint.type)
@ -119,11 +117,92 @@ void PDFSnapper::drawSnapPoints(QPainter* painter, PDFInteger pageIndex, const P
painter->setPen(pen);
}
QPoint point = pagePointToDevicePointMatrix.map(snapPoint.point).toPoint();
QPoint point = snapPoint.viewportPoint.toPoint();
painter->drawPoint(point);
}
if (isSnapped())
{
pen.setColor(Qt::yellow);
painter->setPen(pen);
QPoint point = m_snappedPoint->viewportPoint.toPoint();
painter->drawPoint(point);
}
painter->restore();
}
bool PDFSnapper::isSnappingAllowed(PDFInteger pageIndex) const
{
return (m_currentPage == -1) || (m_currentPage == pageIndex);
}
void PDFSnapper::updateSnappedPoint(const QPointF& mousePoint)
{
m_snappedPoint = std::nullopt;
m_mousePoint = mousePoint;
// Iterate trough all points, check, if some satisfies condition
const PDFReal toleranceSquared = m_snapPointTolerance * m_snapPointTolerance;
for (const ViewportSnapPoint& snapPoint : m_snapPoints)
{
QPointF difference = mousePoint - snapPoint.viewportPoint;
PDFReal distanceSquared = QPointF::dotProduct(difference, difference);
if (distanceSquared < toleranceSquared && isSnappingAllowed(snapPoint.pageIndex))
{
m_snappedPoint = snapPoint;
return;
}
}
}
void PDFSnapper::buildSnapPoints(const PDFWidgetSnapshot& snapshot)
{
// First, clear all snap points
m_snapPoints.clear();
// Second, create snapping points from snapshot
for (const PDFWidgetSnapshot::SnapshotItem& item : snapshot.items)
{
if (!item.compiledPage)
{
continue;
}
const PDFSnapInfo* info = item.compiledPage->getSnapInfo();
for (const PDFSnapInfo::SnapPoint& snapPoint : info->getSnapPoints())
{
ViewportSnapPoint viewportSnapPoint;
viewportSnapPoint.type = snapPoint.type;
viewportSnapPoint.point = snapPoint.point;
viewportSnapPoint.pageIndex = item.pageIndex;
viewportSnapPoint.viewportPoint = item.pageToDeviceMatrix.map(snapPoint.point);
m_snapPoints.push_back(qMove(viewportSnapPoint));
}
}
// Third, update snap shot position
updateSnappedPoint(m_mousePoint);
}
int PDFSnapper::getSnapPointTolerance() const
{
return m_snapPointTolerance;
}
void PDFSnapper::setSnapPointTolerance(int snapPointTolerance)
{
m_snapPointTolerance = snapPointTolerance;
}
QPointF PDFSnapper::getSnappedPoint() const
{
if (isSnapped())
{
return m_snappedPoint->viewportPoint;
}
return m_mousePoint;
}
} // namespace pdf

View File

@ -21,12 +21,14 @@
#include "pdfglobal.h"
#include <array>
#include <optional>
class QPainter;
namespace pdf
{
class PDFPrecompiledPage;
struct PDFWidgetSnapshot;
enum class SnapType
{
@ -90,19 +92,69 @@ class PDFSnapper
public:
PDFSnapper();
/// Draws snapping points onto the page
/// Sets snap point pixel size
/// \param snapPointPixelSize Snap point diameter in pixels
void setSnapPointPixelSize(int snapPointPixelSize) { m_snapPointPixelSize = snapPointPixelSize; }
/// Returns snap point pixel size
int getSnapPointPixelSize() const { return m_snapPointPixelSize; }
/// Draws snapping points onto the painter. This function needs valid snap points,
/// so \p m_snapPoints must be valid before this function is called.
/// \param painter Painter
/// \param pageIndex Page index
/// \param compiledPage Compiled page
/// \param pagePointToDevicePointMatrix Matrix mapping page space to device point space
void drawSnapPoints(QPainter* painter,
pdf::PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
const QMatrix& pagePointToDevicePointMatrix) const;
void drawSnapPoints(QPainter* painter) const;
/// Returns true, if snapping is allowed at current page
/// \param pageIndex Page index to be tested for allowing snapping
bool isSnappingAllowed(PDFInteger pageIndex) const;
/// Updates snapped point using given information. If current page is set, it means, we are
/// using snapping info from current page, and if we are hovering at different page,
/// then nothing happens. Otherwise, other page snap info is used to update snapped point.
/// If mouse point distance from some snap point is lesser than tolerance, then new snap is set.
/// \param mousePoint Mouse point in widget coordinates
void updateSnappedPoint(const QPointF& mousePoint);
/// Returns true, if some point of interest is snapped
bool isSnapped() const { return m_snappedPoint.has_value(); }
/// Builds snap points from the widget snapshot. Updates current value
/// of snapped point (from mouse position).
/// \param snapshot Widget snapshot
void buildSnapPoints(const PDFWidgetSnapshot& snapshot);
/// Returns current snap point tolerance (while aiming with the mouse cursor,
/// when mouse cursor is at most tolerance distance from some snap point,
/// it is snapped.
int getSnapPointTolerance() const;
/// Sets snap point tolerance
void setSnapPointTolerance(int snapPointTolerance);
/// Returns snapped position. If point at mouse cursor is not snapped, then
/// mouse cursor position is returned.
QPointF getSnappedPoint() const;
private:
PDFSnapInfo m_currentPageSnapInfo;
struct ViewportSnapPoint : public PDFSnapInfo::SnapPoint
{
QPointF viewportPoint;
PDFInteger pageIndex;
};
struct SnappedPoint
{
PDFInteger pageIndex = -1;
QPointF snappedPoint;
};
std::vector<ViewportSnapPoint> m_snapPoints;
std::optional<ViewportSnapPoint> m_snappedPoint;
QPointF m_mousePoint;
PDFInteger m_currentPage = -1;
int m_snapPointPixelSize = 0;
int m_snapPointTolerance = 0;
};
} // namespace pdf

View File

@ -18,6 +18,7 @@
#include "pdfwidgettool.h"
#include "pdfdrawwidget.h"
#include "pdfcompiler.h"
#include "pdfwidgetutils.h"
#include <QLabel>
#include <QAction>
@ -951,25 +952,22 @@ PDFPickTool::PDFPickTool(PDFDrawWidgetProxy* proxy, PDFPickTool::Mode mode, QObj
m_mode(mode)
{
setCursor(Qt::BlankCursor);
}
m_snapper.setSnapPointPixelSize(PDFWidgetUtils::scaleDPI_x(proxy->getWidget(), 10));
m_snapper.setSnapPointTolerance(m_snapper.getSnapPointPixelSize());
void PDFPickTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const
{
Q_UNUSED(layoutGetter);
m_snapper.drawSnapPoints(painter, pageIndex, compiledPage, pagePointToDevicePointMatrix);
connect(proxy, &PDFDrawWidgetProxy::drawSpaceChanged, this, &PDFPickTool::buildSnapPoints);
connect(proxy, &PDFDrawWidgetProxy::pageImageChanged, this, &PDFPickTool::buildSnapPoints);
}
void PDFPickTool::drawPostRendering(QPainter* painter, QRect rect) const
{
QPoint hleft = m_mousePosition;
QPoint hright = m_mousePosition;
QPoint vtop = m_mousePosition;
QPoint vbottom = m_mousePosition;
m_snapper.drawSnapPoints(painter);
QPoint snappedPoint = m_snapper.getSnappedPoint().toPoint();
QPoint hleft = snappedPoint;
QPoint hright = snappedPoint;
QPoint vtop = snappedPoint;
QPoint vbottom = snappedPoint;
hleft.setX(0);
hright.setX(rect.width());
@ -1001,10 +999,31 @@ void PDFPickTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
if (m_mousePosition != mousePos)
{
m_mousePosition = mousePos;
m_snapper.updateSnappedPoint(m_mousePosition);
getProxy()->repaintNeeded();
}
}
void PDFPickTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (active)
{
buildSnapPoints();
}
}
void PDFPickTool::buildSnapPoints()
{
if (!isActive())
{
return;
}
m_snapper.buildSnapPoints(getProxy()->getSnapshot());
}
PDFScreenshotTool::PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent) :
BaseClass(proxy, action, parent),
m_pickTool(nullptr)

View File

@ -296,16 +296,21 @@ public:
/// \param parent Parent object
explicit PDFPickTool(PDFDrawWidgetProxy* proxy, Mode mode, QObject* parent);
virtual void drawPage(QPainter* painter, PDFInteger pageIndex, const PDFPrecompiledPage* compiledPage, PDFTextLayoutGetter& layoutGetter, const QMatrix& pagePointToDevicePointMatrix) const override;
virtual void drawPostRendering(QPainter* painter, QRect rect) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void setActiveImpl(bool active) override;
private:
void buildSnapPoints();
Mode m_mode;
PDFSnapper m_snapper;
QPoint m_mousePosition;
std::vector<QPointF> m_pickedPoints;
};
/// Tool that makes screenshot of page area and copies it to the clipboard