ThrowBall/Assets/Plugins/RayFire/Scripts/Components/RayfireCluster.cs

1020 lines
37 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace RayFire
{
[SelectionBase]
[AddComponentMenu("RayFire/Rayfire Cluster")]
[HelpURL("http://rayfirestudios.com/unity-online-help/components/unity-cluster-component/")]
public class RayfireCluster : MonoBehaviour
{
// Cluster Type
public enum ClusterType
{
ByPointCloud = 0,
BySharedArea = 1
}
[Space(2)]
[Header(" Properties")]
[Space (2)]
public ClusterType type = ClusterType.ByPointCloud;
[Range(1, 7)] public int depth = 1;
[Range(0, 100)] public int seed = 1;
[Range(0, 4)] public int smoothPass = 1;
[Header(" By Point Cloud")]
[Space (2)]
[Range(2, 100)] public int baseAmount = 5;
[Range(2, 50)] public int depthAmount = 2;
public ConnectivityType connectivity = ConnectivityType.ByBoundingBox;
[Header(" By Shared Area")]
[Space (2)]
[Range(2, 8)] public int minimumAmount = 2;
[Range(2, 8)] public int maximumAmount = 5;
// Preview
[HideInInspector] public bool showGizmo = true;
[HideInInspector] public bool colorPreview = false;
[HideInInspector] public bool scalePreview = false;
[HideInInspector] public Color wireColor = new Color(0.58f, 0.77f, 1f);
[HideInInspector] public float previewScale = 0f;
[HideInInspector] public List<RFCluster> allClusters = new List<RFCluster>();
[HideInInspector] public List<RFShard> allShards = new List<RFShard>();
int clusterId = 0;
/// /////////////////////////////////////////////////////////
/// Clustering
/// /////////////////////////////////////////////////////////
// Extract all children under root
public void Extract()
{
previewScale = 0f;
allShards.Clear();
allClusters.Clear();
// Get all child nodes
List<Transform> allTm = GetComponentsInChildren<Transform>().ToList();
// Set root as parent
for (int i = allTm.Count - 1; i >= 0; i--)
{
// Get all components
Component[] allComponents = allTm[i].GetComponents(typeof(Component));
// Destroy if empty object with transform only
if (allComponents.Length == 1)
{
DestroyImmediate(allTm[i].gameObject);
allTm.RemoveAt(i);
continue;
}
// Set as parent
allTm[i].parent = transform;
}
}
// Clusterize all children
public void Clusterize()
{
// Reset vars
// soloNow = 0;
// Extract all children first
Extract();
// Clear lists
allShards.Clear();
allClusters.Clear();
// Clusterize by type
ClusterizeVoronoi();
// Clusterize by size and range
ClusterizeRange();
}
/// /////////////////////////////////////////////////////////
/// Voronoi
/// /////////////////////////////////////////////////////////
// Clusterize by Voronoi pc
void ClusterizeVoronoi()
{
if (type == ClusterType.ByPointCloud)
{
// Create Base cluster
RFCluster mainCluster = SetupMainCluster(connectivity);
// Base amount of clusters is more than shards amount
if (baseAmount >= mainCluster.shards.Count)
return;
// Set shard neibs
RFShard.SetShardNeibs(mainCluster.shards, connectivity);
// List with all clusters
List<RFCluster> clusters = new List<RFCluster> {mainCluster};
// Collect base cluster
allClusters.Add(mainCluster);
// Clusterize
while (clusters.Count > 0)
{
// Get local cluster
RFCluster cls = clusters[0];
// Remove current cluster from clustering list
clusters.RemoveAt(0);
// Low amount of shards
if (cls.shards.Count < 4)
continue;
// Get amount
int amount = baseAmount;
if (cls.depth > 0)
amount = depthAmount;
// Get local depth roots
cls.childClusters = ClusterizeClusterByAmount(cls, amount);
// Collect new clusters
allClusters.AddRange(cls.childClusters);
// Check if local cluster should be clusterized further and add to list
if (cls.childClusters.Count > 0 && depth > cls.depth + 1)
clusters.AddRange(cls.childClusters);
}
// Set name to roots
SetClusterNames();
}
}
// Clusterize shards by amount
List<RFCluster> ClusterizeClusterByAmount(RFCluster parentCluster, int amount)
{
// Empty list of all new cluster roots
List<RFCluster> childClusters = new List<RFCluster>();
// Check if root has children more than at least 2
if (parentCluster.tm.childCount <= 2)
return childClusters;
// Shards are more than cluster amount
if (amount >= parentCluster.shards.Count)
return childClusters;
// Get bounds for random point cloud generation
Bounds bound = RFCluster.GetChildrenBound(parentCluster.tm);
// Collect cluster points
List<Vector3> voronoiPoints = VoronoiPointCloud(bound, amount);
// Create cluster for each point
foreach (Vector3 point in voronoiPoints)
{
RFCluster childCluster = new RFCluster();
childCluster.pos = point;
childCluster.depth = parentCluster.depth + 1;
// Set id
clusterId++;
childCluster.id = clusterId;
childClusters.Add(childCluster);
}
// Separate shards by closest to cluster distance
foreach (RFShard shard in parentCluster.shards)
{
// Puck first cluster
float rootDist = Vector3.Distance(shard.tm.position, childClusters[0].pos);
float minDist = rootDist;
shard.cluster = childClusters[0];
// Set closest cluster
if (childClusters.Count > 1)
{
for (int i = 1; i < childClusters.Count; i++)
{
rootDist = Vector3.Distance(shard.tm.position, childClusters[i].pos);
if (rootDist < minDist)
{
minDist = rootDist;
shard.cluster = childClusters[i];
}
}
}
// Apply shard to closest cluster and reset
shard.cluster.shards.Add(shard);
shard.cluster = null;
}
// Check child clusters and remove empty or solo shard clusters
List<RFShard> soloShards = new List<RFShard>();
for (int i = childClusters.Count - 1; i >= 0; i--)
{
if (childClusters[i].shards.Count < 2)
{
soloShards.AddRange(childClusters[i].shards);
childClusters.RemoveAt(i);
}
}
// First pass Find neib cluster for solo shards
SetSoloShardToCluster(soloShards, childClusters);
//// Second pass Find neib cluster for solo shards
SetSoloShardToCluster(soloShards, childClusters);
// Roughness pass. Remove shards from cluster and add to another.
if (smoothPass > 0 && connectivity == ConnectivityType.ByTriangles)
for (int i = 0; i < smoothPass; i++)
RoughnessPassShards(childClusters);
// Create clusters by connectivity check
if (connectivity == ConnectivityType.ByTriangles)
ConnectivityCheck(childClusters);
// Check if only one cluster left
if (childClusters.Count == 1)
{
childClusters.Clear();
return childClusters;
}
// Create root for cluster at shards center and set shards as children
foreach (RFCluster childCluster in childClusters)
CreateRoot(childCluster, parentCluster.tm);
return childClusters;
}
// Check cluster for connectivity and create new connected clusters
void ConnectivityCheck(List<RFCluster> childClusters)
{
// New list for solo shards
List<RFShard> soloShards = new List<RFShard>();
List<RFCluster> newChildClusters = new List<RFCluster>();
// Check every cluster for connectivity
foreach (RFCluster childCluster in childClusters)
{
// Collect solo shards with no neibs
for (int i = childCluster.shards.Count - 1; i >= 0; i--)
if (childCluster.shards[i].neibShards.Count == 0)
soloShards.Add(childCluster.shards[i]);
// Get list of all shards to check
List<RFShard> allShardsLoc = new List<RFShard>();
foreach (RFShard shard in childCluster.shards)
allShardsLoc.Add(shard);
// Check all shards and collect new clusters
int shardsAmount = allShardsLoc.Count;
List<RFCluster> newClusters = new List<RFCluster>();
while (allShardsLoc.Count > 0)
{
// List of connected shards
List<RFShard> newClusterShards = new List<RFShard>();
// List of check shards
List<RFShard> checkShards = new List<RFShard>();
// Start from first shard
checkShards.Add(allShardsLoc[0]);
newClusterShards.Add(allShardsLoc[0]);
// Collect by neibs
while (checkShards.Count > 0)
{
// Add neibs to check
foreach (RFShard neibShard in checkShards[0].neibShards)
{
// If neib among current cluster shards
if (allShardsLoc.Contains(neibShard) == true)
{
// And not already collected
if (newClusterShards.Contains(neibShard) == false)
{
checkShards.Add(neibShard);
newClusterShards.Add(neibShard);
}
}
}
// Remove checked
checkShards.RemoveAt(0);
}
// Child cluster connected
if (shardsAmount == newClusterShards.Count)
allShardsLoc.Clear();
// Child cluster not connected
else
{
// Create new cluster and add to parent
RFCluster newCluster = new RFCluster();
newCluster.pos = childCluster.pos;
newCluster.depth = childCluster.depth;
newCluster.shards = newClusterShards;
// Set id
clusterId++;
newCluster.id = clusterId;
newClusters.Add(newCluster);
// Remove from all shards list
for (int i = allShardsLoc.Count - 1; i >= 0; i--)
if (newClusterShards.Contains(allShardsLoc[i]) == true)
allShardsLoc.RemoveAt(i);
}
}
// Non connectivity. Remove original cluster
if (newClusters.Count > 0)
{
childCluster.shards.Clear();
newChildClusters.AddRange(newClusters);
}
}
// Clear empty clusters
for (int i = childClusters.Count - 1; i >= 0; i--)
if (childClusters[i].shards.Count == 0)
childClusters.RemoveAt(i);
// Collect new clusters
childClusters.AddRange(newChildClusters);
// Set clusters neib info
RFCluster.SetClusterNeib(childClusters, true);
// Second pass Find neib cluster for solo shards
SetSoloShardToCluster(soloShards, childClusters);
// Roughness pass. Remove shards from cluster and add to another.
if (smoothPass > 0)
RoughnessPassShards(childClusters);
}
/// /////////////////////////////////////////////////////////
/// By range
/// /////////////////////////////////////////////////////////
// Second clustering type
void ClusterizeRange()
{
if (type == ClusterType.BySharedArea)
{
Random.InitState(seed);
// Create Base cluster and collect
RFCluster mainCluster = SetupMainCluster(ConnectivityType.ByTriangles);
allClusters.Add(mainCluster);
// Set shard neibs
RFShard.SetShardNeibs(mainCluster.shards, ConnectivityType.ByTriangles);
// Clusterize base shards to clusters
List<RFCluster> childClusters = ClusterizeRangeShards(mainCluster);
// Create root and set shards and children
foreach (RFCluster childCluster in childClusters)
CreateRoot(childCluster, transform);
// Add to all clusters
allClusters.AddRange(childClusters);
// Clusterize clusters in depth
if (depth > 1)
{
for (int i = 1; i < depth; i++)
{
// Set clusters neib info
RFCluster.SetClusterNeib(mainCluster.childClusters, true);
// Get new depth clusters
List<RFCluster> newClusters = ClusterizeRangeClusters(mainCluster);
if (newClusters.Count > 1)
{
// Create root for all new clusters and set as parent for them
foreach (RFCluster cls in newClusters)
{
CreateRoot(cls, mainCluster.tm);
foreach (RFCluster childCLuster in cls.childClusters)
childCLuster.tm.parent = cls.tm;
}
// Set as child cluster for main cluster to be clusterized at next pass
mainCluster.childClusters = newClusters;
// Add to all clusters
allClusters.AddRange(newClusters);
// Get all nested clusters and increment depth
foreach (RFCluster cls in allClusters)
if (cls.id != 0)
cls.depth += 1;
}
}
}
// Set name to roots
SetClusterNames();
}
}
// Base clustering pass for shards
List<RFCluster> ClusterizeRangeShards(RFCluster mainCluster)
{
// Empty list of all new cluster roots
List<RFShard> soloShards = new List<RFShard>();
// List with all clusters
List<RFCluster> childClusters = new List<RFCluster>();
// Sort from smallest to biggest
mainCluster.shards.Sort();
// Clusterize starting from biggest
while (mainCluster.shards.Count > 0)
{
// Local amount of shards in cluster
int shardsAmount = Random.Range(minimumAmount, maximumAmount);
// Start from biggest shard
RFShard startShard = mainCluster.shards[0];
// Remove from lists
mainCluster.shards.RemoveAt(0);
// Starting shard list
List<RFShard> shardGroup = new List<RFShard>();
shardGroup.Add(startShard);
// Find neibs
for (int s = 0; s < shardsAmount - 1; s++)
{
// Get neib shard among cluster.shards with biggest shared area
RFShard biggestShard = GetNeibShardArea(shardGroup, mainCluster.shards);
// No neib with shared area
if (biggestShard == null)
break;
// TODO check if area is much smaller than with another neibs. Set as solo
// Add in group
shardGroup.Add(biggestShard);
// Remove from cluster.shards
mainCluster.shards.RemoveAll(t => t.id == biggestShard.id);
}
// Solo shard
if (shardGroup.Count == 1)
soloShards.Add(startShard);
// Group of shards for cluster
else if (shardGroup.Count > 1)
{
// Clusterize with picked shard
RFCluster childCluster = new RFCluster();
childCluster.shards.AddRange(shardGroup);
childCluster.depth = 1;
// Set id
clusterId++;
childCluster.id = clusterId;
// Collect luster
childClusters.Add(childCluster);
mainCluster.childClusters.Add(childCluster);
}
}
// First pass Find neib cluster for solo shards
SetSoloShardToCluster(soloShards, childClusters);
// Second pass Find neib cluster for solo shards
SetSoloShardToCluster(soloShards, childClusters);
// Roughness pass. Remove shards from cluster and add to another.
if (smoothPass > 0)
for (int i = 0; i < smoothPass; i++)
RoughnessPassShards(childClusters);
// TODO consider solo amount
// Set id
int startId = 1;
for (int i = 0; i < childClusters.Count; i++)
childClusters[i].id = startId + i;
// Set main cluster solo shards back to main cluster
mainCluster.shards.Clear();
mainCluster.shards.AddRange(soloShards);
return childClusters;
}
// Clustering pass for clusters
List<RFCluster> ClusterizeRangeClusters(RFCluster parentCluster)
{
// Empty list of all new solo clusters
List<RFCluster> soloClusters = new List<RFCluster>();
// List with all new clusters
List<RFCluster> newClusters = new List<RFCluster>();
// Sort from smallest to biggest
parentCluster.childClusters.Sort();
// Clusterize starting from biggest
while (parentCluster.childClusters.Count > 0)
{
// Local amount of shards in cluster
int clustersAmount = Random.Range(minimumAmount, maximumAmount);
// Start from biggest cluster
RFCluster startCluster = parentCluster.childClusters[0];
// Remove from lists
parentCluster.childClusters.RemoveAt(0);
// Starting list
List<RFCluster> clusterGroup = new List<RFCluster>();
clusterGroup.Add(startCluster);
for (int s = 0; s < clustersAmount - 1; s++)
{
// Get neib cluster among cluster with biggest shared area
RFCluster biggestCluster = RFCluster.GetNeibClusterArea(clusterGroup, parentCluster.childClusters);
// No neib with shared area
if (biggestCluster == null)
break;
// Add in group
clusterGroup.Add(biggestCluster);
// Remove from mainCluster.childClusters
parentCluster.childClusters.RemoveAll(t => t.id == biggestCluster.id);
}
// Solo
if (clusterGroup.Count == 1)
soloClusters.Add(startCluster);
// Group of clusters. Creat parent clusters for them
else
{
// Clusterize with picked clusters
RFCluster newCluster = new RFCluster();
newCluster.childClusters.AddRange(clusterGroup);
// Set depth
newCluster.depth = 0;
// Set id
clusterId++;
newCluster.id = clusterId;
// Collect luster
newClusters.Add(newCluster);
}
}
// Attach solo clusters to neib clusters
SetSoloClusterToCluster(soloClusters, newClusters);
// Attach solo clusters to neib clusters
SetSoloClusterToCluster(soloClusters, newClusters);
// Roughness pass. Remove shards from cluster and add to another.
if (smoothPass > 0)
for (int i = 0; i < smoothPass; i++)
RoughnessPassClusters(newClusters);
return newClusters;
}
// Roughness pass. Remove shards from cluster and add to another.
static void RoughnessPassShards(List<RFCluster> clusters)
{
// Set clusters neib info
RFCluster.SetClusterNeib(clusters, true);
// Check cluster for shard with one neib among cluster shards
for (int s = clusters.Count - 1; s >= 0; s--)
{
RFCluster cluster = clusters[s];
// Skip clusters with 2 shards
if (cluster.shards.Count == 2)
continue;
// Skip clusters without neib clusters
if (cluster.neibClusters.Count == 0)
continue;
// Collect shards to exclude from cluster
List<RFShard> excludeShards = new List<RFShard>();
List<RFCluster> attachToClusters = new List<RFCluster>();
// Check all shards and compare area with own cluster and neib clusters
foreach (RFShard shard in cluster.shards)
{
// Get amount of neibs among cluster shards
float areaInCluster = 0f;
for (int i = 0; i < shard.neibShards.Count; i++)
if (cluster.shards.Contains(shard.neibShards[i]) == true)
areaInCluster += shard.nArea[i];
// Compare with amount of shards from neib clusters
List<float> neibAreaList = new List<float>();
foreach (RFCluster neibCluster in cluster.neibClusters)
{
float areaInNeibCluster = 0f;
for (int i = 0; i < shard.neibShards.Count; i++)
if (neibCluster.shards.Contains(shard.neibShards[i]) == true)
areaInNeibCluster += shard.nArea[i];
neibAreaList.Add(areaInNeibCluster);
}
// Get maximum neibs in neib cluster
float maxArea = neibAreaList.Max();
// Skip shard because neib clusters has less neib shards
if (areaInCluster >= maxArea)
continue;
// Collect cluster which has more neibs for shard than own cluster
for (int i = 0; i < neibAreaList.Count; i++)
{
if (maxArea == neibAreaList[i])
{
excludeShards.Add(shard);
attachToClusters.Add(cluster.neibClusters[i]);
}
}
}
// Reorder shards
if (excludeShards.Count > 0)
{
for (int i = 0; i < excludeShards.Count; i++)
{
// Exclude from own cluster
for (int c = cluster.shards.Count - 1; c >= 0; c--)
if (cluster.shards[c] == excludeShards[i])
cluster.shards.RemoveAt(c);
// Add to neib cluster
attachToClusters[i].shards.Add(excludeShards[i]);
}
}
}
// Remove empty and solo clusters
for (int i = clusters.Count - 1; i >= 0; i--)
{
// Remove solo shard
if (clusters[i].shards.Count == 1)
{
clusters[i].shards.Clear();
}
// Remove empty cluster
if (clusters[i].shards.Count == 0)
clusters.RemoveAt(i);
}
}
// Roughness pass. Remove shards from cluster and add to another.
void RoughnessPassClusters(List<RFCluster> clusters)
{
// Set clusters neib info
RFCluster.SetClusterNeib(clusters, true);
// Check cluster for shard with one neib among cluster shards
foreach (RFCluster bigCluster in clusters)
{
// Skip clusters with 2 child clusters
if (bigCluster.childClusters.Count <= 2)
continue;
// Skip clusters without neib clusters
if (bigCluster.neibClusters.Count == 0)
continue;
// Collect shards to exclude from cluster
List<RFCluster> excludeClusters = new List<RFCluster>();
List<RFCluster> attachToClusters = new List<RFCluster>();
foreach (RFCluster childCluster in bigCluster.childClusters)
{
// Get amount of neibs among cluster child clusters
float areaInCluster = 0f;
for (int i = 0; i < childCluster.neibClusters.Count; i++)
if (bigCluster.childClusters.Contains(childCluster.neibClusters[i]) == true)
areaInCluster += childCluster.neibArea[i];
// Compare with amount of shards from neib clusters
List<float> neibAreaList = new List<float>();
foreach (RFCluster bigNeibCluster in bigCluster.neibClusters)
{
float areaInNeibCluster = 0f;
for (int i = 0; i < childCluster.neibClusters.Count; i++)
if (bigNeibCluster.childClusters.Contains(childCluster.neibClusters[i]) == true)
areaInNeibCluster += childCluster.neibArea[i];
neibAreaList.Add(areaInNeibCluster);
}
// Get maximum neibs in neib cluster
float maxArea = neibAreaList.Max();
// Skip shard because neib clusters has less neib shards
if (areaInCluster >= maxArea)
continue;
// Collect cluster which has more neibs for shard than own cluster
for (int i = 0; i < neibAreaList.Count; i++)
{
if (maxArea == neibAreaList[i])
{
excludeClusters.Add(childCluster);
attachToClusters.Add(bigCluster.neibClusters[i]);
}
}
}
// Skip if cluster may loose all shards
if (excludeClusters.Count + 1 >= bigCluster.childClusters.Count)
continue;
// Reorder shards
if (excludeClusters.Count > 0)
{
for (int i = 0; i < excludeClusters.Count; i++)
{
// Exclude from own cluster
for (int s = bigCluster.shards.Count - 1; s >= 0; s--)
if (bigCluster.childClusters[s] == excludeClusters[i])
bigCluster.childClusters.RemoveAt(s);
// Add to neib cluster
attachToClusters[i].childClusters.Add(excludeClusters[i]);
}
}
}
}
/// /////////////////////////////////////////////////////////
/// Methods
/// /////////////////////////////////////////////////////////
// Add solo shards to closest cluster
void SetSoloShardToCluster(List<RFShard> soloShards, List<RFCluster> childClusters)
{
// No solo shards
if (soloShards.Count == 0)
return;
// Find neib cluster for solo shards
for (int i = soloShards.Count - 1; i >= 0; i--)
{
int ind = GetNeibIndArea(soloShards[i]);
if (ind >= 0)
{
RFShard neibShard = soloShards[i].neibShards[ind];
for (int c = 0; c < childClusters.Count; c++)
{
if (childClusters[c].shards.Contains(neibShard) == true)
{
childClusters[c].shards.Add(soloShards[i]);
soloShards.RemoveAt(i);
continue;
}
}
}
}
}
// Get neib index with biggest shared area
int GetNeibIndArea(RFShard shard, List<RFShard> shardList = null)
{
// Get neib index with biggest shared area
float biggestArea = 0f;
int neibInd = 0;
for (int i = 0; i < shard.neibShards.Count; i++)
{
// Skip if check neib shard not in filter list
if (shardList != null)
if (shardList.Contains(shard.neibShards[i]) == false)
continue;
// Remember if bigger
if (shard.nArea[i] > biggestArea)
{
biggestArea = shard.nArea[i];
neibInd = i;
}
}
// Return index of neib with biggest shared area
if (biggestArea > 0)
return neibInd;
// No neib
return -1;
}
// Add solo shards to closest cluster
void SetSoloClusterToCluster(List<RFCluster> soloClusters, List<RFCluster> childClusters)
{
// No solo clusters
if (soloClusters.Count == 0)
return;
// Find neib cluster for solo cluster
for (int i = soloClusters.Count - 1; i >= 0; i--)
{
int ind = soloClusters[i].GetNeibIndArea();
if (ind >= 0)
{
RFCluster neibCluster = soloClusters[i].neibClusters[ind];
for (int c = 0; c < childClusters.Count; c++)
{
if (childClusters[c].childClusters.Contains(neibCluster) == true)
{
childClusters[c].childClusters.Add(soloClusters[i]);
soloClusters.RemoveAt(i);
}
}
}
}
}
// Set up main cluster and set shards
RFCluster SetupMainCluster (ConnectivityType connect)
{
// Create Base cluster
RFCluster cluster = new RFCluster();
cluster.tm = transform;
cluster.depth = 0;
cluster.pos = transform.position;
// Set cluster id
cluster.id = 0;
// Set shards for main cluster
RFShard.SetShards(cluster, connectivity);
clusterId = 0;
// Collect all shards
allShards.Clear();
allShards.AddRange(cluster.shards);
// TODO set bound
return cluster;
}
// Set name to roots
void SetClusterNames()
{
foreach (RFCluster cls in allClusters)
if (cls.id > 0)
if (cls.tm != null)
cls.tm.name = gameObject.name + "_cls_" + cls.id;
}
// Create root for cluster at shards center and set shards as children
void CreateRoot(RFCluster childCluster, Transform parentTm)
{
// Get cluster bound
Bounds childBound = GetShardsBound(childCluster.shards, childCluster.childClusters);
// Set cluster bound
childCluster.bound = childBound;
// Create root for cluster
GameObject childRoot = new GameObject();
// Set cluster root position
childCluster.tm = childRoot.transform;
childCluster.pos = childBound.center;
childCluster.tm.position = childBound.center;
// Set cluster parent
childCluster.tm.parent = parentTm;
// Set cluster root as shards parent
foreach (RFShard shard in childCluster.shards)
shard.tm.parent = childCluster.tm;
}
/// /////////////////////////////////////////////////////////
/// Bounds
/// /////////////////////////////////////////////////////////
// Get bound for list of shards
Bounds GetShardsBound(List<RFShard> shards, List<RFCluster> clusters = null)
{
// Get list of bounds
List<Bounds> bounds = new List<Bounds>();
// Consider shards bounds
foreach (RFShard shard in shards)
bounds.Add(shard.bnd);
// Consider clusters bounds
if (clusters != null)
foreach (RFCluster cluster in clusters)
bounds.Add(cluster.bound);
return RFCluster.GetBoundsBound(bounds.ToArray());
}
// Get neib shard from shardList which is neib to one of the shards
static RFShard GetNeibShardArea(List<RFShard> shardGroup, List<RFShard> shardList)
{
// No shards to pick
if (shardList.Count == 0)
return null;
// Get all neibs for shards, exclude neibs not from shardList
List<RFShard> allNeibs = new List<RFShard>();
// Biggest area
float biggestArea = 0f;
RFShard biggestShard = null;
// Check shard
foreach (RFShard shard in shardGroup)
{
// Check neibs
for (int i = 0; i < shard.neibShards.Count; i++)
{
// Neib shard has shared area lower than already founded
if (biggestArea >= shard.nArea[i])
continue;
// Neib already in neib list
if (allNeibs.Contains(shard.neibShards[i]) == true)
continue;
// Neib not among allowed shards
if (shardList.Contains(shard.neibShards[i]) == false)
continue;
// Remember neib
allNeibs.Add(shard.neibShards[i]);
biggestArea = shard.nArea[i];
biggestShard = shard.neibShards[i];
}
}
allNeibs = null;
// Pick shard with biggest area
return biggestShard;
}
/// /////////////////////////////////////////////////////////
/// Point cloud
/// /////////////////////////////////////////////////////////
// Generate random point3 cloud by bound and amount
List<Vector3> VoronoiPointCloud(Bounds bound, int am)
{
Random.InitState(seed + clusterId);
List<Vector3> points = new List<Vector3>();
for (int i = 0; i < am; i++)
{
float randomX = Random.Range(bound.min.x, bound.max.x);
float randomY = Random.Range(bound.min.y, bound.max.y);
float randomZ = Random.Range(bound.min.z, bound.max.z);
Vector3 randomPoint = new Vector3(randomX, randomY, randomZ);
points.Add(randomPoint);
}
return points;
}
}
}