private static Vector3D[] GetVelocities(HullVoronoiExploder_Shard[] hullShards, HullVoronoiExploder_ShotHit[] hits, double hullRadius, HullVoronoiExploder_Options options) { //TODO: When the asteroid shards aren't resmoothed, the velocities are very small // //Maybe add more orth velocity // //Maybe pull the hit source slightly into the asteroid --- done // //Maybe add some random velocity (size proportional to the velocity) --- done Vector3D[] retVal = CalculateVelocites(hullShards.Select(o => o.Center_ParentCoords).ToArray(), hits, hullRadius, options); if (options.RandomVelocityPercent != null) { retVal = retVal. Select(o => o + Math3D.GetRandomVector_Spherical(o.Length * options.RandomVelocityPercent.Value)). ToArray(); } return retVal; }
private static Vector3D[] CalculateVelocites(Point3D[] centers, HullVoronoiExploder_ShotHit[] hits, double hullRadius, HullVoronoiExploder_Options options) { Vector3D[] retVal = new Vector3D[centers.Length]; foreach (var hit in hits) { Point3D shotStart = hit.Hit.Item1; if (options.InteriorVelocityCenterPercent != null) { shotStart += ((hit.Hit.Item2 - hit.Hit.Item1) * options.InteriorVelocityCenterPercent.Value); } //Vector3D[] velocities = DistributeForces(centers, shotStart, hit.Shot.Item2 * (hit.Shot.Item3 * options.ShotMultiplier), hullRadius, options); Vector3D[] velocities = DistributeForces(centers, shotStart, hit.Shot.Item2 * hit.Shot.Item3, hullRadius, options); for (int cntr = 0; cntr < retVal.Length; cntr++) { retVal[cntr] += velocities[cntr]; } } return retVal; }
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 => Tuple.Create(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) { Tuple<int, double>[] fractions = sets. Select((o, i) => Tuple.Create(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(); }