1
0

PatternCursor.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. * PatternCursor.h
  3. * ---------------
  4. * Purpose: Class for storing pattern cursor information.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #pragma once
  10. #include "openmpt/all/BuildSettings.hpp"
  11. #include "../soundlib/Snd_defs.h"
  12. OPENMPT_NAMESPACE_BEGIN
  13. class PatternCursor
  14. {
  15. friend class PatternRect;
  16. public:
  17. enum Columns
  18. {
  19. firstColumn = 0,
  20. noteColumn = firstColumn,
  21. instrColumn,
  22. volumeColumn,
  23. effectColumn,
  24. paramColumn,
  25. lastColumn = paramColumn,
  26. };
  27. protected:
  28. // Pattern cursor structure (MSB to LSB):
  29. // | 16 bits - row | 13 bits - channel | 3 bits - channel component |
  30. // As you can see, the highest 16 bits contain a row index.
  31. // It is followed by a channel index, which is 13 bits wide.
  32. // The lowest 3 bits are used for addressing the components of a channel.
  33. // They are *not* used as a bit set, but treated as one of the Columns enum entries that can be found above.
  34. uint32 cursor;
  35. static_assert(MAX_BASECHANNELS <= 0x1FFF, "Check: Channel index in class PatternCursor is only 13 bits wide!");
  36. static_assert(MAX_PATTERN_ROWS <= 0xFFFF, "Check: Row index in class PatternCursor is only 16 bits wide!");
  37. public:
  38. // Construct cursor from given coordinates.
  39. PatternCursor(ROWINDEX row = 0, CHANNELINDEX channel = 0, Columns column = firstColumn)
  40. {
  41. Set(row, channel, column);
  42. };
  43. // Construct cursor from a given row and another cursor's horizontal position (channel + column).
  44. PatternCursor(ROWINDEX row, const PatternCursor &other)
  45. {
  46. Set(row, other);
  47. };
  48. // Get the pattern row the cursor is located in.
  49. ROWINDEX GetRow() const
  50. {
  51. return static_cast<ROWINDEX>(cursor >> 16);
  52. };
  53. // Get the pattern channel the cursor is located in.
  54. CHANNELINDEX GetChannel() const
  55. {
  56. return static_cast<CHANNELINDEX>((cursor & 0xFFFF) >> 3);
  57. };
  58. // Get the pattern channel column the cursor is located in.
  59. Columns GetColumnType() const
  60. {
  61. return static_cast<Columns>((cursor & 0x07));
  62. };
  63. // Set the cursor to given coordinates.
  64. void Set(ROWINDEX row, CHANNELINDEX channel, Columns column = firstColumn)
  65. {
  66. cursor = (row << 16) | ((channel << 3) & 0x1FFF) | (column & 0x07);
  67. };
  68. // Set the cursor to the same position as another curosr.
  69. void Set(const PatternCursor &other)
  70. {
  71. *this = other;
  72. };
  73. // Set the cursor to a given row and another cursor's horizontal position (channel + column).
  74. void Set(ROWINDEX row, const PatternCursor &other)
  75. {
  76. cursor = (row << 16) | (other.cursor & 0xFFFF);
  77. };
  78. // Only update the row of a cursor.
  79. void SetRow(ROWINDEX row)
  80. {
  81. Set(row, *this);
  82. };
  83. // Only update the horizontal position of a cursor.
  84. void SetColumn(CHANNELINDEX chn, Columns col)
  85. {
  86. Set(GetRow(), chn, col);
  87. };
  88. // Move the cursor relatively.
  89. void Move(int rows, int channels, int columns)
  90. {
  91. int row = std::max(int(0), static_cast<int>(GetRow()) + rows);
  92. int chn = static_cast<int>(GetChannel()) + channels;
  93. int col = static_cast<int>(GetColumnType() + columns);
  94. // Boundary checking
  95. if(chn < 0)
  96. {
  97. // Moving too far left
  98. chn = 0;
  99. col = firstColumn;
  100. }
  101. if(col < firstColumn)
  102. {
  103. // Extending beyond first column
  104. col = lastColumn;
  105. chn--;
  106. } else if(col > lastColumn)
  107. {
  108. // Extending beyond last column
  109. col = firstColumn;
  110. chn++;
  111. }
  112. Set(static_cast<ROWINDEX>(row), static_cast<CHANNELINDEX>(chn), static_cast<Columns>(col));
  113. }
  114. // Remove the channel column type from the cursor position.
  115. void RemoveColType()
  116. {
  117. cursor &= ~0x07;
  118. }
  119. // Compare the row of two cursors.
  120. // Returns -1 if this cursor is above of the other cursor,
  121. // 1 if it is below of the other cursor and 0 if they are at the same vertical position.
  122. int CompareRow(const PatternCursor &other) const
  123. {
  124. if(GetRow() < other.GetRow()) return -1;
  125. if(GetRow() > other.GetRow()) return 1;
  126. return 0;
  127. }
  128. // Compare the column (channel + sub column) of two cursors.
  129. // Returns -1 if this cursor is left of the other cursor,
  130. // 1 if it is right of the other cursor and 0 if they are at the same horizontal position.
  131. int CompareColumn(const PatternCursor &other) const
  132. {
  133. if((cursor & 0xFFFF) < (other.cursor & 0xFFFF)) return -1;
  134. if((cursor & 0xFFFF) > (other.cursor & 0xFFFF)) return 1;
  135. return 0;
  136. }
  137. // Check whether the cursor is placed on the first channel and first column.
  138. bool IsInFirstColumn() const
  139. {
  140. return (cursor & 0xFFFF) == 0;
  141. }
  142. // Ensure that the point lies within a given pattern size.
  143. void Sanitize(ROWINDEX maxRows, CHANNELINDEX maxChans)
  144. {
  145. ROWINDEX row = std::min(GetRow(), static_cast<ROWINDEX>(maxRows - 1));
  146. CHANNELINDEX chn = GetChannel();
  147. Columns col = GetColumnType();
  148. if(chn >= maxChans)
  149. {
  150. chn = maxChans - 1;
  151. col = lastColumn;
  152. } else if(col > lastColumn)
  153. {
  154. col = lastColumn;
  155. }
  156. Set(row, chn, col);
  157. };
  158. bool operator == (const PatternCursor &other) const
  159. {
  160. return cursor == other.cursor;
  161. }
  162. bool operator != (const PatternCursor &other) const
  163. {
  164. return !(*this == other);
  165. }
  166. };
  167. class PatternRect
  168. {
  169. protected:
  170. PatternCursor upperLeft, lowerRight;
  171. public:
  172. // Construct selection from two pattern cursors.
  173. PatternRect(const PatternCursor &p1, const PatternCursor &p2)
  174. {
  175. if(p1.CompareColumn(p2) <= 0)
  176. {
  177. upperLeft.cursor = (p1.cursor & 0xFFFF);
  178. lowerRight.cursor = (p2.cursor & 0xFFFF);
  179. } else
  180. {
  181. upperLeft.cursor = (p2.cursor & 0xFFFF);
  182. lowerRight.cursor = (p1.cursor & 0xFFFF);
  183. }
  184. if(p1.CompareRow(p2) <= 0)
  185. {
  186. upperLeft.cursor |= (p1.GetRow() << 16);
  187. lowerRight.cursor |= (p2.GetRow() << 16);
  188. } else
  189. {
  190. upperLeft.cursor |= (p2.GetRow() << 16);
  191. lowerRight.cursor |= (p1.GetRow() << 16);
  192. }
  193. };
  194. // Construct empty rect.
  195. PatternRect()
  196. {
  197. upperLeft.Set(0);
  198. lowerRight.Set(0);
  199. }
  200. // Return upper-left corner of selection.
  201. PatternCursor GetUpperLeft() const
  202. {
  203. return upperLeft;
  204. };
  205. // Return lower-right corner of selection.
  206. PatternCursor GetLowerRight() const
  207. {
  208. return lowerRight;
  209. };
  210. // Check if a given point is within the horizontal boundaries of the rect
  211. bool ContainsHorizontal(const PatternCursor &point) const
  212. {
  213. return point.CompareColumn(upperLeft) >= 0 && point.CompareColumn(lowerRight) <= 0;
  214. }
  215. // Check if a given point is within the vertical boundaries of the rect
  216. bool ContainsVertical(const PatternCursor &point) const
  217. {
  218. return point.CompareRow(upperLeft) >= 0 && point.CompareRow(lowerRight) <= 0;
  219. }
  220. // Check if a given point is within the rect
  221. bool Contains(const PatternCursor &point) const
  222. {
  223. return ContainsHorizontal(point) && ContainsVertical(point);
  224. }
  225. // Ensure that the selection doesn't exceed a given pattern size.
  226. void Sanitize(ROWINDEX maxRows, CHANNELINDEX maxChans)
  227. {
  228. upperLeft.Sanitize(maxRows, maxChans);
  229. lowerRight.Sanitize(maxRows, maxChans);
  230. };
  231. // Get first row of selection
  232. ROWINDEX GetStartRow() const
  233. {
  234. ASSERT(upperLeft.GetRow() <= lowerRight.GetRow());
  235. return upperLeft.GetRow();
  236. }
  237. // Get last row of selection
  238. ROWINDEX GetEndRow() const
  239. {
  240. ASSERT(upperLeft.GetRow() <= lowerRight.GetRow());
  241. return lowerRight.GetRow();
  242. }
  243. // Get first channel of selection
  244. CHANNELINDEX GetStartChannel() const
  245. {
  246. ASSERT(upperLeft.GetChannel() <= lowerRight.GetChannel());
  247. return upperLeft.GetChannel();
  248. }
  249. // Get last channel of selection
  250. CHANNELINDEX GetEndChannel() const
  251. {
  252. ASSERT(upperLeft.GetChannel() <= lowerRight.GetChannel());
  253. return lowerRight.GetChannel();
  254. }
  255. PatternCursor::Columns GetStartColumn() const
  256. {
  257. ASSERT((upperLeft.cursor & 0xFFFF) <= (lowerRight.cursor & 0xFFFF));
  258. return upperLeft.GetColumnType();
  259. }
  260. PatternCursor::Columns GetEndColumn() const
  261. {
  262. ASSERT((upperLeft.cursor & 0xFFFF) <= (lowerRight.cursor & 0xFFFF));
  263. return lowerRight.GetColumnType();
  264. }
  265. // Create a bitset of the selected columns of a channel. If a column is selected, the corresponding bit is set.
  266. // Example: If the first and second column of the channel are selected, the bits 00000011 would be returned.
  267. uint8 GetSelectionBits(CHANNELINDEX chn) const
  268. {
  269. const CHANNELINDEX startChn = GetStartChannel(), endChn = GetEndChannel();
  270. uint8 bits = 0;
  271. if(chn >= startChn && chn <= endChn)
  272. {
  273. // All columns could be selected (unless this is the first or last channel).
  274. bits = uint8_max;
  275. if(chn == startChn)
  276. {
  277. // First channel: Remove columns left of the start column type.
  278. bits <<= GetUpperLeft().GetColumnType();
  279. }
  280. if(chn == endChn)
  281. {
  282. // Last channel: Remove columns right of the end column type.
  283. bits &= (2 << GetLowerRight().GetColumnType()) - 1;
  284. }
  285. }
  286. return (bits & 0x1F);
  287. }
  288. // Get number of rows in selection
  289. ROWINDEX GetNumRows() const
  290. {
  291. ASSERT(upperLeft.GetRow() <= lowerRight.GetRow());
  292. return 1 + GetEndRow() - GetStartRow();
  293. }
  294. // Get number of channels in selection
  295. CHANNELINDEX GetNumChannels() const
  296. {
  297. ASSERT(upperLeft.GetChannel() <= lowerRight.GetChannel());
  298. return 1 + GetEndChannel() - GetStartChannel();
  299. }
  300. bool operator == (const PatternRect &other) const
  301. {
  302. return upperLeft == other.upperLeft && lowerRight == other.lowerRight;
  303. }
  304. };
  305. OPENMPT_NAMESPACE_END