marker-action.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 viewStart = (start + 3) >> 2;
  63. const viewEnd = viewStart + ((end - 4 * viewStart) >> 2);
  64. if (viewEnd <= viewStart) {
  65. // avoid edge cases with overlapping front/end intervals
  66. for (let i = start; i < end; ++i) {
  67. applyMarkerActionAtPosition(array, i, action);
  68. }
  69. return true;
  70. }
  71. const view = new Uint32Array(array.buffer, 0, array.buffer.byteLength >> 2);
  72. const frontStart = start;
  73. const frontEnd = Math.min(4 * viewStart, end);
  74. const backStart = Math.max(start, 4 * viewEnd);
  75. const backEnd = end;
  76. switch (action) {
  77. case MarkerAction.Highlight:
  78. for (let i = viewStart; i < viewEnd; ++i) view[i] |= 0x01010101;
  79. break;
  80. case MarkerAction.RemoveHighlight:
  81. for (let i = viewStart; i < viewEnd; ++i) view[i] &= ~0x01010101;
  82. break;
  83. case MarkerAction.Select:
  84. for (let i = viewStart; i < viewEnd; ++i) view[i] |= 0x02020202;
  85. break;
  86. case MarkerAction.Deselect:
  87. for (let i = viewStart; i < viewEnd; ++i) view[i] &= ~0x02020202;
  88. break;
  89. case MarkerAction.Toggle:
  90. for (let i = viewStart; i < viewEnd; ++i) view[i] ^= 0x02020202;
  91. break;
  92. case MarkerAction.Clear:
  93. for (let i = viewStart; i < viewEnd; ++i) view[i] = 0;
  94. break;
  95. default:
  96. assertUnreachable(action);
  97. }
  98. for (let i = frontStart; i < frontEnd; ++i) {
  99. applyMarkerActionAtPosition(array, i, action);
  100. }
  101. for (let i = backStart; i < backEnd; ++i) {
  102. applyMarkerActionAtPosition(array, i, action);
  103. }
  104. } else {
  105. switch (action) {
  106. case MarkerAction.Highlight:
  107. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] |= 1;
  108. break;
  109. case MarkerAction.RemoveHighlight:
  110. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] &= ~1;
  111. break;
  112. case MarkerAction.Select:
  113. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] |= 2;
  114. break;
  115. case MarkerAction.Deselect:
  116. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] &= ~2;
  117. break;
  118. case MarkerAction.Toggle:
  119. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] ^= 2;
  120. break;
  121. case MarkerAction.Clear:
  122. for (let i = 0, il = set.length; i < il; ++i) array[set[i]] = 0;
  123. break;
  124. default:
  125. assertUnreachable(action);
  126. }
  127. }
  128. return true;
  129. }
  130. export interface MarkerInfo {
  131. /**
  132. * 0: none marked;
  133. * 1: all marked;
  134. * -1: unclear, need to be calculated
  135. */
  136. average: 0 | 1 | -1
  137. /**
  138. * 0: none marked;
  139. * 1: all highlighted;
  140. * 2: all selected;
  141. * 3: all highlighted and selected
  142. * -1: mixed/unclear
  143. */
  144. status: 0 | 1 | 2 | 3 | -1
  145. }
  146. export function getMarkerInfo(action: MarkerAction, currentStatus: MarkerInfo['status']): MarkerInfo {
  147. let average: MarkerInfo['average'] = -1;
  148. let status: MarkerInfo['status'] = -1;
  149. switch (action) {
  150. case MarkerAction.Highlight:
  151. if (currentStatus === 0 || currentStatus === 1) {
  152. average = 1;
  153. status = 1;
  154. } else if (currentStatus === 2 || currentStatus === 3) {
  155. average = 1;
  156. status = 3;
  157. } else {
  158. average = 1;
  159. }
  160. break;
  161. case MarkerAction.RemoveHighlight:
  162. if (currentStatus === 0 || currentStatus === 1) {
  163. average = 0;
  164. status = 0;
  165. } else if (currentStatus === 2 || currentStatus === 3) {
  166. average = 1;
  167. status = 2;
  168. }
  169. break;
  170. case MarkerAction.Select:
  171. if (currentStatus === 1 || currentStatus === 3) {
  172. average = 1;
  173. status = 3;
  174. } else if (currentStatus === 0 || currentStatus === 2) {
  175. average = 1;
  176. status = 2;
  177. } else {
  178. average = 1;
  179. }
  180. break;
  181. case MarkerAction.Deselect:
  182. if (currentStatus === 1 || currentStatus === 3) {
  183. average = 1;
  184. status = 1;
  185. } else if (currentStatus === 0 || currentStatus === 2) {
  186. average = 0;
  187. status = 0;
  188. }
  189. break;
  190. case MarkerAction.Toggle:
  191. if (currentStatus === 1) {
  192. average = 1;
  193. status = 3;
  194. } else if (currentStatus === 2) {
  195. average = 0;
  196. status = 0;
  197. } else if (currentStatus === 3) {
  198. average = 1;
  199. status = 1;
  200. } else if (currentStatus === 0) {
  201. average = 1;
  202. status = 2;
  203. }
  204. break;
  205. case MarkerAction.Clear:
  206. average = 0;
  207. status = 0;
  208. break;
  209. }
  210. return { average, status };
  211. }
  212. /**
  213. * Assumes the action is applied to a partial set that is
  214. * neither the empty set nor the full set.
  215. */
  216. export function getPartialMarkerAverage(action: MarkerAction, currentStatus: MarkerInfo['status']) {
  217. switch (action) {
  218. case MarkerAction.Highlight:
  219. return 0.5;
  220. case MarkerAction.RemoveHighlight:
  221. if (currentStatus === 0) {
  222. return 0;
  223. } else if (currentStatus === 2 || currentStatus === 3) {
  224. return 0.5;
  225. } else { // 1 | -1
  226. return -1;
  227. }
  228. case MarkerAction.Select:
  229. return 0.5;
  230. case MarkerAction.Deselect:
  231. if (currentStatus === 1 || currentStatus === 3) {
  232. return 0.5;
  233. } else if (currentStatus === 0) {
  234. return 0;
  235. } else { // 2 | -1
  236. return -1;
  237. }
  238. case MarkerAction.Toggle:
  239. if (currentStatus === -1) {
  240. return -1;
  241. } else { // 0 | 1 | 2 | 3
  242. return 0.5;
  243. }
  244. case MarkerAction.Clear:
  245. if (currentStatus === -1) {
  246. return -1;
  247. } else if (currentStatus === 0) {
  248. return 0;
  249. } else { // 1 | 2 | 3
  250. return 0.5;
  251. }
  252. case MarkerAction.None:
  253. return -1;
  254. default:
  255. assertUnreachable(action);
  256. }
  257. }