// Copyright (C) 2020 Jakub Melka // // This file is part of PdfForQt. // // PdfForQt is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // PdfForQt is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with PDFForQt. If not, see . #include "dimensiontool.h" #include "pdfwidgetutils.h" #include "pdfdrawwidget.h" #include DimensionTool::DimensionTool(DimensionTool::Style style, pdf::PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent) : BaseClass(proxy, action, parent), m_style(style), m_previewPointPixelSize(0), m_pickTool(nullptr) { m_pickTool = new pdf::PDFPickTool(proxy, pdf::PDFPickTool::Mode::Points, this); addTool(m_pickTool); connect(m_pickTool, &pdf::PDFPickTool::pointPicked, this, &DimensionTool::onPointPicked); m_previewPointPixelSize = pdf::PDFWidgetUtils::scaleDPI_x(proxy->getWidget(), 5); } void DimensionTool::drawPage(QPainter* painter, pdf::PDFInteger pageIndex, const pdf::PDFPrecompiledPage* compiledPage, pdf::PDFTextLayoutGetter& layoutGetter, const QMatrix& pagePointToDevicePointMatrix, QList& errors) const { Q_UNUSED(compiledPage); Q_UNUSED(layoutGetter); Q_UNUSED(errors); if (m_pickTool->getPageIndex() != pageIndex) { // Other page, nothing to draw return; } painter->setPen(Qt::black); const std::vector& points = m_pickTool->getPickedPoints(); for (size_t i = 1; i < points.size(); ++i) { painter->drawLine(pagePointToDevicePointMatrix.map(points[i - 1]), pagePointToDevicePointMatrix.map(points[i])); } if (!points.empty()) { QMatrix inverted = pagePointToDevicePointMatrix.inverted(); QPointF adjustedPoint = adjustPagePoint(inverted.map(m_pickTool->getSnappedPoint())); painter->drawLine(pagePointToDevicePointMatrix.map(points.back()), pagePointToDevicePointMatrix.map(adjustedPoint)); } QPen pen = painter->pen(); pen.setWidth(m_previewPointPixelSize); pen.setCapStyle(Qt::RoundCap); painter->setPen(pen); for (size_t i = 0; i < points.size(); ++i) { painter->drawPoint(pagePointToDevicePointMatrix.map(points[i])); } } void DimensionTool::onPointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint) { if (Dimension::isComplete(getDimensionType(), m_pickTool->getPickedPoints())) { // Create a new dimension... emit dimensionCreated(Dimension(pageIndex, getMeasuredValue(), m_pickTool->getPickedPoints())); m_pickTool->resetTool(); } } QPointF DimensionTool::adjustPagePoint(QPointF pagePoint) const { switch (m_style) { case Style::LinearHorizontal: { const std::vector& pickedPoints = m_pickTool->getPickedPoints(); if (!pickedPoints.empty()) { pagePoint.setY(pickedPoints.front().y()); } break; } case Style::LinearVertical: { const std::vector& pickedPoints = m_pickTool->getPickedPoints(); if (!pickedPoints.empty()) { pagePoint.setX(pickedPoints.front().x()); } break; } default: break; } return pagePoint; } Dimension::Type DimensionTool::getDimensionType() const { switch (m_style) { case DimensionTool::LinearHorizontal: case DimensionTool::LinearVertical: case DimensionTool::Linear: return Dimension::Type::Linear; case DimensionTool::Perimeter: return Dimension::Type::Perimeter; case DimensionTool::Area: return Dimension::Type::Area; } Q_ASSERT(false); return Dimension::Type::Linear; } pdf::PDFReal DimensionTool::getMeasuredValue() const { const std::vector& pickedPoints = m_pickTool->getPickedPoints(); switch (getDimensionType()) { case Dimension::Linear: case Dimension::Perimeter: { pdf::PDFReal length = 0.0; for (size_t i = 1; i < pickedPoints.size(); ++i) { QPointF vector = pickedPoints[i] - pickedPoints[i - 1]; length += qSqrt(QPointF::dotProduct(vector, vector)); } break; } case Dimension::Area: { pdf::PDFReal area = 0.0; // We calculate the area using standard formula for polygons. // We determine integral over perimeter (for each edge of the polygon). for (size_t i = 1; i < pickedPoints.size(); ++i) { const QPointF& p1 = pickedPoints[i - 1]; const QPointF& p2 = pickedPoints[i]; area += p1.x() * p2.y() - p1.y() * p2.x(); } area = qAbs(area) * 0.5; break; } default: Q_ASSERT(false); break; } return 0.0; } bool Dimension::isComplete(Type type, const std::vector& polygon) { switch (type) { case Dimension::Linear: return polygon.size() == 2; case Dimension::Perimeter: case Dimension::Area: return polygon.size() > 2 && polygon.front() == polygon.back(); default: Q_ASSERT(false); break; } return false; } DimensionUnits DimensionUnit::getLengthUnits() { DimensionUnits units; units.emplace_back(1.0, DimensionTool::tr("pt")); units.emplace_back(pdf::PDF_POINT_TO_INCH, DimensionTool::tr("in")); units.emplace_back(pdf::PDF_POINT_TO_MM, DimensionTool::tr("mm")); units.emplace_back(pdf::PDF_POINT_TO_MM * 0.1, DimensionTool::tr("cm")); return units; } DimensionUnits DimensionUnit::getAreaUnits() { DimensionUnits units; units.emplace_back(1.0, DimensionTool::tr("sq. pt")); units.emplace_back(pdf::PDF_POINT_TO_INCH * pdf::PDF_POINT_TO_INCH, DimensionTool::tr("sq. in")); units.emplace_back(pdf::PDF_POINT_TO_MM * pdf::PDF_POINT_TO_MM, DimensionTool::tr("sq. mm")); units.emplace_back(pdf::PDF_POINT_TO_MM * 0.1 * pdf::PDF_POINT_TO_MM * 0.1, DimensionTool::tr("sq. cm")); return units; } DimensionUnits DimensionUnit::getAngleUnits() { DimensionUnits units; units.emplace_back(qRadiansToDegrees(1.0), DimensionTool::tr("°")); units.emplace_back(1.0, DimensionTool::tr("rad")); return units; }