19#include "cameratool.h"
23#include "pointerevent.h"
24#include "layermanager.h"
25#include "selectionmanager.h"
26#include "playbackmanager.h"
27#include "viewmanager.h"
28#include "layercamera.h"
33#include "scribblearea.h"
43CameraTool::~CameraTool()
48void CameraTool::loadSettings()
50 connect(mEditor->layers(), &LayerManager::currentLayerChanged,
this, &CameraTool::updateProperties);
51 connect(mEditor, &Editor::objectLoaded,
this, &CameraTool::updateProperties);
53 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
55 QSettings pencilSettings(PENCIL2D, PENCIL2D);
57 mPropertyUsed[CameraToolProperties::SHOWPATH_ENABLED] = { Layer::CAMERA };
58 mPropertyUsed[CameraToolProperties::PATH_DOTCOLOR_TYPE] = { Layer::CAMERA };
61 info[CameraToolProperties::PATH_DOTCOLOR_TYPE] = {
static_cast<int>(DotColorType::BLACK),
62 static_cast<int>(DotColorType::WHITE),
63 static_cast<int>(DotColorType::BLACK) };
64 info[CameraToolProperties::SHOWPATH_ENABLED] =
false;
66 toolProperties().insertProperties(info);
67 toolProperties().loadFrom(typeName(), pencilSettings);
69 connect(mEditor->preference(), &PreferenceManager::optionChanged,
this, &CameraTool::updateSettings);
73 mHandleTextColor =
QColor(0, 0, 0);
80void CameraTool::updateUIAssists(
const Layer* layer)
84 Q_ASSERT(layer->type() == Layer::CAMERA);
86 int currentFrame = mEditor->currentFrame();
87 if (!layer->keyExists(currentFrame)) {
return; }
89 const QTransform& localCamT = camLayer->getViewAtFrame(currentFrame);
90 const QRect& cameraRect = camLayer->getViewRect();
92 mCameraRect = Transform::mapFromLocalRect(localCamT, cameraRect);
93 mCameraPolygon = Transform::mapFromLocalPolygon(localCamT, cameraRect);
95 Camera* cam = camLayer->getLastCameraAtFrame(mEditor->currentFrame(), 0);
97 mRotationHandlePoint = localRotationHandlePoint(cameraRect.
topLeft(), localCamT, cam->scaling(), mEditor->view()->
getScaleInversed());
101void CameraTool::updateProperties()
103 Layer* layer = mEditor->layers()->getLayer(mEditor->currentLayerIndex());
104 if (!layer || layer->type() != Layer::CAMERA) {
return; }
107 toolProperties().setBaseValue(CameraToolProperties::PATH_DOTCOLOR_TYPE,
static_cast<int>(layerCam->getDotColorType()));
108 toolProperties().setBaseValue(CameraToolProperties::SHOWPATH_ENABLED, layerCam->getShowCameraPath());
111void CameraTool::updateSettings(
const SETTING setting)
115 case SETTING::ROTATION_INCREMENT:
117 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
130 QPainter cursorPainter(&cursorPixmap);
136 case CameraMoveType::TOPLEFT:
137 case CameraMoveType::BOTTOMRIGHT:
139 moveTypeImage =
QImage(
"://icons/general/cursor-diagonal-left.svg");
142 case CameraMoveType::TOPRIGHT:
143 case CameraMoveType::BOTTOMLEFT:
145 moveTypeImage =
QImage(
"://icons/general/cursor-diagonal-right.svg");
148 case CameraMoveType::ROTATION:
150 moveTypeImage =
QImage(
"://icons/general/cursor-rotate.svg");
153 case CameraMoveType::PATH:
154 case CameraMoveType::CENTER:
156 moveTypeImage =
QImage(
"://icons/general/cursor-move.svg");
168 rotT.
rotate(mCurrentAngle);
170 cursorPainter.setTransform(rotT);
171 cursorPainter.drawImage(offset, moveTypeImage);
177void CameraTool::updateMoveMode(
const QPointF& pos)
180 if (mScribbleArea->isPointerInUse()) {
185 Layer* layer = mEditor->layers()->currentLayer();
186 mCamMoveMode = CameraMoveType::NONE;
187 qreal selectionTolerance = mEditor->select()->selectionTolerance();
189 Q_ASSERT(layer->type() == Layer::CAMERA);
191 if (layer->keyExists(mEditor->currentFrame()))
193 mCamMoveMode = getCameraMoveMode(pos,
195 }
else if (mSettings.showPathEnabled()) {
196 int keyPos = cam->firstKeyFramePosition();
197 while (keyPos <= cam->getMaxKeyFramePosition())
199 mCamMoveMode = getPathMoveMode(cam,
203 if (mCamMoveMode != CameraMoveType::NONE)
205 mDragPathFrame = keyPos;
209 if (keyPos == cam->getNextKeyFramePosition(keyPos)) {
213 keyPos = cam->getNextKeyFramePosition(keyPos);
218void CameraTool::performAction(ActionType actionType)
227 resetTransform(CameraFieldOption::RESET_FIELD);
230 case RESET_ROTATION: {
231 resetTransform(CameraFieldOption::RESET_ROTATION);
234 case RESET_SCALING: {
235 resetTransform(CameraFieldOption::RESET_ROTATION);
238 case RESET_TRANSLATION: {
239 resetTransform(CameraFieldOption::RESET_TRANSLATION);
245void CameraTool::setCameraPathEnabled(
bool enabled)
249 Q_ASSERT(layer->type() == Layer::CAMERA);
250 layer->setShowCameraPath(enabled);
251 toolProperties().setBaseValue(CameraToolProperties::SHOWPATH_ENABLED, enabled);
252 emit cameraPathEnabledChanged(enabled);
257void CameraTool::setPathDotColorType(DotColorType pathDotColor)
260 Q_ASSERT(layer->type() == Layer::CAMERA);
262 layer->updateDotColor(pathDotColor);
263 toolProperties().setBaseValue(CameraToolProperties::PATH_DOTCOLOR_TYPE,
static_cast<int>(pathDotColor));
264 emit pathColorChanged(pathDotColor);
269void CameraTool::resetCameraPath()
272 Q_ASSERT(layer->type() == Layer::CAMERA);
274 layer->setPathMovedAtFrame(mEditor->currentFrame(),
false);
279void CameraTool::resetTransform(CameraFieldOption option)
282 Q_ASSERT(layer->type() == Layer::CAMERA);
284 if (option == CameraFieldOption::RESET_ROTATION || option == CameraFieldOption::RESET_FIELD) {
288 layer->resetCameraAtFrame(option, mEditor->currentFrame());
294 Q_ASSERT(editor()->layers()->currentLayer()->type() == Layer::CAMERA);
298 if (mCamMoveMode == CameraMoveType::ROTATION) {
299 angleDeg = getAngleBetween(pos, mCameraRect.
center()) - mStartAngle;
301 angleDeg = constrainedRotation(angleDeg, mRotationIncrement);
303 mCurrentAngle = angleDeg;
306 transformView(layer, mCamMoveMode, pos, mTransformOffset, -angleDeg, mEditor->currentFrame());
309 mTransformOffset = pos;
312void CameraTool::transformCameraPath(
const QPointF& pos)
314 Q_ASSERT(editor()->layers()->currentLayer()->type() == Layer::CAMERA);
317 layer->updatePathControlPointAtFrame(pos, mDragPathFrame);
321int CameraTool::constrainedRotation(
const qreal rotatedAngle,
const int rotationIncrement)
const
323 return qRound(rotatedAngle / rotationIncrement) * rotationIncrement;
328 updateMoveMode(
event->canvasPos());
329 updateUIAssists(mEditor->layers()->currentLayer());
331 mStartAngle = getAngleBetween(
event->canvasPos(), mCameraRect.
center()) - mCurrentAngle;
332 mTransformOffset =
event->canvasPos();
337 Layer* currentLayer = mEditor->layers()->currentLayer();
338 updateMoveMode(
event->canvasPos());
339 updateUIAssists(currentLayer);
341 if (mScribbleArea->isPointerInUse())
343 if (currentLayer->keyExists(mEditor->currentFrame())) {
344 transformCamera(
event->canvasPos(),
event->modifiers());
346 else if (mCamMoveMode == CameraMoveType::PATH)
348 transformCameraPath(
event->canvasPos());
351 mScribbleArea->updateToolCursor();
352 mEditor->view()->forceUpdateViewTransform();
356void CameraTool::pointerReleaseEvent(
PointerEvent* event)
358 Layer* layer = editor()->layers()->currentLayer();
359 updateMoveMode(
event->canvasPos());
360 updateUIAssists(layer);
362 int frame = mEditor->currentFrame();
363 if (layer->keyExists(frame)) {
364 transformCamera(
event->canvasPos(),
event->modifiers());
365 mEditor->view()->forceUpdateViewTransform();
366 }
else if (mCamMoveMode == CameraMoveType::PATH) {
367 transformCameraPath(
event->canvasPos());
368 mEditor->view()->forceUpdateViewTransform();
373qreal CameraTool::getAngleBetween(
const QPointF& pos1,
const QPointF& pos2)
const
375 return qRadiansToDegrees(MathUtils::getDifferenceAngle(pos1, pos2));
378CameraMoveType CameraTool::getCameraMoveMode(
const QPointF& point, qreal tolerance)
const
382 if (camPoly.count() <= 0) {
return CameraMoveType::NONE; }
386 return CameraMoveType::TOPLEFT;
388 else if (
QLineF(point, camPoly.at(1)).
length() < tolerance)
390 return CameraMoveType::TOPRIGHT;
392 else if (
QLineF(point, camPoly.at(2)).
length() < tolerance)
394 return CameraMoveType::BOTTOMRIGHT;
396 else if (
QLineF(point, camPoly.at(3)).
length() < tolerance)
398 return CameraMoveType::BOTTOMLEFT;
400 else if (
QLineF(point, mRotationHandlePoint).length() < tolerance)
402 return CameraMoveType::ROTATION;
406 return CameraMoveType::CENTER;
408 return CameraMoveType::NONE;
411CameraMoveType CameraTool::getPathMoveMode(
const LayerCamera* layerCamera,
int frameNumber,
const QPointF& point, qreal tolerance)
const
413 int prev = layerCamera->getPreviousKeyFramePosition(frameNumber);
414 int next = layerCamera->getNextKeyFramePosition(frameNumber);
415 if (layerCamera->hasSameTranslation(prev, next))
416 return CameraMoveType::NONE;
418 Camera* camera = layerCamera->getLastCameraAtFrame(frameNumber, 0);
420 if (camera ==
nullptr) {
return CameraMoveType::NONE; }
422 QPointF pathPoint = camera->getPathControlPoint();
424 if (!camera->pathControlPointMoved()) {
425 pathPoint = layerCamera->getCenteredPathPoint(frameNumber);
428 if (
QLineF(pathPoint, point).length() < tolerance) {
429 return CameraMoveType::PATH;
431 return CameraMoveType::NONE;
435QPointF CameraTool::localRotationHandlePoint(
const QPoint& origin,
const QTransform& localT,
const qreal objectScale,
float worldScale)
const
439 qreal topDis = origin.
y() + ((objectScale * origin.
y()) * mRotationHandleOffsetPercentage) * worldScale;
443QPointF CameraTool::worldRotationHandlePoint(
const QPoint& origin,
const QTransform& localT,
const qreal objectScale,
const QTransform& worldT,
float worldScale)
const
445 return worldT.
map(localRotationHandlePoint(origin, localT, objectScale, worldScale));
448void CameraTool::transformView(
LayerCamera* layerCamera, CameraMoveType mode,
const QPointF& point,
const QPointF& offset, qreal angle,
int frameNumber)
const
452 QLineF lineOld(curCenter, point);
453 QLineF lineNew(curCenter, point);
454 Camera* curCam = layerCamera->getCameraAtFrame(frameNumber);
458 case CameraMoveType::CENTER: {
459 curCam->translate(curCam->translation() - (point - offset));
462 case CameraMoveType::TOPLEFT:
463 lineOld.setP2(curPoly.at(0));
464 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
466 case CameraMoveType::TOPRIGHT:
467 lineOld.setP2(curPoly.at(1));
468 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
470 case CameraMoveType::BOTTOMRIGHT:
471 lineOld.setP2(curPoly.at(2));
472 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
474 case CameraMoveType::BOTTOMLEFT:
475 lineOld.setP2(curPoly.at(3));
476 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
478 case CameraMoveType::ROTATION:
479 curCam->rotate(angle);
484 curCam->updateViewTransform();
485 curCam->modification();
490 int frameIndex = mEditor->currentFrame();
491 LayerCamera* cameraLayerBelow =
static_cast<LayerCamera*
>(mEditor->object()->getLayerBelow(mEditor->currentLayerIndex(), Layer::CAMERA));
493 const QTransform& camTransform = cameraLayerBelow->getViewAtFrame(frameIndex);
494 const QRect& cameraRect = cameraLayerBelow->getViewRect();
495 const QTransform& worldT = mEditor->view()->getView();
497 bool isPlaying = mEditor->playback()->isPlaying();
502 int frame = cameraLayerBelow->getPreviousKeyFramePosition(frameIndex);
503 Camera* cam = cameraLayerBelow->getLastCameraAtFrame(qMax(frame, frameIndex), 0);
505 qreal scale = cam->scaling();
506 qreal rotation = cam->rotation();
507 QPointF translation = cam->translation();
508 paintHandles(painter, worldT, camTransform, cameraRect, translation, scale, rotation, !cameraLayerBelow->keyExists(frameIndex));
511 cameraLayerBelow->foreachKeyFrame([&] (
const KeyFrame* keyframe) {
512 paintInterpolations(painter, worldT, frameIndex, cameraLayerBelow,
static_cast<const Camera*
>(keyframe), isPlaying);
517void CameraTool::paintHandles(
QPainter& painter,
const QTransform& worldTransform,
const QTransform& camTransform,
const QRect& cameraRect,
const QPointF translation,
const qreal scale,
const qreal rotation,
bool hollowHandles)
const
532 const QPolygonF& camPolygon = Transform::mapToWorldPolygon(camTransform, worldTransform, cameraRect);
538 scaleT.
translate(translation.
x(), translation.
y());
540 const QPolygonF& nonScaledCamPoly = Transform::mapToWorldPolygon(scaleT, worldTransform, cameraRect);
545 painter.
setPen(mHandleDisabledColor);
548 painter.
setPen(mHandlePen);
551 int handleW = mHandleWidth;
552 int radius = handleW / 2;
554 const QRectF& topRightCorner =
QRectF(camPolygon.at(1).x() - radius,
555 camPolygon.at(1).y() - radius,
559 const QRectF& bottomRightCorner =
QRectF(camPolygon.at(2).x() - radius,
560 camPolygon.at(2).y() - radius,
562 painter.
drawRect(bottomRightCorner);
563 const QRectF& topLeftCorner =
QRectF(camPolygon.at(0).x() - radius,
564 camPolygon.at(0).y() - radius,
568 const QRectF& bottomLeftCorner =
QRectF(camPolygon.at(3).x() - radius,
569 camPolygon.at(3).y() - radius,
575 const QPointF& rotationHandle = worldRotationHandlePoint(cameraRect.
topLeft(), camTransform, scale, worldTransform, mEditor->viewScaleInversed());
577 painter.
drawLine(topCenter, rotationHandle);
580 (rotationHandle.
y() - handleW*0.5),
586void CameraTool::paintInterpolations(
QPainter& painter,
const QTransform& worldTransform,
int currentFrame,
const LayerCamera* cameraLayer,
const Camera* keyframe,
bool isPlaying)
const
588 QColor cameraDotColor = cameraLayer->getDotColor();
589 int frame = keyframe->pos();
590 int nextFrame = cameraLayer->getNextKeyFramePosition(frame);
592 if (cameraLayer->getShowCameraPath() && !cameraLayer->hasSameTranslation(frame, nextFrame)) {
601 const QRect& cameraRect = cameraLayer->getViewRect();
602 const QTransform& cameraTransform = cameraLayer->getViewAtFrame(currentFrame);
603 const QPointF& centerDot = Transform::mapToWorldRect(cameraTransform, worldTransform, cameraRect).
center();
604 painter.
drawEllipse(centerDot, mDotWidth/2., mDotWidth/2.);
607 if (!keyframe->pathControlPointMoved()) {
608 cameraPathPoint = worldTransform.
map(cameraLayer->getCenteredPathPoint(frame + 1));
610 cameraPathPoint = worldTransform.
map(cameraLayer->getPathControlPointAtFrame(frame + 1));
614 QColor color = cameraDotColor;
615 if (currentFrame > frame && currentFrame < nextFrame)
622 for (
int frameInBetween = frame; frameInBetween <= nextFrame ; frameInBetween++)
624 const QTransform& transform = cameraLayer->getViewAtFrame(frameInBetween);
625 const QPointF&
center = Transform::mapToWorldRect(transform, worldTransform, cameraRect).
center();
626 painter.
drawEllipse(center, mDotWidth/2., mDotWidth/2.);
630 int distance = nextFrame - frame;
632 if (distance >= 2 && !isPlaying) {
633 paintControlPoint(painter, worldTransform, cameraLayer, frame, cameraPathPoint, cameraLayer->keyExists(currentFrame));
640void CameraTool::paintControlPoint(
QPainter& painter,
const QTransform& worldTransform,
const LayerCamera* cameraLayer,
const int frameIndex,
const QPointF& pathPoint,
bool hollowHandle)
const
645 const QList<QPointF>& points = cameraLayer->getBezierPointsAtFrame(frameIndex + 1);
649 Q_ASSERT(points.
size() == 3);
664 const QString& pathType = cameraLayer->getInterpolationTextAtFrame(frameIndex);
672 painter.
setPen(mHandleTextColor);
675 painter.
setPen(mHandleDisabledColor);
678 painter.
setPen(mHandlePen);
681 painter.
drawRect(
static_cast<int>(pathPoint.
x() - mHandleWidth/2),
682 static_cast<int>(pathPoint.
y() - mHandleWidth/2),
683 mHandleWidth, mHandleWidth);
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
void updateFrame()
Will call update() and update the canvas Only call this directly If you need the cache to be intact a...
qreal getScaleInversed() const
void setAlphaF(qreal alpha)
qreal length() const const
QPointF pointAt(qreal t) const const
const T & at(int i) const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual bool event(QEvent *e)
void drawEllipse(const QRectF &rectangle)
void drawLine(const QLineF &line)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
void drawRect(const QRectF &rectangle)
void drawText(const QPointF &position, const QString &text)
void setBrush(const QBrush &brush)
void setPen(const QColor &color)
void setColor(const QColor &color)
void fill(const QColor &color)
QPoint toPoint() const const
bool containsPoint(const QPointF &point, Qt::FillRule fillRule) const const
QPoint topLeft() const const
QPointF center() const const
typedef KeyboardModifiers
QTextStream & center(QTextStream &stream)