Advanced snapping, basics of screenshot tool

This commit is contained in:
Jakub Melka 2020-02-28 18:56:28 +01:00
parent 46c267e537
commit 13bfbd7095
7 changed files with 212 additions and 3 deletions

View File

@ -1370,4 +1370,15 @@ void IDocumentDrawInterface::drawPostRendering(QPainter* painter, QRect rect) co
Q_UNUSED(rect);
}
const PDFWidgetSnapshot::SnapshotItem* PDFWidgetSnapshot::getPageSnapshot(PDFInteger pageIndex) const
{
auto it = std::find_if(items.cbegin(), items.cend(), [pageIndex](const auto& item) { return item.pageIndex == pageIndex; });
if (it != items.cend())
{
return &*it;
}
return nullptr;
}
} // namespace pdf

View File

@ -196,6 +196,9 @@ struct PDFWidgetSnapshot
const PDFPrecompiledPage* compiledPage = nullptr; ///< Compiled page (can be nullptr)
};
bool hasPage(PDFInteger pageIndex) const { return getPageSnapshot(pageIndex) != nullptr; }
const SnapshotItem* getPageSnapshot(PDFInteger pageIndex) const;
using SnapshotItems = std::vector<SnapshotItem>;
SnapshotItems items;
};

View File

@ -179,6 +179,41 @@ void PDFSnapper::buildSnapPoints(const PDFWidgetSnapshot& snapshot)
viewportSnapPoint.viewportPoint = item.pageToDeviceMatrix.map(snapPoint.point);
m_snapPoints.push_back(qMove(viewportSnapPoint));
}
// Fill line projections snap points
if (m_currentPage == item.pageIndex && m_referencePoint.has_value())
{
QPointF referencePoint = *m_referencePoint;
for (const QLineF& line : info->getLines())
{
// Project point onto line.
const qreal lineLength = line.length();
QPointF vector = referencePoint - line.p1();
QPointF tangentVector = (line.p2() - line.p1()) / lineLength;
const qreal absoluteParameter = QPointF::dotProduct(vector, tangentVector);
if (absoluteParameter >= 0 && absoluteParameter <= lineLength)
{
QPointF projectedSnapPoint = line.pointAt(absoluteParameter / lineLength);
const PDFReal tolerance = lineLength * 0.01;
const PDFReal squaredTolerance = tolerance * tolerance;
// Test, if projected snap point is not already present in snap points
auto testSamePoints = [projectedSnapPoint, squaredTolerance](const ViewportSnapPoint& testedSnapPoint)
{
return isFuzzyComparedPointsSame(projectedSnapPoint, testedSnapPoint.point, squaredTolerance);
};
if (m_snapPoints.cend() == std::find_if(m_snapPoints.cbegin(), m_snapPoints.cend(), testSamePoints))
{
ViewportSnapPoint viewportSnapPoint;
viewportSnapPoint.type = SnapType::GeneratedLineProjection;
viewportSnapPoint.point = projectedSnapPoint;
viewportSnapPoint.pageIndex = item.pageIndex;
viewportSnapPoint.viewportPoint = item.pageToDeviceMatrix.map(projectedSnapPoint);
m_snapPoints.push_back(qMove(viewportSnapPoint));
}
}
}
}
}
// Third, update snap shot position
@ -205,4 +240,16 @@ QPointF PDFSnapper::getSnappedPoint() const
return m_mousePoint;
}
void PDFSnapper::setReferencePoint(PDFInteger pageIndex, QPointF pagePoint)
{
m_currentPage = pageIndex;
m_referencePoint = pagePoint;
}
void PDFSnapper::clearReferencePoint()
{
m_currentPage = -1;
m_referencePoint = std::nullopt;
}
} // namespace pdf

View File

@ -81,6 +81,9 @@ public:
/// Returns snap points
const std::vector<SnapPoint>& getSnapPoints() const { return m_snapPoints; }
/// Returns lines
const std::vector<QLineF>& getLines() const { return m_snapLines; }
private:
std::vector<SnapPoint> m_snapPoints;
std::vector<QLineF> m_snapLines;
@ -135,6 +138,14 @@ public:
/// mouse cursor position is returned.
QPointF getSnappedPoint() const;
/// Sets current page index and reference point
/// \param pageIndex Page index
/// \param pagePoint Page point
void setReferencePoint(PDFInteger pageIndex, QPointF pagePoint);
/// Resets reference point (and current page)
void clearReferencePoint();
private:
struct ViewportSnapPoint : public PDFSnapInfo::SnapPoint
@ -152,6 +163,7 @@ private:
std::vector<ViewportSnapPoint> m_snapPoints;
std::optional<ViewportSnapPoint> m_snappedPoint;
QPointF m_mousePoint;
std::optional<QPointF> m_referencePoint;
PDFInteger m_currentPage = -1;
int m_snapPointPixelSize = 0;
int m_snapPointTolerance = 0;

View File

@ -471,6 +471,18 @@ private:
T m_q;
};
/// Fuzzy compares two points, with given tolerance (so, if points are at lower distance
/// from each other than squared tolerance, they are considered as same and function returns true).
/// \param p1 First point
/// \param p2 Second point
/// \param squaredTolerance Squared tolerance
static inline bool isFuzzyComparedPointsSame(const QPointF& p1, const QPointF& p2, PDFReal squaredTolerance)
{
QPointF dp = p2 - p1;
const qreal squaredDistance = QPointF::dotProduct(dp, dp);
return squaredDistance < squaredTolerance;
}
} // namespace pdf
#endif // PDFUTILS_H

View File

