volumeBasedPanel.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import { Tools } from "babylonjs/Misc/tools";
  2. import { Matrix, Vector3, TmpVectors } from "babylonjs/Maths/math.vector";
  3. import { int } from "babylonjs/types";
  4. import { Container3D } from "./container3D";
  5. import { Control3D } from "./control3D";
  6. /**
  7. * Abstract class used to create a container panel deployed on the surface of a volume
  8. */
  9. export abstract class VolumeBasedPanel extends Container3D {
  10. private _columns = 10;
  11. private _rows = 0;
  12. private _rowThenColum = true;
  13. private _orientation = Container3D.FACEORIGIN_ORIENTATION;
  14. protected _cellWidth: number;
  15. protected _cellHeight: number;
  16. /**
  17. * Gets or sets the distance between elements
  18. */
  19. public margin = 0;
  20. /**
  21. * Gets or sets the orientation to apply to all controls (BABYLON.Container3D.FaceOriginReversedOrientation by default)
  22. * | Value | Type | Description |
  23. * | ----- | ----------------------------------- | ----------- |
  24. * | 0 | UNSET_ORIENTATION | Control rotation will remain unchanged |
  25. * | 1 | FACEORIGIN_ORIENTATION | Control will rotate to make it look at sphere central axis |
  26. * | 2 | FACEORIGINREVERSED_ORIENTATION | Control will rotate to make it look back at sphere central axis |
  27. * | 3 | FACEFORWARD_ORIENTATION | Control will rotate to look at z axis (0, 0, 1) |
  28. * | 4 | FACEFORWARDREVERSED_ORIENTATION | Control will rotate to look at negative z axis (0, 0, -1) |
  29. */
  30. public get orientation(): number {
  31. return this._orientation;
  32. }
  33. public set orientation(value: number) {
  34. if (this._orientation === value) {
  35. return;
  36. }
  37. this._orientation = value;
  38. Tools.SetImmediate(() => {
  39. this._arrangeChildren();
  40. });
  41. }
  42. /**
  43. * Gets or sets the number of columns requested (10 by default).
  44. * The panel will automatically compute the number of rows based on number of child controls.
  45. */
  46. public get columns(): int {
  47. return this._columns;
  48. }
  49. public set columns(value: int) {
  50. if (this._columns === value) {
  51. return;
  52. }
  53. this._columns = value;
  54. this._rowThenColum = true;
  55. Tools.SetImmediate(() => {
  56. this._arrangeChildren();
  57. });
  58. }
  59. /**
  60. * Gets or sets a the number of rows requested.
  61. * The panel will automatically compute the number of columns based on number of child controls.
  62. */
  63. public get rows(): int {
  64. return this._rows;
  65. }
  66. public set rows(value: int) {
  67. if (this._rows === value) {
  68. return;
  69. }
  70. this._rows = value;
  71. this._rowThenColum = false;
  72. Tools.SetImmediate(() => {
  73. this._arrangeChildren();
  74. });
  75. }
  76. /**
  77. * Creates new VolumeBasedPanel
  78. */
  79. public constructor() {
  80. super();
  81. }
  82. protected _arrangeChildren() {
  83. this._cellWidth = 0;
  84. this._cellHeight = 0;
  85. let rows = 0;
  86. let columns = 0;
  87. let controlCount = 0;
  88. let currentInverseWorld = Matrix.Invert(this.node!.computeWorldMatrix(true));
  89. // Measure
  90. for (var child of this._children) {
  91. if (!child.mesh) {
  92. continue;
  93. }
  94. controlCount++;
  95. child.mesh.computeWorldMatrix(true);
  96. // child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, Tmp.Matrix[0]);
  97. let boundingBox = child.mesh.getHierarchyBoundingVectors();
  98. let extendSize = TmpVectors.Vector3[0];
  99. let diff = TmpVectors.Vector3[1];
  100. boundingBox.max.subtractToRef(boundingBox.min, diff);
  101. diff.scaleInPlace(0.5);
  102. Vector3.TransformNormalToRef(diff, currentInverseWorld, extendSize);
  103. this._cellWidth = Math.max(this._cellWidth, extendSize.x * 2);
  104. this._cellHeight = Math.max(this._cellHeight, extendSize.y * 2);
  105. }
  106. this._cellWidth += this.margin * 2;
  107. this._cellHeight += this.margin * 2;
  108. // Arrange
  109. if (this._rowThenColum) {
  110. columns = this._columns;
  111. rows = Math.ceil(controlCount / this._columns);
  112. } else {
  113. rows = this._rows;
  114. columns = Math.ceil(controlCount / this._rows);
  115. }
  116. let startOffsetX = (columns * 0.5) * this._cellWidth;
  117. let startOffsetY = (rows * 0.5) * this._cellHeight;
  118. let nodeGrid = [];
  119. let cellCounter = 0;
  120. if (this._rowThenColum) {
  121. for (var r = 0; r < rows; r++) {
  122. for (var c = 0; c < columns; c++) {
  123. nodeGrid.push(new Vector3((c * this._cellWidth) - startOffsetX + this._cellWidth / 2, (r * this._cellHeight) - startOffsetY + this._cellHeight / 2, 0));
  124. cellCounter++;
  125. if (cellCounter > controlCount) {
  126. break;
  127. }
  128. }
  129. }
  130. } else {
  131. for (var c = 0; c < columns; c++) {
  132. for (var r = 0; r < rows; r++) {
  133. nodeGrid.push(new Vector3((c * this._cellWidth) - startOffsetX + this._cellWidth / 2, (r * this._cellHeight) - startOffsetY + this._cellHeight / 2, 0));
  134. cellCounter++;
  135. if (cellCounter > controlCount) {
  136. break;
  137. }
  138. }
  139. }
  140. }
  141. cellCounter = 0;
  142. for (var child of this._children) {
  143. if (!child.mesh) {
  144. continue;
  145. }
  146. this._mapGridNode(child, nodeGrid[cellCounter]);
  147. cellCounter++;
  148. }
  149. this._finalProcessing();
  150. }
  151. /** Child classes must implement this function to provide correct control positioning */
  152. protected abstract _mapGridNode(control: Control3D, nodePosition: Vector3): void;
  153. /** Child classes can implement this function to provide additional processing */
  154. protected _finalProcessing() {
  155. }
  156. }