public BrainBurden(int index, LinkItem[] brains, LinkItem[] io) { this.Index = index; this.Brain = brains[index]; this.Size = this.Brain.Size; _io = io; }
private static DistributeDistances GetDistances(LinkItem[] items1, LinkItem[] items2, ItemLinker_OverflowArgs overflowArgs) { // Distances between all item1s (not just delaunay, but all pairs) Tuple<int, double>[][] resistancesItem1 = ItemItemResistance(items1, overflowArgs == null ? 1 : overflowArgs.LinkResistanceMult); // Figure out the distances between 2s and 1s var distances2to1 = Enumerable.Range(0, items2.Length). Select(o => new Distances2to1 ( o, Enumerable.Range(0, items1.Length). Select(p => Tuple.Create(p, (items1[p].Position - items2[o].Position).Length)). //Item1=items1 index, Item2=distance to item1 OrderBy(p => p.Item2). // first item1 needs to be the shortest distance ToArray() )). OrderBy(o => o.DistancesTo1.First().Item2). ToArray(); return new DistributeDistances(resistancesItem1, distances2to1); }
private static Tuple<int, double>[][] ItemItemResistance(LinkItem[] items, double linkResistanceMult) { // Get the AABB, and use the diagonal as the size var aabb = Math3D.GetAABB(items.Select(o => o.Position)); double maxDistance = (aabb.Item2 - aabb.Item1).Length; // Get links between the items and distances of each link var links2 = UtilityCore.GetPairs(items.Length). Select(o => { double distance = (items[o.Item1].Position - items[o.Item2].Position).Length; double resistance = (distance / maxDistance) * linkResistanceMult; return Tuple.Create(o.Item2, o.Item1, resistance); }). ToArray(); Tuple<int, double>[][] retVal = new Tuple<int, double>[items.Length][]; for (int cntr = 0; cntr < items.Length; cntr++) { // Store all links for this item retVal[cntr] = links2. Where(o => o.Item1 == cntr || o.Item2 == cntr). // find links between this item and another Select(o => Tuple.Create(o.Item1 == cntr ? o.Item2 : o.Item1, o.Item3)). // store the link to the other item and the resistance OrderBy(o => o.Item2). ToArray(); } return retVal; }
private static Tuple<int, int>[] Link12_Extra(Tuple<int, int>[] initial, LinkItem[] items1, LinkItem[] items2, DistributeDistances distances, ItemLinker_ExtraArgs extraArgs) { Random rand = StaticRandom.GetRandomForThread(); int wholePercent = extraArgs.Percent.ToInt_Floor(); List<int> addOrder = new List<int>(); if (extraArgs.BySize) { double totalSize = items2.Sum(o => o.Size); double maxSize = extraArgs.Percent * totalSize; double usedSize = 0; if (extraArgs.EvenlyDistribute) { #region by size, evenly distribute // Add some complete passes if over 100% percent for (int cntr = 0; cntr < wholePercent; cntr++) { addOrder.AddRange(UtilityCore.RandomRange(0, items2.Length)); } usedSize = wholePercent * totalSize; #endregion } #region by size, distribute the rest //NOTE: Building this list by size so that larger items have a higher chance of being chosen var bySize = items2. Select((o, i) => Tuple.Create(i, o.Size / totalSize)). OrderByDescending(o => o.Item2). ToArray(); // Keep selecting items unti the extra size is consumed (or if no more can be added) while (true) { bool foundOne = false; for (int cntr = 0; cntr < 1000; cntr++) // this is an infinite loop detector { int attemptIndex = UtilityCore.GetIndexIntoList(rand.NextDouble(), bySize); // get the index into the list that the rand percent represents attemptIndex = bySize[attemptIndex].Item1; // get the index into items2 if (items2[attemptIndex].Size + usedSize <= maxSize) { foundOne = true; usedSize += items2[attemptIndex].Size; addOrder.Add(attemptIndex); break; } } if (!foundOne) { // No more will fit break; } } #endregion } else { if (extraArgs.EvenlyDistribute) { #region ignore size, evenly distribute // Add some complete passes if over 100% percent for (int cntr = 0; cntr < wholePercent; cntr++) { addOrder.AddRange(UtilityCore.RandomRange(0, items2.Length)); } // Add some items based on the portion of percent that is less than 100% int remainder = (items2.Length * (extraArgs.Percent - wholePercent)). ToInt_Round(); addOrder.AddRange(UtilityCore.RandomRange(0, items2.Length, remainder)); #endregion } else { #region ignore size, randomly distribute int totalCount = (items2.Length * extraArgs.Percent). ToInt_Round(); //NOTE: UtilityCore.RandomRange stops when the list is exhausted, and makes sure not to have dupes. That's not what is wanted //here. Just randomly pick X times addOrder.AddRange(Enumerable.Range(0, totalCount).Select(o => rand.Next(items2.Length))); #endregion } } return AddLinks(items1, items2, distances, addOrder, initial); }
/// <summary> /// This adds 2s to 1s one at a time (specified by distances2to1_AddOrder) /// </summary> private static Tuple<int, int>[] AddLinks(LinkItem[] items1, LinkItem[] items2, DistributeDistances distances, IEnumerable<int> distances2to1_AddOrder, Tuple<int, int>[] initial = null) { // Store the inital link burdens BrainBurden[] links = Enumerable.Range(0, items1.Length). Select(o => new BrainBurden(o, items1, items2)). ToArray(); if (initial != null) { #region Store initial foreach (var set in initial.ToLookup(o => o.Item1)) { foreach (int item2Index in set.Select(o => o.Item2)) { links[set.Key].AddIOLink(item2Index); } } #endregion } foreach (var distanceIO in distances2to1_AddOrder.Select(o => distances.Distances2to1[o])) { int ioIndex = distanceIO.Index2; int closestBrainIndex = distanceIO.DistancesTo1[0].Item1; AddIOLink(links, ioIndex, items2[ioIndex].Size, closestBrainIndex, distances.ResistancesItem1[closestBrainIndex]); } // Build the return List<Tuple<int, int>> retVal = new List<Tuple<int, int>>(); foreach (BrainBurden burden in links) { retVal.AddRange(burden.IOLinks.Select(o => Tuple.Create(burden.Index, o))); } return retVal.ToArray(); }
/// <summary> /// This links closest items together /// </summary> private static Tuple<int, int>[] Link12_Closest(LinkItem[] items1, LinkItem[] items2) { var retVal = new Tuple<int, int>[items2.Length]; for (int cntr = 0; cntr < items2.Length; cntr++) { int closest = items1. Select((o, i) => new { Index = i, Position = o.Position, DistSqr = (o.Position - items2[cntr].Position).LengthSquared }). OrderBy(o => o.DistSqr). First(). Index; retVal[cntr] = Tuple.Create(closest, cntr); } return retVal; }
private static Tuple<int, int>[] Link12_Distribute(LinkItem[] items1, LinkItem[] items2, DistributeDistances distances) { IEnumerable<int> addOrder = Enumerable.Range(0, distances.Distances2to1.Length); return AddLinks(items1, items2, distances, addOrder); }
/// <summary> /// Every item in 2 will have at least one link to a 1. There could be some item1s that don't have a link /// </summary> /// <param name="overflowArgs"> /// Item2s get matched to the nearest Item1. /// If this args is null then, it stays that way. /// If this is populated, then links will move from burdened item1s to less burdened /// </param> /// <returns> /// Item1=index into item1 list /// Item2=index into item2 list /// </returns> public static Tuple<int, int>[] Link_1_2(LinkItem[] items1, LinkItem[] items2, ItemLinker_OverflowArgs overflowArgs = null, ItemLinker_ExtraArgs extraArgs = null) { if (items1 == null || items2 == null || items1.Length == 0 || items2.Length == 0) { return new Tuple<int, int>[0]; } Tuple<int, int>[] retVal = null; if (overflowArgs == null) { // Just link to the closest retVal = Link12_Closest(items1, items2); } if (overflowArgs == null && extraArgs == null) { // Nothing special to do, exit early return retVal; } DistributeDistances distances = GetDistances(items1, items2, overflowArgs); if (overflowArgs != null) { // Consider item1s burden when linking them retVal = Link12_Distribute(items1, items2, distances); } if (extraArgs != null) { // Add more links retVal = Link12_Extra(retVal, items1, items2, distances, extraArgs); } return retVal; }
private static LinkSetPair[] PruneLinks(TriangleIndexed[] triangles, SortedList<Tuple<int, int>, double> all, LinkItem[] brains, ItemLinker_CombineArgs args) { List<Tuple<int[], int[]>> retVal = all.Keys. Select(o => Tuple.Create(new[] { o.Item1 }, new[] { o.Item2 })). ToList(); foreach (TriangleIndexed triangle in triangles) { Tuple<bool, TriangleEdge> changeEdge = PruneLinks_LongThin(triangle, all, args); if (changeEdge == null) { continue; } if (changeEdge.Item1) { PruneLinks_Merge(triangle, changeEdge.Item2, retVal); } else { PruneLinks_Remove(triangle, changeEdge.Item2, retVal); } } return retVal.Select(o => new LinkSetPair(o.Item1, o.Item2, brains)).ToArray(); }
public LinkSetPair(IEnumerable<int> set1, IEnumerable<int> set2, LinkItem[] all) : this(new LinkSet(set1, all), new LinkSet(set2, all)) { }
/// <summary> /// Link brains to each other (delaunay graph, then prune thin triangles) /// </summary> /// <param name="all"> /// Item1=Link between two items (sub item1 and 2 are the indices) /// Item2=Distance between those two items /// </param> /// <param name="final"> /// This holds a set of links after the thin triangles are pruned. There's a chance of items being merged /// </param> public static void Link_Self(out SortedList<Tuple<int, int>, double> all, out LinkSetPair[] final, LinkItem[] items, ItemLinker_CombineArgs combineArgs = null) { TriangleIndexed[] triangles; GetItemDelaunay(out all, out triangles, items); if (items.Length < 2) { //throw new ArgumentException("This method requires at least two brains: " + items.Length.ToString()); final = new LinkSetPair[0]; return; } // Prune links that don't make sense if (combineArgs != null && triangles.Length > 0) { final = PruneLinks(triangles, all, items, combineArgs); } else { final = all.Keys. Select(o => new LinkSetPair(o.Item1, o.Item2, items)). ToArray(); } }
public LinkSetPair(int item1, int item2, LinkItem[] all) : this(new LinkSet(item1, all), new LinkSet(item2, all)) { }
public LinkSet(IEnumerable<int> indices, LinkItem[] all) { this.Indices = indices.ToArray(); this.Items = this.Indices.Select(o => all[o]).ToArray(); this.Positions = this.Items.Select(o => o.Position).ToArray(); if (this.Items.Length == 1) { this.Center = this.Items[0].Position; } else { this.Center = Math3D.GetCenter(this.Positions); } }
public LinkSet(int index, LinkItem[] all) : this(new[] { index }, all) { }
//NOTE: This makes sure that key.item1 is less than key.item2 private static SortedList<Tuple<int, int>, double> GetLengths(Tuple<int, int>[] keys, LinkItem[] items) { SortedList<Tuple<int, int>, double> retVal = new SortedList<Tuple<int, int>, double>(); foreach (Tuple<int, int> key in keys.Select(o => o.Item1 < o.Item2 ? o : Tuple.Create(o.Item2, o.Item1))) { double distance = (items[key.Item1].Position - items[key.Item2].Position).Length; retVal.Add(key, distance); } return retVal; }
private static void GetItemDelaunay(out SortedList<Tuple<int, int>, double> segments, out TriangleIndexed[] triangles, LinkItem[] items) { Tuple<int, int>[] links = null; if (items.Length < 2) { links = new Tuple<int, int>[0]; triangles = new TriangleIndexed[0]; } else if (items.Length == 2) { links = new[] { Tuple.Create(0, 1) }; triangles = new TriangleIndexed[0]; } else if (items.Length == 3) { links = new[] { Tuple.Create(0, 1), Tuple.Create(0, 2), Tuple.Create(1, 2), }; triangles = new[] { new TriangleIndexed(0, 1, 2, items.Select(o => o.Position).ToArray()) }; } else { Tetrahedron[] tetras = Math3D.GetDelaunay(items.Select(o => o.Position).ToArray()); links = Tetrahedron.GetUniqueLines(tetras); triangles = Tetrahedron.GetUniqueTriangles(tetras); } segments = GetLengths(links, items); }