Загрузка данных
using System.Collections.Generic;
using Sbrt.Av.Gameplay;
using UnityEngine;
using Random = System.Random;
public class InfoflowGenerator : MonoBehaviour
{
[Header("Bounds")]
[SerializeField]
private Transform boundsRoot;
[SerializeField]
private float stepSize = 0.25f;
[SerializeField]
private float verticalOffset;
private Bounds commonBounds;
private float FloorLocalY => verticalOffset;
[Header("Modules")]
[SerializeField]
public List<Transform> FloorModules;
[SerializeField]
public List<Transform> WallXYModules;
[SerializeField]
public List<Transform> WallZYModules;
[Header("Common path")]
[SerializeField]
private List<Vector3> commonPath = new List<Vector3>();
[SerializeField]
private int commonPathOffsetCellsZ = 2;
[SerializeField]
private int commonPathOffsetCellsX = 2;
[SerializeField]
private int commonPathOffsetCellsY = 2;
[SerializeField]
private int sourceRowToleranceCells = 1;
[SerializeField]
private int targetColumnToleranceCells = 1;
[SerializeField]
private int consumerColumnToleranceCells = 1;
[SerializeField]
private List<Vector3> floorModuleConnectionPoints = new List<Vector3>();
[SerializeField]
private List<Vector3> sourceRowMergePoints = new List<Vector3>();
[SerializeField]
private List<Vector3> targetColumnConnectionPoints = new List<Vector3>();
[SerializeField]
private bool drawSourceModuleConnectionPoints;
[SerializeField]
private bool drawSourceRowMergePoints;
[SerializeField]
private bool drawTargetColumnConnectionPoints;
[SerializeField]
private bool drawCommonPath;
[Header("Flow connections")]
[SerializeField]
private int randomConnectionSeed = 12345;
[SerializeField]
private int maximumTargetConnectionsPerSource = 3;
[SerializeField]
private int maximumConsumerConnectionsPerSource = 2;
[SerializeField]
public List<InfoflowModuleConnection> xzModuleConnections = new List<InfoflowModuleConnection>();
[SerializeField]
public List<InfoflowModuleConnection> yzModuleConnections = new List<InfoflowModuleConnection>();
[Header("Detailed flows")]
[SerializeField]
private List<InfoflowDetailedFlow> xzDetailedFlows = new List<InfoflowDetailedFlow>();
[SerializeField]
private List<InfoflowDetailedFlow> yzDetailedFlows = new List<InfoflowDetailedFlow>();
[SerializeField]
private float detailedFlowSpacingCells = 0.18f;
[Tooltip("Local Y offset for consumer flows on the XZ floor plane.")]
[SerializeField]
private float consumerFlowVerticalOffset = 0.25f;
[SerializeField]
private bool drawDetailedFlows;
[SerializeField]
private bool drawConsumerDetailedFlows = true;
[SerializeField]
private float detailedFlowPointRadius = 0.04f;
[SerializeField]
private bool useRoundedDetailedFlowPoints = true;
[SerializeField]
private float detailedFlowCornerRadiusCells = 0.45f;
[SerializeField]
private int detailedFlowCornerSegments = 8;
[SerializeField, Range(0f, 1f)]
private float detailedFlowColorSaturationMin = 0.65f;
[SerializeField, Range(0f, 1f)]
private float detailedFlowColorSaturationMax = 1f;
[SerializeField, Range(0f, 1f)]
private float detailedFlowColorValueMin = 0.75f;
[SerializeField, Range(0f, 1f)]
private float detailedFlowColorValueMax = 1f;
[Header("Detailed flow LineRenderers")]
[SerializeField]
private Transform lineRendererRoot;
[SerializeField]
private Material detailedFlowLineMaterial;
[SerializeField]
private float detailedFlowLineWidth = 0.025f;
[SerializeField]
private bool generateTargetFlowLineRenderers = true;
[SerializeField]
private bool generateConsumerFlowLineRenderers = true;
private List<InfoflowDataModel> allInfoflows;
private Dictionary<long, DataFragment> fp;
private Dictionary<long, DataFragment> consumers;
private Dictionary<long, DataFragment> providers;
private Dictionary<long, DataFragment> allFragments;
[Button()]
public void UpdateCommonBounds()
{
Transform targetBoundsRoot = boundsRoot != null ? boundsRoot : transform;
commonBounds = ObjectToFrustumFitter.GetLocalBoundsWithChildren(targetBoundsRoot.gameObject, transform);
}
[Button()]
public void BuildCommonFlowPath()
{
if (stepSize <= 0f)
{
Debug.LogWarning($"{nameof(InfoflowGenerator)}: Cell size must be greater than zero.", this);
return;
}
UpdateCommonBounds();
commonPath = new List<Vector3>();
floorModuleConnectionPoints = new List<Vector3>();
sourceRowMergePoints = new List<Vector3>();
targetColumnConnectionPoints = new List<Vector3>();
List<InfoflowModuleBounds> floorModuleBounds = GetModuleBounds(FloorModules, ModuleProjection.XZ);
List<InfoflowModuleBounds> targetModuleBounds = GetModuleBounds(WallXYModules, ModuleProjection.XY);
if (floorModuleBounds.Count == 0)
{
Debug.LogWarning($"{nameof(InfoflowGenerator)}: Source modules are empty.", this);
return;
}
if (targetModuleBounds.Count == 0)
{
Debug.LogWarning($"{nameof(InfoflowGenerator)}: Target modules are empty.", this);
return;
}
float offsetLocalX = GetOffsetLocal(commonPathOffsetCellsX);
float offsetLocalY = GetOffsetLocal(commonPathOffsetCellsY);
float offsetLocalZ = GetOffsetLocal(commonPathOffsetCellsZ);
float sourceBusLocalX = GetRightMostLocalMaxX(floorModuleBounds) + offsetLocalX;
List<SourceRowData> sourceRows = BuildSourceRows(floorModuleBounds, offsetLocalZ);
sourceRows.Sort((firstRow, secondRow) => firstRow.RowLocalZ.CompareTo(secondRow.RowLocalZ));
foreach (SourceRowData sourceRow in sourceRows)
{
sourceRow.Modules.Sort((firstModule, secondModule) =>
firstModule.LocalBounds.center.x.CompareTo(secondModule.LocalBounds.center.x));
foreach (InfoflowModuleBounds moduleBounds in sourceRow.Modules)
{
Vector3 sourceConnectionPoint = new Vector3(
moduleBounds.LocalBounds.center.x,
FloorLocalY,
sourceRow.RowLocalZ);
floorModuleConnectionPoints.Add(sourceConnectionPoint);
}
Vector3 sourceRowMergePoint = new Vector3(
sourceBusLocalX,
FloorLocalY,
sourceRow.RowLocalZ);
sourceRowMergePoints.Add(sourceRowMergePoint);
AddLocalPointToCommonPath(sourceRowMergePoint);
}
float targetWallLocalZ = GetAverageNearFaceLocalZ(targetModuleBounds);
float targetVerticalBusLocalX = GetLeftMostLocalMinX(targetModuleBounds) - offsetLocalX;
if (targetVerticalBusLocalX < sourceBusLocalX)
{
targetVerticalBusLocalX = sourceBusLocalX;
}
float targetBusLocalY = GetLowestLocalMinY(targetModuleBounds) - offsetLocalY;
AddLocalPointToCommonPath(new Vector3(sourceBusLocalX, FloorLocalY, targetWallLocalZ));
AddLocalPointToCommonPath(new Vector3(targetVerticalBusLocalX, FloorLocalY, targetWallLocalZ));
AddLocalPointToCommonPath(new Vector3(targetVerticalBusLocalX, targetBusLocalY, targetWallLocalZ));
List<TargetColumnData> targetColumns = BuildTargetColumns(targetModuleBounds);
targetColumns.Sort((firstColumn, secondColumn) =>
firstColumn.ColumnLocalX.CompareTo(secondColumn.ColumnLocalX));
foreach (TargetColumnData targetColumn in targetColumns)
{
Vector3 targetColumnConnectionPoint = new Vector3(
targetColumn.ColumnLocalX,
targetBusLocalY,
targetWallLocalZ);
targetColumnConnectionPoints.Add(targetColumnConnectionPoint);
AddLocalPointToCommonPath(targetColumnConnectionPoint);
}
}
[Button()]
public void GenerateRandomConnections()
{
GenerateRandomConnections(randomConnectionSeed);
}
public List<InfoflowModuleConnection> GenerateRandomConnections(int seed, List<DataFragment> fragments)
{
GenerateRandomConnections(seed);
HashSet<Transform> fragmentSet = new HashSet<Transform>(GetValidModules(fragments));
xzModuleConnections = FilterConnectionsByFragments(xzModuleConnections, fragmentSet);
yzModuleConnections = FilterConnectionsByFragments(yzModuleConnections, fragmentSet);
List<InfoflowModuleConnection> result = new List<InfoflowModuleConnection>(xzModuleConnections);
result.AddRange(yzModuleConnections);
return result;
}
private List<InfoflowModuleConnection> FilterConnectionsByFragments(List<InfoflowModuleConnection> connections,
HashSet<Transform> fragments)
{
List<InfoflowModuleConnection> filteredConnections = new List<InfoflowModuleConnection>();
if (connections == null || fragments == null || fragments.Count == 0)
return filteredConnections;
foreach (InfoflowModuleConnection connection in connections)
{
if (connection == null)
continue;
if (fragments.Contains(connection.SourceModule) || fragments.Contains(connection.TargetModule))
{
filteredConnections.Add(connection);
}
}
return filteredConnections;
}
public void GenerateRandomConnections(int seed)
{
xzModuleConnections = new List<InfoflowModuleConnection>();
yzModuleConnections = new List<InfoflowModuleConnection>();
if (FloorModules == null || FloorModules.Count == 0)
{
Debug.LogWarning($"{nameof(InfoflowGenerator)}: Source modules are empty.", this);
return;
}
List<Transform> validTargetModules = GetValidModules(WallXYModules);
List<Transform> validConsumerModules = GetValidModules(WallZYModules);
int clampedMaximumTargetConnectionsPerSource = Mathf.Clamp(
maximumTargetConnectionsPerSource,
0,
validTargetModules.Count);
int clampedMaximumConsumerConnectionsPerSource = Mathf.Clamp(
maximumConsumerConnectionsPerSource,
0,
validConsumerModules.Count);
Random random = new Random(seed);
foreach (Transform module in FloorModules)
{
if (module == null)
continue;
AddRandomConnectionsForSource(
module,
validTargetModules,
clampedMaximumTargetConnectionsPerSource,
xzModuleConnections,
random);
AddRandomConnectionsForSource(
module,
validConsumerModules,
clampedMaximumConsumerConnectionsPerSource,
yzModuleConnections,
random);
}
Debug.Log(
$"{nameof(InfoflowGenerator)}: Generated {xzModuleConnections.Count} target connections and {yzModuleConnections.Count} consumer connections. Seed: {seed}.",
this);
}
public void SetupInfoflowData(List<InfoflowDataModel> infoflows, DataFragment[] relatedConsumers,
DataFragment[] relatedProviders, List<DataFragment> fpList)
{
allInfoflows = infoflows;
fp = new Dictionary<long, DataFragment>(fpList.Count);
consumers = new Dictionary<long, DataFragment>(fpList.Count);
providers = new Dictionary<long, DataFragment>(fpList.Count);
allFragments = new Dictionary<long, DataFragment>(fpList.Count);
FloorModules = new List<Transform>(fpList.Count);
WallXYModules = new List<Transform>(relatedConsumers.Length);
WallZYModules = new List<Transform>(relatedProviders.Length);
foreach (var fragment in fpList)
{
fp.TryAdd(fragment.Id, fragment);
allFragments.TryAdd(fragment.Id, fragment);
FloorModules.Add(fragment.transform);
}
foreach (var fragment in relatedConsumers)
{
if (!fragment.gameObject.activeSelf)
continue;
consumers.TryAdd(fragment.Id, fragment);
allFragments.TryAdd(fragment.Id, fragment);
WallXYModules.Add(fragment.transform);
}
foreach (var fragment in relatedProviders)
{
if (!fragment.gameObject.activeSelf)
continue;
providers.TryAdd(fragment.Id, fragment);
allFragments.TryAdd(fragment.Id, fragment);
WallZYModules.Add(fragment.transform);
}
}
public List<InfoflowModuleConnection> GenerateConnections(Dictionary<long, DataFragment> newOnes)
{
xzModuleConnections = new List<InfoflowModuleConnection>();
yzModuleConnections = new List<InfoflowModuleConnection>();
foreach (var infoflow in allInfoflows)
{
if (newOnes.ContainsKey(infoflow.ProviderId) || newOnes.ContainsKey(infoflow.ConsumerId))
{
if (consumers.ContainsKey(infoflow.ProviderId) || consumers.ContainsKey(infoflow.ConsumerId))
{
xzModuleConnections.Add(new InfoflowModuleConnection()
{
SourceModule = allFragments[infoflow.ProviderId].transform,
TargetModule = allFragments[infoflow.ConsumerId].transform
});
}
else if (providers.ContainsKey(infoflow.ProviderId) || providers.ContainsKey(infoflow.ConsumerId))
{
yzModuleConnections.Add(new InfoflowModuleConnection()
{
SourceModule = allFragments[infoflow.ProviderId].transform,
TargetModule = allFragments[infoflow.ConsumerId].transform
});
}
}
}
List<InfoflowModuleConnection> result = new List<InfoflowModuleConnection>(xzModuleConnections);
result.AddRange(yzModuleConnections);
return result;
}
private void AddRandomConnectionsForSource(Transform floorModule,
List<Transform> possibleModules,
int maximumConnections,
List<InfoflowModuleConnection> resultConnections,
Random random)
{
if (possibleModules == null || possibleModules.Count == 0)
return;
if (maximumConnections <= 0)
return;
int connectionCount = random.Next(0, maximumConnections + 1);
if (connectionCount == 0)
return;
List<Transform> shuffledModules = GetShuffledModules(possibleModules, random);
for (int connectionIndex = 0; connectionIndex < connectionCount; connectionIndex++)
{
Transform targetModule = shuffledModules[connectionIndex];
resultConnections.Add(new InfoflowModuleConnection
{
SourceModule = floorModule,
TargetModule = targetModule
});
}
}
[Button()]
public void BuildDetailedFlows()
{
if (stepSize <= 0f)
{
Debug.LogWarning($"{nameof(InfoflowGenerator)}: Cell size must be greater than zero.", this);
return;
}
UpdateCommonBounds();
xzDetailedFlows = new List<InfoflowDetailedFlow>();
yzDetailedFlows = new List<InfoflowDetailedFlow>();
BuildXYDetailedFlowsInternal();
BuildYZDetailedFlowsInternal();
Debug.Log(
$"{nameof(InfoflowGenerator)}: Generated {xzDetailedFlows.Count} target detailed flows and {yzDetailedFlows.Count} consumer detailed flows.",
this);
}
private void BuildXYDetailedFlowsInternal()
{
if (xzModuleConnections == null || xzModuleConnections.Count == 0)
return;
List<InfoflowModuleBounds> floorModuleBounds = GetModuleBounds(FloorModules, ModuleProjection.XZ);
List<InfoflowModuleBounds> xyModuleBounds = GetModuleBounds(WallXYModules, ModuleProjection.XY);
Dictionary<Transform, InfoflowModuleBounds> floorBoundsByModule = BuildBoundsDictionary(floorModuleBounds);
Dictionary<Transform, InfoflowModuleBounds> targetBoundsByModule = BuildBoundsDictionary(xyModuleBounds);
List<DetailedFlowBuildData> flowBuildDataList =
BuildDetailedFlowData(floorBoundsByModule, targetBoundsByModule, xzModuleConnections);
if (flowBuildDataList.Count == 0)
return;
AssignSourcePortPositions(flowBuildDataList);
AssignTargetPortPositions(flowBuildDataList);
flowBuildDataList.Sort(CompareDetailedFlowOrder);
float spacingLocal = GetSpacingLocal();
float maximumLaneOffsetLocal = flowBuildDataList.Count * spacingLocal;
for (int flowIndex = 0; flowIndex < flowBuildDataList.Count; flowIndex++)
{
DetailedFlowBuildData flowBuildData = flowBuildDataList[flowIndex];
flowBuildData.LaneIndex = flowIndex;
flowBuildData.LaneOffsetLocal = (flowIndex + 1) * spacingLocal;
}
AssignSourceRowLaneOffsets(flowBuildDataList, spacingLocal);
TargetRouteData targetRouteData = BuildTargetRouteData(floorModuleBounds, xyModuleBounds);
Random detailedFlowColorRandom = new Random(randomConnectionSeed);
foreach (DetailedFlowBuildData flowBuildData in flowBuildDataList)
{
List<Vector3> sourceToTargetLocalPath =
BuildTargetLocalPath(
flowBuildData,
targetRouteData,
flowBuildData.SourceRowLaneOffsetLocal,
flowBuildData.LaneOffsetLocal,
maximumLaneOffsetLocal);
sourceToTargetLocalPath.Reverse();
InfoflowDetailedFlow detailedFlow = new InfoflowDetailedFlow
{
SourceModule = flowBuildData.Connection.SourceModule,
TargetModule = flowBuildData.Connection.TargetModule,
LaneIndex = flowBuildData.LaneIndex,
Color = GetRandomDetailedFlowColor(detailedFlowColorRandom),
Points = sourceToTargetLocalPath,
RoundedPoints = BuildRoundedLocalPath(sourceToTargetLocalPath)
};
xzDetailedFlows.Add(detailedFlow);
}
}
private void BuildYZDetailedFlowsInternal()
{
if (yzModuleConnections == null || yzModuleConnections.Count == 0)
return;
List<InfoflowModuleBounds> floorModuleBounds = GetModuleBounds(FloorModules, ModuleProjection.XZ);
List<InfoflowModuleBounds> yzModuleBounds = GetModuleBounds(WallZYModules, ModuleProjection.ZY);
Dictionary<Transform, InfoflowModuleBounds> sourceBoundsByModule = BuildBoundsDictionary(floorModuleBounds);
Dictionary<Transform, InfoflowModuleBounds>
consumerBoundsByModule = BuildBoundsDictionary(yzModuleBounds);
List<YZFlowBuildData> yzFlowBuildDataList = BuildYZFlowData(sourceBoundsByModule, consumerBoundsByModule);
if (yzFlowBuildDataList.Count == 0)
return;
AssignYZSourcePortPositions(yzFlowBuildDataList);
AssignYZPortPositions(yzFlowBuildDataList);
YZRouteData yzRouteData = BuildYZRouteData(floorModuleBounds, yzModuleBounds, yzFlowBuildDataList);
float spacingLocal = GetSpacingLocal();
AssignYZDirectionalLaneOffsets(
yzFlowBuildDataList,
yzRouteData,
spacingLocal);
yzFlowBuildDataList.Sort(CompareYZFlowOrder);
float maximumLaneOffsetLocal = yzFlowBuildDataList.Count * spacingLocal;
for (int flowIndex = 0; flowIndex < yzFlowBuildDataList.Count; flowIndex++)
{
YZFlowBuildData flowBuildData = yzFlowBuildDataList[flowIndex];
flowBuildData.LaneIndex = flowIndex;
flowBuildData.LaneOffsetLocal = (flowIndex + 1) * spacingLocal;
}
AssignYZSourceRowLaneOffsets(yzFlowBuildDataList, spacingLocal);
Random detailedFlowColorRandom = new Random(randomConnectionSeed + 7919);
foreach (YZFlowBuildData flowBuildData in yzFlowBuildDataList)
{
List<Vector3> sourceToYZLocalPath =
BuildYZLocalPath(
flowBuildData,
yzRouteData,
flowBuildData.SourceRowLaneOffsetLocal,
flowBuildData.LaneOffsetLocal,
maximumLaneOffsetLocal);
sourceToYZLocalPath.Reverse();
InfoflowDetailedFlow detailedFlow = new InfoflowDetailedFlow
{
SourceModule = flowBuildData.Connection.SourceModule,
TargetModule = flowBuildData.Connection.TargetModule,
LaneIndex = flowBuildData.LaneIndex,
Color = GetRandomDetailedFlowColor(detailedFlowColorRandom),
Points = sourceToYZLocalPath,
RoundedPoints = BuildRoundedLocalPath(sourceToYZLocalPath)
};
yzDetailedFlows.Add(detailedFlow);
}
}
private TargetRouteData BuildTargetRouteData(List<InfoflowModuleBounds> floorModuleBounds,
List<InfoflowModuleBounds> targetModuleBounds)
{
float offsetLocalX = GetOffsetLocal(commonPathOffsetCellsX);
float offsetLocalY = GetOffsetLocal(commonPathOffsetCellsY);
float sourceBusLocalX = GetRightMostLocalMaxX(floorModuleBounds) + offsetLocalX;
float targetVerticalBusLocalX = GetLeftMostLocalMinX(targetModuleBounds) - offsetLocalX;
if (targetVerticalBusLocalX < sourceBusLocalX)
{
targetVerticalBusLocalX = sourceBusLocalX;
}
return new TargetRouteData
{
SourceBusLocalX = sourceBusLocalX,
TargetVerticalBusLocalX = targetVerticalBusLocalX,
TargetWallLocalZ = GetAverageNearFaceLocalZ(targetModuleBounds),
TargetBusLocalY = GetLowestLocalMinY(targetModuleBounds) - offsetLocalY
};
}
private YZRouteData BuildYZRouteData(List<InfoflowModuleBounds> floorModuleBounds,
List<InfoflowModuleBounds> consumerModuleBounds,
List<YZFlowBuildData> consumerFlowBuildDataList)
{
float offsetLocalX = GetOffsetLocal(commonPathOffsetCellsX);
float offsetLocalY = GetOffsetLocal(commonPathOffsetCellsY);
float offsetLocalZ = GetOffsetLocal(commonPathOffsetCellsZ);
float sourceLeftBusLocalX = GetLeftMostLocalMinX(floorModuleBounds) - offsetLocalX;
float minSourceRowLocalZ = float.MaxValue;
float maxSourceRowLocalZ = float.MinValue;
foreach (YZFlowBuildData flowBuildData in consumerFlowBuildDataList)
{
float sourceConnectionLocalZ = GetSourceConnectionLocalZ(flowBuildData.SourceBounds, offsetLocalZ);
minSourceRowLocalZ = Mathf.Min(minSourceRowLocalZ, sourceConnectionLocalZ);
maxSourceRowLocalZ = Mathf.Max(maxSourceRowLocalZ, sourceConnectionLocalZ);
}
if (minSourceRowLocalZ == float.MaxValue)
{
minSourceRowLocalZ = commonBounds.center.z;
maxSourceRowLocalZ = commonBounds.center.z;
}
float middleSourceRowsLocalZ = (minSourceRowLocalZ + maxSourceRowLocalZ) * 0.5f;
return new YZRouteData
{
SourceLeftBusLocalX = sourceLeftBusLocalX,
MiddleSourceRowsLocalZ = middleSourceRowsLocalZ,
ConsumerLeftFaceLocalX = GetLeftMostLocalMinX(consumerModuleBounds),
ConsumerBusLocalY = GetLowestLocalMinY(consumerModuleBounds) - offsetLocalY
};
}
private List<Vector3> BuildTargetLocalPath(DetailedFlowBuildData flowBuildData,
TargetRouteData targetRouteData,
float sourceRowOffsetLocal,
float commonOffsetLocal,
float maximumLaneOffsetLocal)
{
float offsetLocalY = GetOffsetLocal(commonPathOffsetCellsY);
float offsetLocalZ = GetOffsetLocal(commonPathOffsetCellsZ);
float sourceLaneOffsetLocal = Mathf.Max(0f, sourceRowOffsetLocal);
float invertedTurnOffsetLocal = Mathf.Max(0f, maximumLaneOffsetLocal - commonOffsetLocal);
float sourceEntryLocalX = flowBuildData.SourcePortLocalX;
float sourceEntryLocalZ = flowBuildData.SourceBounds.LocalBounds.min.z;
float sourcePreEntryLocalZ = sourceEntryLocalZ - offsetLocalZ;
float sourceLaneLocalZ = sourcePreEntryLocalZ - sourceLaneOffsetLocal;
float sourceTurnLocalX = targetRouteData.SourceBusLocalX + invertedTurnOffsetLocal;
float targetTurnLocalX = targetRouteData.TargetVerticalBusLocalX + invertedTurnOffsetLocal;
float targetEntryLocalX = flowBuildData.TargetPortLocalX;
float targetEntryLocalY = flowBuildData.TargetBounds.LocalBounds.min.y;
float targetPreEntryLocalY = targetEntryLocalY - offsetLocalY;
float targetBusLaneLocalY = targetRouteData.TargetBusLocalY - commonOffsetLocal;
float targetWallLocalZ = targetRouteData.TargetWallLocalZ;
List<Vector3> path = new List<Vector3>
{
new Vector3(sourceEntryLocalX, FloorLocalY, sourceEntryLocalZ),
new Vector3(sourceEntryLocalX, FloorLocalY, sourcePreEntryLocalZ),
new Vector3(sourceEntryLocalX, FloorLocalY, sourceLaneLocalZ),
new Vector3(sourceTurnLocalX, FloorLocalY, sourceLaneLocalZ),
new Vector3(sourceTurnLocalX, FloorLocalY, targetWallLocalZ),
new Vector3(targetTurnLocalX, FloorLocalY, targetWallLocalZ),
new Vector3(targetTurnLocalX, targetBusLaneLocalY, targetWallLocalZ),
new Vector3(targetEntryLocalX, targetBusLaneLocalY, targetWallLocalZ),
new Vector3(targetEntryLocalX, targetPreEntryLocalY, targetWallLocalZ),
new Vector3(targetEntryLocalX, targetEntryLocalY, targetWallLocalZ)
};
return RemoveDuplicateLocalPoints(path);
}
private List<Vector3> BuildYZLocalPath(YZFlowBuildData flowBuildData,
YZRouteData yzRouteData,
float sourceRowOffsetLocal,
float commonOffsetLocal,
float maximumLaneOffsetLocal)
{
float offsetLocalZ = GetOffsetLocal(commonPathOffsetCellsZ);
float sourceLaneOffsetLocal = Mathf.Max(0f, sourceRowOffsetLocal);
float invertedTurnOffsetLocal = Mathf.Max(0f, maximumLaneOffsetLocal - commonOffsetLocal);
float directionalLaneOffsetLocal = Mathf.Max(0f, flowBuildData.ConsumerDirectionalLaneOffsetLocal);
float floorFlowLocalY = FloorLocalY + consumerFlowVerticalOffset;
float sourceEntryLocalX = flowBuildData.SourcePortLocalX;
float sourceEntryLocalZ = flowBuildData.SourceBounds.LocalBounds.min.z;
float sourcePreEntryLocalZ = sourceEntryLocalZ - offsetLocalZ;
float sourceLaneLocalZ = sourcePreEntryLocalZ - sourceLaneOffsetLocal;
float sourceTurnLocalX = yzRouteData.SourceLeftBusLocalX - invertedTurnOffsetLocal;
float middleLaneLocalZ =
yzRouteData.MiddleSourceRowsLocalZ +
flowBuildData.ConsumerDirection * directionalLaneOffsetLocal;
float consumerLeftFaceLocalX = flowBuildData.ConsumerBounds.LocalBounds.min.x;
float consumerColumnLocalZ = flowBuildData.ConsumerPortLocalZ;
float consumerEntryLocalY = flowBuildData.ConsumerBounds.LocalBounds.min.y;
float consumerBusLaneLocalY = yzRouteData.ConsumerBusLocalY - directionalLaneOffsetLocal;
List<Vector3> path = new List<Vector3>
{
new Vector3(sourceEntryLocalX, floorFlowLocalY, sourceEntryLocalZ),
new Vector3(sourceEntryLocalX, floorFlowLocalY, sourcePreEntryLocalZ),
new Vector3(sourceEntryLocalX, floorFlowLocalY, sourceLaneLocalZ),
new Vector3(sourceTurnLocalX, floorFlowLocalY, sourceLaneLocalZ),
new Vector3(sourceTurnLocalX, floorFlowLocalY, middleLaneLocalZ),
new Vector3(yzRouteData.ConsumerLeftFaceLocalX, floorFlowLocalY, middleLaneLocalZ),
new Vector3(yzRouteData.ConsumerLeftFaceLocalX, consumerBusLaneLocalY, middleLaneLocalZ),
new Vector3(yzRouteData.ConsumerLeftFaceLocalX, consumerBusLaneLocalY, consumerColumnLocalZ),
new Vector3(consumerLeftFaceLocalX, consumerBusLaneLocalY, consumerColumnLocalZ),
new Vector3(consumerLeftFaceLocalX, consumerEntryLocalY, consumerColumnLocalZ)
};
return RemoveDuplicateLocalPoints(path);
}
private List<DetailedFlowBuildData> BuildDetailedFlowData(
Dictionary<Transform, InfoflowModuleBounds> sourceBoundsByModule,
Dictionary<Transform, InfoflowModuleBounds> targetBoundsByModule,
List<InfoflowModuleConnection> connections)
{
List<DetailedFlowBuildData> flowBuildDataList = new List<DetailedFlowBuildData>();
foreach (InfoflowModuleConnection connection in connections)
{
if (connection == null)
continue;
if (connection.SourceModule == null || connection.TargetModule == null)
continue;
if (sourceBoundsByModule.TryGetValue(connection.SourceModule, out InfoflowModuleBounds sourceBounds) ==
false)
continue;
if (targetBoundsByModule.TryGetValue(connection.TargetModule, out InfoflowModuleBounds targetBounds) ==
false)
continue;
flowBuildDataList.Add(new DetailedFlowBuildData
{
Connection = connection,
SourceBounds = sourceBounds,
TargetBounds = targetBounds
});
}
return flowBuildDataList;
}
private List<YZFlowBuildData> BuildYZFlowData(Dictionary<Transform, InfoflowModuleBounds> sourceBoundsByModule,
Dictionary<Transform, InfoflowModuleBounds> consumerBoundsByModule)
{
List<YZFlowBuildData> consumerFlowBuildDataList = new List<YZFlowBuildData>();
foreach (InfoflowModuleConnection connection in yzModuleConnections)
{
if (connection == null)
continue;
if (connection.SourceModule == null || connection.TargetModule == null)
continue;
if (sourceBoundsByModule.TryGetValue(connection.SourceModule, out InfoflowModuleBounds sourceBounds) ==
false)
continue;
if (consumerBoundsByModule.TryGetValue(connection.TargetModule, out InfoflowModuleBounds consumerBounds) ==
false)
continue;
consumerFlowBuildDataList.Add(new YZFlowBuildData
{
Connection = connection,
SourceBounds = sourceBounds,
ConsumerBounds = consumerBounds
});
}
return consumerFlowBuildDataList;
}
private void AssignSourcePortPositions(List<DetailedFlowBuildData> flowBuildDataList)
{
Dictionary<Transform, List<DetailedFlowBuildData>> flowsBySourceModule =
new Dictionary<Transform, List<DetailedFlowBuildData>>();
foreach (DetailedFlowBuildData flowBuildData in flowBuildDataList)
{
Transform floorModule = flowBuildData.Connection.SourceModule;
if (flowsBySourceModule.ContainsKey(floorModule) == false)
{
flowsBySourceModule.Add(floorModule, new List<DetailedFlowBuildData>());
}
flowsBySourceModule[floorModule].Add(flowBuildData);
}
foreach (KeyValuePair<Transform, List<DetailedFlowBuildData>> pair in flowsBySourceModule)
{
List<DetailedFlowBuildData> flows = pair.Value;
flows.Sort((firstFlow, secondFlow) =>
{
int targetXComparison = secondFlow.TargetBounds.LocalBounds.center.x.CompareTo(
firstFlow.TargetBounds.LocalBounds.center.x);
if (targetXComparison != 0)
return targetXComparison;
return secondFlow.TargetBounds.LocalBounds.center.y.CompareTo(
firstFlow.TargetBounds.LocalBounds.center.y);
});
float sourceCenterLocalX = flows[0].SourceBounds.LocalBounds.center.x;
for (int flowIndex = 0; flowIndex < flows.Count; flowIndex++)
{
DetailedFlowBuildData flowBuildData = flows[flowIndex];
flowBuildData.SourcePortLocalX = GetCenteredPortPosition(
sourceCenterLocalX,
flowIndex,
flows.Count,
GetSpacingLocal());
}
}
}
private void AssignYZSourcePortPositions(List<YZFlowBuildData> flowBuildDataList)
{
Dictionary<Transform, List<YZFlowBuildData>> flowsBySourceModule =
new Dictionary<Transform, List<YZFlowBuildData>>();
foreach (YZFlowBuildData flowBuildData in flowBuildDataList)
{
Transform floorModule = flowBuildData.Connection.SourceModule;
if (flowsBySourceModule.ContainsKey(floorModule) == false)
{
flowsBySourceModule.Add(floorModule, new List<YZFlowBuildData>());
}
flowsBySourceModule[floorModule].Add(flowBuildData);
}
foreach (KeyValuePair<Transform, List<YZFlowBuildData>> pair in flowsBySourceModule)
{
List<YZFlowBuildData> flows = pair.Value;
flows.Sort((firstFlow, secondFlow) =>
{
int consumerZComparison = secondFlow.ConsumerBounds.LocalBounds.center.z.CompareTo(
firstFlow.ConsumerBounds.LocalBounds.center.z);
if (consumerZComparison != 0)
return consumerZComparison;
return secondFlow.ConsumerBounds.LocalBounds.center.y.CompareTo(
firstFlow.ConsumerBounds.LocalBounds.center.y);
});
float sourceCenterLocalX = flows[0].SourceBounds.LocalBounds.center.x;
for (int flowIndex = 0; flowIndex < flows.Count; flowIndex++)
{
YZFlowBuildData flowBuildData = flows[flowIndex];
flowBuildData.SourcePortLocalX = GetCenteredPortPosition(
sourceCenterLocalX,
flowIndex,
flows.Count,
GetSpacingLocal());
}
}
}
private void AssignTargetPortPositions(List<DetailedFlowBuildData> flowBuildDataList)
{
List<TargetFlowColumnData> targetFlowColumns = new List<TargetFlowColumnData>();
foreach (DetailedFlowBuildData flowBuildData in flowBuildDataList)
{
float targetColumnLocalX = flowBuildData.TargetBounds.LocalBounds.center.x;
TargetFlowColumnData targetFlowColumn = FindTargetFlowColumn(targetFlowColumns, targetColumnLocalX);
if (targetFlowColumn == null)
{
targetFlowColumn = new TargetFlowColumnData
{
ColumnLocalX = targetColumnLocalX
};
targetFlowColumns.Add(targetFlowColumn);
}
targetFlowColumn.Flows.Add(flowBuildData);
}
foreach (TargetFlowColumnData targetFlowColumn in targetFlowColumns)
{
targetFlowColumn.Flows.Sort((firstFlow, secondFlow) =>
{
int targetYComparison = firstFlow.TargetBounds.LocalBounds.min.y.CompareTo(
secondFlow.TargetBounds.LocalBounds.min.y);
if (targetYComparison != 0)
return targetYComparison;
int sourceXComparison = firstFlow.SourceBounds.LocalBounds.center.x.CompareTo(
secondFlow.SourceBounds.LocalBounds.center.x);
if (sourceXComparison != 0)
return sourceXComparison;
return firstFlow.SourceBounds.LocalBounds.center.z.CompareTo(
secondFlow.SourceBounds.LocalBounds.center.z);
});
for (int flowIndex = 0; flowIndex < targetFlowColumn.Flows.Count; flowIndex++)
{
DetailedFlowBuildData flowBuildData = targetFlowColumn.Flows[flowIndex];
flowBuildData.TargetPortLocalX = GetCenteredPortPosition(
targetFlowColumn.ColumnLocalX,
flowIndex,
targetFlowColumn.Flows.Count,
GetSpacingLocal());
}
}
}
private void AssignYZPortPositions(List<YZFlowBuildData> flowBuildDataList)
{
List<ConsumerFlowColumnData> consumerFlowColumns = new List<ConsumerFlowColumnData>();
foreach (YZFlowBuildData flowBuildData in flowBuildDataList)
{
float consumerColumnLocalZ = flowBuildData.ConsumerBounds.LocalBounds.center.z;
ConsumerFlowColumnData consumerFlowColumn = FindConsumerFlowColumn(
consumerFlowColumns,
consumerColumnLocalZ);
if (consumerFlowColumn == null)
{
consumerFlowColumn = new ConsumerFlowColumnData
{
ColumnLocalZ = consumerColumnLocalZ
};
consumerFlowColumns.Add(consumerFlowColumn);
}
consumerFlowColumn.Flows.Add(flowBuildData);
}
foreach (ConsumerFlowColumnData consumerFlowColumn in consumerFlowColumns)
{
consumerFlowColumn.Flows.Sort((firstFlow, secondFlow) =>
{
int consumerYComparison = firstFlow.ConsumerBounds.LocalBounds.min.y.CompareTo(
secondFlow.ConsumerBounds.LocalBounds.min.y);
if (consumerYComparison != 0)
return consumerYComparison;
int sourceXComparison = firstFlow.SourceBounds.LocalBounds.center.x.CompareTo(
secondFlow.SourceBounds.LocalBounds.center.x);
if (sourceXComparison != 0)
return sourceXComparison;
return firstFlow.SourceBounds.LocalBounds.center.z.CompareTo(
secondFlow.SourceBounds.LocalBounds.center.z);
});
for (int flowIndex = 0; flowIndex < consumerFlowColumn.Flows.Count; flowIndex++)
{
YZFlowBuildData flowBuildData = consumerFlowColumn.Flows[flowIndex];
flowBuildData.ConsumerPortLocalZ = GetCenteredPortPosition(
consumerFlowColumn.ColumnLocalZ,
flowIndex,
consumerFlowColumn.Flows.Count,
GetSpacingLocal());
}
}
}
private void AssignYZDirectionalLaneOffsets(List<YZFlowBuildData> flowBuildDataList,
YZRouteData yzRouteData,
float spacingLocal)
{
List<YZFlowBuildData> negativeDirectionFlows = new List<YZFlowBuildData>();
List<YZFlowBuildData> positiveDirectionFlows = new List<YZFlowBuildData>();
foreach (YZFlowBuildData flowBuildData in flowBuildDataList)
{
float distanceToSplitPoint = Mathf.Abs(
flowBuildData.ConsumerPortLocalZ - yzRouteData.MiddleSourceRowsLocalZ);
flowBuildData.ConsumerDistanceToSplitZ = distanceToSplitPoint;
flowBuildData.ConsumerDirection =
flowBuildData.ConsumerPortLocalZ >= yzRouteData.MiddleSourceRowsLocalZ ? 1 : -1;
if (flowBuildData.ConsumerDirection > 0)
{
positiveDirectionFlows.Add(flowBuildData);
}
else
{
negativeDirectionFlows.Add(flowBuildData);
}
}
AssignConsumerDirectionalLaneOffsetsForGroup(positiveDirectionFlows, spacingLocal);
AssignConsumerDirectionalLaneOffsetsForGroup(negativeDirectionFlows, spacingLocal);
}
private void AssignConsumerDirectionalLaneOffsetsForGroup(List<YZFlowBuildData> directionalFlows,
float spacingLocal)
{
directionalFlows.Sort((firstFlow, secondFlow) =>
{
int distanceComparison = firstFlow.ConsumerDistanceToSplitZ.CompareTo(
secondFlow.ConsumerDistanceToSplitZ);
if (distanceComparison != 0)
return distanceComparison;
int consumerYComparison = firstFlow.ConsumerBounds.LocalBounds.min.y.CompareTo(
secondFlow.ConsumerBounds.LocalBounds.min.y);
if (consumerYComparison != 0)
return consumerYComparison;
int sourceXComparison = firstFlow.SourceBounds.LocalBounds.center.x.CompareTo(
secondFlow.SourceBounds.LocalBounds.center.x);
if (sourceXComparison != 0)
return sourceXComparison;
return firstFlow.SourceBounds.LocalBounds.center.z.CompareTo(
secondFlow.SourceBounds.LocalBounds.center.z);
});
for (int flowIndex = 0; flowIndex < directionalFlows.Count; flowIndex++)
{
YZFlowBuildData flowBuildData = directionalFlows[flowIndex];
flowBuildData.ConsumerDirectionalLaneIndex = flowIndex;
flowBuildData.ConsumerDirectionalLaneOffsetLocal = (flowIndex + 1) * spacingLocal;
}
}
private void AssignSourceRowLaneOffsets(List<DetailedFlowBuildData> flowBuildDataList, float spacingLocal)
{
List<SourceFlowRowData> sourceFlowRows = new List<SourceFlowRowData>();
float offsetLocalZ = GetOffsetLocal(commonPathOffsetCellsZ);
foreach (DetailedFlowBuildData flowBuildData in flowBuildDataList)
{
float sourceConnectionLocalZ = GetSourceConnectionLocalZ(flowBuildData.SourceBounds, offsetLocalZ);
SourceFlowRowData sourceFlowRow = FindSourceFlowRow(sourceFlowRows, sourceConnectionLocalZ);
if (sourceFlowRow == null)
{
sourceFlowRow = new SourceFlowRowData
{
RowLocalZ = sourceConnectionLocalZ
};
sourceFlowRows.Add(sourceFlowRow);
}
sourceFlowRow.TargetFlows.Add(flowBuildData);
}
sourceFlowRows.Sort((firstRow, secondRow) =>
firstRow.RowLocalZ.CompareTo(secondRow.RowLocalZ));
for (int rowIndex = 0; rowIndex < sourceFlowRows.Count; rowIndex++)
{
SourceFlowRowData sourceFlowRow = sourceFlowRows[rowIndex];
sourceFlowRow.TargetFlows.Sort((firstFlow, secondFlow) =>
{
int sourcePortXComparison = secondFlow.SourcePortLocalX.CompareTo(firstFlow.SourcePortLocalX);
if (sourcePortXComparison != 0)
return sourcePortXComparison;
return firstFlow.LaneIndex.CompareTo(secondFlow.LaneIndex);
});
for (int flowIndex = 0; flowIndex < sourceFlowRow.TargetFlows.Count; flowIndex++)
{
DetailedFlowBuildData flowBuildData = sourceFlowRow.TargetFlows[flowIndex];
flowBuildData.SourceRowIndex = rowIndex;
flowBuildData.SourceRowLaneOffsetLocal = (flowIndex + 1) * spacingLocal;
}
}
}
private void AssignYZSourceRowLaneOffsets(List<YZFlowBuildData> flowBuildDataList, float spacingLocal)
{
List<SourceFlowRowData> sourceFlowRows = new List<SourceFlowRowData>();
float offsetLocalZ = GetOffsetLocal(commonPathOffsetCellsZ);
foreach (YZFlowBuildData flowBuildData in flowBuildDataList)
{
float sourceConnectionLocalZ = GetSourceConnectionLocalZ(flowBuildData.SourceBounds, offsetLocalZ);
SourceFlowRowData sourceFlowRow = FindSourceFlowRow(sourceFlowRows, sourceConnectionLocalZ);
if (sourceFlowRow == null)
{
sourceFlowRow = new SourceFlowRowData
{
RowLocalZ = sourceConnectionLocalZ
};
sourceFlowRows.Add(sourceFlowRow);
}
sourceFlowRow.ConsumerFlows.Add(flowBuildData);
}
sourceFlowRows.Sort((firstRow, secondRow) =>
firstRow.RowLocalZ.CompareTo(secondRow.RowLocalZ));
for (int rowIndex = 0; rowIndex < sourceFlowRows.Count; rowIndex++)
{
SourceFlowRowData sourceFlowRow = sourceFlowRows[rowIndex];
sourceFlowRow.ConsumerFlows.Sort((firstFlow, secondFlow) =>
{
int sourcePortXComparison = firstFlow.SourcePortLocalX.CompareTo(secondFlow.SourcePortLocalX);
if (sourcePortXComparison != 0)
return sourcePortXComparison;
return firstFlow.LaneIndex.CompareTo(secondFlow.LaneIndex);
});
for (int flowIndex = 0; flowIndex < sourceFlowRow.ConsumerFlows.Count; flowIndex++)
{
YZFlowBuildData flowBuildData = sourceFlowRow.ConsumerFlows[flowIndex];
flowBuildData.SourceRowIndex = rowIndex;
flowBuildData.SourceRowLaneOffsetLocal = (flowIndex + 1) * spacingLocal;
}
}
}
private List<SourceRowData> BuildSourceRows(List<InfoflowModuleBounds> floorModuleBounds,
float offsetLocalZ)
{
List<SourceRowData> sourceRows = new List<SourceRowData>();
foreach (InfoflowModuleBounds moduleBounds in floorModuleBounds)
{
float sourceConnectionLocalZ = GetSourceConnectionLocalZ(moduleBounds, offsetLocalZ);
SourceRowData sourceRow = FindSourceRow(sourceRows, sourceConnectionLocalZ);
if (sourceRow == null)
{
sourceRow = new SourceRowData
{
RowLocalZ = sourceConnectionLocalZ
};
sourceRows.Add(sourceRow);
}
sourceRow.Modules.Add(moduleBounds);
}
return sourceRows;
}
private SourceRowData FindSourceRow(List<SourceRowData> sourceRows, float sourceConnectionLocalZ)
{
float toleranceLocal = GetOffsetLocal(sourceRowToleranceCells);
SourceRowData closestRow = null;
float closestDistance = float.MaxValue;
foreach (SourceRowData sourceRow in sourceRows)
{
float distance = Mathf.Abs(sourceRow.RowLocalZ - sourceConnectionLocalZ);
if (distance > toleranceLocal)
continue;
if (distance >= closestDistance)
continue;
closestDistance = distance;
closestRow = sourceRow;
}
return closestRow;
}
private SourceFlowRowData FindSourceFlowRow(List<SourceFlowRowData> sourceFlowRows,
float sourceConnectionLocalZ)
{
float toleranceLocal = GetOffsetLocal(sourceRowToleranceCells);
SourceFlowRowData closestRow = null;
float closestDistance = float.MaxValue;
foreach (SourceFlowRowData sourceFlowRow in sourceFlowRows)
{
float distance = Mathf.Abs(sourceFlowRow.RowLocalZ - sourceConnectionLocalZ);
if (distance > toleranceLocal)
continue;
if (distance >= closestDistance)
continue;
closestDistance = distance;
closestRow = sourceFlowRow;
}
return closestRow;
}
private List<TargetColumnData> BuildTargetColumns(List<InfoflowModuleBounds> targetModuleBounds)
{
List<TargetColumnData> targetColumns = new List<TargetColumnData>();
foreach (InfoflowModuleBounds moduleBounds in targetModuleBounds)
{
float columnLocalX = moduleBounds.LocalBounds.center.x;
TargetColumnData targetColumn = FindTargetColumn(targetColumns, columnLocalX);
if (targetColumn == null)
{
targetColumn = new TargetColumnData
{
ColumnLocalX = columnLocalX
};
targetColumns.Add(targetColumn);
}
targetColumn.Modules.Add(moduleBounds);
}
return targetColumns;
}
private TargetColumnData FindTargetColumn(List<TargetColumnData> targetColumns, float columnLocalX)
{
float toleranceLocal = GetOffsetLocal(targetColumnToleranceCells);
TargetColumnData closestColumn = null;
float closestDistance = float.MaxValue;
foreach (TargetColumnData targetColumn in targetColumns)
{
float distance = Mathf.Abs(targetColumn.ColumnLocalX - columnLocalX);
if (distance > toleranceLocal)
continue;
if (distance >= closestDistance)
continue;
closestDistance = distance;
closestColumn = targetColumn;
}
return closestColumn;
}
private TargetFlowColumnData FindTargetFlowColumn(List<TargetFlowColumnData> targetFlowColumns,
float targetColumnLocalX)
{
float toleranceLocal = GetOffsetLocal(targetColumnToleranceCells);
TargetFlowColumnData closestColumn = null;
float closestDistance = float.MaxValue;
foreach (TargetFlowColumnData targetFlowColumn in targetFlowColumns)
{
float distance = Mathf.Abs(targetFlowColumn.ColumnLocalX - targetColumnLocalX);
if (distance > toleranceLocal)
continue;
if (distance >= closestDistance)
continue;
closestDistance = distance;
closestColumn = targetFlowColumn;
}
return closestColumn;
}
private ConsumerFlowColumnData FindConsumerFlowColumn(List<ConsumerFlowColumnData> consumerFlowColumns,
float consumerColumnLocalZ)
{
float toleranceLocal = GetOffsetLocal(consumerColumnToleranceCells);
ConsumerFlowColumnData closestColumn = null;
float closestDistance = float.MaxValue;
foreach (ConsumerFlowColumnData consumerFlowColumn in consumerFlowColumns)
{
float distance = Mathf.Abs(consumerFlowColumn.ColumnLocalZ - consumerColumnLocalZ);
if (distance > toleranceLocal)
continue;
if (distance >= closestDistance)
continue;
closestDistance = distance;
closestColumn = consumerFlowColumn;
}
return closestColumn;
}
private int CompareDetailedFlowOrder(DetailedFlowBuildData firstFlow, DetailedFlowBuildData secondFlow)
{
int targetPortXComparison = secondFlow.TargetPortLocalX.CompareTo(firstFlow.TargetPortLocalX);
if (targetPortXComparison != 0)
return targetPortXComparison;
int targetYComparison = secondFlow.TargetBounds.LocalBounds.min.y.CompareTo(
firstFlow.TargetBounds.LocalBounds.min.y);
if (targetYComparison != 0)
return targetYComparison;
int sourceXComparison = secondFlow.SourcePortLocalX.CompareTo(firstFlow.SourcePortLocalX);
if (sourceXComparison != 0)
return sourceXComparison;
return secondFlow.SourceBounds.LocalBounds.center.z.CompareTo(firstFlow.SourceBounds.LocalBounds.center.z);
}
private int CompareYZFlowOrder(YZFlowBuildData firstFlow, YZFlowBuildData secondFlow)
{
int directionComparison = secondFlow.ConsumerDirection.CompareTo(firstFlow.ConsumerDirection);
if (directionComparison != 0)
return directionComparison;
int directionalLaneComparison =
firstFlow.ConsumerDirectionalLaneIndex.CompareTo(secondFlow.ConsumerDirectionalLaneIndex);
if (directionalLaneComparison != 0)
return directionalLaneComparison;
int consumerYComparison = secondFlow.ConsumerBounds.LocalBounds.min.y.CompareTo(
firstFlow.ConsumerBounds.LocalBounds.min.y);
if (consumerYComparison != 0)
return consumerYComparison;
int sourceXComparison = secondFlow.SourcePortLocalX.CompareTo(firstFlow.SourcePortLocalX);
if (sourceXComparison != 0)
return sourceXComparison;
return secondFlow.SourceBounds.LocalBounds.center.z.CompareTo(firstFlow.SourceBounds.LocalBounds.center.z);
}
private void AddLocalPointToCommonPath(Vector3 localPosition)
{
if (commonPath.Count > 0)
{
Vector3 previousPoint = commonPath[commonPath.Count - 1];
if ((previousPoint - localPosition).sqrMagnitude < 0.0001f)
return;
}
commonPath.Add(localPosition);
}
private float GetSourceConnectionLocalZ(InfoflowModuleBounds moduleBounds, float offsetLocalZ)
{
return moduleBounds.LocalBounds.min.z - offsetLocalZ;
}
private float GetOffsetLocal(int offsetCells)
{
return Mathf.Max(0, offsetCells) * stepSize;
}
private float GetSpacingLocal()
{
return Mathf.Max(0f, detailedFlowSpacingCells) * stepSize;
}
private float GetCenteredPortPosition(float centerPosition, int portIndex, int portCount, float spacingLocal)
{
if (portCount <= 1)
return centerPosition;
float safeSpacingLocal = Mathf.Max(0.001f, spacingLocal);
float totalWidth = (portCount - 1) * safeSpacingLocal;
float firstPortPosition = centerPosition - totalWidth * 0.5f;
return firstPortPosition + portIndex * safeSpacingLocal;
}
private float GetRightMostLocalMaxX(List<InfoflowModuleBounds> moduleBounds)
{
float rightMostLocalX = float.MinValue;
foreach (InfoflowModuleBounds bounds in moduleBounds)
{
rightMostLocalX = Mathf.Max(rightMostLocalX, bounds.LocalBounds.max.x);
}
return rightMostLocalX;
}
private float GetLeftMostLocalMinX(List<InfoflowModuleBounds> moduleBounds)
{
float leftMostLocalX = float.MaxValue;
foreach (InfoflowModuleBounds bounds in moduleBounds)
{
leftMostLocalX = Mathf.Min(leftMostLocalX, bounds.LocalBounds.min.x);
}
return leftMostLocalX;
}
private float GetLowestLocalMinY(List<InfoflowModuleBounds> moduleBounds)
{
float lowestLocalY = float.MaxValue;
foreach (InfoflowModuleBounds bounds in moduleBounds)
{
lowestLocalY = Mathf.Min(lowestLocalY, bounds.LocalBounds.min.y);
}
return lowestLocalY;
}
private float GetAverageNearFaceLocalZ(List<InfoflowModuleBounds> moduleBounds)
{
if (moduleBounds == null || moduleBounds.Count == 0)
return commonBounds.max.z;
float sum = 0f;
int count = 0;
foreach (InfoflowModuleBounds bounds in moduleBounds)
{
sum += GetNearFaceLocalZ(bounds);
count++;
}
if (count == 0)
return commonBounds.max.z;
return sum / count;
}
private float GetNearFaceLocalZ(InfoflowModuleBounds moduleBounds)
{
if (moduleBounds.LocalBounds.center.z >= commonBounds.center.z)
return moduleBounds.LocalBounds.max.z;
return moduleBounds.LocalBounds.max.z;
}
[Button()]
public void RefreshRoundedDetailedFlowPoints()
{
RefreshRoundedFlowPoints(xzDetailedFlows);
RefreshRoundedFlowPoints(yzDetailedFlows);
}
private void RefreshRoundedFlowPoints(List<InfoflowDetailedFlow> flows)
{
if (flows == null)
return;
foreach (InfoflowDetailedFlow flow in flows)
{
if (flow == null)
continue;
flow.RoundedPoints = BuildRoundedLocalPath(flow.Points);
}
}
private List<Vector3> GetRenderableFlowPoints(InfoflowDetailedFlow detailedFlow)
{
if (detailedFlow == null)
return null;
if (useRoundedDetailedFlowPoints &&
detailedFlow.RoundedPoints != null &&
detailedFlow.RoundedPoints.Count >= 2)
{
return detailedFlow.RoundedPoints;
}
return detailedFlow.Points;
}
private List<Vector3> BuildRoundedLocalPath(List<Vector3> points)
{
if (points == null)
return new List<Vector3>();
if (points.Count < 3)
return new List<Vector3>(points);
float radius = Mathf.Max(0f, detailedFlowCornerRadiusCells) * stepSize;
int segmentCount = Mathf.Max(1, detailedFlowCornerSegments);
if (radius <= 0.0001f)
return new List<Vector3>(points);
List<Vector3> roundedPoints = new List<Vector3>();
AddUniqueLocalPoint(roundedPoints, points[0]);
for (int pointIndex = 1; pointIndex < points.Count - 1; pointIndex++)
{
Vector3 previousPoint = points[pointIndex - 1];
Vector3 currentPoint = points[pointIndex];
Vector3 nextPoint = points[pointIndex + 1];
Vector3 incomingVector = currentPoint - previousPoint;
Vector3 outgoingVector = nextPoint - currentPoint;
float incomingLength = incomingVector.magnitude;
float outgoingLength = outgoingVector.magnitude;
if (incomingLength < 0.0001f || outgoingLength < 0.0001f)
continue;
Vector3 incomingDirection = incomingVector / incomingLength;
Vector3 outgoingDirection = outgoingVector / outgoingLength;
bool isStraightLine = Vector3.Cross(incomingDirection, outgoingDirection).sqrMagnitude < 0.0001f;
if (isStraightLine)
{
AddUniqueLocalPoint(roundedPoints, currentPoint);
continue;
}
float cornerRadius = Mathf.Min(
radius,
incomingLength * 0.5f,
outgoingLength * 0.5f);
if (cornerRadius < 0.0001f)
{
AddUniqueLocalPoint(roundedPoints, currentPoint);
continue;
}
Vector3 cornerStartPoint = currentPoint - incomingDirection * cornerRadius;
Vector3 cornerEndPoint = currentPoint + outgoingDirection * cornerRadius;
AddUniqueLocalPoint(roundedPoints, cornerStartPoint);
for (int segmentIndex = 1; segmentIndex <= segmentCount; segmentIndex++)
{
float t = segmentIndex / (float) segmentCount;
Vector3 roundedPoint = GetQuadraticBezierPoint(cornerStartPoint, currentPoint, cornerEndPoint, t);
AddUniqueLocalPoint(roundedPoints, roundedPoint);
}
}
AddUniqueLocalPoint(roundedPoints, points[points.Count - 1]);
return roundedPoints;
}
private Vector3 GetQuadraticBezierPoint(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint, float t)
{
float inverseT = 1f - t;
return inverseT * inverseT * startPoint +
2f * inverseT * t * controlPoint +
t * t * endPoint;
}
private void AddUniqueLocalPoint(List<Vector3> points, Vector3 point)
{
if (points.Count > 0)
{
Vector3 previousPoint = points[points.Count - 1];
if ((previousPoint - point).sqrMagnitude < 0.000001f)
return;
}
points.Add(point);
}
[Button()]
public void GenerateDetailedFlowLineRenderers()
{
if ((xzDetailedFlows == null || xzDetailedFlows.Count == 0) &&
(yzDetailedFlows == null || yzDetailedFlows.Count == 0))
{
BuildDetailedFlows();
}
RefreshRoundedDetailedFlowPoints();
ClearDetailedFlowLineRenderers();
Transform root = GetOrCreateLineRendererRoot();
if (generateTargetFlowLineRenderers)
{
GenerateLineRenderersForFlowList(
xzDetailedFlows,
root,
"Target flow");
}
if (generateConsumerFlowLineRenderers)
{
GenerateLineRenderersForFlowList(
yzDetailedFlows,
root,
"Consumer flow");
}
}
[Button()]
public void ClearDetailedFlowLineRenderers()
{
Transform root = GetOrCreateLineRendererRoot();
for (int childIndex = root.childCount - 1; childIndex >= 0; childIndex--)
{
Transform child = root.GetChild(childIndex);
if (Application.isPlaying)
{
Destroy(child.gameObject);
}
else
{
DestroyImmediate(child.gameObject);
}
}
}
private Transform GetOrCreateLineRendererRoot()
{
if (lineRendererRoot != null)
{
NormalizeLineRendererRootTransform();
return lineRendererRoot;
}
const string rootName = "Generated Infoflow LineRenderers";
Transform existingRoot = transform.Find(rootName);
if (existingRoot != null)
{
lineRendererRoot = existingRoot;
NormalizeLineRendererRootTransform();
return lineRendererRoot;
}
GameObject rootGameObject = new GameObject(rootName);
rootGameObject.transform.SetParent(transform, false);
lineRendererRoot = rootGameObject.transform;
NormalizeLineRendererRootTransform();
return lineRendererRoot;
}
private void NormalizeLineRendererRootTransform()
{
if (lineRendererRoot == null)
return;
if (lineRendererRoot.parent != transform)
{
lineRendererRoot.SetParent(transform, false);
}
lineRendererRoot.localPosition = Vector3.zero;
lineRendererRoot.localRotation = Quaternion.identity;
lineRendererRoot.localScale = Vector3.one;
}
private void GenerateLineRenderersForFlowList(List<InfoflowDetailedFlow> flows,
Transform root,
string namePrefix)
{
if (flows == null)
return;
for (int flowIndex = 0; flowIndex < flows.Count; flowIndex++)
{
InfoflowDetailedFlow flow = flows[flowIndex];
if (flow == null)
continue;
List<Vector3> points = GetRenderableFlowPoints(flow);
if (points == null || points.Count < 2)
continue;
CreateLineRendererForFlow(
flow,
points,
root,
$"{namePrefix} {flowIndex:000}");
}
}
private void CreateLineRendererForFlow(InfoflowDetailedFlow flow,
List<Vector3> points,
Transform root,
string objectName)
{
GameObject lineRendererGameObject = new GameObject(objectName);
lineRendererGameObject.transform.SetParent(root, false);
lineRendererGameObject.transform.localPosition = Vector3.zero;
lineRendererGameObject.transform.localRotation = Quaternion.identity;
lineRendererGameObject.transform.localScale = Vector3.one;
LineRenderer lineRenderer = lineRendererGameObject.AddComponent<LineRenderer>();
lineRenderer.useWorldSpace = false;
lineRenderer.positionCount = points.Count;
lineRenderer.SetPositions(points.ToArray());
lineRenderer.startWidth = detailedFlowLineWidth;
lineRenderer.endWidth = detailedFlowLineWidth;
lineRenderer.startColor = flow.Color;
lineRenderer.endColor = flow.Color;
lineRenderer.numCornerVertices = 0;
lineRenderer.numCapVertices = 4;
lineRenderer.material = GetLineRendererMaterial();
}
private Material GetLineRendererMaterial()
{
if (detailedFlowLineMaterial != null)
return detailedFlowLineMaterial;
Shader shader = Shader.Find("Sprites/Default");
if (shader == null)
shader = Shader.Find("Universal Render Pipeline/Unlit");
if (shader == null)
shader = Shader.Find("Standard");
detailedFlowLineMaterial = new Material(shader)
{
name = "Generated Infoflow Line Material"
};
return detailedFlowLineMaterial;
}
private List<Vector3> RemoveDuplicateLocalPoints(List<Vector3> points)
{
List<Vector3> result = new List<Vector3>();
foreach (Vector3 point in points)
{
if (result.Count > 0)
{
Vector3 previousPoint = result[result.Count - 1];
if ((previousPoint - point).sqrMagnitude < 0.0001f)
continue;
}
result.Add(point);
}
return result;
}
private List<InfoflowModuleBounds> GetModuleBounds(List<Transform> modules,
ModuleProjection moduleProjection)
{
List<InfoflowModuleBounds> moduleBounds = new List<InfoflowModuleBounds>();
if (modules == null)
return moduleBounds;
foreach (Transform module in modules)
{
if (module == null)
continue;
Bounds moduleLocalBounds = ObjectToFrustumFitter.GetLocalBoundsWithChildren(module.gameObject, transform);
moduleBounds.Add(new InfoflowModuleBounds
{
Module = module,
LocalBounds = moduleLocalBounds,
Projection = moduleProjection
});
}
return moduleBounds;
}
private Dictionary<Transform, InfoflowModuleBounds> BuildBoundsDictionary(List<InfoflowModuleBounds> moduleBounds)
{
Dictionary<Transform, InfoflowModuleBounds> boundsByModule =
new Dictionary<Transform, InfoflowModuleBounds>();
foreach (InfoflowModuleBounds bounds in moduleBounds)
{
if (bounds.Module == null)
continue;
if (boundsByModule.ContainsKey(bounds.Module))
continue;
boundsByModule.Add(bounds.Module, bounds);
}
return boundsByModule;
}
private List<Transform> GetValidModules(List<Transform> modules)
{
List<Transform> validModules = new List<Transform>();
if (modules == null)
return validModules;
foreach (var module in modules)
{
if (module == null)
continue;
validModules.Add(module);
}
return validModules;
}
private List<Transform> GetValidModules(List<DataFragment> modules)
{
List<Transform> validModules = new List<Transform>();
if (modules == null)
return validModules;
foreach (var module in modules)
{
if (module == null || module.transform == null)
continue;
validModules.Add(module.transform);
}
return validModules;
}
private List<Transform> GetShuffledModules(List<Transform> modules, Random random)
{
List<Transform> shuffledModules = new List<Transform>(modules);
for (int moduleIndex = shuffledModules.Count - 1; moduleIndex > 0; moduleIndex--)
{
int randomIndex = random.Next(0, moduleIndex + 1);
Transform temporaryModule = shuffledModules[moduleIndex];
shuffledModules[moduleIndex] = shuffledModules[randomIndex];
shuffledModules[randomIndex] = temporaryModule;
}
return shuffledModules;
}
private Color GetRandomDetailedFlowColor(Random random)
{
float hue = (float) random.NextDouble();
float saturation = Mathf.Lerp(
detailedFlowColorSaturationMin,
detailedFlowColorSaturationMax,
(float) random.NextDouble());
float value = Mathf.Lerp(
detailedFlowColorValueMin,
detailedFlowColorValueMax,
(float) random.NextDouble());
return Color.HSVToRGB(hue, saturation, value);
}
private void OnDrawGizmos()
{
if (drawCommonPath && commonPath != null)
{
Gizmos.color = Color.green;
for (int pointIndex = 1; pointIndex < commonPath.Count; pointIndex++)
{
Gizmos.DrawLine(
transform.TransformPoint(commonPath[pointIndex - 1]),
transform.TransformPoint(commonPath[pointIndex]));
}
foreach (Vector3 point in commonPath)
{
Gizmos.DrawSphere(transform.TransformPoint(point), stepSize * 0.2f);
}
}
if (drawSourceModuleConnectionPoints)
DrawPointList(floorModuleConnectionPoints, Color.cyan, stepSize * 0.16f);
if (drawSourceRowMergePoints)
DrawPointList(sourceRowMergePoints, Color.yellow, stepSize * 0.18f);
if (drawTargetColumnConnectionPoints)
DrawPointList(targetColumnConnectionPoints, Color.magenta, stepSize * 0.18f);
if (drawDetailedFlows)
DrawFlowList(xzDetailedFlows);
if (drawConsumerDetailedFlows)
DrawFlowList(yzDetailedFlows);
}
private void DrawPointList(List<Vector3> points, Color color, float radius)
{
if (points == null)
return;
Gizmos.color = color;
foreach (Vector3 point in points)
{
Gizmos.DrawSphere(transform.TransformPoint(point), radius);
}
}
private void DrawFlowList(List<InfoflowDetailedFlow> flows)
{
if (flows == null)
return;
foreach (InfoflowDetailedFlow detailedFlow in flows)
{
if (detailedFlow == null)
continue;
List<Vector3> points = GetRenderableFlowPoints(detailedFlow);
if (points == null || points.Count < 2)
continue;
Gizmos.color = detailedFlow.Color;
for (int pointIndex = 1; pointIndex < points.Count; pointIndex++)
{
Gizmos.DrawLine(
transform.TransformPoint(points[pointIndex - 1]),
transform.TransformPoint(points[pointIndex]));
}
foreach (Vector3 point in points)
{
Gizmos.DrawSphere(transform.TransformPoint(point), detailedFlowPointRadius);
}
}
}
}