1 #pragma once
2 
3 #ifndef XSHEET_INCLUDED
4 #define XSHEET_INCLUDED
5 
6 #include <memory>
7 
8 // TnzCore includes
9 #include "traster.h"
10 #include "tpersist.h"
11 #include "tsound.h"
12 
13 // TnzLib includes
14 #include "toonz/txshcolumn.h"
15 #include "toonz/txshlevel.h"
16 #include "toonz/txsheetcolumnchange.h"
17 
18 #include "cellposition.h"
19 
20 // STD includes
21 #include <set>
22 
23 #undef DVAPI
24 #undef DVVAR
25 #ifdef TOONZLIB_EXPORTS
26 #define DVAPI DV_EXPORT_API
27 #define DVVAR DV_EXPORT_VAR
28 #else
29 #define DVAPI DV_IMPORT_API
30 #define DVVAR DV_IMPORT_VAR
31 #endif
32 
33 //=============================================================================
34 
35 //    Forward declarations
36 
37 class TXshLevel;
38 class TXshCell;
39 class TAffine;
40 class TStageObjectId;
41 class TStageObject;
42 class TStageObjectTree;
43 class TStroke;
44 class TVectorImageP;
45 class FxDag;
46 class TControlPointObserver;
47 class TOfflineGL;
48 class ColumnFan;
49 class ToonzScene;
50 class TXshSoundColumn;
51 class TXshNoteSet;
52 class TFrameId;
53 class Orientation;
54 class TXsheetColumnChangeObserver;
55 class ExpressionReferenceMonitor;
56 
57 //=============================================================================
58 
59 //****************************************************************************************
60 //    TXsheet  declaration
61 //****************************************************************************************
62 
63 //! This is the base class for an xsheet. An Xsheet is composed of colums of
64 //! frames.
65 /*!
66 Inherits \b TSmartObject and \b TPersist.
67 
68         The class provides a collection of functions that returns xsheet
69 elements, defined in
70         struct \b TXsheetImp, and enables manipulation of these. Most important
71 xsheet elements
72         are: a \b column \b set \b TColumnSetT, a \b pegbar \b tree \b
73 TStageObjectTree, a \b fx
74         \b dag \b FxDag, a \b sound \b track \b TSoundTrack and a \b scene \b
75 ToonzScene.
76 
77                 For purposes of this class, a Column is a graphics layer, and a
78 row is a frame number.
79                 (Whether horizontal or vertial is a matter of displaying).
80 
81                 A \b column \b set contains all xsheet columns. A collection of
82 functions, concerning column
83         set, allows to manage xsheet column. It's possible to know xsheet column
84 count,
85         getColumnCount(), know first non empty column index,
86 getFirstFreeColumnIndex(),
87         or know if column in datum index is empty, isColumnEmpty(). You can work
88 on single xsheet
89         column, getColumn(), using its indexes: insert and remove a column with
90 insertColumn()
91         and removeColumn() or move column from an index to another using
92 moveColumn().
93 
94         You can manage also column visualization in xsheet, using the xsheet
95 object \b ColumnFan
96         getColumnFan(), and find column icon getColumnIcon().
97 
98                 cell positions will be identified by a pair of row+column index,
99 which is a separate class.
100 
101         It's possible work on xsheet cells directly, getCell() and setCell() or
102 getCells() and
103         setCells(); cells are identified in xsheet by two index one for row and
104 one for column.
105         You can insert, remove or clear cells using insertCells(), removeCells()
106 or clearCells();
107         the difference between the remove and the clear function is the shift of
108 remains cells.
109         Also there are functions to manipulate cells reverseCells(),
110 swingCells(),
111         incrementCells(), duplicateCells(), int upTo, stepCells(), eachCells().
112 
113         About \b pegbar \b tree \b TStageObjectTree, it's possible to manage it
114 through the stage object's
115         related functions.
116 
117         The \b fx \b dag \b FxDag getFxDag() is not managed with direct
118 functions but is always
119         up to date; in fact same functions update it. For example
120 insertColumn(), if necessary,
121         insert column index in fx dag, the same happen in setCells().
122 
123         The \b sound \b track \b TSoundTrack contain a mixed sound, computed
124 using makeSound(),
125         of all \b TXshSoundColumn present in xsheet.
126 
127         It's possible take and know the \b scene \b ToonzScene to which the
128 xsheet refers using
129         getScene() and setScene().
130 */
131 
132 class DVAPI TXsheet final : public TSmartObject, public TPersist {
PERSIST_DECLARATION(TXsheet)133   PERSIST_DECLARATION(TXsheet)
134 
135 public:
136   class DVAPI SoundProperties {
137   public:
138     int m_fromFrame;
139     int m_toFrame;
140     int m_frameRate;
141     bool m_isPreview;
142 
143     SoundProperties();
144     ~SoundProperties();
145 
146     inline bool operator==(const SoundProperties &c) const;
147     inline bool operator!=(const SoundProperties &c) const;
148   };
149 
150 private:
151   TSoundOutputDevice *m_player;
152 
153   /*!	\struct TXsheet::TXsheetImp
154   The TXsheetImp struct provides all objects necessary to define the \b TXsheet
155   class.
156 */
157   struct TXsheetImp;
158   std::unique_ptr<TXsheetImp> m_imp;
159   TXshNoteSet *m_notes;
160   SoundProperties *m_soundProperties;
161 
162   int m_cameraColumnIndex;
163   TXshColumn *m_cameraColumn;
164 
165   TXsheetColumnChangeObserver *m_observer;
166 
167   DECLARE_CLASS_CODE
168 
169 public:
170   TXsheet();
171   ~TXsheet();
172 
173   //! Returns a unique identifier associated with the xsheet instance.
174   unsigned long id() const;
175 
176   /*! Returns max frame number used in xsheet.
177           \sa getMaxFrame()
178   */
179   int getFrameCount() const;
180 
181   /*! Returns true if all cells in rect delimited by first frame \b \e
182      pos0.frame,
183      last frame \b \e pos1.frame and first layer \b \e pos0.layer, last layer \b
184      \e pos1.layer
185          are empty; otherwise, false.
186   */
187   bool isRectEmpty(const CellPosition &pos0, const CellPosition &pos1) const;
188 
189   /*! Returns the \b TXshCell cell in row identified by index \b \e row and
190      column identified by index \b \e col. If column \b TXshColumnP in \b \e col
191      is empty return
192      an empty cell.
193           \sa setCell(), getCells(), setCells()
194   */
195   const TXshCell &getCell(int row, int col) const;
196 
197   const TXshCell &getCell(const CellPosition &pos) const;
198 
199   bool setCell(int row, int col, const TXshCell &cell);
200   /*! Set \b \e cells[] to \b \e rowCount cells of column identified by index \b
201      \e col starting from row identified by index \b \e row. If column is empty
202      or is not a \b
203      TXshCellColumn set \b \e cells[] to \b \e rowCount empty cells.
204           \sa getCells(), setCells(), getCell()
205 */
206   void getCells(int row, int col, int rowCount, TXshCell cells[]) const;
207   /*! If column identified by index \b \e col is a \b TXshCellColumn or is empty
208     and is not
209     locked, this method sets to \b \e cells[] the given \b \e rowCount cells of
210     column \b \e col starting from
211     row \b \e row. If column in \b \e col is empty it creates a new column
212     recalling
213     \b TColumnSetT::touchColumn() and sets the new cells to \b \e cells[], and
214     on creating a new
215     column it adds it to fx dag \b FxDag.
216     If cells in \b \e row and \b \e col are not empty recalls \b
217     TXshCellColumn::setCells(),
218     insert the new cells \b \e cells[] in \b \e row \b \e col and shift old
219     cells.
220     If xsheet change it updates xsheet's frame count. Return false if cannot set
221     cells.
222     \sa getCells(), setCell(), getCell()
223   */
224   bool setCells(int row, int col, int rowCount, const TXshCell cells[]);
225   /*! If column identified by index \b \e col is not empty, is a \b \e
226      TXshCellColumn and is not locked this method inserts in row identified by
227      index \b \e row \b \e
228      rowCount empty cells, it calls TXshCellColumn::insertEmptyCells(). An
229      update of xsheet's frame count
230      is performed.
231           \sa setCells(), removeCells()
232   */
233   void insertCells(int row, int col, int rowCount = 1);
234 
235   /*! If column identified by index \b \e col is not empty, is a \b
236      TXshCellColumn and is not locked, this method removes \b \e rowCount cells
237      starting from \b \e
238      row, it calls TXshCellColumn::removeCells(). It removes cells without shift
239      remaining cells. An update of xsheet's frame count is performed.
240           \sa  clearCells(), insertCells()
241   */
242   void removeCells(int row, int col, int rowCount = 1);
243 
244   /*! If column identified by index \b \e col is not empty, is a \b
245     TXshCellColumn and is not locked, clear \b \e rowCount cells starting from
246     \b \e row and it recalls TXshCellColumn::clearCells(). Clears cells and it
247     shifts remaining cells. Xsheet's frame count is updated. \sa removeCells(),
248     insertCells()
249 */ void clearCells(int row, int col, int rowCount = 1);
250   /*! Clears xsheet. It sets to default values all xsheet elements contained in
251    * struct \b TXsheetImp.
252    */
253   void clearAll();
254   /*! Returns cell range of column identified by index \b \e col and set \b \e
255      r0 and \b \e r1 respectively to first and last not empty cell, it then
256      recalls \b
257      TXshCellColumn::getRange(). If column is empty or is not a \b
258      TXshCellColumn this method returns
259      zero and sets
260           \b \e r0 to 0 and \b \e r1 to -1.
261   */
262   int getCellRange(int col, int &r0, int &r1) const;
263   /*! Returns the max frame number of xsheet column identified by \b \e col and
264      calls \b TXshColumn::getMaxFrame().
265           \sa getFrameCount()
266   */
267   int getMaxFrame(int col) const;
268   /*! Returns true if xsheet column identified by \b \e col is empty, it calls
269           \b TXshColumn::isEmpty(), otherwise returns false.
270   */
271   bool isColumnEmpty(int col) const;
272   /*! Sets the level set \b \e levels with all current xsheet levels used, it
273      includes
274           in \b \e levels sub-xsheet levels too.
275           \sa isLevelUsed()
276   */
277   void getUsedLevels(std::set<TXshLevel *> &levels) const;
278   /*! Returns true if \b \e level is used in current xsheet or in sub-xsheet,
279      otherwise returns
280           false. It verifies if \b \e level is contained in level set \b
281      getUsedLevels().
282   */
283   bool isLevelUsed(TXshLevel *) const;
284   /*! The method retrieves a pegbar \b TStageObject with the passed \b \e id \b
285      TStageObjectId
286           specialization, calling TStageObjectTree::getStageObject().
287           \sa getStageObjectTree()
288   */
289   TStageObject *getStageObject(const TStageObjectId &id) const;
290   /*! The method retrieves the pegbar tree \b TStageObjectTree contained in
291      struct \b TXsheetImp.
292           \sa getStageObject()
293   */
294   TStageObjectTree *getStageObjectTree() const;
295   /*! Returns transformation matrix \b TAffine related to pegbar \b TStageObject
296      with \b \e id
297           \b TStageObjectId specialization in frame \b \e frame, it calls \b
298      TStageObject::getPlacement().
299           \sa getParentPlacement()
300   */
301   TAffine getPlacement(const TStageObjectId &id, int frame) const;
302   /*! Returns z value related to pegbar \b TStageObject with \b \e id
303      specialization \b TStageObjectId
304           in frame \b \e frame, it calls \b TStageObject::getZ().
305   */
306   double getZ(const TStageObjectId &id, int frame) const;
307 
308   double getNoScaleZ(const TStageObjectId &id) const;
309   /*! Returns transformation matrix \b TAffine related to parent of pegbar \b
310      TStageObject with
311           \b \e id \b TStageObjectId specialization in frame \b \e frame, it
312      calls \b TStageObject::getParentPlacement().
313           \sa getPlacement()
314   */
315   TAffine getParentPlacement(const TStageObjectId &id, int frame) const;
316   /*! Returns the center related to pegbar \b TStageObject with \b \e id
317      TStageObjectId specialization
318           in frame \b \e frame; it calls \b TStageObject::getCenter().
319           \sa setCenter()
320   */
321   TPointD getCenter(const TStageObjectId &id, int frame) const;
322   /*! Sets the center of pegbar \b TStageObject with \b \e id TStageObjectId
323      specialization in frame
324           \b \e frame, to \b \e center; it calls \b TStageObject::setCenter().
325           \sa getCenter()
326   */
327   void setCenter(const TStageObjectId &id, int frame, const TPointD &center);
328   /*! Returns parent related to pegbar \b TStageObject with \b \e id
329      TStageObjectId specialization;
330           it calls \b TStageObject::getParent().
331           \sa setStageObjectParent()
332   */
333   TStageObjectId getStageObjectParent(const TStageObjectId &id);
334   /*! Sets parent of pegbar \b TStageObject with \b \e id TStageObjectId
335      specialization to \b \e parentId;
336           it calls \b TStageObject::setParent().
337           \sa getStageObjectParent()
338   */
339   void setStageObjectParent(const TStageObjectId &id,
340                             const TStageObjectId &parentId);
341   /*! Returns true if pegbar \b TStageObject with \b \e id TStageObjectId
342      specialization has children,
343           that is if pegbar is a parent of another pegbar; it calls \b
344      TStageObject::hasChildren().
345           \sa getStageObjectParent() and setStageObjectParent()
346   */
347   bool hasChildren(const TStageObjectId &id) const;
348   /*! Returns current camera's transformation matrix \b TAffine related to frame
349      \b \e frame,
350           inclusive of zdepth. It Doesn't take care of transformation matrix
351      related to pegbar
352           linked to camera.
353           \note Used only in tab code, "the Tab" has just one camera while
354      "Toonz 5.2 Harlequin"
355           manages several camera.
356   */
357   TAffine getCameraAff(int frame) const;
358 
359   void reverseCells(int r0, int c0, int r1, int c1);
360   /*! The cells, contained in rect delimited by first row \b \e r0, last row \b
361      \e r1 and
362           first column \b \e c0, are appended at the end of the rect in a
363      reversed order.
364           The last cell of rect will not be repeated.
365   */
366   void swingCells(int r0, int c0, int r1, int c1);
367   /*! If cells is increasingly numbered and belong from same level return true;
368           otherwise return false.
369   \n	If returns true, the cells, contained in rect delimited by first row
370   \b \e r0, last row
371           \b \e r1 and first column \b \e c0, are repeated as if filling the
372   numbering gap between
373           two subsequence cell. For example if the command is applied to two
374   cell with
375           \b TFrameId number equal to 2 and 5, the result will be four cells: a
376   cell with
377           \b TFrameId number equal to 2 repeated three times and one cell with
378   \b TFrameId
379           number equal to 5.
380   \n	This method inserts in \b \e forUndo vector all the inserted cells, it
381   is useful for the undo process.
382   */
383   bool incrementCells(int r0, int c0, int r1, int c1,
384                       std::vector<std::pair<TRect, TXshCell>> &forUndo);
385   /*! A copy of cells, contained in rect delimited by first row \b \e r0, last
386      row \b \e r1
387           and first column \b \e c0, is made in row cells from \b r1+1 to \b \e
388      upTo. The duplicated
389           cells will be inserted by shifting the other down.
390   */
391   void duplicateCells(int r0, int c0, int r1, int c1, int upTo);
392   /*! The cells, contained in rect delimited by first row \b \e r0, last row \b
393      \e r1 and
394           first column \b \e c0, are repeated in order to have a step \b \e
395      type. The duplicated
396           cells will be inserted by shifting the other down.
397   */
398   void stepCells(int r0, int c0, int r1, int c1, int type);
399   /*! For each sequence of frame with same number, contained in rect delimited
400      by first row \b \e r0, last row \b \e r1 and
401           first column \b \e c0, a frame with same number is inserted.
402 */
403   void increaseStepCells(int r0, int c0, int &r1, int c1);
404   /*!
405 For each sequence of frame with same number, contained in rect delimited by
406 first row \b \e r0, last row \b \e r1 and
407           first column \b \e c0, a frame with same number is removed.
408 */
409   void decreaseStepCells(int r0, int c0, int &r1, int c1);
410   /*!
411 The cells, contained in rect delimited by first row \b \e r0, last row \b \e r1
412 and
413           first column \b \e c0, are reset in order to have no sequential
414 frame duplication.
415 */
416   void resetStepCells(int r0, int c0, int r1, int c1);
417   /*! Only one cell each step \b \e type, of the cells, contained in rect
418      delimited by
419           first row \b \e r0, last row \b \e r1 and first column \b \e c0, are
420      preserved,  the
421           others are deleted.
422   */
423   void eachCells(int r0, int c0, int r1, int c1, int type);
424   void rollupCells(int r0, int c0, int r1, int c1);
425   void rolldownCells(int r0, int c0, int r1, int c1);
426   void timeStretch(int r0, int c0, int r1, int c1, int nr);
427 
428   // force cells order in n-steps. returns the row amount after process
429   // if withBlank is greater than -1, remove empty cell from its order and
430   // insert blank frames with type*withBlank length at each n-step.
431   int reframeCells(int r0, int r1, int col, int type, int withBlank = -1);
432 
433   /*! Exposes level \b \e xl in xsheet starting from cell identified by \b \e
434      row and \b \e col.
435           Returns the number of the inserted cells.
436   */
437   int exposeLevel(int row, int col, TXshLevel *xl, bool overwrite = false);
438 
439   // cutomized exposseLevel used from LoadLevel command
440   int exposeLevel(int row, int col, TXshLevel *xl, std::vector<TFrameId> &fIds_,
441                   int xFrom = -1, int xTo = -1, int step = -1, int inc = -1,
442                   int frameCount = -1, bool doesFileActuallyExist = true);
443 
444   /*! Exposes level \b \e xl \b \e fids in xsheet starting from cell identified
445    * by \b \e row and \b \e col.
446    */
447   void exposeLevel(int row, int col, TXshLevel *xl, std::vector<TFrameId> fids,
448                    bool overwrite);
449   /*! Updates xsheet frame count, find max frame count between all
450           xsheet column, using getMaxFrame().
451   */
452   void updateFrameCount();
453   /*! Clears xsheet calling \b clearAll() and sets all xsheet elements,
454      contained
455           in \b TXsheetImp, to information contained in \b TIStream \b \e is.
456           \sa saveData()
457   */
458   void loadData(TIStream &is) override;
459   /*! Save all xsheet elements information, contained in \b TXsheetImp, in \b
460      TOStream \b \e os.
461           \sa loadData()
462   */
463   void saveData(TOStream &os) override;
464   /*! Inserts an empty column in \b \e index calling \b insertColumn().
465    */
466   void insertColumn(int index,
467                     TXshColumn::ColumnType type = TXshColumn::eLevelType);
468   /*! Insert \b \e column in column \b \e index. Insert column in the column
469      set, in the
470           pegbarTree \b TStageObjectTree contained in TXsheetImp and if column
471      fx \b TFx is
472           not empty in fx dag, calling FxDag::addToXsheet().
473           \sa removeColumn()
474   */
475   void insertColumn(int index, TXshColumn *column);  // becomes owner
476   /*! Removes column identified by \b \e index from xsheet column. If column in
477 \b \e index
478 is not empty and it has a \b TFx fx, removes column from fx dag, calls
479 FxDag::removeFromXsheet() and disconnects fx column from all output fx \b TFx,
480 than removes column from column set and from pegbarTree \b TStageObjectTree
481 contained
482 in TXsheetImp.
483 \sa insertColumn()
484 */
485   void removeColumn(int index);
486   /*! Moves column from xsheet column index \b \e srcIndex to \b \e dstIndex,
487                   columns between \b \e srcIndex+1 and \b \e dstIndex (included)
488      are shifted of -1.
489           \sa insertColumn() and removeColumn()
490   */
491   void moveColumn(int srcIndex, int dstIndex);
492 
493   /*! Returns a pointer to the \b TXshColumn identified in xsheet by index \b \e
494    * index.
495    */
496   TXshColumn *getColumn(int index) const;
497   /*! Returns xsheet column count, i.e the number of xsheet column used, it
498      calls
499           \b TColumnSetT::getColumnCount().
500           \sa getColumn() and getMaxFrame().
501   */
502   int getColumnCount() const;
503   /*! Returns first not empty column index in xsheet.
504    */
505   int getFirstFreeColumnIndex() const;
506 
507   TSoundTrack *makeSound(SoundProperties *properties);
508 #ifdef BUTTA
509   /*! Returns \b TSoundTrack with frame rate \b \e frameRate computed calling
510           \b TXshSoundColumn::mixingTogether() of all sound column contained in
511           xsheet. If xsheet doesn't have sound column it returns zero.
512   */
513   TSoundTrack *makeSound(int frameRate, bool isPreview = true);
514   /*! Utility Function */
515   TSoundTrack *makeSound(int fromFrame, int toFrame, int frameRate = -1);
516 #endif
517   void scrub(int frame, bool isPreview = false);
518   void stopScrub();
519   void play(TSoundTrackP soundtrack, int s0, int s1, bool loop);
520 
521   /*! Returns a pointer to object \b FxDag contained in \b TXsheetImp, this
522      object
523           allows the user to manage all fx dag, i.e. all element of fx
524      schematic.
525   */
526   FxDag *getFxDag() const;
527   /*! Returns a pointer to object \b ColumnFan contained in \b TXsheetImp, this
528           object allows the user to manage columns visualization in xsheet.
529           TXsheet maintains one column fan per each orientation.
530   */
531   ColumnFan *getColumnFan(const Orientation *o) const;
532   /*! Returns a pointer to \b ToonzScene contained in \b TXsheetImp, that is the
533      scene to
534           which the xsheet refers.
535           \sa setScene()
536   */
537   ToonzScene *getScene() const;
538   /*! Set scene \b ToonzScene contained in \b TXsheetImp to \b scene, change the
539           scene to which the xsheet refers.
540           \sa setScene()
541   */
542   void setScene(ToonzScene *scene);
543 
getNotes()544   TXshNoteSet *getNotes() const { return m_notes; }
545 
546   //! Returns true if the \b cellCandidate creates a circular reference.
547   //! A circular reference is obtained when \b cellCandidate is a subXsheet cell
548   //! and contains or matches
549   //! with this XSheet.
550   bool checkCircularReferences(const TXshCell &cellCandidate);
551 
552   //! Returns true if the \b columnCandidate creates a circular reference.
553   //! A circular reference is obtained when \b columnCandidate is a subXsheet
554   //! column and contains or matches
555   //! with this XSheet.
556   bool checkCircularReferences(TXshColumn *columnCandidate);
557 
558   void invalidateSound();
559 
560   //! Returns the xsheet content's \a camstand bbox at the specified row.
561   TRectD getBBox(int row) const;
562 
563   void autoInputCellNumbers(int increment, int interval, int step, int repeat,
564                             int from, int to, int r0, int r1, bool isOverwrite,
565                             std::vector<int> columnIndices,
566                             std::vector<TXshLevelP> levels, int rowsCount);
567 
setCameraColumnIndex(int index)568   void setCameraColumnIndex(int index) { m_cameraColumnIndex = index; }
getCameraColumnIndex()569   int getCameraColumnIndex() { return m_cameraColumnIndex; }
570 
setCameraColumnLocked(bool locked)571   void setCameraColumnLocked(bool locked) { m_cameraColumn->lock(locked); }
isCameraColumnLocked()572   bool isCameraColumnLocked() { return m_cameraColumn->isLocked(); }
573 
574   ExpressionReferenceMonitor *getExpRefMonitor() const;
575   void setObserver(TXsheetColumnChangeObserver *observer);
576 
577   void notify(const TXsheetColumnChange &change);
578   void notifyFxAdded(const std::vector<TFx *> &fxs);
579   void notifyStageObjectAdded(const TStageObjectId id);
580   bool isReferenceManagementIgnored(TDoubleParam *);
581 
582 protected:
583   bool checkCircularReferences(TXsheet *childCandidate);
584 
585 private:
586   // Not copiable
587   TXsheet(const TXsheet &);
588   TXsheet &operator=(const TXsheet &);
589 
590   /*! Return column in index if exists, overwise create a new column;
591 if column exist and is empty check \b isSoundColumn and return right type.
592 */
593   TXshColumn *touchColumn(int index,
594                           TXshColumn::ColumnType = TXshColumn::eLevelType);
595 };
596 
597 //-----------------------------------------------------------------------------
598 
599 #ifdef _WIN32
600 template class DVAPI TSmartPointerT<TXsheet>;
601 #endif
602 typedef TSmartPointerT<TXsheet> TXsheetP;
603 
604 #endif  // XSHEET_INCLUDED
605