@ -52,7 +52,7 @@ PDFWidgetTool::PDFWidgetTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject
m_action(action),
m_proxy(proxy)
{
updateActions();
}
PDFWidgetTool::~PDFWidgetTool()
@ -949,7 +949,8 @@ void PDFMagnifierTool::setMagnifierSize(int magnifierSize)
PDFPickTool::PDFPickTool(PDFDrawWidgetProxy* proxy, PDFPickTool::Mode mode, QObject* parent) :
BaseClass(proxy, parent),
m_mode(mode)
m_mode(mode),
m_pageIndex(-1)
{
setCursor(Qt::BlankCursor);
m_snapper.setSnapPointPixelSize(PDFWidgetUtils::scaleDPI_x(proxy->getWidget(), 10));
@ -959,6 +960,36 @@ PDFPickTool::PDFPickTool(PDFDrawWidgetProxy* proxy, PDFPickTool::Mode mode, QObj
connect(proxy, &PDFDrawWidgetProxy::pageImageChanged, this, &PDFPickTool::buildSnapPoints);
}
void PDFPickTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix) const
{
Q_UNUSED(compiledPage);
Q_UNUSED(layoutGetter);
// If we are picking rectangles, then draw current selection rectangle
if (m_mode == Mode::Rectangles && m_pageIndex == pageIndex && !m_pickedPoints.empty())
{
QPoint p1 = pagePointToDevicePointMatrix.map(m_pickedPoints.back()).toPoint();
QPoint p2 = m_snapper.getSnappedPoint().toPoint();
int xMin = qMin(p1.x(), p2.x());
int xMax = qMax(p1.x(), p2.x());
int yMin = qMin(p1.y(), p2.y());
int yMax = qMax(p1.y(), p2.y());
QRect selectionRectangle(xMin, yMin, xMax - xMin, yMax - yMin);
if (selectionRectangle.isValid())
{
QColor selectionColor(Qt::blue);
selectionColor.setAlphaF(0.25);
painter->fillRect(selectionRectangle, selectionColor);
}
}
}
void PDFPickTool::drawPostRendering(QPainter* painter, QRect rect) const
{
m_snapper.drawSnapPoints(painter);
@ -983,6 +1014,48 @@ void PDFPickTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->button() == Qt::LeftButton)
{
// Try to perform pick
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(m_snapper.getSnappedPoint().toPoint(), &pagePoint);
if (pageIndex != -1 && // We have picked some point on page
(m_pageIndex == -1 || m_pageIndex == pageIndex)) // We are under current page
{
m_pageIndex = pageIndex;
m_pickedPoints.push_back(pagePoint);
m_snapper.setReferencePoint(pageIndex, pagePoint);
// Emit signal about picked point
emit pointPicked(pageIndex, pagePoint);
if (m_mode == Mode::Rectangles && m_pickedPoints.size() == 2)
{
QPointF first = m_pickedPoints.front();
QPointF second = m_pickedPoints.back();
const qreal xMin = qMin(first.x(), second.x());
const qreal xMax = qMax(first.x(), second.x());
const qreal yMin = qMin(first.y(), second.y());
const qreal yMax = qMax(first.y(), second.y());
QRectF pageRectangle(xMin, yMin, xMax - xMin, yMax - yMin);
emit rectanglePicked(pageIndex, pageRectangle);
// We must reset tool, to pick next rectangle
resetTool();
}
buildSnapPoints();
getProxy()->repaintNeeded();
}
}
else if (event->button() == Qt::RightButton)
{
// Reset tool to enable new picking (right button means reset the tool)
resetTool();
}
}
void PDFPickTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
@ -1012,6 +1085,22 @@ void PDFPickTool::setActiveImpl(bool active)
{
buildSnapPoints();
}
else
{
// Reset tool to reinitialize it for future use. If tool
// is activated, then it should be in initial state.
resetTool();
}
}
void PDFPickTool::resetTool()
{
m_pickedPoints.clear();
m_pageIndex = -1;
m_snapper.clearReferencePoint();
buildSnapPoints();
getProxy()->repaintNeeded();
}
void PDFPickTool::buildSnapPoints()
@ -1030,6 +1119,28 @@ PDFScreenshotTool::PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action,
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFScreenshotTool::onRectanglePicked);
}
void PDFScreenshotTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
PDFWidgetSnapshot snapshot = getProxy()->getSnapshot();
if (const PDFWidgetSnapshot::SnapshotItem* pageSnapshot = snapshot.getPageSnapshot(pageIndex))
{
QRect selectedRectangle = pageSnapshot->pageToDeviceMatrix.mapRect(pageRectangle).toRect();
if (selectedRectangle.isValid())
{
QImage image(selectedRectangle.size(), QImage::Format_RGB888);
{
QPainter painter(&image);
painter.translate(-selectedRectangle.topLeft());
getProxy()->drawPages(&painter, getProxy()->getWidget()->rect());
}
QApplication::clipboard()->setImage(image, QClipboard::Clipboard);
}
}
}
} // namespace pdf

View File

@ -296,24 +296,35 @@ 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;
signals:
void pointPicked(PDFInteger pageIndex, QPointF pagePoint);
void rectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
protected:
virtual void setActiveImpl(bool active) override;
private:
void resetTool();
void buildSnapPoints();
Mode m_mode;
PDFSnapper m_snapper;
QPoint m_mousePosition;
PDFInteger m_pageIndex;
std::vector<QPointF> m_pickedPoints;
};
/// Tool that makes screenshot of page area and copies it to the clipboard
/// Tool that makes screenshot of page area and copies it to the clipboard,
/// using current client area to determine image size.
class PDFFORQTLIBSHARED_EXPORT PDFScreenshotTool : public PDFWidgetTool
{
Q_OBJECT
@ -329,6 +340,8 @@ public:
explicit PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
private:
void onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
PDFPickTool* m_pickTool;
};