public T[] GetSamples(int count) { lock (_lock) { //TODO: May want to consider when count > one of these lists. But probably not // This is used when the front of the list should be favored Random rand = StaticRandom.GetRandomForThread(); var randFunc = new Func <int, int, int>((min, max) => min + UtilityCore.GetIndexIntoList(rand.NextPow(2), max - min)); if (_longTermIndex < 0 && _tentativeBuffer.Count == 0) { // Empty return(new T[0]); } else if (_longTermIndex >= _abandonTentativeSize) { #region all from longterm return(UtilityCore.RandomRange(0, _longTermIndex + 1, count). // the randrange function takes care of reducing count if greater than range Select(o => _longTermBuffer[o].Item). ToArray()); #endregion } else if (_longTermIndex < 0) { #region all from tentative // Don't pull uniform randomly from tentative. Favor items at the front of the list return(UtilityCore.RandomRange(0, _tentativeBuffer.Count, count, randFunc). Select(o => _tentativeBuffer[o].Item). ToArray()); #endregion } else { #region mixture // Pull from long term then tentative return(UtilityCore.RandomRange(0, _longTermIndex + 1 + _tentativeBuffer.Count, count, randFunc). Select(o => { if (o <= _longTermIndex) { return _longTermBuffer[o].Item; } else { return _tentativeBuffer[o - (_longTermIndex + 1)].Item; } }). ToArray()); #endregion } } }
private static Point3D[] GetVoronoiCtrlPoints(HullVoronoiExploder_ShotHit[] hits, ITriangle[] convexHull, int minCount, int maxCount, double aabbLen) { const int MIN = 5; Random rand = StaticRandom.GetRandomForThread(); #region examine hits Tuple <int, double>[] hitsByLength = hits. Select((o, i) => Tuple.Create(i, (o.Hit.Item2 - o.Hit.Item1).Length)). OrderByDescending(o => o.Item2). ToArray(); double totalLength = hitsByLength.Sum(o => o.Item2); Tuple <int, double>[] hitsByPercentLength = hitsByLength. Select(o => Tuple.Create(o.Item1, o.Item2 / totalLength)). ToArray(); #endregion #region define control point cones double entryRadius = aabbLen * .05; double exitRadius = aabbLen * .35; double maxAxisLength = aabbLen * .75; int count = hits.Length * 2; count += (totalLength / (aabbLen * .1)).ToInt_Round(); if (count < minCount) { count = minCount; } else if (count > maxCount) { count = maxCount; } #endregion #region randomly pick control points // Keep adding rings around shots until the count is exceeded var sets = new List <Tuple <int, List <Point3D> > >(); int runningSum = 0; // Make sure each hit line gets some points for (int cntr = 0; cntr < hits.Length; cntr++) { AddToHitline(ref runningSum, sets, cntr, hits[cntr].Hit, entryRadius, exitRadius, maxAxisLength, rand); } // Now that all hit lines have points, randomly choose lines until count is exceeded while (runningSum < count) { var pointsPerLength = hitsByLength. Select(o => { var pointsForIndex = sets.FirstOrDefault(p => p.Item1 == o.Item1); int countForIndex = pointsForIndex == null ? 0 : pointsForIndex.Item2.Count; return(Tuple.Create(o.Item1, countForIndex / o.Item2)); }). OrderBy(o => o.Item2). ToArray(); double sumRatio = pointsPerLength.Sum(o => o.Item2); var pointsPerLengthNormalized = pointsPerLength. Select(o => (o.Item1, o.Item2 / sumRatio)). ToArray(); int index = UtilityCore.GetIndexIntoList(rand.NextPow(3), pointsPerLengthNormalized); AddToHitline(ref runningSum, sets, index, hits[index].Hit, entryRadius, exitRadius, maxAxisLength, rand); } #endregion #region remove excessive points while (runningSum > count) { var fractions = sets. Select((o, i) => (i, o.Item2.Count.ToDouble() / runningSum.ToDouble())). OrderByDescending(o => o.Item2). ToArray(); int fractionIndex = UtilityCore.GetIndexIntoList(rand.NextPow(1.5), fractions); //nextPow will favor the front of the list, which is where the rings with the most points are int setIndex = fractions[fractionIndex].Item1; sets[setIndex].Item2.RemoveAt(UtilityCore.GetIndexIntoList(rand.NextDouble(), sets[setIndex].Item2.Count)); runningSum--; } #endregion #region ensure enough for voronoi algorithm List <Point3D> retVal = new List <Point3D>(); retVal.AddRange(sets.SelectMany(o => o.Item2)); // The voronoi algrorithm fails if there aren't at least 5 points (really should fix that). So add points that are // way away from the hull. This will make the voronoi algorithm happy, and won't affect the local results if (count < MIN) { retVal.AddRange( Enumerable.Range(0, MIN - count). Select(o => Math3D.GetRandomVector_Spherical_Shell(aabbLen * 20).ToPoint()) ); } #endregion return(retVal.ToArray()); }