новые иконки в OpenBoard
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
OpenBoard/src/tools/UBGraphicsProtractor.cpp

649 lines
20 KiB

/*
* Copyright (C) 2015-2022 Département de l'Instruction Publique (DIP-SEM)
*
* Copyright (C) 2013 Open Education Foundation
*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
* l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of OpenBoard.
*
* OpenBoard is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* OpenBoard 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBoard. If not, see <http://www.gnu.org/licenses/>.
*/
#include "UBGraphicsProtractor.h"
#include "core/UBApplication.h"
#include "gui/UBResources.h"
#include "domain/UBGraphicsScene.h"
#include "board/UBBoardController.h"
#include "board/UBDrawingController.h"
#include "UBAbstractDrawRuler.h"
#include "core/memcheck.h"
#include <QtWidgets/QGraphicsView>
const QRectF UBGraphicsProtractor::sDefaultRect = QRectF(-250, -250, 500, 500);
const qreal UBGraphicsProtractor::minRadius = 70;
UBGraphicsProtractor::UBGraphicsProtractor()
: QGraphicsEllipseItem(sDefaultRect)
, mCurrentTool(None)
, mShowButtons(false)
, mCurrentAngle(0)
, mSpan(180)
, mStartAngle(0)
, mScaleFactor(1)
, mResetSvgItem(0)
, mResizeSvgItem(0)
, mMarkerSvgItem(0)
{
sFillTransparency = 127;
sDrawTransparency = 192;
create(*this);
setCacheMode(QGraphicsItem::DeviceCoordinateCache);
setStartAngle(0);
setSpanAngle(180 * 16);
mResetSvgItem = new QGraphicsSvgItem(":/images/resetTool.svg", this);
mResetSvgItem->setVisible(false);
mResetSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));
mResizeSvgItem = new QGraphicsSvgItem(":/images/resizeTool.svg", this);
mResizeSvgItem->setVisible(false);
mResizeSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));
mMarkerSvgItem = new QGraphicsSvgItem(":/images/angleMarker.svg", this);
mMarkerSvgItem->setVisible(false);
mMarkerSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Tool));
mRotateSvgItem = new QGraphicsSvgItem(":/images/rotateTool.svg", this);
mRotateSvgItem->setVisible(false);
mRotateSvgItem->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));
setData(UBGraphicsItemData::itemLayerType, QVariant(itemLayerType::CppTool)); //Necessary to set if we want z value to be assigned correctly
setFlag(QGraphicsItem::ItemIsSelectable, false);
mCloseSvgItem->setPos(closeButtonRect().topLeft());
mResetSvgItem->setPos(resetButtonRect().topLeft());
mResizeSvgItem->setPos(resizeButtonRect().topLeft());
mMarkerSvgItem->setPos(markerButtonRect().topLeft());
mRotateSvgItem->setPos(rotateButtonRect().topLeft());
}
void UBGraphicsProtractor::paint(QPainter *painter, const QStyleOptionGraphicsItem *styleOption, QWidget *widget)
{
painter->save();
Q_UNUSED(styleOption);
Q_UNUSED(widget);
QPen pen_(drawColor());
pen_.setWidth(0); // Line width = 1 pixel regardless of scale / zoom
painter->setPen(pen_);
painter->setFont(QFont("Arial", 11));
painter->setBrush(fillBrush());
painter->drawPie(QRectF(rect().center().x() - radius(), rect().center().y() - radius(), 2 * radius(), 2 * radius()), mStartAngle * 16, mSpan * 16);
paintGraduations(painter);
paintButtons(painter);
paintAngleMarker(painter);
painter->restore();
}
QVariant UBGraphicsProtractor::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemSceneChange)
{
mCloseSvgItem->setParentItem(this);
mResizeSvgItem->setParentItem(this);
mResetSvgItem->setParentItem(this);
mRotateSvgItem->setParentItem(this);
mMarkerSvgItem->setParentItem(this);
}
return QGraphicsEllipseItem::itemChange(change, value);
}
QRectF UBGraphicsProtractor::boundingRect() const
{
QPointF center = rect().center();
qreal centerX = center.x();
qreal centerY = center.y();
QRectF bounds = resizeButtonRect().adjusted(centerX, centerY, centerX, centerY);
bounds = bounds.united(closeButtonRect().adjusted(centerX, centerY, centerX, centerY));
bounds = bounds.united(resetButtonRect().adjusted(centerX, centerY, centerX, centerY));
QTransform t;
t.translate(centerX, centerY);
t.rotate(-mStartAngle);
t.translate(-centerX, -centerY);
bounds = t.mapRect(bounds);
bounds = bounds.united(QGraphicsEllipseItem::boundingRect());
return bounds;
}
QPainterPath UBGraphicsProtractor::shape() const
{
QPainterPath path = QGraphicsEllipseItem::shape();
QPainterPath buttonPath;
QRectF markerRect = markerButtonRect();
QPointF center = rect().center();
qreal centerX = center.x();
qreal centerY = center.y();
buttonPath.addRect(resizeButtonRect().adjusted(centerX, centerY, centerX, centerY));
if (!resizeButtonRect().contains(markerRect))
{
buttonPath.addRect(markerRect.adjusted(centerX - markerRect.left() * 2 - markerRect.width(), centerY
, centerX - markerRect.left() * 2 - markerRect.width(), centerY));
buttonPath.addRect(markerRect.adjusted(centerX, centerY, centerX, centerY));
}
buttonPath.addRect(closeButtonRect().adjusted(centerX, centerY, centerX, centerY));
buttonPath.addRect(resetButtonRect().adjusted(centerX, centerY, centerX, centerY));
QTransform t;
t.translate(centerX, centerY);
t.rotate(-mStartAngle);
t.translate(-centerX, -centerY);
buttonPath = t.map(buttonPath);
buttonPath = buttonPath.subtracted(path);
path.addPath(buttonPath);
return path;
}
void UBGraphicsProtractor::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
mPreviousMousePos = event->pos();
mCurrentTool = toolFromPos(event->pos());
mShowButtons = mCurrentTool == Reset || mCurrentTool == Close;
if (mCurrentTool == None || mCurrentTool == Move)
QGraphicsEllipseItem::mousePressEvent(event);
else
event->accept();
}
void UBGraphicsProtractor::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QPointF currentPoint = event->pos();
QLineF startLine(rect().center(), mPreviousMousePos);
QLineF currentLine(rect().center(), currentPoint);
qreal angle = startLine.angleTo(currentLine);
qreal scaleFactor = currentLine.length()/startLine.length();
switch (mCurrentTool)
{
case Rotate :
prepareGeometryChange();
mStartAngle = mStartAngle + angle;
setStartAngle(mStartAngle * 16);
mPreviousMousePos = currentPoint;
break;
case Resize :
if (radius() * mScaleFactor * scaleFactor > minRadius) {
prepareGeometryChange();
setTransform(QTransform::fromTranslate(rect().center().x(), rect().center().y()), true);
setTransform(QTransform::fromScale(scaleFactor, scaleFactor), true);
setTransform(QTransform::fromTranslate(-rect().center().x(), -rect().center().y()), true);
mScaleFactor *= scaleFactor;
}
break;
case MoveMarker :
mCurrentAngle += angle;
if ((int)mCurrentAngle % 360 > 270)
mCurrentAngle = 0;
else if ((int)mCurrentAngle % 360 >= 180)
mCurrentAngle = 180;
mPreviousMousePos = currentPoint;
update();
break;
case Move :
QGraphicsEllipseItem::mouseMoveEvent(event);
break;
default :
break;
}
if (mCurrentTool != Move)
event->accept();
}
void UBGraphicsProtractor::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
switch (mCurrentTool)
{
case Reset :
setStartAngle(0);
mStartAngle = 0;
break;
case Close :
hide();
break;
case MoveMarker :
update();
break;
case Resize:
update();
break;
default :
QGraphicsEllipseItem::mouseReleaseEvent(event);
break;
}
if (mCurrentTool != Move)
event->accept();
if (scene())
scene()->setModified(true);
if (!mShowButtons)
{
mShowButtons = true;
update();
}
mCurrentTool = None;
}
void UBGraphicsProtractor::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
if (UBDrawingController::drawingController ()->stylusTool() != UBStylusTool::Selector && UBDrawingController::drawingController ()->stylusTool() != UBStylusTool::Play)
return;
if (!mShowButtons)
{
mShowButtons = true;
mCloseSvgItem->setParentItem(this);
mResizeSvgItem->setParentItem(this);
mResetSvgItem->setParentItem(this);
mRotateSvgItem->setParentItem(this);
mMarkerSvgItem->setParentItem(this);
update();
}
event->accept();
}
void UBGraphicsProtractor::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
mShowButtons = false;
unsetCursor();
update();
event->accept();
}
void UBGraphicsProtractor::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
if (UBDrawingController::drawingController ()->stylusTool() != UBStylusTool::Selector)
return;
Tool currentTool = toolFromPos(event->pos());
if (currentTool == Move)
setCursor(Qt::SizeAllCursor);
else if (currentTool == Rotate)
setCursor(rotateCursor());
else
setCursor(Qt::ArrowCursor);
event->accept();
}
qreal UBGraphicsProtractor::antiScale() const
{
return 1 / (mScaleFactor * UBApplication::boardController->systemScaleFactor() * UBApplication::boardController->currentZoom());
}
QRectF UBGraphicsProtractor::resetButtonRect () const
{
qreal refWidth = buttonSizeReference().width();
qreal width = mResetSvgItem->boundingRect().width();
qreal height = mResetSvgItem->boundingRect().height();
return QRectF(-refWidth * 7, -height/2.0, width, height);
// Note: these hardcoded position values make it impossible
// to use setScale() on the buttons without affecting their
// relative position on the tool.
}
QRectF UBGraphicsProtractor::closeButtonRect () const
{
qreal refWidth = buttonSizeReference().width();
qreal width = mCloseSvgItem->boundingRect().width();
qreal height = mCloseSvgItem->boundingRect().height();
return QRectF(-refWidth * 9, -height/2.0, width, height);
}
QRectF UBGraphicsProtractor::resizeButtonRect () const
{
qreal refWidth = buttonSizeReference().width();
qreal width = mResizeSvgItem->boundingRect().width();
qreal height = mResizeSvgItem->boundingRect().height();
return QRectF(refWidth * 8, -height/2.0, width, height);
}
QRectF UBGraphicsProtractor::rotateButtonRect () const
{
qreal refWidth = buttonSizeReference().width();
qreal width = mRotateSvgItem->boundingRect().width();
qreal height = mRotateSvgItem->boundingRect().height();
return QRectF(refWidth * 5.5, -refWidth * 5, width, height);
}
QRectF UBGraphicsProtractor::markerButtonRect () const
{
qreal width = mMarkerSvgItem->boundingRect().width();
qreal height = mMarkerSvgItem->boundingRect().height();
return QRectF(radius() + 3, -height/2, width, height);
}
void UBGraphicsProtractor::paintGraduations(QPainter *painter)
{
painter->save();
const int tenDegreeGraduationLength = 22;
const int fiveDegreeGraduationLength = 15;
const int oneDegreeGraduationLength = 7;
QFont font1 = painter->font();
#ifdef Q_OS_OSX
font1.setPointSizeF(font1.pointSizeF() + 3);
font1.setWeight(QFont::Thin);
#endif
QFontMetricsF fm1(font1);
//Font for internal arc
QFont font2 = painter->font();
font2.setPointSizeF(font1.pointSizeF()/1.5);
QFontMetricsF fm2(font2);
qreal rad = radius();
QPointF center = rect().center();
painter->drawArc(QRectF(center.x() - rad/2, center.y() - rad/2, rad, rad), mStartAngle*16, mSpan*16);
for (int angle = 1; angle < mSpan; angle++)
{
int graduationLength = (0 == angle % 10) ? tenDegreeGraduationLength : ((0 == angle % 5) ? fiveDegreeGraduationLength : oneDegreeGraduationLength);
qreal co = cos(((qreal)angle + mStartAngle) * PI/180);
qreal si = sin(((qreal)angle + mStartAngle) * PI/180);
if (0 == angle % 90)
painter->drawLine(QLineF(QPointF(center.x(), center.y()),
QPointF(center.x() + co*tenDegreeGraduationLength, center.y() - si*tenDegreeGraduationLength)));
//external arc
painter->drawLine(QLineF(QPointF(center.x()+ rad*co, center.y() - rad*si),
QPointF(center.x()+ (rad - graduationLength)*co, center.y() - (rad - graduationLength)*si)));
//internal arc
painter->drawLine(QLineF(QPointF(center.x()+ rad/2*co, center.y() - rad/2*si),
QPointF(center.x()+ (rad/2 + graduationLength)*co,
center.y() - (rad/2 + graduationLength)*si)));
if (0 == angle % 10)
{
//external arc
painter->setFont(font1);
QString grad = QString("%1").arg((int)(angle));
QString grad2 = QString("%1").arg((int)(mSpan - angle));
painter->drawText(QRectF(center.x() + (rad - graduationLength*1.5)*co - fm1.width(grad)/2,
center.y() - (rad - graduationLength*1.5)*si - fm1.height()/2,
fm1.width(grad), fm1.height()), Qt::AlignTop, grad);
//internal arc
painter->setFont(font2);
painter->drawText(QRectF(center.x() + (rad/2 + graduationLength*1.5)*co - fm2.width(grad2)/2,
center.y() - (rad/2 + graduationLength*1.5)*si - fm2.height()/2,
fm2.width(grad2), fm2.height()), Qt::AlignTop, grad2);
painter->setFont(font1);
}
}
painter->restore();
}
void UBGraphicsProtractor::paintButtons(QPainter *painter)
{
Q_UNUSED(painter);
if (mShowButtons)
{
// The buttons (close, reset, resize & rotate) are rotated around the center
// of the protractor, to follow it as it is rotated.
// As coordinates are local to each QGraphicsItem, the position of the rotation
// center relative to each button is the inverse of that button's position.
mCloseSvgItem->setTransformOriginPoint(closeButtonRect().topLeft() * -1);
mCloseSvgItem->setRotation(rotation() - mStartAngle);
mResetSvgItem->setTransformOriginPoint(resetButtonRect().topLeft() * -1);
mResetSvgItem->setRotation(rotation() - mStartAngle);
mResizeSvgItem->setTransformOriginPoint(resizeButtonRect().topLeft() * -1);
mResizeSvgItem->setRotation(rotation() - mStartAngle);
mRotateSvgItem->setTransformOriginPoint(rotateButtonRect().topLeft() * -1);
mRotateSvgItem->setRotation(rotation() - mStartAngle);
}
mMarkerSvgItem->setTransformOriginPoint(markerButtonRect().topLeft() * -1);
mMarkerSvgItem->setRotation(rotation() - mStartAngle - mCurrentAngle);
mCloseSvgItem->setVisible(mShowButtons);
mResetSvgItem->setVisible(mShowButtons);
mResizeSvgItem->setVisible(mShowButtons);
mRotateSvgItem->setVisible(mShowButtons);
mMarkerSvgItem->setVisible(true);
}
void UBGraphicsProtractor::paintAngleMarker(QPainter *painter)
{
painter->save();
painter->translate(rect().center());
painter->rotate(-mStartAngle);
painter->translate(-rect().center().x(), -rect().center().y());
qreal co = cos(mCurrentAngle * PI/180);
qreal si = sin(mCurrentAngle * PI/180);
qreal rad = radius();
painter->drawLine(QLineF(rect().center(), QPointF(rect().center().x()+ (rad+ 20)*co, rect().center().y() - (rad + 20)*si)));
QPointF center = rect().center();
painter->drawArc(QRectF(center.x() - rad/8, center.y() - rad/8, rad / 4, rad / 4), 0
, (mCurrentAngle - (int)(mCurrentAngle/360)*360)*16);
painter->translate(rect().center());
painter->rotate(-mCurrentAngle);
painter->translate(-rect().center().x(), -rect().center().y());
//Paint Angle text (horizontally)
//restore transformations
painter->translate(rect().center());
painter->rotate(mCurrentAngle);
painter->rotate(mStartAngle);
painter->translate(-rect().center().x(), -rect().center().y());
qreal angle = mCurrentAngle - (int)(mCurrentAngle/360)*360;
if (angle != 0)
{
QString ang = QString("%1°").arg(angle,0, 'f', 1);
QFont font2 = painter->font();
font2.setBold(true);
QFontMetricsF fm2(font2);
painter->setFont(font2);
if (angle < 50)
angle = 90;
else
angle = angle / 2;
co = cos((mStartAngle + angle) * PI/180);
si = sin((mStartAngle + angle) * PI/180);
painter->drawText(QRectF(rect().center().x() + (rad*2.5/10)*co - fm2.width(ang)/2,
rect().center().y() - (rad*2.5/10)*si - fm2.height()/2,
fm2.width(ang), fm2.height()), Qt::AlignTop, ang);
}
painter->restore();
}
UBGraphicsProtractor::Tool UBGraphicsProtractor::toolFromPos(QPointF pos)
{
pos = pos - rect().center();
QLineF line(QPointF(0,0), pos);
QTransform t;
t.rotate(mStartAngle);
QPointF p1 = t.map(pos);
t.rotate(mCurrentAngle);
QPointF p2 = t.map(pos);
if (resizeButtonRect().contains(p1.x(),p1.y()))
return Resize;
else if (closeButtonRect().contains(p1))
return Close;
else if (resetButtonRect().contains(p1))
return Reset;
else if (rotateButtonRect().contains(p1))
return Rotate;
else if (markerButtonRect().contains(p2))
return MoveMarker;
else if (line.length() <= radius())
return Move;
else
return None;
}
UBGraphicsScene* UBGraphicsProtractor::scene() const
{
return static_cast<UBGraphicsScene*>(QGraphicsEllipseItem::scene());
}
QBrush UBGraphicsProtractor::fillBrush() const
{
QColor fillColor = edgeFillColor();// scene()->isDarkBackground() ? sDarkBackgroundFillColor : sFillColor;
QColor fillColorCenter = middleFillColor();//scene()->isDarkBackground() ? sDarkBackgroundFillColorCenter : sFillColorCenter;
QColor transparentWhite = Qt::white;
transparentWhite.setAlpha(scene()->isDarkBackground() ? sDrawTransparency : sFillTransparency);
QRadialGradient radialGradient(rect().center(), radius(), rect().center());
radialGradient.setColorAt(0, fillColorCenter);
radialGradient.setColorAt(1, fillColor);
return radialGradient;
}
UBItem* UBGraphicsProtractor::deepCopy() const
{
UBGraphicsProtractor* copy = new UBGraphicsProtractor();
copyItemParameters(copy);
// TODO UB 4.7 ... complete all members ?
return copy;
}
void UBGraphicsProtractor::copyItemParameters(UBItem *copy) const
{
UBGraphicsProtractor *cp = dynamic_cast<UBGraphicsProtractor*>(copy);
if (cp)
{
cp->setPos(this->pos());
cp->setRect(this->rect());
cp->setTransform(this->transform());
cp->mCurrentAngle = this->mCurrentAngle;
cp->mSpan = this->mSpan;
cp->mStartAngle = this->mStartAngle;
cp->mScaleFactor = this->mScaleFactor;
}
}
void UBGraphicsProtractor::rotateAroundCenter(qreal angle)
{
// Q_UNUSED(angle);
// Align the implementation with the others apps objects.
QTransform transform;
transform.translate(rotationCenter().x(), rotationCenter().y());
transform.rotate(angle);
transform.translate(- rotationCenter().x(), - rotationCenter().y());
setTransform(transform, true);
}
QPointF UBGraphicsProtractor::rotationCenter() const
{
return QPointF(rect().x(), rect().y());
}