AbstractZoomView.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { IControllerOutputListener } from "../Common/Interfaces/IControllerOutputListener";
  2. import { IDisplayInteractionListener } from "../Common/Interfaces/IDisplayInteractionListener";
  3. import { IZoomView } from "../Common/Interfaces/IZoomView";
  4. import { AbstractDisplayInteractionManager } from "./AbstractDisplayInteractionManager";
  5. import { PointF2D } from "../Common/DataObjects";
  6. export abstract class AbstractZoomView implements IControllerOutputListener, IDisplayInteractionListener {
  7. constructor(displayInteractionManager: AbstractDisplayInteractionManager) {
  8. this.displayInteractionManager = displayInteractionManager;
  9. this.displayInteractionManager.addListener(this);
  10. this.offsetXMin = Number.MIN_VALUE;
  11. this.offsetYMin = Number.MIN_VALUE;
  12. this.rangeXMin = 1;
  13. this.rangeYMin = 1;
  14. this.offsetXMax = Number.MAX_VALUE;
  15. this.offsetYMax = Number.MAX_VALUE;
  16. this.rangeXMax = 1000000000;
  17. this.rangeYMax = 1000000000;
  18. this.XScrollingEnabled = false;
  19. this.YScrollingEnabled = true;
  20. }
  21. protected displayInteractionManager: AbstractDisplayInteractionManager;
  22. protected rangeX: number;
  23. protected offsetX: number;
  24. protected rangeY: number;
  25. protected offsetY: number;
  26. protected lastRangeX: number;
  27. protected lastOffsetX: number;
  28. protected lastRangeY: number;
  29. protected lastOffsetY: number;
  30. protected aspectRatio: number = 1;
  31. protected zoomViews: IZoomView[] = [];
  32. protected mouseZoomMode: boolean = false;
  33. protected usesManuallyControlledZoomMode: boolean;
  34. private autoScrollY: boolean = true;
  35. protected abstract convertToUnitsReady(): boolean;
  36. protected abstract getPositionInUnits(relativePositionX: number, relativePositionY: number): PointF2D;
  37. protected abstract unitPosTouched(PosInUnits: PointF2D, relPosX: number, relPosY: number): void;
  38. protected abstract unitPosDoubleTouched(PosInUnits: PointF2D, relPosX: number, relPosY: number): void;
  39. protected abstract unitPosTouchDown(PosInUnits: PointF2D, relPosX: number, relPosY: number): void;
  40. protected abstract unitPosTouchUp(PosInUnits: PointF2D, relPosX: number, relPosY: number): void;
  41. protected abstract unitPosMove(PosInUnits: PointF2D, relativeDisplayPositionX: number, relativeDisplayPositionY: number): void;
  42. public positionTouched(relativePositionX: number, relativePositionY: number): void {
  43. if (!this.convertToUnitsReady()) {
  44. return;
  45. }
  46. const clickPosition: PointF2D = this.getPositionInUnits(relativePositionX, relativePositionY);
  47. this.unitPosTouched(clickPosition, relativePositionX, relativePositionY);
  48. }
  49. public get TouchActive(): boolean {
  50. return this.displayInteractionManager.TouchActive;
  51. }
  52. public get TouchMoving(): boolean {
  53. return this.displayInteractionManager.TouchMoving;
  54. }
  55. public positionDoubleTouched(relativePositionX: number, relativePositionY: number): void {
  56. if (!this.convertToUnitsReady()) {
  57. return;
  58. }
  59. const clickPosition: PointF2D = this.getPositionInUnits(relativePositionX, relativePositionY);
  60. this.unitPosDoubleTouched(clickPosition, relativePositionX, relativePositionY);
  61. }
  62. public get UsesManuallyControlledZoomMode(): boolean {
  63. return this.usesManuallyControlledZoomMode;
  64. }
  65. public set UsesManuallyControlledZoomMode(value: boolean) {
  66. this.usesManuallyControlledZoomMode = value;
  67. }
  68. public mouseDown(relativePositionX: number, relativePositionY: number, activateZoomOnRightMouseButton: boolean = false): void {
  69. if (!this.convertToUnitsReady()) {
  70. return;
  71. }
  72. this.selectScrollControllerY(false);
  73. this.lastRangeX = Math.max(1, this.RangeX);
  74. this.lastRangeY = Math.max(1, this.RangeY);
  75. this.lastOffsetX = this.OffsetX;
  76. this.lastOffsetY = this.OffsetY;
  77. const clickPosition: PointF2D = this.getPositionInUnits(relativePositionX, relativePositionY);
  78. this.unitPosTouchDown(clickPosition, relativePositionX, relativePositionY);
  79. if (!this.usesManuallyControlledZoomMode) {
  80. if (activateZoomOnRightMouseButton) { // zooming
  81. this.mouseZoomMode = true;
  82. } else { // panning
  83. this.mouseZoomMode = false;
  84. }
  85. }
  86. }
  87. public mouseUp(relativePositionX: number, relativePositionY: number): void {
  88. const clickPosition: PointF2D = this.getPositionInUnits(relativePositionX, relativePositionY);
  89. this.unitPosTouchUp(clickPosition, relativePositionX, relativePositionY);
  90. }
  91. public mouseMove(relativeDisplayPositionX: number, relativeDisplayPositionY: number, deltaX: number, deltaY: number): void {
  92. if (this.mouseZoomMode) { // zoom
  93. // zoom horizontally
  94. if (Math.abs(deltaX - 0) > 0.00000001) {
  95. this.RangeX = Math.abs(this.lastRangeX / (1 + deltaX));
  96. }
  97. // zoom vertically
  98. if (!this.lockRanges && Math.abs(deltaY - 0) > 0.00000001) {
  99. this.RangeY = Math.abs(this.lastRangeY / (1 - deltaY));
  100. }
  101. } else { // shift horizontally and/or vertically
  102. if (Math.abs(deltaX - 0) > 0.00000001) {
  103. this.OffsetX = this.lastOffsetX - deltaX * this.RangeX;
  104. }
  105. if (Math.abs(deltaY - 0) > 0.00000001) {
  106. this.OffsetY = this.lastOffsetY - deltaY * this.RangeY;
  107. }
  108. }
  109. const clickPosition: PointF2D = this.getPositionInUnits(relativeDisplayPositionX, relativeDisplayPositionY);
  110. this.unitPosMove(clickPosition, relativeDisplayPositionX, relativeDisplayPositionY);
  111. }
  112. public zoom(scale: number): void {
  113. // zoom horizontally
  114. this.RangeX = Math.abs(this.lastRangeX / scale);
  115. }
  116. public addZoomView(zoomable: IZoomView): void {
  117. this.zoomViews.push(zoomable);
  118. }
  119. public XScrollingEnabled: boolean;
  120. public YScrollingEnabled: boolean;
  121. public offsetXMin: number;
  122. public offsetYMin: number;
  123. public rangeXMin: number;
  124. public rangeYMin: number;
  125. public offsetXMax: number;
  126. public offsetYMax: number;
  127. public rangeXMax: number;
  128. public rangeYMax: number;
  129. public lockRanges: boolean;
  130. public get OffsetX(): number {
  131. return this.offsetX;
  132. }
  133. public set OffsetX(value: number) {
  134. this.offsetX = Math.min(this.offsetXMax, Math.max(this.offsetXMin, value));
  135. for (const zoomable of this.zoomViews) {
  136. zoomable.viewportXChanged(this.offsetX, this.RangeX);
  137. }
  138. }
  139. public get OffsetY(): number {
  140. return this.offsetY;
  141. }
  142. public set OffsetY(value: number) {
  143. this.offsetY = value;
  144. if (this.offsetY > this.offsetYMax) {
  145. this.offsetY = this.offsetYMax;
  146. } else if (this.offsetY < this.offsetYMin) {
  147. this.offsetY = this.offsetYMin;
  148. }
  149. for (const zoomable of this.zoomViews) {
  150. zoomable.viewportYChanged(this.offsetY, this.RangeY);
  151. }
  152. }
  153. public get RangeX(): number {
  154. return this.rangeX;
  155. }
  156. public set RangeX(value: number) {
  157. this.rangeX = Math.min(this.rangeXMax, Math.max(this.rangeXMin, value));
  158. if (this.lockRanges) {
  159. this.RangeY = this.RangeX / this.aspectRatio;
  160. for (const zoomable of this.zoomViews) {
  161. zoomable.viewportXChanged(this.OffsetX, this.RangeX);
  162. zoomable.viewportYChanged(this.OffsetY, this.RangeY);
  163. }
  164. } else {
  165. for (const zoomable of this.zoomViews) {
  166. zoomable.viewportXChanged(this.OffsetX, this.RangeX);
  167. }
  168. }
  169. }
  170. public get RangeY(): number {
  171. return this.rangeY;
  172. }
  173. public set RangeY(value: number) {
  174. this.rangeY = Math.min(this.rangeYMax, Math.max(this.rangeYMin, value));
  175. for (const zoomable of this.zoomViews) {
  176. zoomable.viewportYChanged(this.OffsetY, this.RangeY);
  177. }
  178. }
  179. protected set AspectRatio(value: number) {
  180. this.aspectRatio = value;
  181. }
  182. public initialize(offsetX: number, rangeX: number, offsetY: number, rangeY: number): void {
  183. this.setVerticalViewport(offsetY, rangeY);
  184. this.setHorizontalViewport(offsetX, rangeX);
  185. }
  186. public setHorizontalViewport(offsetX: number, rangeX: number): void {
  187. this.RangeX = rangeX;
  188. this.OffsetX = offsetX;
  189. this.lastRangeX = this.RangeX;
  190. this.lastOffsetX = this.OffsetX;
  191. }
  192. public setVerticalViewport(offsetY: number, rangeY: number): void {
  193. this.RangeY = rangeY;
  194. this.OffsetY = offsetY;
  195. this.lastRangeY = this.RangeY;
  196. this.lastOffsetY = this.OffsetY;
  197. }
  198. public viewSizeChanged(displayWidthInPixel: number, displayHeightInPixel: number): void {
  199. if (this.lockRanges) {
  200. this.aspectRatio = displayWidthInPixel / displayHeightInPixel;
  201. this.RangeY = this.RangeX / this.aspectRatio;
  202. this.lastRangeY = this.RangeY;
  203. }
  204. }
  205. public outputChanged(directlySet: boolean, currentValue: number, expectedValue: number): void {
  206. this.OffsetY = <number>currentValue;
  207. }
  208. public setOffsetXValueOnly(offsetX: number): void {
  209. this.offsetX = Math.min(this.offsetXMax, Math.max(this.offsetXMin, offsetX));
  210. }
  211. public setXOffset(offsetX: number, animated: boolean): void {
  212. if (this.displayInteractionManager.TouchActive || !this.XScrollingEnabled) {
  213. return;
  214. }
  215. }
  216. public setOffsetYValueOnly(offsetY: number): void {
  217. this.offsetY = Math.min(this.offsetYMax, Math.max(this.offsetYMin, offsetY));
  218. }
  219. public setYOffset(offsetY: number, animated: boolean): void {
  220. if (this.displayInteractionManager.TouchActive || !this.YScrollingEnabled) {
  221. return;
  222. }
  223. if (animated) {
  224. this.selectScrollControllerY(true);
  225. }
  226. }
  227. private selectScrollControllerY(autoScroll: boolean): void {
  228. if (this.autoScrollY !== autoScroll) {
  229. this.autoScrollY = autoScroll;
  230. }
  231. }
  232. public displaySizeChanged(width: number, height: number): void {
  233. throw new Error("Method not implemented.");
  234. }
  235. }