marker-action.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /**
  2. * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { OrderedSet, Interval } from '../mol-data/int';
  7. import { BitFlags } from './bit-flags';
  8. import { assertUnreachable } from './type-helpers';
  9. export enum MarkerAction {
  10. None = 0x0,
  11. Highlight = 0x1,
  12. RemoveHighlight = 0x2,
  13. Select = 0x4,
  14. Deselect = 0x8,
  15. Toggle = 0x10,
  16. Clear = 0x20
  17. }
  18. export type MarkerActions = BitFlags<MarkerAction>
  19. export namespace MarkerActions {
  20. export const is: (m: MarkerActions, f: MarkerAction) => boolean = BitFlags.has;
  21. export const All = (
  22. MarkerAction.Highlight | MarkerAction.RemoveHighlight |
  23. MarkerAction.Select | MarkerAction.Deselect | MarkerAction.Toggle |
  24. MarkerAction.Clear
  25. ) as MarkerActions;
  26. export const Highlighting = (
  27. MarkerAction.Highlight | MarkerAction.RemoveHighlight |
  28. MarkerAction.Clear
  29. ) as MarkerActions;
  30. export const Selecting = (
  31. MarkerAction.Select | MarkerAction.Deselect | MarkerAction.Toggle |
  32. MarkerAction.Clear
  33. ) as MarkerActions;
  34. export function isReverse(a: MarkerAction, b: MarkerAction) {
  35. return (
  36. (a === MarkerAction.Highlight && b === MarkerAction.RemoveHighlight) ||
  37. (a === MarkerAction.RemoveHighlight && b === MarkerAction.Highlight) ||
  38. (a === MarkerAction.Select && b === MarkerAction.Deselect) ||
  39. (a === MarkerAction.Deselect && b === MarkerAction.Select) ||
  40. (a === MarkerAction.Toggle && b === MarkerAction.Toggle)
  41. );
  42. }
  43. }
  44. export function setMarkerValue(array: Uint8Array, status: 0 | 1 | 2 | 3, count: number) {
  45. array.fill(status, 0, count);
  46. }
  47. export function applyMarkerActionAtPosition(array: Uint8Array, i: number, action: MarkerAction) {
  48. switch (action) {
  49. case MarkerAction.Highlight: array[i] |= 1; break;
  50. case MarkerAction.RemoveHighlight: array[i] &= ~1; break;
  51. case MarkerAction.Select: array[i] |= 2; break;
  52. case MarkerAction.Deselect: array[i] &= ~2; break;
  53. case MarkerAction.Toggle: array[i] ^= 2; break;
  54. case MarkerAction.Clear: array[i] = 0; break;
  55. }
  56. }
  57. export function applyMarkerAction(array: Uint8Array, set: OrderedSet, action: MarkerAction) {
  58. if (action === MarkerAction.None) return false;
  59. if (Interval.is(set)) {
  60. const start = Interval.start(set);
  61. const end = Interval.end(set);
  62. const view = new Uint32Array(array.buffer, 0, array.buffer.byteLength >> 2);
  63. const viewStart = (start + 3) >> 2;
  64. const viewEnd = viewStart + ((end - 4 * viewStart) >> 2);
  65. const frontStart = start;
  66. const frontEnd = Math.min(4 * viewStart, end);
  67. const backStart = Math.max(start, 4 * viewEnd);
  68. const backEnd = end;
  69. switch (action) {
  70. case MarkerAction.Highlight:
  71. for (let i = viewStart; i < viewEnd; ++i) view[i] |= 0x01010101;
  72. break;
  73. case MarkerAction.RemoveHighlight:
  74. for (let i = viewStart; i < viewEnd; ++i) view[i] &= ~0x01010101;
  75. break;
  76. case MarkerAction.Select:
  77. for (let i = viewStart; i < viewEnd; ++i) view[i] |= 0x02020202;
  78. break;
  79. case MarkerAction.Deselect:
  80. for (let i = viewStart; i < viewEnd; ++i) view[i] &= ~0x02020202;
  81. break;
  82. case MarkerAction.Toggle:
  83. for (let i = viewStart; i < viewEnd; ++i) view[i] ^= 0x02020202;
  84. break;
  85. case MarkerAction.Clear:
  86. for (let i = viewStart; i < viewEnd; ++i) view[i] = 0;
  87. break;
  88. default:
  89. assertUnreachable(action);
  90. }
  91. for (let i = frontStart; i < frontEnd; ++i) {
  92. applyMarkerActionAtPosition(array, i, action);
  93. }
  94. for (let i = backStart; i < backEnd; ++i) {
  95. applyMarkerActionAtPosition(array, i, action);
  96. }
  97. } else {
  98. switch (action) {
  99. case MarkerAction.Highlight:
  100. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] |= 1;
  101. break;
  102. case MarkerAction.RemoveHighlight:
  103. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] &= ~1;
  104. break;
  105. case MarkerAction.Select:
  106. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] |= 2;
  107. break;
  108. case MarkerAction.Deselect:
  109. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] &= ~2;
  110. break;
  111. case MarkerAction.Toggle:
  112. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] ^= 2;
  113. break;
  114. case MarkerAction.Clear:
  115. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] = 0;
  116. break;
  117. default:
  118. assertUnreachable(action);
  119. }
  120. }
  121. return true;
  122. }
  123. export interface MarkerInfo {
  124. /**
  125. * 0: none marked;
  126. * 1: all marked;
  127. * -1: unclear, need to be calculated
  128. */
  129. average: 0 | 1 | -1
  130. /**
  131. * 0: none marked;
  132. * 1: all highlighted;
  133. * 2: all selected;
  134. * 3: all highlighted and selected
  135. * -1: mixed/unclear
  136. */
  137. status: 0 | 1 | 2 | 3 | -1
  138. }
  139. export function getMarkerInfo(action: MarkerAction, currentStatus: MarkerInfo['status']): MarkerInfo {
  140. let average: MarkerInfo['average'] = -1;
  141. let status: MarkerInfo['status'] = -1;
  142. switch (action) {
  143. case MarkerAction.Highlight:
  144. if (currentStatus === 0 || currentStatus === 1) {
  145. average = 1;
  146. status = 1;
  147. } else if (currentStatus === 2 || currentStatus === 3) {
  148. average = 1;
  149. status = 3;
  150. } else {
  151. average = 1;
  152. }
  153. break;
  154. case MarkerAction.RemoveHighlight:
  155. if (currentStatus === 0 || currentStatus === 1) {
  156. average = 0;
  157. status = 0;
  158. } else if (currentStatus === 2 || currentStatus === 3) {
  159. average = 1;
  160. status = 2;
  161. }
  162. break;
  163. case MarkerAction.Select:
  164. if (currentStatus === 1 || currentStatus === 3) {
  165. average = 1;
  166. status = 3;
  167. } else if (currentStatus === 0 || currentStatus === 2) {
  168. average = 1;
  169. status = 2;
  170. } else {
  171. average = 1;
  172. }
  173. break;
  174. case MarkerAction.Deselect:
  175. if (currentStatus === 1 || currentStatus === 3) {
  176. average = 1;
  177. status = 1;
  178. } else if (currentStatus === 0 || currentStatus === 2) {
  179. average = 0;
  180. status = 0;
  181. }
  182. break;
  183. case MarkerAction.Toggle:
  184. if (currentStatus === 1) {
  185. average = 1;
  186. status = 3;
  187. } else if (currentStatus === 2) {
  188. average = 0;
  189. status = 0;
  190. } else if (currentStatus === 3) {
  191. average = 1;
  192. status = 1;
  193. } else if (currentStatus === 0) {
  194. average = 1;
  195. status = 2;
  196. }
  197. break;
  198. case MarkerAction.Clear:
  199. average = 0;
  200. status = 0;
  201. break;
  202. }
  203. return { average, status };
  204. }
  205. /**
  206. * Assumes the action is applied to a partial set that is
  207. * neither the empty set nor the full set.
  208. */
  209. export function getPartialMarkerAverage(action: MarkerAction, currentStatus: MarkerInfo['status']) {
  210. switch (action) {
  211. case MarkerAction.Highlight:
  212. return 0.5;
  213. case MarkerAction.RemoveHighlight:
  214. if (currentStatus === 0) {
  215. return 0;
  216. } else if (currentStatus === 2 || currentStatus === 3) {
  217. return 0.5;
  218. } else { // 1 | -1
  219. return -1;
  220. }
  221. case MarkerAction.Select:
  222. return 0.5;
  223. case MarkerAction.Deselect:
  224. if (currentStatus === 1 || currentStatus === 3) {
  225. return 0.5;
  226. } else if (currentStatus === 0) {
  227. return 0;
  228. } else { // 2 | -1
  229. return -1;
  230. }
  231. case MarkerAction.Toggle:
  232. if (currentStatus === -1) {
  233. return -1;
  234. } else { // 0 | 1 | 2 | 3
  235. return 0.5;
  236. }
  237. case MarkerAction.Clear:
  238. if (currentStatus === -1) {
  239. return -1;
  240. } else if (currentStatus === 0) {
  241. return 0;
  242. } else { // 1 | 2 | 3
  243. return 0.5;
  244. }
  245. case MarkerAction.None:
  246. return -1;
  247. default:
  248. assertUnreachable(action);
  249. }
  250. }