Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • tool
cameratool.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18
19#include "cameratool.h"
20
21#include "object.h"
22#include "editor.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"
29#include "mathutils.h"
30#include "transform.h"
31#include "camera.h"
32
33#include "scribblearea.h"
34
35#include <QPixmap>
36#include <QPainter>
37#include <QSettings>
38
39CameraTool::CameraTool(QObject* object) : BaseTool(object)
40{
41}
42
43CameraTool::~CameraTool()
44{
45 saveSettings();
46}
47
48void CameraTool::loadSettings()
49{
50 connect(mEditor->layers(), &LayerManager::currentLayerChanged, this, &CameraTool::updateProperties);
51 connect(mEditor, &Editor::objectLoaded, this, &CameraTool::updateProperties);
52
53 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
54
55 QSettings pencilSettings(PENCIL2D, PENCIL2D);
56
57 mPropertyUsed[CameraToolProperties::SHOWPATH_ENABLED] = { Layer::CAMERA };
58 mPropertyUsed[CameraToolProperties::PATH_DOTCOLOR_TYPE] = { Layer::CAMERA };
59
60 QHash<int, PropertyInfo> info;
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;
65
66 toolProperties().insertProperties(info);
67 toolProperties().loadFrom(typeName(), pencilSettings);
68
69 connect(mEditor->preference(), &PreferenceManager::optionChanged, this, &CameraTool::updateSettings);
70
71 mHandleColor = Qt::white;
72 mHandleDisabledColor = Qt::black;
73 mHandleTextColor = QColor(0, 0, 0);
74
75 mHandlePen = QPen();
76 mHandlePen.setColor(QColor(0, 0, 0, 255));
77 mHandlePen.setWidth(2);
78}
79
80void CameraTool::updateUIAssists(const Layer* layer)
81{
82 const LayerCamera* camLayer = static_cast<const LayerCamera*>(layer);
83
84 Q_ASSERT(layer->type() == Layer::CAMERA);
85
86 int currentFrame = mEditor->currentFrame();
87 if (!layer->keyExists(currentFrame)) { return; }
88
89 const QTransform& localCamT = camLayer->getViewAtFrame(currentFrame);
90 const QRect& cameraRect = camLayer->getViewRect();
91
92 mCameraRect = Transform::mapFromLocalRect(localCamT, cameraRect);
93 mCameraPolygon = Transform::mapFromLocalPolygon(localCamT, cameraRect);
94
95 Camera* cam = camLayer->getLastCameraAtFrame(mEditor->currentFrame(), 0);
96 if (cam) {
97 mRotationHandlePoint = localRotationHandlePoint(cameraRect.topLeft(), localCamT, cam->scaling(), mEditor->view()->getScaleInversed());
98 }
99}
100
101void CameraTool::updateProperties()
102{
103 Layer* layer = mEditor->layers()->getLayer(mEditor->currentLayerIndex());
104 if (!layer || layer->type() != Layer::CAMERA) { return; }
105
106 LayerCamera* layerCam = static_cast<LayerCamera*>(layer);
107 toolProperties().setBaseValue(CameraToolProperties::PATH_DOTCOLOR_TYPE, static_cast<int>(layerCam->getDotColorType()));
108 toolProperties().setBaseValue(CameraToolProperties::SHOWPATH_ENABLED, layerCam->getShowCameraPath());
109}
110
111void CameraTool::updateSettings(const SETTING setting)
112{
113 switch (setting)
114 {
115 case SETTING::ROTATION_INCREMENT:
116 {
117 mRotationIncrement = mEditor->preference()->getInt(SETTING::ROTATION_INCREMENT);
118 break;
119 }
120 default:
121 break;
122
123 }
124}
125
126QCursor CameraTool::cursor()
127{
128 QPixmap cursorPixmap = QPixmap(24, 24);
129 cursorPixmap.fill(Qt::transparent);
130 QPainter cursorPainter(&cursorPixmap);
131
132 QImage moveTypeImage;
133 QPoint offset = QPoint(6, 6);
134 switch(mCamMoveMode)
135 {
136 case CameraMoveType::TOPLEFT:
137 case CameraMoveType::BOTTOMRIGHT:
138 {
139 moveTypeImage = QImage("://icons/general/cursor-diagonal-left.svg");
140 break;
141 }
142 case CameraMoveType::TOPRIGHT:
143 case CameraMoveType::BOTTOMLEFT:
144 {
145 moveTypeImage = QImage("://icons/general/cursor-diagonal-right.svg");
146 break;
147 }
148 case CameraMoveType::ROTATION:
149 {
150 moveTypeImage = QImage("://icons/general/cursor-rotate.svg");
151 break;
152 }
153 case CameraMoveType::PATH:
154 case CameraMoveType::CENTER:
155 {
156 moveTypeImage = QImage("://icons/general/cursor-move.svg");
157 break;
158 }
159 default:
160 return Qt::ArrowCursor;
161 }
162
163 QTransform rotT;
164 QPointF center = QPointF(moveTypeImage.size().width()*0.5, moveTypeImage.size().height()*0.5);
165
166 // rotate around center
167 rotT.translate(center.x() + offset.x(), center.y() + offset.y());
168 rotT.rotate(mCurrentAngle);
169 rotT.translate(-center.x() - offset.x(), -center.y() - offset.y());
170 cursorPainter.setTransform(rotT);
171 cursorPainter.drawImage(offset, moveTypeImage);
172 cursorPainter.end();
173
174 return QCursor(cursorPixmap);
175}
176
177void CameraTool::updateMoveMode(const QPointF& pos)
178{
179
180 if (mScribbleArea->isPointerInUse()) {
181 // Pointer in use, keep previous used mode
182 return;
183 }
184
185 Layer* layer = mEditor->layers()->currentLayer();
186 mCamMoveMode = CameraMoveType::NONE;
187 qreal selectionTolerance = mEditor->select()->selectionTolerance();
188
189 Q_ASSERT(layer->type() == Layer::CAMERA);
190 LayerCamera* cam = static_cast<LayerCamera*>(layer);
191 if (layer->keyExists(mEditor->currentFrame()))
192 {
193 mCamMoveMode = getCameraMoveMode(pos,
194 selectionTolerance);
195 } else if (mSettings.showPathEnabled()) {
196 int keyPos = cam->firstKeyFramePosition();
197 while (keyPos <= cam->getMaxKeyFramePosition())
198 {
199 mCamMoveMode = getPathMoveMode(cam,
200 keyPos,
201 pos,
202 selectionTolerance);
203 if (mCamMoveMode != CameraMoveType::NONE)
204 {
205 mDragPathFrame = keyPos;
206 break;
207 }
208
209 if (keyPos == cam->getNextKeyFramePosition(keyPos)) {
210 break;
211 }
212
213 keyPos = cam->getNextKeyFramePosition(keyPos);
214 }
215 }
216}
217
218void CameraTool::performAction(ActionType actionType)
219{
220 switch (actionType)
221 {
222 case RESET_PATH: {
223 resetCameraPath();
224 break;
225 }
226 case RESET_FIELD: {
227 resetTransform(CameraFieldOption::RESET_FIELD);
228 break;
229 }
230 case RESET_ROTATION: {
231 resetTransform(CameraFieldOption::RESET_ROTATION);
232 break;
233 }
234 case RESET_SCALING: {
235 resetTransform(CameraFieldOption::RESET_ROTATION);
236 break;
237 }
238 case RESET_TRANSLATION: {
239 resetTransform(CameraFieldOption::RESET_TRANSLATION);
240 break;
241 }
242 }
243}
244
245void CameraTool::setCameraPathEnabled(bool enabled)
246{
247 LayerCamera* layer = static_cast<LayerCamera*>(editor()->layers()->currentLayer());
248
249 Q_ASSERT(layer->type() == Layer::CAMERA);
250 layer->setShowCameraPath(enabled);
251 toolProperties().setBaseValue(CameraToolProperties::SHOWPATH_ENABLED, enabled);
252 emit cameraPathEnabledChanged(enabled);
253
254 emit mEditor->frameModified(mEditor->currentFrame());
255}
256
257void CameraTool::setPathDotColorType(DotColorType pathDotColor)
258{
259 LayerCamera* layer = static_cast<LayerCamera*>(editor()->layers()->currentLayer());
260 Q_ASSERT(layer->type() == Layer::CAMERA);
261
262 layer->updateDotColor(pathDotColor);
263 toolProperties().setBaseValue(CameraToolProperties::PATH_DOTCOLOR_TYPE, static_cast<int>(pathDotColor));
264 emit pathColorChanged(pathDotColor);
265
266 emit mEditor->frameModified(mEditor->currentFrame());
267}
268
269void CameraTool::resetCameraPath()
270{
271 LayerCamera* layer = static_cast<LayerCamera*>(editor()->layers()->currentLayer());
272 Q_ASSERT(layer->type() == Layer::CAMERA);
273
274 layer->setPathMovedAtFrame(mEditor->currentFrame(), false);
275
276 emit mEditor->frameModified(mEditor->currentFrame());
277}
278
279void CameraTool::resetTransform(CameraFieldOption option)
280{
281 LayerCamera* layer = static_cast<LayerCamera*>(editor()->layers()->currentLayer());
282 Q_ASSERT(layer->type() == Layer::CAMERA);
283
284 if (option == CameraFieldOption::RESET_ROTATION || option == CameraFieldOption::RESET_FIELD) {
285 mCurrentAngle = 0;
286 }
287
288 layer->resetCameraAtFrame(option, mEditor->currentFrame());
289 emit mEditor->frameModified(mEditor->currentFrame());
290}
291
292void CameraTool::transformCamera(const QPointF& pos, Qt::KeyboardModifiers keyMod)
293{
294 Q_ASSERT(editor()->layers()->currentLayer()->type() == Layer::CAMERA);
295 LayerCamera* layer = static_cast<LayerCamera*>(editor()->layers()->currentLayer());
296
297 qreal angleDeg = 0;
298 if (mCamMoveMode == CameraMoveType::ROTATION) {
299 angleDeg = getAngleBetween(pos, mCameraRect.center()) - mStartAngle;
300 if (keyMod == Qt::ShiftModifier) {
301 angleDeg = constrainedRotation(angleDeg, mRotationIncrement);
302 }
303 mCurrentAngle = angleDeg;
304 }
305
306 transformView(layer, mCamMoveMode, pos, mTransformOffset, -angleDeg, mEditor->currentFrame());
307
308 emit mEditor->frameModified(mEditor->currentFrame());
309 mTransformOffset = pos;
310}
311
312void CameraTool::transformCameraPath(const QPointF& pos)
313{
314 Q_ASSERT(editor()->layers()->currentLayer()->type() == Layer::CAMERA);
315 LayerCamera* layer = static_cast<LayerCamera*>(editor()->layers()->currentLayer());
316
317 layer->updatePathControlPointAtFrame(pos, mDragPathFrame);
318 emit mEditor->frameModified(mEditor->currentFrame());
319}
320
321int CameraTool::constrainedRotation(const qreal rotatedAngle, const int rotationIncrement) const
322{
323 return qRound(rotatedAngle / rotationIncrement) * rotationIncrement;
324}
325
326void CameraTool::pointerPressEvent(PointerEvent* event)
327{
328 updateMoveMode(event->canvasPos());
329 updateUIAssists(mEditor->layers()->currentLayer());
330
331 mStartAngle = getAngleBetween(event->canvasPos(), mCameraRect.center()) - mCurrentAngle;
332 mTransformOffset = event->canvasPos();
333}
334
335void CameraTool::pointerMoveEvent(PointerEvent* event)
336{
337 Layer* currentLayer = mEditor->layers()->currentLayer();
338 updateMoveMode(event->canvasPos());
339 updateUIAssists(currentLayer);
340
341 if (mScribbleArea->isPointerInUse()) // the user is also pressing the mouse (dragging)
342 {
343 if (currentLayer->keyExists(mEditor->currentFrame())) {
344 transformCamera(event->canvasPos(), event->modifiers());
345 }
346 else if (mCamMoveMode == CameraMoveType::PATH)
347 {
348 transformCameraPath(event->canvasPos());
349 }
350 }
351 mScribbleArea->updateToolCursor();
352 mEditor->view()->forceUpdateViewTransform();
353 mEditor->updateFrame();
354}
355
356void CameraTool::pointerReleaseEvent(PointerEvent* event)
357{
358 Layer* layer = editor()->layers()->currentLayer();
359 updateMoveMode(event->canvasPos());
360 updateUIAssists(layer);
361
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();
369 }
370 emit mEditor->frameModified(frame);
371}
372
373qreal CameraTool::getAngleBetween(const QPointF& pos1, const QPointF& pos2) const
374{
375 return qRadiansToDegrees(MathUtils::getDifferenceAngle(pos1, pos2));
376}
377
378CameraMoveType CameraTool::getCameraMoveMode(const QPointF& point, qreal tolerance) const
379{
380 QPolygonF camPoly = mCameraPolygon;
381
382 if (camPoly.count() <= 0) { return CameraMoveType::NONE; }
383
384 if (QLineF(point, camPoly.at(0)).length() < tolerance)
385 {
386 return CameraMoveType::TOPLEFT;
387 }
388 else if (QLineF(point, camPoly.at(1)).length() < tolerance)
389 {
390 return CameraMoveType::TOPRIGHT;
391 }
392 else if (QLineF(point, camPoly.at(2)).length() < tolerance)
393 {
394 return CameraMoveType::BOTTOMRIGHT;
395 }
396 else if (QLineF(point, camPoly.at(3)).length() < tolerance)
397 {
398 return CameraMoveType::BOTTOMLEFT;
399 }
400 else if (QLineF(point, mRotationHandlePoint).length() < tolerance)
401 {
402 return CameraMoveType::ROTATION;
403 }
404 else if (camPoly.containsPoint(point.toPoint(), Qt::FillRule::OddEvenFill))
405 {
406 return CameraMoveType::CENTER;
407 }
408 return CameraMoveType::NONE;
409}
410
411CameraMoveType CameraTool::getPathMoveMode(const LayerCamera* layerCamera, int frameNumber, const QPointF& point, qreal tolerance) const
412{
413 int prev = layerCamera->getPreviousKeyFramePosition(frameNumber);
414 int next = layerCamera->getNextKeyFramePosition(frameNumber);
415 if (layerCamera->hasSameTranslation(prev, next))
416 return CameraMoveType::NONE;
417
418 Camera* camera = layerCamera->getLastCameraAtFrame(frameNumber, 0);
419
420 if (camera == nullptr) { return CameraMoveType::NONE; }
421
422 QPointF pathPoint = camera->getPathControlPoint();
423
424 if (!camera->pathControlPointMoved()) {
425 pathPoint = layerCamera->getCenteredPathPoint(frameNumber);
426 }
427
428 if (QLineF(pathPoint, point).length() < tolerance) {
429 return CameraMoveType::PATH;
430 }
431 return CameraMoveType::NONE;
432}
433
434
435QPointF CameraTool::localRotationHandlePoint(const QPoint& origin, const QTransform& localT, const qreal objectScale, float worldScale) const
436{
437 // Calculate the perceived distance from the frame to the handle
438 // so that it looks like the handle is always x pixels above the origin
439 qreal topDis = origin.y() + ((objectScale * origin.y()) * mRotationHandleOffsetPercentage) * worldScale;
440 return QPointF(localT.inverted().map(QPointF(0, topDis)));
441}
442
443QPointF CameraTool::worldRotationHandlePoint(const QPoint& origin, const QTransform& localT, const qreal objectScale, const QTransform& worldT, float worldScale) const
444{
445 return worldT.map(localRotationHandlePoint(origin, localT, objectScale, worldScale));
446}
447
448void CameraTool::transformView(LayerCamera* layerCamera, CameraMoveType mode, const QPointF& point, const QPointF& offset, qreal angle, int frameNumber) const
449{
450 QPolygonF curPoly = mCameraPolygon;
451 QPointF curCenter = QLineF(curPoly.at(0), curPoly.at(2)).pointAt(0.5).toPoint();
452 QLineF lineOld(curCenter, point);
453 QLineF lineNew(curCenter, point);
454 Camera* curCam = layerCamera->getCameraAtFrame(frameNumber);
455
456 switch (mode)
457 {
458 case CameraMoveType::CENTER: {
459 curCam->translate(curCam->translation() - (point - offset));
460 break;
461 }
462 case CameraMoveType::TOPLEFT:
463 lineOld.setP2(curPoly.at(0));
464 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
465 break;
466 case CameraMoveType::TOPRIGHT:
467 lineOld.setP2(curPoly.at(1));
468 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
469 break;
470 case CameraMoveType::BOTTOMRIGHT:
471 lineOld.setP2(curPoly.at(2));
472 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
473 break;
474 case CameraMoveType::BOTTOMLEFT:
475 lineOld.setP2(curPoly.at(3));
476 curCam->scale(curCam->scaling() * (lineOld.length() / lineNew.length()));
477 break;
478 case CameraMoveType::ROTATION:
479 curCam->rotate(angle);
480 break;
481 default:
482 break;
483 }
484 curCam->updateViewTransform();
485 curCam->modification();
486}
487
488void CameraTool::paint(QPainter& painter, const QRect&)
489{
490 int frameIndex = mEditor->currentFrame();
491 LayerCamera* cameraLayerBelow = static_cast<LayerCamera*>(mEditor->object()->getLayerBelow(mEditor->currentLayerIndex(), Layer::CAMERA));
492
493 const QTransform& camTransform = cameraLayerBelow->getViewAtFrame(frameIndex);
494 const QRect& cameraRect = cameraLayerBelow->getViewRect();
495 const QTransform& worldT = mEditor->view()->getView();
496
497 bool isPlaying = mEditor->playback()->isPlaying();
498
499
500 // Show handles while we're on a camera layer and not doing playback
501 if (!isPlaying) {
502 int frame = cameraLayerBelow->getPreviousKeyFramePosition(frameIndex);
503 Camera* cam = cameraLayerBelow->getLastCameraAtFrame(qMax(frame, frameIndex), 0);
504 Q_ASSERT(cam);
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));
509 }
510
511 cameraLayerBelow->foreachKeyFrame([&] (const KeyFrame* keyframe) {
512 paintInterpolations(painter, worldT, frameIndex, cameraLayerBelow, static_cast<const Camera*>(keyframe), isPlaying);
513 });
514}
515
516
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
518{
519 painter.save();
520
521 // if the current view is narrower than the camera field
522 // Indicates that the quality of the output will be degraded
523 if (scale > 1)
524 {
525 painter.setPen(Qt::red);
526 }
527 else
528 {
529 painter.setPen(QColor(0, 0, 0, 255));
530 }
531
532 const QPolygonF& camPolygon = Transform::mapToWorldPolygon(camTransform, worldTransform, cameraRect);
533 painter.drawPolygon(camPolygon);
534
535 QTransform scaleT;
536 scaleT.scale(1, 1);
537 scaleT.rotate(rotation);
538 scaleT.translate(translation.x(), translation.y());
539
540 const QPolygonF& nonScaledCamPoly = Transform::mapToWorldPolygon(scaleT, worldTransform, cameraRect);
541 painter.drawPolygon(nonScaledCamPoly);
542 painter.drawText(nonScaledCamPoly[0]-QPoint(0, 2), "100%");
543
544 if (hollowHandles) {
545 painter.setPen(mHandleDisabledColor);
546 painter.setBrush(Qt::gray);
547 } else {
548 painter.setPen(mHandlePen);
549 painter.setBrush(mHandleColor);
550 }
551 int handleW = mHandleWidth;
552 int radius = handleW / 2;
553
554 const QRectF& topRightCorner = QRectF(camPolygon.at(1).x() - radius,
555 camPolygon.at(1).y() - radius,
556 handleW, handleW);
557 painter.drawRect(topRightCorner);
558
559 const QRectF& bottomRightCorner = QRectF(camPolygon.at(2).x() - radius,
560 camPolygon.at(2).y() - radius,
561 handleW, handleW);
562 painter.drawRect(bottomRightCorner);
563 const QRectF& topLeftCorner = QRectF(camPolygon.at(0).x() - radius,
564 camPolygon.at(0).y() - radius,
565 handleW, handleW);
566 painter.drawRect(topLeftCorner);
567
568 const QRectF& bottomLeftCorner = QRectF(camPolygon.at(3).x() - radius,
569 camPolygon.at(3).y() - radius,
570 handleW, handleW);
571 painter.drawRect(bottomLeftCorner);
572
573 // Paint rotation handle
574 const QPointF& topCenter = QLineF(camPolygon.at(0), camPolygon.at(1)).pointAt(.5);
575 const QPointF& rotationHandle = worldRotationHandlePoint(cameraRect.topLeft(), camTransform, scale, worldTransform, mEditor->viewScaleInversed());
576
577 painter.drawLine(topCenter, rotationHandle);
578
579 painter.drawEllipse(QRectF((rotationHandle.x() - handleW*0.5),
580 (rotationHandle.y() - handleW*0.5),
581 handleW, handleW));
582
583 painter.restore();
584}
585
586void CameraTool::paintInterpolations(QPainter& painter, const QTransform& worldTransform, int currentFrame, const LayerCamera* cameraLayer, const Camera* keyframe, bool isPlaying) const
587{
588 QColor cameraDotColor = cameraLayer->getDotColor();
589 int frame = keyframe->pos();
590 int nextFrame = cameraLayer->getNextKeyFramePosition(frame);
591
592 if (cameraLayer->getShowCameraPath() && !cameraLayer->hasSameTranslation(frame, nextFrame)) {
593 painter.save();
594
595 painter.setBrush(cameraDotColor);
596
597 // Highlight current dot
598 QPen pen(Qt::black);
599 pen.setWidth(2);
600 painter.setPen(pen);
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.);
605
606 QPointF cameraPathPoint;
607 if (!keyframe->pathControlPointMoved()) {
608 cameraPathPoint = worldTransform.map(cameraLayer->getCenteredPathPoint(frame + 1));
609 } else {
610 cameraPathPoint = worldTransform.map(cameraLayer->getPathControlPointAtFrame(frame + 1));
611 }
612
613 painter.save();
614 QColor color = cameraDotColor;
615 if (currentFrame > frame && currentFrame < nextFrame)
616 color.setAlphaF(.5f);
617 else
618 color.setAlphaF(.2f);
619 painter.setPen(Qt::black);
620 painter.setBrush(color);
621
622 for (int frameInBetween = frame; frameInBetween <= nextFrame ; frameInBetween++)
623 {
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.);
627 }
628 painter.restore();
629
630 int distance = nextFrame - frame;
631 // It makes no sense to paint the path when there's no interpolation.
632 if (distance >= 2 && !isPlaying) {
633 paintControlPoint(painter, worldTransform, cameraLayer, frame, cameraPathPoint, cameraLayer->keyExists(currentFrame));
634 }
635
636 painter.restore();
637 }
638}
639
640void CameraTool::paintControlPoint(QPainter& painter, const QTransform& worldTransform, const LayerCamera* cameraLayer, const int frameIndex, const QPointF& pathPoint, bool hollowHandle) const
641{
642 painter.save();
643
644 // if active path, draw bezier help lines for active path
645 const QList<QPointF>& points = cameraLayer->getBezierPointsAtFrame(frameIndex + 1);
646
647 if (!points.empty())
648 {
649 Q_ASSERT(points.size() == 3);
650 QPointF p0 = worldTransform.map(points.at(0));
651 QPointF p1 = worldTransform.map(points.at(1));
652 QPointF p2 = worldTransform.map(points.at(2));
653
654 painter.save();
655 QPen pen (Qt::black, 0.5, Qt::PenStyle::DashLine);
656 painter.setPen(pen);
657 painter.drawLine(p0, p1);
658 painter.drawLine(p1, p2);
659 painter.restore();
660 }
661
662 // draw movemode in text
663 painter.setPen(Qt::black);
664 const QString& pathType = cameraLayer->getInterpolationTextAtFrame(frameIndex);
665
666 // Space text according to path point so it doesn't overlap
667 painter.drawText(pathPoint - QPoint(0, mHandleWidth), pathType);
668 painter.restore();
669
670 // if active path, draw move handle
671 painter.save();
672 painter.setPen(mHandleTextColor);
673
674 if (hollowHandle) {
675 painter.setPen(mHandleDisabledColor);
676 painter.setBrush(Qt::gray);
677 } else {
678 painter.setPen(mHandlePen);
679 painter.setBrush(mHandleColor);
680 }
681 painter.drawRect(static_cast<int>(pathPoint.x() - mHandleWidth/2),
682 static_cast<int>(pathPoint.y() - mHandleWidth/2),
683 mHandleWidth, mHandleWidth);
684 painter.restore();
685}
BaseTool
Definition: basetool.h:47
Camera
Definition: camera.h:25
Editor::frameModified
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
Editor::updateFrame
void updateFrame()
Will call update() and update the canvas Only call this directly If you need the cache to be intact a...
Definition: editor.cpp:852
KeyFrame
Definition: keyframe.h:30
LayerCamera
Definition: layercamera.h:30
Layer
Definition: layer.h:33
PointerEvent
Definition: pointerevent.h:8
ViewManager::getScaleInversed
qreal getScaleInversed() const
Definition: viewmanager.cpp:111
QColor
QColor::setAlphaF
void setAlphaF(qreal alpha)
QCursor
QHash
QImage
QImage::size
QSize size() const const
QLineF
QLineF::length
qreal length() const const
QLineF::pointAt
QPointF pointAt(qreal t) const const
QList
QList::at
const T & at(int i) const const
QList::empty
bool empty() const const
QList::size
int size() const const
QObject
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::event
virtual bool event(QEvent *e)
QPainter
QPainter::drawEllipse
void drawEllipse(const QRectF &rectangle)
QPainter::drawLine
void drawLine(const QLineF &line)
QPainter::drawPolygon
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QPainter::drawRect
void drawRect(const QRectF &rectangle)
QPainter::drawText
void drawText(const QPointF &position, const QString &text)
QPainter::restore
void restore()
QPainter::save
void save()
QPainter::setBrush
void setBrush(const QBrush &brush)
QPainter::setPen
void setPen(const QColor &color)
QPen
QPen::setColor
void setColor(const QColor &color)
QPen::setWidth
void setWidth(int width)
QPixmap
QPixmap::fill
void fill(const QColor &color)
QPoint
QPoint::x
int x() const const
QPoint::y
int y() const const
QPointF
QPointF::toPoint
QPoint toPoint() const const
QPointF::x
qreal x() const const
QPointF::y
qreal y() const const
QPolygonF
QPolygonF::containsPoint
bool containsPoint(const QPointF &point, Qt::FillRule fillRule) const const
QRect
QRect::topLeft
QPoint topLeft() const const
QRectF
QRectF::center
QPointF center() const const
QSettings
QSize::height
int height() const const
QSize::width
int width() const const
QString
Qt::ArrowCursor
ArrowCursor
Qt::white
white
Qt::KeyboardModifiers
typedef KeyboardModifiers
Qt::center
QTextStream & center(QTextStream &stream)
QTransform
QTransform::inverted
QTransform inverted(bool *invertible) const const
QTransform::map
QPoint map(const QPoint &point) const const
QTransform::rotate
QTransform & rotate(qreal angle, Qt::Axis axis)
QTransform::scale
QTransform & scale(qreal sx, qreal sy)
QTransform::translate
QTransform & translate(qreal dx, qreal dy)
Generated on Thu Jan 29 2026 05:10:58 for Pencil2D by doxygen 1.9.6 based on revision 2c971b937d0608b05aa496b0f7e9aebcddf8e7fc