5 using System.Collections.Generic;
17 #region public members 18 public Action<ObjectCollection> OnCollectionUpdated;
27 public List<CollectionNode> NodeList =
new List<CollectionNode>();
32 [Tooltip(
"Type of surface to map the collection to")]
38 [Tooltip(
"Type of sorting to use")]
44 [Tooltip(
"Should the objects in the collection be rotated / how should they be rotated")]
50 [Tooltip(
"Whether to sort objects by row first or by column first")]
56 public bool IgnoreInactiveTransforms =
true;
62 [Tooltip(
"Radius for the sphere or cylinder")]
63 public float Radius = 2f;
66 [Tooltip(
"Radial range for radial layout")]
68 private float radialRange = 180f;
73 public float RadialRange
75 get {
return radialRange; }
76 set { radialRange = value; }
82 [Tooltip(
"Number of rows per column")]
88 [Tooltip(
"Width of cell per object")]
89 public float CellWidth = 0.5f;
94 [Tooltip(
"Height of cell per object")]
95 public float CellHeight = 0.5f;
98 [Tooltip(
"Margin between objects horizontally")]
99 private float horizontalMargin = 0.2f;
104 public float HorizontalMargin
106 get {
return horizontalMargin; }
107 set { horizontalMargin = value; }
111 [Tooltip(
"Margin between objects vertically")]
112 private float verticalMargin = 0.2f;
117 public float VerticalMargin
119 get {
return verticalMargin; }
120 set { verticalMargin = value; }
124 [Tooltip(
"Margin between objects in depth")]
125 private float depthMargin = 0.2f;
130 public float DepthMargin
132 get {
return depthMargin; }
133 set { depthMargin = value; }
148 public float Width {
get;
private set; }
150 public float Height {
get;
private set; }
153 #region private variables 154 private int _columns;
155 private float _circumference;
156 private float _radialCellAngle;
157 private Vector2 _halfCell;
167 List<CollectionNode> emptyNodes =
new List<CollectionNode>();
169 for (
int i = 0; i < NodeList.Count; i++)
171 if (NodeList[i].transform == null || (IgnoreInactiveTransforms && !NodeList[i].transform.gameObject.activeSelf) || NodeList[i].transform.parent==null || !(NodeList[i].transform.parent.gameObject==this.gameObject))
173 emptyNodes.Add(NodeList[i]);
178 for (
int i = 0; i < emptyNodes.Count; i++)
180 NodeList.Remove(emptyNodes[i]);
186 for (
int i = 0; i < this.transform.childCount; i++)
188 Transform child = this.transform.GetChild(i);
190 if (!ContainsNode(child) && (child.gameObject.activeSelf || !IgnoreInactiveTransforms))
194 node.
Name = child.name;
224 _columns = Mathf.CeilToInt((
float)NodeList.Count / Rows);
225 Width = _columns * CellWidth;
226 Height = Rows * CellHeight;
227 _halfCell =
new Vector2(CellWidth * 0.5f, CellHeight * 0.5f);
228 _circumference = 2f * Mathf.PI * Radius;
229 _radialCellAngle = RadialRange / _columns;
233 if (OnCollectionUpdated != null)
235 OnCollectionUpdated.Invoke(
this);
242 private void LayoutChildren() {
248 Vector3[] nodeGrid =
new Vector3[NodeList.Count];
249 Vector3 newPos = Vector3.zero;
252 startOffsetX = (_columns * 0.5f) * CellWidth;
253 startOffsetY = (Rows * 0.5f) * CellHeight;
262 for (
int c = 0; c < _columns; c++)
264 for (
int r = 0; r < Rows; r++)
266 if (cellCounter < NodeList.Count)
268 nodeGrid[cellCounter] =
new Vector3((c * CellWidth) - startOffsetX + _halfCell.x, -(r * CellHeight) + startOffsetY - _halfCell.y, 0f) + (Vector3)((NodeList[cellCounter])).Offset;
276 for (
int r = 0; r < Rows; r++)
278 for (
int c = 0; c < _columns; c++)
280 if (cellCounter < NodeList.Count)
282 nodeGrid[cellCounter] =
new Vector3((c * CellWidth) - startOffsetX + _halfCell.x, -(r * CellHeight) + startOffsetY - _halfCell.y, 0f) + (Vector3)((NodeList[cellCounter])).Offset;
291 switch (SurfaceType) {
293 for (
int i = 0; i < NodeList.Count; i++)
295 newPos = nodeGrid[i];
296 NodeList[i].transform.localPosition = newPos;
297 UpdateNodeFacing(NodeList[i], OrientType, newPos);
301 for (
int i = 0; i < NodeList.Count; i++)
303 newPos = CylindricalMapping(nodeGrid[i], Radius);
304 NodeList[i].transform.localPosition = newPos;
305 UpdateNodeFacing(NodeList[i], OrientType, newPos);
310 for (
int i = 0; i < NodeList.Count; i++)
312 newPos = SphericalMapping(nodeGrid[i], Radius);
313 NodeList[i].transform.localPosition = newPos;
314 UpdateNodeFacing(NodeList[i], OrientType, newPos);
321 for (
int i = 0; i < NodeList.Count; i++)
323 newPos = RadialMapping(nodeGrid[i], Radius, curRow, curColumn);
324 if (curColumn == (_columns - 1))
334 NodeList[i].transform.localPosition = newPos;
335 UpdateNodeFacing(NodeList[i], OrientType, newPos);
342 for (
int i = 0; i < NodeList.Count; i++)
344 newPos = ScatterMapping (nodeGrid[i], Radius);
345 Collider nodeCollider = NodeList[i].transform.GetComponentInChildren<Collider>();
346 if (nodeCollider != null)
349 Bounds bounds = nodeCollider.bounds;
350 NodeList[i].Radius = Mathf.Max (Mathf.Max(bounds.size.x, bounds.size.y), bounds.size.z) * 0.5f;
356 NodeList[i].Radius = 1f;
358 NodeList[i].transform.localPosition = newPos;
359 UpdateNodeFacing(NodeList[i], OrientType, newPos);
364 for (
int i = 0; i < 100; i++)
366 IterateScatterPacking (NodeList, Radius);
381 Vector3 pointOnAxisNearestNode;
385 node.
transform.rotation = Quaternion.LookRotation(node.
transform.position -
this.transform.position,
this.transform.up);
389 node.
transform.rotation = Quaternion.LookRotation(this.transform.position - node.
transform.position,
this.transform.up);
393 centerAxis = Vector3.Project(node.
transform.position -
this.transform.position,
this.transform.up);
394 pointOnAxisNearestNode = this.transform.position + centerAxis;
395 node.
transform.rotation = Quaternion.LookRotation(node.
transform.position - pointOnAxisNearestNode,
this.transform.up);
399 centerAxis = Vector3.Project(node.
transform.position -
this.transform.position,
this.transform.up);
400 pointOnAxisNearestNode = this.transform.position + centerAxis;
401 node.
transform.rotation = Quaternion.LookRotation(pointOnAxisNearestNode - node.
transform.position,
this.transform.up);
405 node.
transform.forward = transform.rotation * Vector3.forward;
409 node.
transform.forward = transform.rotation * Vector3.back;
413 node.
transform.forward = transform.rotation * Vector3.up;
417 node.
transform.forward = transform.rotation * Vector3.down;
424 throw new ArgumentOutOfRangeException();
435 private Vector3 SphericalMapping(Vector3 source,
float radius)
437 Radius = radius >= 0 ? Radius : radius;
438 Vector3 newPos =
new Vector3(0f, 0f, Radius);
440 float xAngle = (source.x / _circumference) * 360f;
441 float yAngle = -(source.y / _circumference) * 360f;
443 Quaternion rot = Quaternion.Euler(yAngle, xAngle, 0.0f);
444 newPos = rot * newPos;
455 private Vector3 CylindricalMapping(Vector3 source,
float radius)
457 Radius = radius >= 0 ? Radius : radius;
458 Vector3 newPos =
new Vector3(0f, source.y, Radius);
460 float xAngle = (source.x / _circumference) * 360f;
462 Quaternion rot = Quaternion.Euler(0.0f, xAngle, 0.0f);
463 newPos = rot * newPos;
476 private Vector3 RadialMapping(Vector3 source,
float radius,
int row,
int column)
478 Radius = radius >= 0 ? Radius : radius;
482 source.z = (Radius / Rows) * row;
484 float yAngle = _radialCellAngle * (column - (_columns * 0.5f)) + (_radialCellAngle * .5f);
486 Quaternion rot = Quaternion.Euler(0.0f, yAngle, 0.0f);
487 source = rot * source;
497 private bool ContainsNode(
Transform node)
499 for (
int i = 0; i < NodeList.Count; i++)
501 if (NodeList[i] != null)
503 if (NodeList[i].transform == node)
518 private Vector3 ScatterMapping(Vector3 source,
float radius)
520 source.x =
UnityEngine.Random.Range(-radius, radius);
521 source.y =
UnityEngine.Random.Range(-radius, radius);
531 private void IterateScatterPacking(List<CollectionNode> nodes,
float radiusPadding)
536 float distance1 = (circle1.
transform.localPosition).sqrMagnitude;
537 float distance2 = (circle2.
transform.localPosition).sqrMagnitude;
538 return distance1.CompareTo(distance2);
542 Vector2 difference2D;
545 float radiusPaddingSquared = Mathf.Pow(radiusPadding, 2f);
547 for (
int i = 0; i < nodes.Count - 1; i++)
549 for (
int j = i + 1; j < nodes.Count; j++)
553 difference = nodes[j].transform.localPosition - nodes[i].transform.localPosition;
555 difference2D.x = difference.x;
556 difference2D.y = difference.y;
557 float combinedRadius = nodes[i].Radius + nodes[j].Radius;
558 float distance = difference2D.SqrMagnitude() - radiusPaddingSquared;
559 float minSeparation = Mathf.Min(distance, radiusPaddingSquared);
560 distance -= minSeparation;
562 if (distance < (Mathf.Pow(combinedRadius, 2)))
564 difference2D.Normalize();
565 difference *= ((combinedRadius - Mathf.Sqrt(distance)) * 0.5f);
566 nodes[j].transform.localPosition += difference;
567 nodes[i].transform.localPosition -= difference;
579 Vector3 scale = (2f * Radius) * Vector3.one;
585 Gizmos.color = Color.green;
586 Gizmos.DrawWireMesh(CylinderMesh, transform.position, transform.rotation, scale);
589 Gizmos.color = Color.green;
590 Gizmos.DrawWireMesh(SphereMesh, transform.position, transform.rotation, scale);