1
2
3 // TnzCore includes
4 #include "tstream.h"
5
6 // TnzBase includes
7 #include "toutputproperties.h"
8 #include "tfx.h"
9 #include "tparamcontainer.h"
10 #include "tparamset.h"
11 #include "tfxattributes.h"
12
13 // TnzLib includes
14 #include "toonz/fxdag.h"
15 #include "toonz/txshchildlevel.h"
16 #include "toonz/txshcell.h"
17 #include "toonz/observer.h"
18 #include "toonz/controlpointobserver.h"
19 #include "toonz/tcolumnfx.h"
20 #include "toonz/tcolumnfxset.h"
21 #include "toonz/txshlevelcolumn.h"
22 #include "toonz/txshpalettecolumn.h"
23 #include "toonz/txshzeraryfxcolumn.h"
24 #include "toonz/txshsoundcolumn.h"
25 #include "toonz/sceneproperties.h"
26 #include "toonz/toonzscene.h"
27 #include "toonz/columnfan.h"
28 #include "toonz/txshleveltypes.h"
29 #include "toonz/txshnoteset.h"
30 #include "toonz/txshsimplelevel.h"
31 #include "toonz/stage.h"
32 #include "toonz/textureutils.h"
33 #include "xshhandlemanager.h"
34 #include "orientation.h"
35 #include "toonz/expressionreferencemonitor.h"
36
37 #include "toonz/txsheet.h"
38 #include "toonz/preferences.h"
39
40 // STD includes
41 #include <set>
42
43 using namespace std;
44
45 DEFINE_CLASS_CODE(TXsheet, 18)
46
47 //-----------------------------------------------------------------------------
48 namespace {
49 //-----------------------------------------------------------------------------
50
getColumnDefaultName(TXsheet * xsh,int col,QString oldName)51 string getColumnDefaultName(TXsheet *xsh, int col, QString oldName) {
52 TXshColumn *column = xsh->getColumn(col);
53 if (column) {
54 TXshLevelColumn *lc = column->getLevelColumn();
55 if (lc) {
56 int r0, r1;
57 if (lc->getRange(r0, r1)) {
58 TXshCell cell = lc->getCell(r0);
59 assert(!cell.isEmpty());
60 TXshLevel *level = cell.m_level.getPointer();
61 if (level) {
62 bool isNumber = true;
63 oldName.right(oldName.size() - 3).toInt(&isNumber);
64 if (oldName.left(3) == "Col" && isNumber)
65 return ::to_string(level->getName());
66 else
67 return "";
68 }
69 }
70 }
71 }
72 return "Col" + std::to_string(col + 1);
73 }
74
75 //-----------------------------------------------------------------------------
76
setColumnName(TXsheet * xsh,int col)77 void setColumnName(TXsheet *xsh, int col) {
78 TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(col));
79 QString oldName = QString::fromStdString(obj->getName());
80 string name = getColumnDefaultName(xsh, col, oldName);
81 if (!name.empty()) obj->setName(name);
82 }
83
84 } // namespace
85
86 //=============================================================================
87 // TXsheetImp
88
89 struct TXsheet::TXsheetImp {
90 unsigned long m_id; //!< The xsheet instance's unique identifier
91
92 TColumnSetT<TXshColumn> m_columnSet;
93 TStageObjectTree *m_pegTree;
94 FxDag *m_fxDag;
95
96 int m_frameCount;
97 int m_soloColumn;
98 int m_viewColumn;
99
100 TSoundTrackP m_mixedSound;
101 ColumnFan m_columnFans[Orientations::COUNT];
102 XshHandleManager *m_handleManager;
103 ToonzScene *m_scene;
104
105 ExpressionReferenceMonitor *m_expRefMonitor;
106
107 public:
108 TXsheetImp();
109 ~TXsheetImp();
110
newIdentifierTXsheet::TXsheetImp111 static inline unsigned long newIdentifier() {
112 static unsigned long currentId = 0;
113 return ++currentId;
114 }
115
116 void copyFoldedState();
117
118 private:
119 void initColumnFans();
120
121 // not implemented
122 TXsheetImp(const TXsheetImp &);
123 TXsheetImp &operator=(const TXsheetImp &);
124 };
125
126 //-----------------------------------------------------------------------------
127
SoundProperties()128 TXsheet::SoundProperties::SoundProperties()
129 : m_fromFrame(-1), m_toFrame(-1), m_frameRate(-1), m_isPreview(false) {}
130
131 //-----------------------------------------------------------------------------
132
~SoundProperties()133 TXsheet::SoundProperties::~SoundProperties() {}
134
135 //-----------------------------------------------------------------------------
136
operator ==(const SoundProperties & c) const137 inline bool TXsheet::SoundProperties::operator==(
138 const SoundProperties &c) const {
139 return m_fromFrame == c.m_fromFrame && m_toFrame == c.m_toFrame &&
140 m_frameRate == c.m_frameRate && m_isPreview == c.m_isPreview;
141 }
142
operator !=(const SoundProperties & c) const143 inline bool TXsheet::SoundProperties::operator!=(
144 const SoundProperties &c) const {
145 return !(*this == c);
146 }
147
148 //-----------------------------------------------------------------------------
149
TXsheetImp()150 TXsheet::TXsheetImp::TXsheetImp()
151 : m_id(newIdentifier())
152 , m_pegTree(new TStageObjectTree)
153 , m_handleManager(0)
154 , m_fxDag(new FxDag())
155 , m_frameCount(0)
156 , m_soloColumn(-1)
157 , m_viewColumn(-1)
158 , m_mixedSound(0)
159 , m_scene(0)
160 , m_expRefMonitor(new ExpressionReferenceMonitor()) {
161 initColumnFans();
162 }
163
164 //-----------------------------------------------------------------------------
165
~TXsheetImp()166 TXsheet::TXsheetImp::~TXsheetImp() {
167 assert(m_pegTree);
168 assert(m_fxDag);
169 assert(m_handleManager);
170 delete m_pegTree;
171 delete m_fxDag;
172 delete m_handleManager;
173 }
174
175 //-----------------------------------------------------------------------------
176
initColumnFans()177 void TXsheet::TXsheetImp::initColumnFans() {
178 for (auto o : Orientations::all()) {
179 int index = o->dimension(PredefinedDimension::INDEX);
180 m_columnFans[index].setDimensions(
181 o->dimension(PredefinedDimension::LAYER),
182 o->dimension(PredefinedDimension::CAMERA_LAYER));
183 }
184 }
185
copyFoldedState()186 void TXsheet::TXsheetImp::copyFoldedState() {
187 for (int i = 1; i < Orientations::COUNT; i++)
188 m_columnFans[i].copyFoldedStateFrom(m_columnFans[0]);
189 }
190
191 //=============================================================================
192 // TXsheet
193
TXsheet()194 TXsheet::TXsheet()
195 : TSmartObject(m_classCode)
196 , m_player(0)
197 , m_imp(new TXsheet::TXsheetImp)
198 , m_notes(new TXshNoteSet())
199 , m_cameraColumnIndex(0)
200 , m_observer(nullptr) {
201 // extern TSyntax::Grammar *createXsheetGrammar(TXsheet*);
202 m_soundProperties = new TXsheet::SoundProperties();
203 m_imp->m_handleManager = new XshHandleManager(this);
204 m_imp->m_pegTree->setHandleManager(m_imp->m_handleManager);
205 m_imp->m_pegTree->createGrammar(this);
206
207 // Dummy camera column
208 m_cameraColumn = TXshColumn::createEmpty(0);
209 m_cameraColumn->m_index = -1;
210 m_cameraColumn->setXsheet(this);
211 }
212
213 //-----------------------------------------------------------------------------
214
~TXsheet()215 TXsheet::~TXsheet() {
216 texture_utils::invalidateTextures(this);
217
218 assert(m_imp);
219 if (m_notes) delete m_notes;
220 if (m_soundProperties) delete m_soundProperties;
221 }
222
223 //-----------------------------------------------------------------------------
224
id() const225 unsigned long TXsheet::id() const { return m_imp->m_id; }
226
227 //-----------------------------------------------------------------------------
228
getFrameCount() const229 int TXsheet::getFrameCount() const { return m_imp->m_frameCount; }
230
231 //-----------------------------------------------------------------------------
232
getCell(int row,int col) const233 const TXshCell &TXsheet::getCell(int row, int col) const {
234 return getCell(CellPosition(row, col));
235 }
236
getCell(const CellPosition & pos) const237 const TXshCell &TXsheet::getCell(const CellPosition &pos) const {
238 static const TXshCell emptyCell;
239
240 TXshColumnP column = m_imp->m_columnSet.getColumn(pos.layer());
241 if (!column) return emptyCell;
242 TXshCellColumn *xshColumn = column->getCellColumn();
243 if (!xshColumn) return emptyCell;
244 return xshColumn->getCell(pos.frame());
245 }
246
247 //-----------------------------------------------------------------------------
248
setCell(int row,int col,const TXshCell & cell)249 bool TXsheet::setCell(int row, int col, const TXshCell &cell) {
250 if (row < 0 || col < 0) return false;
251
252 bool wasColumnEmpty = isColumnEmpty(col);
253 TXshCellColumn *cellColumn;
254
255 if (!cell.isEmpty()) {
256 TXshLevel *level = cell.m_level.getPointer();
257 assert(level);
258
259 int levelType = level->getType();
260 TXshColumn::ColumnType type = TXshColumn::eLevelType;
261
262 if (levelType == SND_XSHLEVEL)
263 type = TXshColumn::eSoundType;
264 else if (levelType == SND_TXT_XSHLEVEL)
265 type = TXshColumn::eSoundTextType;
266 else if (levelType == PLT_XSHLEVEL)
267 type = TXshColumn::ePaletteType;
268 else if (levelType == ZERARYFX_XSHLEVEL)
269 type = TXshColumn::eZeraryFxType;
270 else if (levelType == MESH_XSHLEVEL)
271 type = TXshColumn::eMeshType;
272
273 cellColumn = touchColumn(col, type)->getCellColumn();
274 } else {
275 TXshColumn *column = getColumn(col);
276 cellColumn = column ? column->getCellColumn() : 0;
277 }
278
279 if (!cellColumn || cellColumn->isLocked()) return false;
280
281 cellColumn->setXsheet(this);
282
283 if (!cellColumn->setCell(row, cell)) {
284 if (wasColumnEmpty) {
285 removeColumn(col);
286 insertColumn(col);
287 }
288
289 return false;
290 }
291
292 TFx *fx = cellColumn->getFx();
293 if (wasColumnEmpty && fx && fx->getOutputConnectionCount() == 0 &&
294 cellColumn->getPaletteColumn() == 0)
295 getFxDag()->addToXsheet(fx);
296
297 if (cell.isEmpty())
298 updateFrameCount();
299 else if (row >= m_imp->m_frameCount)
300 m_imp->m_frameCount = row + 1;
301
302 TNotifier::instance()->notify(TXsheetChange());
303
304 return true;
305 }
306
307 //-----------------------------------------------------------------------------
308
getCells(int row,int col,int rowCount,TXshCell cells[]) const309 void TXsheet::getCells(int row, int col, int rowCount, TXshCell cells[]) const {
310 static const TXshCell emptyCell;
311 int i;
312 TXshColumnP column = m_imp->m_columnSet.getColumn(col);
313 if (!column) {
314 for (i = 0; i < rowCount; i++) cells[i] = emptyCell;
315 return;
316 }
317 TXshCellColumn *xshColumn = column->getCellColumn();
318 if (!xshColumn) {
319 for (i = 0; i < rowCount; i++) cells[i] = emptyCell;
320 return;
321 }
322 xshColumn->getCells(row, rowCount, cells);
323 }
324
325 //-----------------------------------------------------------------------------
326
setCells(int row,int col,int rowCount,const TXshCell cells[])327 bool TXsheet::setCells(int row, int col, int rowCount, const TXshCell cells[]) {
328 static const TXshCell emptyCell;
329 int i = 0;
330 while (i < rowCount && cells[i].isEmpty()) i++;
331
332 // inserito da Elisa verso novembre 2009.
333 // cosi' ha il difetto che se assegno celle vuote non fa nulla
334 // per ora lo commento. bisogna indagare se questo rompe qualcosa
335
336 // ho modificato il seguito per gestire il caso in cui i>=rowCount
337 // => niente livelli dentro cells
338
339 // if(i>=rowCount)
340 // return false;
341
342 TXshColumn::ColumnType type = TXshColumn::eLevelType;
343 if (i < rowCount) {
344 TXshLevel *level = cells[i].m_level.getPointer();
345 int levelType = level->getType();
346
347 if (levelType == SND_XSHLEVEL)
348 type = TXshColumn::eSoundType;
349 else if (levelType == SND_TXT_XSHLEVEL)
350 type = TXshColumn::eSoundTextType;
351 else if (levelType == PLT_XSHLEVEL)
352 type = TXshColumn::ePaletteType;
353 else if (levelType == ZERARYFX_XSHLEVEL)
354 type = TXshColumn::eZeraryFxType;
355 else if (levelType == MESH_XSHLEVEL)
356 type = TXshColumn::eMeshType;
357 }
358 bool wasColumnEmpty = isColumnEmpty(col);
359 if (col < 0) return false;
360 TXshCellColumn *column = touchColumn(col, type)->getCellColumn();
361 if (!column) return false;
362
363 int oldColRowCount = column->getMaxFrame() + 1;
364 bool ret = column->setCells(row, rowCount, cells);
365 if (!ret || column->isLocked()) {
366 if (wasColumnEmpty) {
367 removeColumn(col);
368 insertColumn(col);
369 }
370 return false;
371 }
372 int newColRowCount = column->getMaxFrame() + 1;
373
374 TFx *fx = column->getFx();
375 if (wasColumnEmpty && fx && fx->getOutputConnectionCount() == 0)
376 getFxDag()->addToXsheet(fx);
377 column->setXsheet(this);
378
379 if (newColRowCount > m_imp->m_frameCount)
380 m_imp->m_frameCount = newColRowCount;
381 else {
382 if (oldColRowCount == m_imp->m_frameCount &&
383 newColRowCount < m_imp->m_frameCount)
384 updateFrameCount();
385 }
386
387 return true;
388 }
389
390 //-----------------------------------------------------------------------------
391
insertCells(int row,int col,int rowCount)392 void TXsheet::insertCells(int row, int col, int rowCount) {
393 TXshColumnP column = m_imp->m_columnSet.getColumn(col);
394 if (!column || column->isLocked()) return;
395 TXshCellColumn *xshColumn = column->getCellColumn();
396 if (!xshColumn) return;
397 xshColumn->insertEmptyCells(row, rowCount);
398 // aggiorno il frame count
399 int fc = xshColumn->getMaxFrame() + 1;
400 if (fc > m_imp->m_frameCount) m_imp->m_frameCount = fc;
401 }
402
403 //-----------------------------------------------------------------------------
404
removeCells(int row,int col,int rowCount)405 void TXsheet::removeCells(int row, int col, int rowCount) {
406 TXshColumnP column = m_imp->m_columnSet.getColumn(col);
407 if (!column || column->isLocked()) return;
408
409 TXshCellColumn *xshCellColumn = column->getCellColumn();
410 if (!xshCellColumn) return;
411
412 int oldColRowCount = xshCellColumn->getMaxFrame() + 1;
413 xshCellColumn->removeCells(row, rowCount);
414
415 // aggiornamento framecount
416 if (oldColRowCount == m_imp->m_frameCount) updateFrameCount();
417
418 TNotifier::instance()->notify(TXsheetChange());
419 }
420
421 //-----------------------------------------------------------------------------
422
clearCells(int row,int col,int rowCount)423 void TXsheet::clearCells(int row, int col, int rowCount) {
424 const TXshColumnP &column = m_imp->m_columnSet.getColumn(col);
425 if (!column || column->isLocked()) return;
426
427 TXshCellColumn *xshCellColumn = column->getCellColumn();
428 if (!xshCellColumn) return;
429
430 int oldColRowCount = xshCellColumn->getMaxFrame() + 1;
431 xshCellColumn->clearCells(row, rowCount);
432
433 // aggiornamento framecount
434 if (oldColRowCount == m_imp->m_frameCount) updateFrameCount();
435 }
436
437 //-----------------------------------------------------------------------------
438
clearAll()439 void TXsheet::clearAll() {
440 int c0 = 0, c1 = m_imp->m_columnSet.getColumnCount() - 1;
441 int r0 = 0, r1 = getFrameCount() - 1;
442 m_imp->m_columnSet.clear();
443
444 if (m_imp->m_pegTree) {
445 delete m_imp->m_pegTree;
446 m_imp->m_pegTree = new TStageObjectTree();
447 m_imp->m_pegTree->setHandleManager(m_imp->m_handleManager);
448 m_imp->m_pegTree->createGrammar(this);
449 }
450
451 if (m_imp->m_fxDag) {
452 delete m_imp->m_fxDag;
453 m_imp->m_fxDag = new FxDag();
454 }
455
456 m_imp->m_frameCount = 0;
457 m_imp->m_mixedSound = 0;
458 }
459
460 //-----------------------------------------------------------------------------
461
getCellRange(int col,int & r0,int & r1) const462 int TXsheet::getCellRange(int col, int &r0, int &r1) const {
463 r0 = 0;
464 r1 = -1;
465 TXshColumnP column = m_imp->m_columnSet.getColumn(col);
466 if (!column) return 0;
467 TXshCellColumn *cellColumn = column->getCellColumn();
468 if (!cellColumn) return 0;
469 return cellColumn->getRange(r0, r1);
470 }
471
472 //-----------------------------------------------------------------------------
473
getMaxFrame(int col) const474 int TXsheet::getMaxFrame(int col) const {
475 TXshColumnP column = m_imp->m_columnSet.getColumn(col);
476 if (!column) return 0;
477 return column->getMaxFrame();
478 }
479
480 //-----------------------------------------------------------------------------
481
isColumnEmpty(int col) const482 bool TXsheet::isColumnEmpty(int col) const {
483 TXshColumnP column = m_imp->m_columnSet.getColumn(col);
484 return column ? column->isEmpty() : true;
485 }
486
487 //-----------------------------------------------------------------------------
488
getUsedLevels(set<TXshLevel * > & levels) const489 void TXsheet::getUsedLevels(set<TXshLevel *> &levels) const {
490 set<const TXsheet *> visitedXshs;
491 vector<const TXsheet *> todoXshs;
492
493 visitedXshs.insert(this);
494 todoXshs.push_back(this);
495
496 while (!todoXshs.empty()) {
497 const TXsheet *xsh = todoXshs.back();
498 todoXshs.pop_back();
499
500 int c0 = 0, c1 = xsh->getColumnCount() - 1;
501 for (int c = c0; c <= c1; ++c) {
502 TXshColumnP column = const_cast<TXsheet *>(xsh)->getColumn(c);
503 if (!column) continue;
504
505 TXshCellColumn *cellColumn = column->getCellColumn();
506 if (!cellColumn) continue;
507
508 int r0, r1;
509 if (!cellColumn->getRange(r0, r1)) continue;
510
511 TXshLevel *level = 0;
512 for (int r = r0; r <= r1; r++) {
513 TXshCell cell = cellColumn->getCell(r);
514 if (cell.isEmpty() || !cell.m_level) continue;
515
516 if (level != cell.m_level.getPointer()) {
517 level = cell.m_level.getPointer();
518 levels.insert(level);
519 if (level->getChildLevel()) {
520 TXsheet *childXsh = level->getChildLevel()->getXsheet();
521 if (visitedXshs.count(childXsh) == 0) {
522 visitedXshs.insert(childXsh);
523 todoXshs.push_back(childXsh);
524 }
525 }
526 }
527 }
528 }
529 }
530 }
531
532 //-----------------------------------------------------------------------------
533
isLevelUsed(TXshLevel * level) const534 bool TXsheet::isLevelUsed(TXshLevel *level) const {
535 set<TXshLevel *> levels;
536 getUsedLevels(levels);
537 return levels.count(level) > 0;
538 }
539
540 //-----------------------------------------------------------------------------
541
getStageObject(const TStageObjectId & id) const542 TStageObject *TXsheet::getStageObject(const TStageObjectId &id) const {
543 assert(id != TStageObjectId::NoneId);
544 return m_imp->m_pegTree->getStageObject(id);
545 }
546
547 //-----------------------------------------------------------------------------
548
getStageObjectTree() const549 TStageObjectTree *TXsheet::getStageObjectTree() const {
550 return m_imp->m_pegTree;
551 }
552
553 //-----------------------------------------------------------------------------
554
getPlacement(const TStageObjectId & id,int frame) const555 TAffine TXsheet::getPlacement(const TStageObjectId &id, int frame) const {
556 assert(id != TStageObjectId::NoneId);
557 return m_imp->m_pegTree->getStageObject(id)->getPlacement(frame);
558 }
559
560 //-----------------------------------------------------------------------------
561
getZ(const TStageObjectId & id,int frame) const562 double TXsheet::getZ(const TStageObjectId &id, int frame) const {
563 assert(id != TStageObjectId::NoneId);
564 return m_imp->m_pegTree->getStageObject(id)->getZ(frame);
565 }
566
567 //-----------------------------------------------------------------------------
568
getNoScaleZ(const TStageObjectId & id) const569 double TXsheet::getNoScaleZ(const TStageObjectId &id) const {
570 assert(id != TStageObjectId::NoneId);
571 return m_imp->m_pegTree->getStageObject(id)->getNoScaleZ();
572 }
573
574 //-----------------------------------------------------------------------------
575
getParentPlacement(const TStageObjectId & id,int frame) const576 TAffine TXsheet::getParentPlacement(const TStageObjectId &id, int frame) const {
577 assert(id != TStageObjectId::NoneId);
578 return m_imp->m_pegTree->getStageObject(id)->getParentPlacement(frame);
579 }
580
581 //-----------------------------------------------------------------------------
582
getCenter(const TStageObjectId & id,int frame) const583 TPointD TXsheet::getCenter(const TStageObjectId &id, int frame) const {
584 assert(id != TStageObjectId::NoneId);
585 return m_imp->m_pegTree->getStageObject(id)->getCenter(frame);
586 }
587
588 //-----------------------------------------------------------------------------
589
setCenter(const TStageObjectId & id,int frame,const TPointD & center)590 void TXsheet::setCenter(const TStageObjectId &id, int frame,
591 const TPointD ¢er) {
592 assert(id != TStageObjectId::NoneId);
593 m_imp->m_pegTree->getStageObject(id)->setCenter(frame, center);
594 }
595
596 //-----------------------------------------------------------------------------
597
getStageObjectParent(const TStageObjectId & id)598 TStageObjectId TXsheet::getStageObjectParent(const TStageObjectId &id) {
599 assert(id != TStageObjectId::NoneId);
600 return m_imp->m_pegTree->getStageObject(id)->getParent();
601 }
602
603 //-----------------------------------------------------------------------------
604
setStageObjectParent(const TStageObjectId & id,const TStageObjectId & parentId)605 void TXsheet::setStageObjectParent(const TStageObjectId &id,
606 const TStageObjectId &parentId) {
607 assert(id != TStageObjectId::NoneId);
608 m_imp->m_pegTree->getStageObject(id)->setParent(parentId);
609 }
610
611 //-----------------------------------------------------------------------------
612
hasChildren(const TStageObjectId & id) const613 bool TXsheet::hasChildren(const TStageObjectId &id) const {
614 assert(id != TStageObjectId::NoneId);
615 return m_imp->m_pegTree->getStageObject(id)->hasChildren();
616 }
617
618 //-----------------------------------------------------------------------------
619
getCameraAff(int frame) const620 TAffine TXsheet::getCameraAff(int frame) const {
621 TStageObjectId cameraId = getStageObjectTree()->getCurrentCameraId();
622 TAffine cameraAff = getPlacement(cameraId, frame);
623 double cameraZ = getZ(cameraId, frame);
624 TAffine aff = cameraAff * TScale((1000 + cameraZ) / 1000);
625 return aff;
626 }
627
628 //-----------------------------------------------------------------------------
629
reverseCells(int r0,int c0,int r1,int c1)630 void TXsheet::reverseCells(int r0, int c0, int r1, int c1) {
631 int rowCount = r1 - r0;
632 if (rowCount < 0 || c1 - c0 < 0) return;
633
634 for (int j = c0; j <= c1; j++) {
635 int i1, i2;
636 for (i1 = r0, i2 = r1; i1 < i2; i1++, i2--) {
637 TXshCell app1 = getCell(CellPosition(i1, j));
638 TXshCell app2 = getCell(CellPosition(i2, j));
639 setCell(i1, j, app2);
640 setCell(i2, j, app1);
641 }
642 }
643 }
644
645 //-----------------------------------------------------------------------------
646
swingCells(int r0,int c0,int r1,int c1)647 void TXsheet::swingCells(int r0, int c0, int r1, int c1) {
648 int rowCount = r1 - r0;
649 if (rowCount < 0 || c1 - c0 < 0) return;
650 int r0Mod = r1 + 1;
651 for (int c = c0; c <= c1; ++c) insertCells(r0Mod, c, rowCount);
652
653 for (int j = c0; j <= c1; j++) {
654 for (int i1 = r0Mod, i2 = r1 - 1; i2 >= r0; i1++, i2--) {
655 TXshCell cell = getCell(CellPosition(i2, j));
656 setCell(i1, j, cell);
657 }
658 }
659 }
660
661 //-----------------------------------------------------------------------------
662
incrementCells(int r0,int c0,int r1,int c1,vector<std::pair<TRect,TXshCell>> & forUndo)663 bool TXsheet::incrementCells(int r0, int c0, int r1, int c1,
664 vector<std::pair<TRect, TXshCell>> &forUndo) {
665 for (int j = c0; j <= c1; j++) {
666 int i = r0;
667 while (getCell(CellPosition(i, j)).isEmpty() && i <= r1 - 1) i++;
668
669 for (; i <= r1 - 1; i++) {
670 if (getCell(CellPosition(i + 1, j)).isEmpty()) break;
671 const TXshCell &ce1 = getCell(CellPosition(i, j)),
672 &ce2 = getCell(CellPosition(i + 1, j));
673 if (ce2.getSimpleLevel() != ce1.getSimpleLevel() ||
674 ce2.getFrameId().getNumber() < ce1.getFrameId().getNumber())
675 return false;
676 }
677 i = r0;
678 while (getCell(CellPosition(i, j)).isEmpty() && i <= r1 - 1) i++;
679 int count;
680 for (; i <= r1 - 1; i++) {
681 count = 1;
682 if (getCell(CellPosition(i + 1, j)).isEmpty()) continue;
683
684 int frame1 = getCell(CellPosition(i, j)).getFrameId().getNumber();
685 if (frame1 == -1) break;
686 while (!getCell(CellPosition(i + 1, j)).isEmpty() &&
687 getCell(CellPosition(i + 1, j)).getFrameId().getNumber() ==
688 getCell(CellPosition(i, j)).getFrameId().getNumber())
689 i++, count++;
690
691 int frame2 = getCell(CellPosition(i + 1, j)).getFrameId().getNumber();
692 if (frame2 == -1) break;
693
694 if (frame1 + count == frame2)
695 continue;
696 else if (frame1 + count < frame2) // add
697 {
698 int numCells = frame2 - frame1 - count;
699 insertCells(i + 1, j, numCells);
700 forUndo.push_back(std::pair<TRect, TXshCell>(
701 TRect(i + 1, j, i + 1 + numCells - 1, j), TXshCell()));
702 for (int k = 1; k <= numCells; k++)
703 setCell(i + k, j, getCell(CellPosition(i, j)));
704 i += numCells;
705 r1 += numCells;
706 } else // remove
707 {
708 int numCells = count - frame2 + frame1;
709 i = i - numCells;
710 forUndo.push_back(
711 std::pair<TRect, TXshCell>(TRect(i + 1, j, i + 1 + numCells - 1, j),
712 getCell(CellPosition(i + 1, j))));
713 removeCells(i + 1, j, numCells);
714 r1 -= numCells;
715 }
716 }
717 }
718 return true;
719 }
720
721 //-----------------------------------------------------------------------------
722
duplicateCells(int r0,int c0,int r1,int c1,int upTo)723 void TXsheet::duplicateCells(int r0, int c0, int r1, int c1, int upTo) {
724 assert(upTo >= r1 + 1);
725 int chunk = r1 - r0 + 1;
726
727 for (int j = c0; j <= c1; j++) {
728 insertCells(r1 + 1, j, upTo - (r1 + 1) + 1);
729 for (int i = r1 + 1; i <= upTo; i++)
730 setCell(i, j, getCell(CellPosition(r0 + ((i - (r1 + 1)) % chunk), j)));
731 }
732 }
733
734 //-----------------------------------------------------------------------------
735
stepCells(int r0,int c0,int r1,int c1,int type)736 void TXsheet::stepCells(int r0, int c0, int r1, int c1, int type) {
737 int nr = r1 - r0 + 1;
738 int nc = c1 - c0 + 1;
739 if (nr < 1 || nc <= 0) return;
740 int size = nr * nc;
741 std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
742 if (!cells) return;
743 // salvo il contenuto delle celle in cells
744 int k = 0;
745 for (int r = r0; r <= r1; r++)
746 for (int c = c0; c <= c1; c++) {
747 cells[k++] = getCell(CellPosition(r, c));
748 }
749
750 int nrows = nr * (type - 1);
751
752 for (int c = c0; c <= c1; ++c) insertCells(r1 + 1, c, nrows);
753
754 for (int j = c0; j <= c1; j++) {
755 int i, k;
756 for (i = r0, k = j - c0; k < size; k += nc) {
757 for (int i1 = 0; i1 < type; i1++) {
758 if (cells[k].isEmpty())
759 clearCells(i + i1, j);
760 else
761 setCell(i + i1, j, cells[k]);
762 }
763 i += type; // dipende dal tipo di step (2 o 3 per ora)
764 }
765 }
766 }
767
768 //-----------------------------------------------------------------------------
769
increaseStepCells(int r0,int c0,int & r1,int c1)770 void TXsheet::increaseStepCells(int r0, int c0, int &r1, int c1) {
771 int c, size = r1 - r0 + 1;
772 QList<int> ends;
773 for (c = c0; c <= c1; c++) {
774 int r = r0, i = 0, rEnd = r1;
775 while (r <= rEnd) {
776 TXshCell cell = getCell(CellPosition(r, c));
777 if (!cell.isEmpty()) {
778 insertCells(r, c);
779 setCell(r, c, cell);
780 rEnd++;
781 r++;
782 while (cell == getCell(CellPosition(r, c)) && r <= rEnd) r++;
783 } else
784 r++;
785 i++;
786 }
787 ends.append(rEnd);
788 }
789 if (ends.isEmpty()) return;
790 // controllo se devo cambiare la selezione
791 bool allIncreaseIsEqual = true;
792 for (c = 0; c < ends.size() - 1 && allIncreaseIsEqual; c++)
793 allIncreaseIsEqual = allIncreaseIsEqual && ends[c] == ends[c + 1];
794 if (allIncreaseIsEqual) r1 = ends[0];
795 }
796
797 //-----------------------------------------------------------------------------
798
decreaseStepCells(int r0,int c0,int & r1,int c1)799 void TXsheet::decreaseStepCells(int r0, int c0, int &r1, int c1) {
800 int c, size = r1 - r0 + 1;
801 QList<int> ends;
802 for (c = c0; c <= c1; c++) {
803 int r = r0, i = 0, rEnd = r1;
804 while (r <= rEnd) {
805 TXshCell cell = getCell(CellPosition(r, c));
806 if (!cell.isEmpty()) {
807 r++;
808 bool removed = false;
809 while (cell == getCell(CellPosition(r, c)) && r <= rEnd) {
810 if (!removed) {
811 removed = true;
812 removeCells(r, c);
813 rEnd--;
814 } else
815 r++;
816 }
817 } else
818 r++;
819 i++;
820 }
821 ends.append(rEnd);
822 }
823 if (ends.isEmpty()) return;
824 // controllo se devo cambiare la selezione
825 bool allDecreaseIsEqual = true;
826 for (c = 0; c < ends.size() - 1 && allDecreaseIsEqual; c++)
827 allDecreaseIsEqual = allDecreaseIsEqual && ends[c] == ends[c + 1];
828 if (allDecreaseIsEqual) r1 = ends[0];
829 }
830
831 //-----------------------------------------------------------------------------
832
eachCells(int r0,int c0,int r1,int c1,int type)833 void TXsheet::eachCells(int r0, int c0, int r1, int c1, int type) {
834 int nr = r1 - r0 + 1;
835 int nc = c1 - c0 + 1;
836 if (nr < type || nc <= 0) return;
837
838 int newRows = nr % type ? nr / type + 1 : nr / type;
839
840 int size = newRows * nc;
841 assert(size > 0);
842 std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
843 assert(cells);
844
845 int i, j, k;
846 for (j = r0, i = 0; i < size;
847 j += type) // in cells copio il contenuto delle celle che mi interessano
848 {
849 for (k = c0; k <= c1; k++, i++) cells[i] = getCell(CellPosition(j, k));
850 }
851
852 int c;
853 for (c = c0; c <= c1; ++c) removeCells(r0 + newRows, c, nr - newRows);
854
855 for (i = r0, k = 0; i < r0 + newRows && k < size; i++)
856 for (j = c0; j <= c1; j++) {
857 //----110523 iwasawa
858 // Eachでできた空きセルに、操作前のセルの中身が残ってしまう不具合を修正
859 if (cells[k].isEmpty())
860 clearCells(i, j);
861 else
862 setCell(i, j, cells[k]);
863 k++;
864 }
865 }
866
867 //-----------------------------------------------------------------------------
868 /*! force cells order in n-steps. returns the row amount after process
869 */
reframeCells(int r0,int r1,int col,int type,int withBlank)870 int TXsheet::reframeCells(int r0, int r1, int col, int type, int withBlank) {
871 // Row amount in the selection
872 int nr = r1 - r0 + 1;
873
874 if (nr < 1) return 0;
875
876 QVector<TXshCell> cells;
877
878 cells.clear();
879
880 for (int r = r0; r <= r1; r++) {
881 if (cells.size() == 0 || cells.last() != getCell(CellPosition(r, col)))
882 cells.push_back(getCell(CellPosition(r, col)));
883 }
884
885 // if withBlank is greater than -1, remove empty cells from cell order
886 if (withBlank >= 0) {
887 auto itr = cells.begin();
888 while (itr != cells.end()) {
889 if ((*itr).isEmpty())
890 itr = cells.erase(itr);
891 else
892 itr++;
893 }
894 }
895
896 if (cells.empty()) return 0;
897
898 // row amount after n-step
899 int nrows = cells.size() * type;
900
901 if (withBlank > 0) {
902 nrows += cells.size() * withBlank * type;
903 }
904
905 // if needed, insert cells
906 if (nr < nrows) {
907 insertCells(r1 + 1, col, nrows - nr);
908 }
909 // if needed, remove cells
910 else if (nr > nrows) {
911 removeCells(r0 + nrows, col, nr - nrows);
912 }
913
914 for (int i = r0, k = 0; i < r0 + nrows; k++) {
915 for (int i1 = 0; i1 < type; i1++) {
916 if (cells[k].isEmpty())
917 clearCells(i + i1, col);
918 else
919 setCell(i + i1, col, cells[k]);
920 }
921 i += type; // dipende dal tipo di step (2 o 3 per ora)
922
923 if (withBlank > 0) {
924 for (int i1 = 0; i1 < withBlank * type; i1++) {
925 clearCells(i + i1, col);
926 }
927 i += withBlank * type;
928 }
929 }
930
931 return nrows; // return row amount after process
932 }
933
934 //-----------------------------------------------------------------------------
935
resetStepCells(int r0,int c0,int r1,int c1)936 void TXsheet::resetStepCells(int r0, int c0, int r1, int c1) {
937 int c, size = r1 - r0 + 1;
938 for (c = c0; c <= c1; c++) {
939 int r = r0, i = 0;
940 TXshCell *cells = new TXshCell[size];
941 while (r <= r1) {
942 // mi prendo le celle che mi servono
943 cells[i] = getCell(CellPosition(r, c));
944 r++;
945 while (cells[i] == getCell(CellPosition(r, c)) && r <= r1) r++;
946 i++;
947 }
948
949 size = i;
950 removeCells(r0, c, r1 - r0 + 1);
951 insertCells(r0, c, i);
952 i = 0;
953 r = r0;
954 for (i = 0; i < size; i++, r++) setCell(r, c, cells[i]);
955 }
956 }
957
958 //-----------------------------------------------------------------------------
959 /*! Roll first cells of rect r0,c0,r1,c1. Move cells contained in first row to
960 * last row.
961 */
rollupCells(int r0,int c0,int r1,int c1)962 void TXsheet::rollupCells(int r0, int c0, int r1, int c1) {
963 int nc = c1 - c0 + 1;
964 int size = 1 * nc;
965 assert(size > 0);
966 std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
967 assert(cells);
968
969 // in cells copio il contenuto delle celle che mi interessano
970 int k;
971 for (k = c0; k <= c1; k++) cells[k - c0] = getCell(CellPosition(r0, k));
972
973 for (k = c0; k <= c1; k++) removeCells(r0, k, 1);
974
975 for (k = c0; k <= c1; k++) {
976 insertCells(r1, k, 1);
977 setCell(r1, k, cells[k - c0]); // setto le celle
978 }
979 }
980
981 //-----------------------------------------------------------------------------
982 /*! Roll last cells of rect r0,c0,r1,c1. Move cells contained in last row to
983 * first row.
984 */
rolldownCells(int r0,int c0,int r1,int c1)985 void TXsheet::rolldownCells(int r0, int c0, int r1, int c1) {
986 int nc = c1 - c0 + 1;
987 int size = 1 * nc;
988 assert(size > 0);
989 std::unique_ptr<TXshCell[]> cells(new TXshCell[size]);
990 assert(cells);
991
992 // in cells copio il contenuto delle celle che mi interessano
993 int k;
994 for (k = c0; k <= c1; k++) cells[k - c0] = getCell(CellPosition(r1, k));
995
996 for (k = c0; k <= c1; k++) removeCells(r1, k, 1);
997
998 for (k = c0; k <= c1; k++) {
999 insertCells(r0, k, 1);
1000 setCell(r0, k, cells[k - c0]); // setto le celle
1001 }
1002 }
1003
1004 //-----------------------------------------------------------------------------
1005 /*! Stretch cells contained in rect r0,c0,r1,c1, from r1-r0+1 to nr.
1006 If nr>r1-r0+1 add cells, otherwise remove cells. */
timeStretch(int r0,int c0,int r1,int c1,int nr)1007 void TXsheet::timeStretch(int r0, int c0, int r1, int c1, int nr) {
1008 int oldNr = r1 - r0 + 1;
1009 if (nr > oldNr) /* ingrandisce */
1010 {
1011 int c;
1012 for (c = c0; c <= c1; c++) {
1013 int dn = nr - oldNr;
1014 assert(oldNr > 0);
1015 std::unique_ptr<TXshCell[]> cells(new TXshCell[oldNr]);
1016 assert(cells);
1017 getCells(r0, c, oldNr, cells.get());
1018 insertCells(r0 + 1, c, dn);
1019 int i;
1020 for (i = nr - 1; i >= 0; i--) {
1021 int j = i * double(oldNr) / double(nr);
1022 if (j < i) setCell(i + r0, c, cells[j]);
1023 }
1024 }
1025 } else /* rimpicciolisce */
1026 {
1027 int c;
1028 for (c = c0; c <= c1; c++) {
1029 int dn = oldNr - nr;
1030 std::unique_ptr<TXshCell[]> cells(new TXshCell[oldNr]);
1031 assert(cells);
1032 getCells(r0, c, oldNr, cells.get());
1033 int i;
1034 for (i = 0; i < nr; i++) {
1035 int j = i * double(oldNr) / double(nr);
1036 if (j > i) setCell(i + r0, c, cells[j]);
1037 }
1038 removeCells(r1 - dn + 1, c, dn);
1039 }
1040 }
1041 }
1042
1043 //-----------------------------------------------------------------------------
1044
exposeLevel(int row,int col,TXshLevel * xl,bool overwrite)1045 int TXsheet::exposeLevel(int row, int col, TXshLevel *xl, bool overwrite) {
1046 if (!xl) return 0;
1047 std::vector<TFrameId> fids;
1048 xl->getFids(fids);
1049 int frameCount = 1;
1050 if (fids.empty()) {
1051 setCell(row, col, TXshCell(xl, TFrameId(1)));
1052 updateFrameCount();
1053 return frameCount;
1054 }
1055 exposeLevel(row, col, xl, fids, overwrite);
1056 return (int)fids.size();
1057 }
1058
1059 //-----------------------------------------------------------------------------
1060 // customized version for load level popup
exposeLevel(int row,int col,TXshLevel * xl,std::vector<TFrameId> & fIds_,int xFrom,int xTo,int step,int inc,int frameCount,bool doesFileActuallyExist)1061 int TXsheet::exposeLevel(int row, int col, TXshLevel *xl,
1062 std::vector<TFrameId> &fIds_, int xFrom, int xTo,
1063 int step, int inc, int frameCount,
1064 bool doesFileActuallyExist) {
1065 if (!xl) return 0;
1066 std::vector<TFrameId> fids;
1067
1068 if (doesFileActuallyExist)
1069 xl->getFids(fids);
1070 else {
1071 for (int i = 0; i < (int)fIds_.size(); i++) {
1072 fids.push_back(fIds_[i]);
1073 }
1074 }
1075
1076 // multiple exposing
1077 if (frameCount < 0 || xFrom < 0 || xTo < 0 || step < 0 || inc < 0) {
1078 insertCells(row, col, xl->getFrameCount());
1079
1080 frameCount = 1;
1081 if (fids.empty())
1082 setCell(row, col, TXshCell(xl, TFrameId(1)));
1083 else {
1084 frameCount = (int)fids.size();
1085 insertCells(row, col, frameCount);
1086 std::vector<TFrameId>::iterator it;
1087 for (it = fids.begin(); it != fids.end(); ++it)
1088 setCell(row++, col, TXshCell(xl, *it));
1089 }
1090 updateFrameCount();
1091 return frameCount;
1092 }
1093
1094 // single exposing
1095
1096 insertCells(row, col, frameCount);
1097
1098 if (fids.empty()) {
1099 setCell(row, col, TXshCell(xl, TFrameId(1)));
1100 } else {
1101 if (inc == 0) // inc = Auto
1102 {
1103 std::vector<TFrameId>::iterator it;
1104 it = fids.begin();
1105 while (it->getNumber() < xFrom) it++;
1106
1107 if (step == 0) // Step = Auto
1108 {
1109 std::vector<TFrameId>::iterator next_it;
1110 next_it = it;
1111 next_it++;
1112
1113 int startFrame = it->getNumber();
1114
1115 for (int f = startFrame; f < startFrame + frameCount; f++) {
1116 if (next_it != fids.end() && f >= next_it->getNumber()) {
1117 it++;
1118 next_it++;
1119 }
1120 setCell(row++, col, TXshCell(xl, *it));
1121 }
1122 }
1123
1124 else // Step != Auto
1125 {
1126 int loopCount = frameCount / step;
1127 for (int loop = 0; loop < loopCount; loop++) {
1128 for (int s = 0; s < step; s++) {
1129 setCell(row++, col, TXshCell(xl, *it));
1130 }
1131 it++;
1132 }
1133 }
1134
1135 } else // inc != Auto
1136 {
1137 int loopCount;
1138 if (step == 0) // Step = Auto
1139 step = inc;
1140
1141 loopCount = frameCount / step;
1142
1143 for (int loop = 0; loop < loopCount; loop++) {
1144 TFrameId id(xFrom + loop * inc, fids.begin()->getLetter());
1145 for (int s = 0; s < step; s++) {
1146 setCell(row++, col, TXshCell(xl, id));
1147 }
1148 }
1149 }
1150 }
1151 updateFrameCount();
1152 return frameCount;
1153 }
1154
1155 //-----------------------------------------------------------------------------
1156
exposeLevel(int row,int col,TXshLevel * xl,std::vector<TFrameId> fids,bool overwrite)1157 void TXsheet::exposeLevel(int row, int col, TXshLevel *xl,
1158 std::vector<TFrameId> fids, bool overwrite) {
1159 int frameCount = (int)fids.size();
1160 if (!overwrite) insertCells(row, col, frameCount);
1161 std::vector<TFrameId>::iterator it;
1162 for (it = fids.begin(); it != fids.end(); ++it)
1163 setCell(row++, col, TXshCell(xl, *it));
1164 updateFrameCount();
1165 }
1166
1167 //-----------------------------------------------------------------------------
1168
updateFrameCount()1169 void TXsheet::updateFrameCount() {
1170 m_imp->m_frameCount = 0;
1171 for (int i = 0; i < m_imp->m_columnSet.getColumnCount(); ++i) {
1172 TXshColumnP cc = m_imp->m_columnSet.getColumn(i);
1173 if (cc && !cc->isEmpty())
1174 m_imp->m_frameCount =
1175 std::max(m_imp->m_frameCount, cc->getMaxFrame() + 1);
1176 }
1177 }
1178
1179 //-----------------------------------------------------------------------------
1180
loadData(TIStream & is)1181 void TXsheet::loadData(TIStream &is) {
1182 clearAll();
1183 TStageObjectId cameraId = TStageObjectId::CameraId(0);
1184 TStageObject *firstCamera = getStageObject(cameraId);
1185 m_imp->m_pegTree->removeStageObject(cameraId);
1186
1187 int col = 0;
1188 string tagName;
1189 while (is.openChild(tagName)) {
1190 if (tagName == "columns") {
1191 while (!is.eos()) {
1192 TPersist *p = 0;
1193 is >> p;
1194 TXshColumn *column = dynamic_cast<TXshColumn *>(p);
1195 if (!column) throw TException("expected xsheet column");
1196 m_imp->m_columnSet.insertColumn(col++, column);
1197 column->setXsheet(this);
1198 if (TXshZeraryFxColumn *zc =
1199 dynamic_cast<TXshZeraryFxColumn *>(column)) {
1200 TFx *fx = zc->getZeraryColumnFx()->getZeraryFx();
1201 int fxTypeCount = m_imp->m_fxDag->getFxTypeCount(fx);
1202 int maxFxTypeId = std::max(fxTypeCount, fx->getAttributes()->getId());
1203 m_imp->m_fxDag->updateFxTypeTable(fx, maxFxTypeId);
1204 m_imp->m_fxDag->updateFxIdTable(fx);
1205 for (int j = 0; j < fx->getParams()->getParamCount(); j++) {
1206 TParam *param = fx->getParams()->getParam(j);
1207 if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(param))
1208 getStageObjectTree()->setGrammar(dp);
1209 else if (dynamic_cast<TPointParam *>(param) ||
1210 dynamic_cast<TRangeParam *>(param) ||
1211 dynamic_cast<TPixelParam *>(param)) {
1212 TParamSet *paramSet = dynamic_cast<TParamSet *>(param);
1213 assert(paramSet);
1214 int f;
1215 for (f = 0; f < paramSet->getParamCount(); f++) {
1216 TDoubleParam *dp = dynamic_cast<TDoubleParam *>(
1217 paramSet->getParam(f).getPointer());
1218 if (!dp) continue;
1219 getStageObjectTree()->setGrammar(dp);
1220 }
1221 }
1222 }
1223 }
1224 }
1225 } else if (tagName == "pegbars") {
1226 TPersist *p = m_imp->m_pegTree;
1227 m_imp->m_pegTree->loadData(is, this);
1228 } else if (tagName == "fxnodes") {
1229 m_imp->m_fxDag->loadData(is);
1230 std::vector<TFx *> fxs;
1231 m_imp->m_fxDag->getFxs(fxs);
1232 for (int i = 0; i < (int)fxs.size(); i++) {
1233 TFx *fx = fxs[i];
1234 for (int j = 0; j < fx->getParams()->getParamCount(); j++) {
1235 TParam *param = fx->getParams()->getParam(j);
1236 if (TDoubleParam *dp = dynamic_cast<TDoubleParam *>(param))
1237 getStageObjectTree()->setGrammar(dp);
1238 else if (dynamic_cast<TPointParam *>(param) ||
1239 dynamic_cast<TRangeParam *>(param) ||
1240 dynamic_cast<TPixelParam *>(param)) {
1241 TParamSet *paramSet = dynamic_cast<TParamSet *>(param);
1242 assert(paramSet);
1243 int f;
1244 for (f = 0; f < paramSet->getParamCount(); f++) {
1245 TDoubleParam *dp = dynamic_cast<TDoubleParam *>(
1246 paramSet->getParam(f).getPointer());
1247 if (!dp) continue;
1248 getStageObjectTree()->setGrammar(dp);
1249 }
1250 }
1251 }
1252 }
1253
1254 if (is.matchEndTag()) continue;
1255
1256 // was ist dass?
1257 TFxSet fxSet;
1258 fxSet.loadData(is);
1259 } else if (tagName == "columnFan") {
1260 m_imp->m_columnFans[0].loadData(is);
1261 m_imp->copyFoldedState();
1262 } else if (tagName == "noteSet") {
1263 m_notes->loadData(is);
1264 } else {
1265 throw TException("xsheet, unknown tag: " + tagName);
1266 }
1267 is.closeChild();
1268 }
1269 updateFrameCount();
1270 }
1271
1272 //-----------------------------------------------------------------------------
1273
saveData(TOStream & os)1274 void TXsheet::saveData(TOStream &os) {
1275 os.openChild("columns");
1276 for (int c = 0; c < m_imp->m_columnSet.getColumnCount(); ++c) {
1277 TXshColumnP column = m_imp->m_columnSet.getColumn(c);
1278 if (column && c < getFirstFreeColumnIndex()) os << column.getPointer();
1279 }
1280 os.closeChild();
1281 os.openChild("pegbars");
1282 m_imp->m_pegTree->saveData(os, getFirstFreeColumnIndex(), this);
1283 // os << *(m_imp->m_pegTree);
1284 os.closeChild();
1285
1286 FxDag *fxDag = getFxDag();
1287 os.openChild("fxnodes");
1288 fxDag->saveData(os, getFirstFreeColumnIndex());
1289 os.closeChild();
1290
1291 // does not matter which Orientation to take, as all fans share folded data
1292 ColumnFan *columnFan = getColumnFan(Orientations::topToBottom());
1293 if (!columnFan->isEmpty()) {
1294 os.openChild("columnFan");
1295 columnFan->saveData(os);
1296 os.closeChild();
1297 }
1298
1299 TXshNoteSet *notes = getNotes();
1300 if (notes->getCount() > 0) {
1301 os.openChild("noteSet");
1302 notes->saveData(os);
1303 os.closeChild();
1304 }
1305 }
1306
1307 //-----------------------------------------------------------------------------
1308
1309 PERSIST_IDENTIFIER(TXsheet, "xsheet")
1310
1311 //-----------------------------------------------------------------------------
1312
insertColumn(int col,TXshColumn::ColumnType type)1313 void TXsheet::insertColumn(int col, TXshColumn::ColumnType type) {
1314 insertColumn(col, TXshColumn::createEmpty(type));
1315 }
1316
1317 //-----------------------------------------------------------------------------
1318
insertColumn(int col,TXshColumn * column)1319 void TXsheet::insertColumn(int col, TXshColumn *column) {
1320 if (col < 0) col = 0;
1321 column->setXsheet(this);
1322 m_imp->m_columnSet.insertColumn(col, column);
1323 m_imp->m_pegTree->insertColumn(col);
1324 if (column->getPaletteColumn() ==
1325 0) // palette column are not connected to the xsheet fx node
1326 {
1327 TFx *fx = column->getFx();
1328 if (fx) getFxDag()->addToXsheet(fx);
1329 }
1330
1331 for (ColumnFan &columnFan : m_imp->m_columnFans) {
1332 columnFan.rollRightFoldedState(col,
1333 m_imp->m_columnSet.getColumnCount() - col);
1334 }
1335
1336 notify(TXsheetColumnChange(TXsheetColumnChange::Insert, col));
1337 }
1338
1339 //-----------------------------------------------------------------------------
1340
removeColumn(int col)1341 void TXsheet::removeColumn(int col) {
1342 TXshColumn *column = getColumn(col);
1343 if (column) {
1344 TFx *fx = column->getFx();
1345 if (fx) {
1346 getFxDag()->removeFromXsheet(fx);
1347
1348 // disconnetto dal columnFx tutti gli effetti connessi in uscita
1349 TFxPort *outPort = 0;
1350 while ((outPort = fx->getOutputConnection(0))) outPort->setFx(0);
1351 }
1352 }
1353 m_imp->m_columnSet.removeColumn(col);
1354 m_imp->m_pegTree->removeColumn(col);
1355
1356 for (ColumnFan &columnFan : m_imp->m_columnFans) {
1357 columnFan.rollLeftFoldedState(col,
1358 m_imp->m_columnSet.getColumnCount() - col);
1359 }
1360
1361 notify(TXsheetColumnChange(TXsheetColumnChange::Remove, col));
1362 }
1363
1364 //-----------------------------------------------------------------------------
1365
moveColumn(int srcIndex,int dstIndex)1366 void TXsheet::moveColumn(int srcIndex, int dstIndex) {
1367 if (srcIndex == dstIndex) return;
1368 assert(srcIndex >= 0);
1369 assert(dstIndex >= 0);
1370 int col = std::max(srcIndex, dstIndex);
1371 if (col >= m_imp->m_columnSet.getColumnCount()) {
1372 int n = m_imp->m_columnSet.getColumnCount();
1373 touchColumn(col, TXshColumn::eLevelType);
1374 while (n <= col) {
1375 TXshColumn *column = getColumn(n);
1376 assert(column);
1377 column->setXsheet(this);
1378 n++;
1379 }
1380 }
1381 assert(m_imp->m_columnSet.getColumnCount() > srcIndex);
1382 assert(m_imp->m_columnSet.getColumnCount() > dstIndex);
1383
1384 if (srcIndex < dstIndex) {
1385 int c0 = srcIndex;
1386 int c1 = dstIndex;
1387 assert(c0 < c1);
1388 m_imp->m_columnSet.rollLeft(c0, c1 - c0 + 1);
1389 for (ColumnFan &columnFan : m_imp->m_columnFans)
1390 columnFan.rollLeftFoldedState(c0, c1 - c0 + 1);
1391 for (int c = c0; c < c1; ++c) m_imp->m_pegTree->swapColumns(c, c + 1);
1392 } else {
1393 int c0 = dstIndex;
1394 int c1 = srcIndex;
1395 assert(c0 < c1);
1396 m_imp->m_columnSet.rollRight(c0, c1 - c0 + 1);
1397 for (ColumnFan &columnFan : m_imp->m_columnFans)
1398 columnFan.rollRightFoldedState(c0, c1 - c0 + 1);
1399 for (int c = c1 - 1; c >= c0; --c) m_imp->m_pegTree->swapColumns(c, c + 1);
1400 }
1401
1402 notify(TXsheetColumnChange(TXsheetColumnChange::Move, srcIndex, dstIndex));
1403 }
1404
1405 //-----------------------------------------------------------------------------
1406
getColumn(int col) const1407 TXshColumn *TXsheet::getColumn(int col) const {
1408 if (col < 0) return m_cameraColumn;
1409 return m_imp->m_columnSet.getColumn(col).getPointer();
1410 }
1411
1412 //-----------------------------------------------------------------------------
1413
getColumnCount() const1414 int TXsheet::getColumnCount() const {
1415 return m_imp->m_columnSet.getColumnCount();
1416 }
1417
1418 //-----------------------------------------------------------------------------
1419
getFirstFreeColumnIndex() const1420 int TXsheet::getFirstFreeColumnIndex() const {
1421 int i = getColumnCount();
1422 while (i > 0 && isColumnEmpty(i - 1)) --i;
1423 return i;
1424 }
1425
1426 //-----------------------------------------------------------------------------
1427
touchColumn(int index,TXshColumn::ColumnType type)1428 TXshColumn *TXsheet::touchColumn(int index, TXshColumn::ColumnType type) {
1429 TXshColumn *column = m_imp->m_columnSet.touchColumn(index, type).getPointer();
1430 if (index < 0 || !column) return 0;
1431
1432 // NOTE (Daniele): The following && should be a bug... but I fear I'd break
1433 // something changing it.
1434 // Observe that the implied behavior is that of REPLACING AN EXISTING
1435 // LEGITIMATE COLUMN!
1436 // Please, Inquire further if you're not upon release!
1437
1438 if (column->isEmpty() && column->getColumnType() != type) {
1439 removeColumn(index);
1440 insertColumn(index, type);
1441 column = getColumn(index);
1442 }
1443
1444 return column;
1445 }
1446
1447 //=============================================================================
1448 namespace { // Utility function
1449 //-----------------------------------------------------------------------------
1450
searchAudioColumn(TXsheet * xsh,std::vector<TXshSoundColumn * > & sounds,bool isPreview=true)1451 void searchAudioColumn(TXsheet *xsh, std::vector<TXshSoundColumn *> &sounds,
1452 bool isPreview = true) {
1453 int i = 0;
1454 int columns = xsh->getColumnCount();
1455
1456 for (; i < columns; ++i) {
1457 TXshColumn *column = xsh->getColumn(i);
1458 if (column) {
1459 TXshSoundColumn *soundCol = column->getSoundColumn();
1460 if (soundCol && !soundCol->isEmpty() &&
1461 ((isPreview && soundCol->isCamstandVisible()) ||
1462 (!isPreview && soundCol->isPreviewVisible()))) {
1463 sounds.push_back(soundCol);
1464 continue;
1465 }
1466 }
1467 }
1468 }
1469 //-----------------------------------------------------------------------------
1470 } // namespace
1471 //-----------------------------------------------------------------------------
1472
makeSound(SoundProperties * properties)1473 TSoundTrack *TXsheet::makeSound(SoundProperties *properties) {
1474 std::vector<TXshSoundColumn *> sounds;
1475 searchAudioColumn(this, sounds, properties->m_isPreview);
1476 if (!m_imp->m_mixedSound || *properties != *m_soundProperties) {
1477 if (!sounds.empty() && properties->m_fromFrame <= properties->m_toFrame)
1478 m_imp->m_mixedSound = sounds[0]->mixingTogether(
1479 sounds, properties->m_fromFrame, properties->m_toFrame,
1480 properties->m_frameRate);
1481 else
1482 m_imp->m_mixedSound = 0;
1483 delete m_soundProperties;
1484 m_soundProperties = properties;
1485 } else
1486 delete properties;
1487 return m_imp->m_mixedSound.getPointer();
1488 }
1489
1490 //-----------------------------------------------------------------------------
1491
scrub(int frame,bool isPreview)1492 void TXsheet::scrub(int frame, bool isPreview) {
1493 try {
1494 double fps =
1495 getScene()->getProperties()->getOutputProperties()->getFrameRate();
1496
1497 TXsheet::SoundProperties *prop = new TXsheet::SoundProperties();
1498 prop->m_isPreview = isPreview;
1499
1500 TSoundTrack *st = makeSound(prop); // Absorbs prop's ownership
1501 if (!st) return;
1502
1503 double samplePerFrame = st->getSampleRate() / fps;
1504
1505 double s0 = frame * samplePerFrame, s1 = s0 + samplePerFrame;
1506 // if (m_player && m_player->isPlaying()) {
1507 // try {
1508 // m_player->stop();
1509 // }
1510 // catch (const std::runtime_error& e) {
1511 // int i = 0;
1512 // }
1513 // catch (const std::exception& e) {
1514 // int i = 0;
1515 // }
1516 // catch (...) {
1517 // int i = 0;
1518 // }
1519 //}
1520 play(st, s0, s1, false);
1521 } catch (TSoundDeviceException &e) {
1522 if (e.getType() == TSoundDeviceException::NoDevice) {
1523 std::cout << ::to_string(e.getMessage()) << std::endl;
1524 } else {
1525 throw TSoundDeviceException(e.getType(), e.getMessage());
1526 }
1527 }
1528 }
1529
1530 //-----------------------------------------------------------------------------
1531
stopScrub()1532 void TXsheet::stopScrub() {
1533 if (m_player) m_player->stop();
1534 }
1535
1536 //-----------------------------------------------------------------------------
1537
play(TSoundTrackP soundtrack,int s0,int s1,bool loop)1538 void TXsheet::play(TSoundTrackP soundtrack, int s0, int s1, bool loop) {
1539 if (!TSoundOutputDevice::installed()) return;
1540
1541 if (!m_player) m_player = new TSoundOutputDevice();
1542
1543 if (m_player) {
1544 try {
1545 m_player->play(soundtrack, s0, s1, loop);
1546 } catch (TSoundDeviceException &) {
1547 }
1548 }
1549 }
1550
1551 //-----------------------------------------------------------------------------
1552
getFxDag() const1553 FxDag *TXsheet::getFxDag() const { return m_imp->m_fxDag; }
1554
1555 //-----------------------------------------------------------------------------
1556
getColumnFan(const Orientation * o) const1557 ColumnFan *TXsheet::getColumnFan(const Orientation *o) const {
1558 int index = o->dimension(PredefinedDimension::INDEX);
1559 return &m_imp->m_columnFans[index];
1560 }
1561
1562 //-----------------------------------------------------------------------------
1563
getScene() const1564 ToonzScene *TXsheet::getScene() const { return m_imp->m_scene; }
1565
1566 //-----------------------------------------------------------------------------
1567
setScene(ToonzScene * scene)1568 void TXsheet::setScene(ToonzScene *scene) { m_imp->m_scene = scene; }
1569
1570 //-----------------------------------------------------------------------------
1571
checkCircularReferences(const TXshCell & cellCandidate)1572 bool TXsheet::checkCircularReferences(const TXshCell &cellCandidate) {
1573 if (cellCandidate.isEmpty() || !cellCandidate.m_level->getChildLevel())
1574 return false;
1575 TXsheet *childCandidate = cellCandidate.m_level->getChildLevel()->getXsheet();
1576 return checkCircularReferences(childCandidate);
1577 }
1578
1579 //-----------------------------------------------------------------------------
1580
checkCircularReferences(TXshColumn * columnCandidate)1581 bool TXsheet::checkCircularReferences(TXshColumn *columnCandidate) {
1582 if (!columnCandidate || !columnCandidate->getLevelColumn()) return false;
1583 TXshLevelColumn *lc = columnCandidate->getLevelColumn();
1584 int r0 = 0, r1 = -1;
1585 if (lc->getRange(r0, r1) <= 0) return false;
1586 int r;
1587 TXshCell oldCell;
1588 for (r = r0; r <= r1; r++) {
1589 TXshCell cell = lc->getCell(r);
1590 // to speed up:
1591 if (cell.m_level.getPointer() == oldCell.m_level.getPointer()) continue;
1592 if (checkCircularReferences(cell)) return true;
1593 oldCell = cell;
1594 }
1595 return false;
1596 }
1597
1598 //-----------------------------------------------------------------------------
1599
invalidateSound()1600 void TXsheet::invalidateSound() { m_imp->m_mixedSound = TSoundTrackP(); }
1601
1602 //-----------------------------------------------------------------------------
1603
checkCircularReferences(TXsheet * childCandidate)1604 bool TXsheet::checkCircularReferences(TXsheet *childCandidate) {
1605 if (this == childCandidate) return true;
1606 if (childCandidate == 0) return false;
1607 int i;
1608 for (i = 0; i < childCandidate->getColumnCount(); i++)
1609 if (checkCircularReferences(childCandidate->getColumn(i))) return true;
1610 return false;
1611 }
1612
1613 //-----------------------------------------------------------------------------
1614
1615 // Builds the camstand bbox associated to the specified xsheet
getBBox(int r) const1616 TRectD TXsheet::getBBox(int r) const {
1617 static const double maxDouble = (std::numeric_limits<double>::max)();
1618 static const TRectD voidRect(maxDouble, maxDouble, -maxDouble, -maxDouble);
1619
1620 //-----------------------------------------------------------------------
1621
1622 struct locals {
1623 static TRectD getBBox(const TXsheet *xsh, int r, int c) {
1624 // Discriminate cell content
1625 const TXshCell &cell = xsh->getCell(CellPosition(r, c));
1626 if (cell.isEmpty()) return voidRect;
1627
1628 if (TXshChildLevel *cl = cell.getChildLevel())
1629 return cl->getXsheet()->getBBox(cell.getFrameId().getNumber() - 1);
1630
1631 TXshSimpleLevel *sl = cell.getSimpleLevel();
1632 if (!sl ||
1633 !(sl->getType() &
1634 LEVELCOLUMN_XSHLEVEL)) // Avoid other mesh levels - which could
1635 return voidRect; // be deformed too...
1636
1637 // Retrieve column affine
1638 TAffine columnZaff;
1639 {
1640 TStageObject *colObj = xsh->getStageObject(TStageObjectId::ColumnId(c));
1641
1642 const TAffine &columnAff = colObj->getPlacement(r); // ...
1643 double columnZ = colObj->getZ(r); // ...
1644 double columnNoScaleZ = colObj->getGlobalNoScaleZ();
1645
1646 TStageObjectId cameraId =
1647 xsh->getStageObjectTree()->getCurrentCameraId();
1648 TStageObject *camera = xsh->getStageObject(cameraId);
1649
1650 const TAffine &cameraAff = camera->getPlacement(r); // ...
1651 double cameraZ = camera->getZ(r); // ...
1652
1653 if (!TStageObject::perspective(columnZaff, cameraAff, cameraZ,
1654 columnAff, columnZ, columnNoScaleZ))
1655 return voidRect;
1656 }
1657
1658 const TRectD &bbox = sl->getBBox(cell.getFrameId());
1659 if (bbox.getLx() <= 0.0 || bbox.getLy() <= 0.0) return voidRect;
1660
1661 return columnZaff * TScale(Stage::inch, Stage::inch) * bbox;
1662 }
1663 };
1664
1665 //-----------------------------------------------------------------------
1666
1667 // Initialize a union-neutral rect
1668 TRectD bbox(voidRect);
1669
1670 // Traverse the xsheet's columns, adding the bbox of each
1671 int c, cCount = getColumnCount();
1672 for (c = 0; c != cCount; ++c) {
1673 // Skip empty or invisible columns
1674 TXshColumn *column = getColumn(c);
1675 if (column->isEmpty() || !column->isCamstandVisible()) continue;
1676
1677 const TRectD &colBBox = locals::getBBox(this, r, c);
1678
1679 // Make the union
1680 bbox.x0 = std::min(bbox.x0, colBBox.x0);
1681 bbox.y0 = std::min(bbox.y0, colBBox.y0);
1682 bbox.x1 = std::max(bbox.x1, colBBox.x1);
1683 bbox.y1 = std::max(bbox.y1, colBBox.y1);
1684 }
1685
1686 return bbox;
1687 }
1688
1689 //-----------------------------------------------------------------------
1690
isRectEmpty(const CellPosition & pos0,const CellPosition & pos1) const1691 bool TXsheet::isRectEmpty(const CellPosition &pos0,
1692 const CellPosition &pos1) const {
1693 for (int frame = pos0.frame(); frame <= pos1.frame(); frame++)
1694 for (int layer = pos0.layer(); layer <= pos1.layer(); layer++)
1695 if (!getCell(CellPosition(frame, layer)).isEmpty()) return false;
1696 return true;
1697 }
1698
1699 //-----------------------------------------------------------------------
1700 // Function triggered by AutoInputCellNumberPopup.
1701 // executing this on column selection, set r1 = -1.
1702 // Here are the expected behaviors
1703 // 1. Cell Selection + Overwrite
1704 // Cells will be input from the top of the selected range.
1705 // New arrangement CANNOT run over the selected range.
1706 // If the new arrangement is shorter than the selected range,
1707 // excess cells will not be cleared but keep their contents.
1708 // (It is the same behavior as Celsys' QuickChecker)
1709 // 2. Cell Selection + Insert
1710 // New arrangement will be inserted before the selected range.
1711 // If the selected range has multiple columns, then the inserted
1712 // cells will be expanded to the longest arrangement with empty cells.
1713 // 3. Column Selection + Overwrite
1714 // Cells will be input from the top of the columns.
1715 // New arrangement CAN run over the existing column range.
1716 // If the new arrangement is shorter than the selected range,
1717 // excess cells will not be cleared but keep their contents.
1718 // 4. Column Selection + Insert
1719 // New arrangement will be inserted at the top of the columns.
1720 // If multiple columns are selected, then the inserted cells
1721 // will be expanded to the longest arrangement with empty cells.
1722 //
autoInputCellNumbers(int increment,int interval,int step,int repeat,int from,int to,int r0,int r1,bool isOverwrite,std::vector<int> columnIndices,std::vector<TXshLevelP> levels,int rowsCount)1723 void TXsheet::autoInputCellNumbers(int increment, int interval, int step,
1724 int repeat, int from, int to, int r0, int r1,
1725 bool isOverwrite,
1726 std::vector<int> columnIndices,
1727 std::vector<TXshLevelP> levels,
1728 int rowsCount) {
1729 int rowUpTo = (r1 == -1) ? rowsCount - 1
1730 : ((isOverwrite) ? std::min(r1, r0 + rowsCount - 1)
1731 : r0 + rowsCount - 1);
1732 // for each column
1733 for (int c = 0; c < columnIndices.size(); c++) {
1734 int columnIndex = columnIndices.at(c);
1735 TXshLevelP level = levels.at(c);
1736
1737 // on insertion, insert empty cells first
1738 if (!isOverwrite) insertCells(r0, columnIndex, rowsCount);
1739
1740 // obtain fids to be input
1741 std::vector<TFrameId> fids;
1742 if (increment == 0) {
1743 std::vector<TFrameId> wholeFids;
1744 level->getFids(wholeFids);
1745 if (from <= to) {
1746 for (auto itr = wholeFids.begin(); itr != wholeFids.end(); ++itr) {
1747 if ((*itr).getNumber() >= from && (*itr).getNumber() <= to)
1748 fids.push_back(*itr);
1749 else if ((*itr).getNumber() > to)
1750 break;
1751 }
1752 } else { // from > to
1753 for (auto itr = wholeFids.rbegin(); itr != wholeFids.rend(); ++itr) {
1754 if ((*itr).getNumber() <= from && (*itr).getNumber() >= to)
1755 fids.push_back(*itr);
1756 else if ((*itr).getNumber() < to)
1757 break;
1758 }
1759 }
1760 } else { // increment != 0
1761 int f = from;
1762 if (from <= to) {
1763 while (f <= to) {
1764 fids.push_back(TFrameId(f));
1765 f += increment;
1766 }
1767 } else { // from > to
1768 while (f >= to) {
1769 fids.push_back(TFrameId(f));
1770 f -= increment;
1771 }
1772 }
1773 }
1774
1775 // input cells
1776 int row = r0;
1777 int repeat_itr = 0;
1778 int fid_itr = 0;
1779 int step_interv_itr = 0;
1780 while (row <= rowUpTo) {
1781 // input cell
1782 if (step_interv_itr < step)
1783 setCell(row, columnIndex, TXshCell(level, fids.at(fid_itr)));
1784 // .. or set empty cell as interval
1785 else
1786 setCell(row, columnIndex, TXshCell());
1787
1788 // increment
1789 step_interv_itr++;
1790 // next frame
1791 if (step_interv_itr == step + interval) {
1792 fid_itr++;
1793 step_interv_itr = 0;
1794 }
1795 // next repeat cycle
1796 if (fid_itr == fids.size()) {
1797 repeat_itr++;
1798 fid_itr = 0;
1799 }
1800 if (repeat_itr == repeat) break;
1801
1802 row++;
1803 }
1804 }
1805 }
1806
1807 //---------------------------------------------------------
1808
setObserver(TXsheetColumnChangeObserver * observer)1809 void TXsheet::setObserver(TXsheetColumnChangeObserver *observer) {
1810 m_observer = observer;
1811 }
1812
1813 //---------------------------------------------------------
1814
notify(const TXsheetColumnChange & change)1815 void TXsheet::notify(const TXsheetColumnChange &change) {
1816 if (m_observer) m_observer->onChange(change);
1817 }
notifyFxAdded(const std::vector<TFx * > & fxs)1818 void TXsheet::notifyFxAdded(const std::vector<TFx *> &fxs) {
1819 if (m_observer) m_observer->onFxAdded(fxs);
1820 }
notifyStageObjectAdded(const TStageObjectId id)1821 void TXsheet::notifyStageObjectAdded(const TStageObjectId id) {
1822 if (m_observer) m_observer->onStageObjectAdded(id);
1823 }
isReferenceManagementIgnored(TDoubleParam * param)1824 bool TXsheet::isReferenceManagementIgnored(TDoubleParam *param) {
1825 if (m_observer) return m_observer->isIgnored(param);
1826 return false;
1827 }
getExpRefMonitor() const1828 ExpressionReferenceMonitor *TXsheet::getExpRefMonitor() const {
1829 return m_imp->m_expRefMonitor;
1830 }
1831 //---------------------------------------------------------
1832