scatterPanel.ts 3.9 KB

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