Fixed crash in iTween network
[yamf.git] / yamf / drawing / tool / itween.cpp
blobf581a0157de6cba39055a4a17adb4e9256ad731a
1 /**************************************************************************
2 * Copyright (C) 2005 by David Cuadrado *
3 * krawek@toonka.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "itween.h"
23 #include <QGraphicsView>
24 #include <QGraphicsSceneMouseEvent>
25 #include <QSet>
26 #include <QHBoxLayout>
27 #include <QRadioButton>
28 #include <QToolBar>
29 #include <QButtonGroup>
30 #include <QXmlStreamWriter>
32 #include <dcore/global.h>
33 #include <dcore/debug.h>
35 #include <dgui/iconloader.h>
36 #include <dgui/action.h>
38 #include <dgraphics/pathhelper.h>
39 #include <dgraphics/algorithm.h>
41 #include "yamf/model/object.h"
42 #include "yamf/model/frame.h"
43 #include "yamf/model/scene.h"
45 #include "yamf/drawing/paintarea.h"
46 #include "yamf/drawing/brushmanager.h"
47 #include "yamf/drawing/photogram.h"
48 #include "yamf/item/path.h"
49 #include "yamf/item/proxy.h"
50 #include "yamf/item/line.h"
51 #include "yamf/item/converter.h"
52 #include "yamf/item/tweener.h"
53 #include "yamf/item/serializer.h"
55 #include "yamf/model/command/addtweening.h"
56 #include "private/contoureditor.h"
58 #include <dgraphics/itemtransformer.h>
60 #include "commonalgorithms.h"
62 #define DEBUG 0
64 namespace YAMF {
65 namespace Drawing {
66 class BrushManager;
68 namespace Tool {
70 struct ITween::Private {
71 Private() : target(0), nodeEditor(0), transformer(0), pathItem(0), paintArea(0) {}
72 enum Type {
73 PathMotion = 0x01,
74 Fill,
75 Transform,
76 NodeMotion
79 DGui::Action * action;
80 QWidget *configWidget;
81 QButtonGroup group;
83 Model::Object *target;
85 QPointF currentPos;
86 QPointF absItemPos;
89 Drawing::Tool::Private::ContourEditor *nodeEditor;
90 DGraphics::ItemTransformer* transformer;
92 QGraphicsPathItem *pathItem;
93 QPainterPath path;
94 QPointF firstPos;
95 QPointF lastPos;
97 Drawing::PaintArea *paintArea;
99 Item::Tweener *createObjectTweener(int frames = 1);
100 void saveChanges();
101 void releaseObject();
104 Item::Tweener *ITween::Private::createObjectTweener(int frames)
106 Item::Tweener *tweener = target->tweener();
108 if( tweener == 0 )
110 tweener = new Item::Tweener(1, target);
112 tweener->setPosAt(0, target->item()->scenePos());
113 // tweener->setRotationAt(0, 0);
114 // tweener->setScaleAt(0, 1.0, 1.0);
116 // if( item()->type() != QAbstractGraphicsShapeItem::Type ) return;
117 if( QAbstractGraphicsShapeItem *shape = dynamic_cast<QAbstractGraphicsShapeItem *>(target->item()) )
119 tweener->setBrushAt(0, shape->brush());
120 tweener->setPenAt(0, shape->pen());
123 if( QGraphicsPathItem *path = dynamic_cast<QGraphicsPathItem*>(target->item()) )
125 tweener->setPathAt(0, path->path());
128 target->setTweener(tweener);
131 frames = qMax(frames, tweener->frames());
132 tweener->setFrames(frames);
134 return tweener;
137 void ITween::Private::saveChanges()
139 if( target )
141 int objectPos = target->frame()->visualIndex();
142 int framePos = paintArea->currentFrame()->visualIndex();
143 int step = qAbs(framePos - objectPos);
145 int frames = step;
146 if( target->tweener() )
148 frames = qMax(step, target->tweener()->frames());
151 Item::Tweener *tweener = createObjectTweener(frames);
153 switch(group.checkedId())
155 case Private::Transform:
157 if( currentPos != target->item()->pos() ) // The object was changed
159 tweener->setPosAt(step, target->item()->pos());
162 if( transformer )
164 if( transformer->isModified() )
166 switch(transformer->operation() )
168 case DGraphics::ItemTransformer::Scale:
170 tweener->setScaleAt(step, transformer->currentVerticalScale(), transformer->currentHorizontalScale() );
172 tweener->setPosAt(step, transformer->currentPosition() );
174 break;
175 case DGraphics::ItemTransformer::Rotate:
177 QPointF pos = transformer->currentPosition();
178 tweener->setRotationAt(step, transformer->currentRotation());
179 tweener->setPosAt(step, pos );
181 break;
184 // transformer->setItem(target->item()); // To reset
188 break;
189 case Private::PathMotion:
191 if( path.elementCount() < 3 ) break;
193 QRectF br = target->item()->sceneBoundingRect();
194 QPointF origin = absItemPos;
196 QTransform transform;
197 transform.translate(-origin.x(), -origin.y());
199 double factor = 1.0/step;
201 QPainterPath npath = transform.map(path);
203 int n = 0;
204 for(double percent = 0; percent < 1; percent += factor )
206 QPointF pos = npath.pointAtPercent(percent);
207 tweener->setPosAt(n, pos);
209 n++;
212 #if DEBUG
213 paintArea->currentFrame()->addItem(pathItem);
214 #endif
216 break;
217 case Private::NodeMotion:
219 if( QGraphicsPathItem *item = dynamic_cast<QGraphicsPathItem *>(target->item()) )
221 tweener->setPathAt(step, item->path());
224 break;
225 case Private::Fill:
227 if( QAbstractGraphicsShapeItem *item = dynamic_cast<QAbstractGraphicsShapeItem *>(target->item()) )
229 tweener->setBrushAt(step, item->brush());
230 tweener->setPenAt(step, item->pen());
233 break;
234 default:
237 break;
240 paintArea->addCommand(new YAMF::Command::AddTweening(target, tweener));
241 tweener->setStep(step);
245 void ITween::Private::releaseObject()
247 if( target )
249 if( transformer )
251 transformer->setVisible(false);
252 foreach(QGraphicsItem *node, transformer->nodes())
254 paintArea->setAlwaysVisible(node, false);
257 if(pathItem)
259 paintArea->setAlwaysVisible(pathItem, false);
260 delete pathItem;
261 pathItem = 0;
265 if( nodeEditor )
267 foreach(QGraphicsItem *node, nodeEditor->nodes())
269 paintArea->setAlwaysVisible(node, false);
272 delete nodeEditor;
273 nodeEditor = 0;
277 target->scene()->setAlwaysVisible(target, false);
278 if( target->tweener() )
280 target->tweener()->setFrames(target->tweener()->frames());
282 target = 0;
286 ITween::ITween(QObject *parent) : AbstractTool(parent), d(new Private)
288 d->action = new DGui::Action( DGui::IconLoader::self()->load("itween.svg"), tr("iTween"), this);
290 // d->action->setShortcut( QKeySequence(tr("Ctrl+W")) );
292 d->configWidget = new QWidget;
293 QHBoxLayout *layout = new QHBoxLayout(d->configWidget);
294 layout->setMargin(0);
295 layout->setSpacing(2);
298 QRadioButton *transform = new QRadioButton(tr("Transform"));
299 transform->setChecked(true);
300 layout->addWidget(transform);
302 QRadioButton *pathMotion = new QRadioButton(tr("Path motion"));
303 layout->addWidget(pathMotion);
305 QRadioButton *fill = new QRadioButton(tr("Fill"));
306 layout->addWidget(fill);
308 QRadioButton *nodeMotion = new QRadioButton(tr("Node motion"));
309 layout->addWidget(nodeMotion);
311 d->group.addButton(pathMotion, Private::PathMotion);
312 d->group.addButton(fill, Private::Fill);
313 d->group.addButton(transform, Private::Transform);
314 d->group.addButton(nodeMotion, Private::NodeMotion);
316 connect(&d->group, SIGNAL(buttonClicked(int)), this, SLOT(onChangeTool(int)));
319 ITween::~ITween()
321 if( d->nodeEditor )
323 foreach(QGraphicsItem *node, d->nodeEditor->nodes())
325 paintArea()->setAlwaysVisible(node, false);
327 delete d->nodeEditor;
330 if( d->transformer )
332 foreach(QGraphicsItem *node, d->transformer->nodes())
334 paintArea()->setAlwaysVisible(node, false);
336 delete d->transformer;
339 if(d->pathItem)
341 paintArea()->setAlwaysVisible(d->pathItem, false);
342 delete d->pathItem;
346 delete d;
349 void ITween::init(Photogram *photogram)
351 foreach(QGraphicsItem *item, photogram->items() )
353 item->setFlag(QGraphicsItem::ItemIsSelectable, true);
354 item->setFlag(QGraphicsItem::ItemIsFocusable, true);
355 item->setFlag(QGraphicsItem::ItemIsMovable, true);
359 QString ITween::id() const
361 return "itween";
364 void ITween::press(const QGraphicsSceneMouseEvent *input)
366 PaintArea *paintArea = this->paintArea(); // FIXME
368 d->firstPos = input->scenePos();
369 d->lastPos = input->scenePos();
371 d->paintArea = paintArea;
372 Photogram *photogram = paintArea->photogram();
374 if( d->target )
376 if( !d->target->item()->isSelected() )
378 d->saveChanges();
379 d->releaseObject();
383 QList<QGraphicsItem *> selected = photogram->selectedItems();
384 if( selected.isEmpty() ) return;
386 Model::Object *object = paintArea->currentFrame()->graphic(selected.first());
388 if( object == 0 )
390 // If the object has a tweener, it may be not in the current frame.
391 object = paintArea->currentScene()->tweeningObject(selected.first());
393 if( !object )
394 return;
397 if( d->target != object )
399 if( d->target )
401 d->target->scene()->setAlwaysVisible(d->target, false);
404 d->target = object;
405 d->target->scene()->setAlwaysVisible(d->target, true);
407 d->createObjectTweener();
409 d->currentPos = d->target->item()->pos();
410 d->absItemPos = d->target->item()->boundingRect().topLeft();
412 int objectPos = d->target->frame()->visualIndex();
413 int framePos = paintArea->currentFrame()->visualIndex();
414 int step = qAbs(framePos - objectPos);
416 int frames = step;
417 if( d->target->tweener() )
419 frames = qMax(step, d->target->tweener()->frames());
422 if(input->buttons() == Qt::LeftButton)
424 switch(d->group.checkedId())
426 case Private::Transform:
428 QList<DGraphics::ItemTransformer *> transformers;
430 if(d->transformer)
432 transformers << d->transformer;
435 CommonAlgorithms::createTransfromers(transformers, QList<QGraphicsItem*>() << d->target->item(), paintArea);
437 if(!transformers.isEmpty())
439 d->transformer = transformers.first();
441 #if 0
442 else if( input->modifiers() != Qt::ControlModifier )
444 if(photogram->mouseGrabberItem() == d->transformer->item())
446 switch(d->transformer->operation())
448 case DGraphics::ItemTransformer::Scale:
450 d->transformer->setOperation(DGraphics::ItemTransformer::Rotate);
452 break;
453 case DGraphics::ItemTransformer::Rotate:
455 d->transformer->setOperation(DGraphics::ItemTransformer::Scale);
457 break;
461 #endif
463 break;
464 case Private::PathMotion:
466 d->path = QPainterPath();
467 d->path.moveTo(d->firstPos);
469 if(d->pathItem)
471 paintArea->setAlwaysVisible(d->pathItem, false);
472 delete d->pathItem;
473 d->pathItem = 0;
476 if( d->nodeEditor )
478 foreach(QGraphicsItem *node, d->nodeEditor->nodes())
480 paintArea->setAlwaysVisible(node, false);
483 delete d->nodeEditor;
484 d->nodeEditor = 0;
487 d->pathItem = new QGraphicsPathItem;
488 d->pathItem->setZValue(1000);
491 QColor c = Qt::lightGray;
492 c.setAlpha(120);
494 QPen pen(c, 1);
495 pen.setStyle(Qt::DashDotDotLine);
496 d->pathItem->setPen(pen);
498 photogram->addItem( d->pathItem );
500 break;
501 default:
503 // qFatal("Not implemented!");
505 break;
510 void ITween::move(const QGraphicsSceneMouseEvent *input)
512 if( d->target == 0 ) return;
514 PaintArea *paintArea = this->paintArea();
515 if(input->buttons() == Qt::LeftButton)
517 if( d->group.checkedId() == Private::PathMotion )
519 d->lastPos = paintArea->mapToScene(paintArea->viewport()->mapFromGlobal(QCursor::pos()));;
520 d->path.lineTo( d->lastPos );
522 d->pathItem->setPath(d->path);
523 paintArea->brushManager()->map(d->pathItem);
528 void ITween::release(const QGraphicsSceneMouseEvent *input)
530 if( d->target == 0 ) return;
532 PaintArea *paintArea = this->paintArea();
533 switch(d->group.checkedId())
535 case Private::Transform:
537 d->saveChanges();
538 if(d->transformer)
540 d->transformer->show();
542 else
544 QList<DGraphics::ItemTransformer *> transformers;
545 CommonAlgorithms::createTransfromers(transformers, QList<QGraphicsItem*>() << d->target->item(), paintArea);
547 if(!transformers.isEmpty())
549 d->transformer = transformers.first();
554 break;
555 case Private::PathMotion:
557 if(!d->path.isEmpty())
560 double smoothness = 1.0;
562 QPolygonF polygon = d->path.toFillPolygon();
563 if(!polygon.isEmpty() && polygon.isClosed())
564 polygon.pop_back();
566 d->path = DGraphics::Algorithm::smooth(polygon, smoothness);
567 d->pathItem->setPath(d->path);
569 paintArea->setAlwaysVisible(d->pathItem, true);
573 d->saveChanges();
574 if( d->nodeEditor )
576 foreach(QGraphicsItem *node, d->nodeEditor->nodes())
578 paintArea->setAlwaysVisible(node, false);
580 delete d->nodeEditor;
582 d->nodeEditor = new Drawing::Tool::Private::ContourEditor(d->pathItem, paintArea->photogram());
584 d->nodeEditor->show();
585 foreach(QGraphicsItem *node, d->nodeEditor->nodes())
587 paintArea->setAlwaysVisible(node, true);
591 break;
592 case Private::NodeMotion:
594 QList<Drawing::Tool::Private::ContourEditor *> editors;
595 if( d->nodeEditor )
597 editors << d->nodeEditor;
600 CommonAlgorithms::createEditors(editors, QList<QGraphicsItem*>() << d->target->item(), paintArea);
602 if(!editors.isEmpty())
604 d->nodeEditor = editors.first();
605 d->nodeEditor->show();
608 d->saveChanges();
610 break;
611 case Private::Fill:
613 if( QAbstractGraphicsShapeItem *item = dynamic_cast<QAbstractGraphicsShapeItem *>(d->target->item()) )
615 paintArea->brushManager()->apply(item);
616 d->saveChanges();
618 else
620 d->releaseObject();
621 return;
624 break;
628 DGui::Action * ITween::action() const
630 return d->action;
633 int ITween::type() const
635 return AbstractTool::Brush;
638 void ITween::aboutToChangeTool()
640 PaintArea *paintArea = this->paintArea();
641 d->saveChanges();
642 d->releaseObject();
644 if(d->pathItem)
646 paintArea->setAlwaysVisible(d->pathItem, false);
647 delete d->pathItem;
648 d->pathItem = 0;
651 if( d->nodeEditor )
653 foreach(QGraphicsItem *node, d->nodeEditor->nodes())
655 paintArea->setAlwaysVisible(node, false);
657 delete d->nodeEditor;
658 d->nodeEditor = 0;
661 if( d->transformer )
663 foreach(QGraphicsItem *node, d->transformer->nodes())
665 paintArea->setAlwaysVisible(node, false);
667 delete d->transformer;
668 d->transformer = 0;
672 void ITween::setupConfigBar(QToolBar *configBar)
674 configBar->addWidget(d->configWidget)->setVisible(true);
677 void ITween::onChangeTool(int id)
679 PaintArea *paintArea = this->paintArea();
680 switch(id)
682 // case Private::Transform:
683 // {
684 // }
685 // break;
686 case Private::NodeMotion:
688 if(d->pathItem)
690 paintArea->setAlwaysVisible(d->pathItem, false);
691 delete d->pathItem;
692 d->pathItem = 0;
695 if( d->nodeEditor )
697 foreach(QGraphicsItem *node, d->nodeEditor->nodes())
699 paintArea->setAlwaysVisible(node, false);
702 delete d->nodeEditor;
703 d->nodeEditor = 0;
706 break;
707 default:
710 break;
712 d->saveChanges();