scatterPanel.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { VolumeBasedPanel } from "./volumeBasedPanel";
  2. import { float, Tools, Vector3, Tmp } from "babylonjs";
  3. import { Control3D } from "./control3D";
  4. import { Container3D } from "./container3D";
  5. /**
  6. * Class used to create a container panel where items get randomized planar mapping
  7. */
  8. export class ScatterPanel extends VolumeBasedPanel {
  9. private _iteration = 100.0;
  10. /**
  11. * Gets or sets the number of iteration to use to scatter the controls (100 by default)
  12. */
  13. public get iteration(): float {
  14. return this._iteration;
  15. }
  16. public set iteration(value: float) {
  17. if (this._iteration === value) {
  18. return;
  19. }
  20. this._iteration = value;
  21. Tools.SetImmediate(() => {
  22. this._arrangeChildren();
  23. });
  24. }
  25. protected _mapGridNode(control: Control3D, nodePosition: Vector3) {
  26. let mesh = control.mesh;
  27. let newPos = this._scatterMapping(nodePosition);
  28. if (!mesh) {
  29. return;
  30. }
  31. switch (this.orientation) {
  32. case Container3D.FACEORIGIN_ORIENTATION:
  33. case Container3D.FACEFORWARD_ORIENTATION:
  34. mesh.lookAt(new Vector3(0, 0, -1));
  35. break;
  36. case Container3D.FACEFORWARDREVERSED_ORIENTATION:
  37. case Container3D.FACEORIGINREVERSED_ORIENTATION:
  38. mesh.lookAt(new Vector3(0, 0, 1));
  39. break;
  40. }
  41. control.position = newPos;
  42. }
  43. private _scatterMapping(source: Vector3): Vector3 {
  44. source.x = (1.0 - Math.random() * 2.0) * this._cellWidth;
  45. source.y = (1.0 - Math.random() * 2.0) * this._cellHeight;
  46. return source;
  47. }
  48. protected _finalProcessing() {
  49. var meshes = [];
  50. for (var child of this._children) {
  51. if (!child.mesh) {
  52. continue;
  53. }
  54. meshes.push(child.mesh);
  55. }
  56. for (var count = 0; count < this._iteration; count++) {
  57. meshes.sort((a, b) => {
  58. let distance1 = a.position.lengthSquared();
  59. let distance2 = b.position.lengthSquared();
  60. if (distance1 < distance2) {
  61. return 1;
  62. } else if (distance1 > distance2) {
  63. return -1;
  64. }
  65. return 0;
  66. });
  67. let radiusPaddingSquared = Math.pow(this.margin, 2.0);
  68. let cellSize = Math.max(this._cellWidth, this._cellHeight);
  69. let difference2D = Tmp.Vector2[0];
  70. let difference = Tmp.Vector3[0];
  71. for (let i = 0; i < meshes.length - 1; i++) {
  72. for (let j = i + 1; j < meshes.length; j++) {
  73. if (i != j) {
  74. meshes[j].position.subtractToRef(meshes[i].position, difference);
  75. // Ignore Z axis
  76. difference2D.x = difference.x;
  77. difference2D.y = difference.y;
  78. let combinedRadius = cellSize;
  79. let distance = difference2D.lengthSquared() - radiusPaddingSquared;
  80. let minSeparation = Math.min(distance, radiusPaddingSquared);
  81. distance -= minSeparation;
  82. if (distance < (Math.pow(combinedRadius, 2.0))) {
  83. difference2D.normalize();
  84. difference.scaleInPlace((combinedRadius - Math.sqrt(distance)) * 0.5);
  85. meshes[j].position.addInPlace(difference);
  86. meshes[i].position.subtractInPlace(difference);
  87. }
  88. }
  89. }
  90. }
  91. }
  92. }
  93. }