public Cargo_ShipPart(ShipPartDNA dna, ItemOptions options, EditorOptions editorOptions) : base(CargoType.ShipPart) { PartDesignBase part = BotConstructor.GetPartDesign(dna, editorOptions, true); //TODO: This is really ineficient, let design calculate it for real //TODO: Volume and Mass should be calculated by the design class (add to PartBase interface) var aabb = Math3D.GetAABB(UtilityWPF.GetPointsFromMesh(part.Model)); this.Volume = (aabb.Item2.X - aabb.Item1.X) * (aabb.Item2.Y - aabb.Item1.Y) * (aabb.Item2.Y - aabb.Item1.Y); //TODO: Let the design class return this (expose a property called DryDensity) this.Density = Math1D.Avg(options.Thruster_Density, options.Sensor_Density); this.PartDNA = dna; }
public static HullVoronoiExploder_Response ShootHull(ITriangleIndexed[] convexHull, Tuple <Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options = null) { options = options ?? new HullVoronoiExploder_Options(); var aabb = Math3D.GetAABB(convexHull); double aabbLen = (aabb.Item2 - aabb.Item1).Length; // Create a voronoi, and intersect with the hull Tuple <HullVoronoiExploder_Response, bool> retVal = null; for (int cntr = 0; cntr < 15; cntr++) { try { retVal = SplitHull(convexHull, shots, options, aabbLen); break; } catch (Exception) { // Every once in a while, there is an error with the voronoi, or voronoi intersecting the hull, etc. Just try // again with new random points } } if (retVal == null) { return(null); } else if (!retVal.Item2) { return(retVal.Item1); } // Figure out velocities retVal.Item1.Velocities = GetVelocities(retVal.Item1.Shards, retVal.Item1.Hits, Math.Sqrt(aabbLen / 2), options); return(retVal.Item1); }
private static Tuple <HullVoronoiExploder_Response, bool> SplitHull(ITriangleIndexed[] convexHull, Tuple <Point3D, Vector3D, double>[] shots, HullVoronoiExploder_Options options, double aabbLen) { #region intersect with the hull var hits = shots. Select(o => new HullVoronoiExploder_ShotHit() { Shot = o, Hit = GetHit(convexHull, o.Item1, o.Item2, o.Item3), }). Where(o => o.Hit != null). ToArray(); if (hits.Length == 0) { return(null); } #endregion #region voronoi Point3D[] controlPoints = GetVoronoiCtrlPoints(hits, convexHull, options.MinCount, options.MaxCount, aabbLen); VoronoiResult3D voronoi = Math3D.GetVoronoi(controlPoints, true); if (voronoi == null) { return(null); } #endregion // There is enough to start populating the response HullVoronoiExploder_Response retVal = new HullVoronoiExploder_Response() { Hits = hits, ControlPoints = controlPoints, Voronoi = voronoi, }; #region intersect voronoi and hull // Intersect Tuple <int, ITriangleIndexed[]>[] shards = null; try { shards = Math3D.GetIntersection_Hull_Voronoi_full(convexHull, voronoi); } catch (Exception) { return(Tuple.Create(retVal, false)); } if (shards == null) { return(Tuple.Create(retVal, false)); } // Smooth if (options.ShouldSmoothShards) { shards = shards. Select(o => Tuple.Create(o.Item1, Asteroid.SmoothTriangles(o.Item2))). ToArray(); } // Validate shards = shards. Where(o => o.Item2 != null && o.Item2.Length >= 3). Where(o => { Vector3D firstNormal = o.Item2[0].NormalUnit; return(o.Item2.Skip(1).Any(p => !Math.Abs(Vector3D.DotProduct(firstNormal, p.NormalUnit)).IsNearValue(1))); }). ToArray(); if (shards.Length == 0) { return(Tuple.Create(retVal, false)); } #endregion #region populate shards retVal.Shards = shards. Select(o => { var aabb = Math3D.GetAABB(o.Item2); double radius = Math.Sqrt((aabb.Item2 - aabb.Item1).Length / 2); Point3D center = Math3D.GetCenter(TriangleIndexed.GetUsedPoints(o.Item2)); Vector3D centerVect = center.ToVector(); Point3D[] allPoints = o.Item2[0].AllPoints. Select(p => p - centerVect). ToArray(); TriangleIndexed[] shiftedTriangles = o.Item2. Select(p => new TriangleIndexed(p.Index0, p.Index1, p.Index2, allPoints)). ToArray(); return(new HullVoronoiExploder_Shard() { VoronoiControlPointIndex = o.Item1, Hull_ParentCoords = o.Item2, Hull_Centered = shiftedTriangles, Radius = radius, Center_ParentCoords = center, }); }). Where(o => o != null). ToArray(); #endregion return(Tuple.Create(retVal, true)); }
private static NeuronMapping[] MapNeurons(Point3D[] externalPoints, Point3D[] internalPoints) { // FuzzyLink wasn't designed for from and to points to be sitting on top of each other, so need to pull them apart // // external is pulled to -Z, internal is +Z. These offset coordinates don't have any meaning outside this function, they are just // a hack to allow FuzzyLink to work properly // Figure out how far to separate them to ensure they are fully separated var aabb = Math3D.GetAABB(externalPoints.Concat(internalPoints)); double offsetDist = Math1D.Max(aabb.Item2.X - aabb.Item1.X, aabb.Item2.Y - aabb.Item1.Y, aabb.Item2.Z - aabb.Item1.Z); offsetDist *= 3; Vector3D offset = new Vector3D(0, 0, offsetDist); // Create seed links. The easiest approach is just to create one per internal point and then let the fuzzy linker find the best // external point // // I could see the argument for remembering the links across generations so that if the external points drift around too much, // the original linking will better persist. But if the bot is mutating that much over generations, the neat NN should be retrained // from time to time. Also, if a mismapping causes the bot to perform bad, then it won't be making children (and a mismapping // might end up causing better performance) Tuple <Point3D, Point3D, double>[] initialLinks = internalPoints. Select(o => Tuple.Create(o - offset, o + offset, 1d)). ToArray(); Point3D[] pointsForFuzzy = externalPoints.Select(o => o - offset). Concat(internalPoints.Select(o => o + offset)). ToArray(); // Allow a few more links than points. If the external and internal points are aligned well, then the extra link allowance won't // be used int numLinks = (internalPoints.Length * 1.1).ToInt_Ceiling(); var finalLinks = ItemLinker.FuzzyLink(initialLinks, pointsForFuzzy, numLinks, 6); const double THICKNESS = .005; const double DOT = THICKNESS * 3; Debug3DWindow window = new Debug3DWindow(); window.AddDots(externalPoints, DOT, Colors.IndianRed); window.AddDots(internalPoints, DOT, Colors.DodgerBlue); window.AddDots(pointsForFuzzy, DOT, Colors.Silver); window.AddLines(initialLinks.Select(o => (o.Item1, o.Item2)), THICKNESS, Colors.Orchid); List <NeuronMapping> retVal = new List <NeuronMapping>(); foreach (var link in finalLinks) { int?externalIndex = null; int?internalIndex = null; foreach (int index in new[] { link.Item1, link.Item2 }) { if (index < externalPoints.Length) { externalIndex = index; } else { internalIndex = index - externalPoints.Length; } } if (externalIndex == null || internalIndex == null) { // This should never happen in practice, the internal and external sets are pulled too far apart to accidentally be linked together. // Just ignore this link continue; } retVal.Add(new NeuronMapping() { Index_External = externalIndex.Value, Index_NEAT = internalIndex.Value, Weight = link.Item3, }); } foreach (var link in finalLinks) { window.AddLine(pointsForFuzzy[link.Item1], pointsForFuzzy[link.Item2], THICKNESS * link.Item3, Colors.GhostWhite); } foreach (var link in retVal) { window.AddLine(externalPoints[link.Index_External], internalPoints[link.Index_NEAT], THICKNESS * link.Weight, Colors.Coral); } window.Show(); return(retVal.ToArray()); }
private void ChooseForcePoints() { SortedList <int, List <Point3D>[]> pointSets = new SortedList <int, List <Point3D>[]>(); for (int cntr = 0; cntr < _numSets; cntr++) { //TODO: This method should return barycentric coords directly // Create random points across the triangles SortedList <int, List <Point3D> > points = Math3D.GetRandomPoints_OnHull_Structured(_triangles, _numPointsPerSet); // Add these to the sets foreach (int triangleIndex in points.Keys) { if (!pointSets.ContainsKey(triangleIndex)) { pointSets.Add(triangleIndex, new List <Point3D> [_numSets]); } pointSets[triangleIndex][cntr] = points[triangleIndex]; } } foreach (int triangleIndex in pointSets.Keys) { List <Vector[]> localSets = new List <Vector[]>(); for (int setCntr = 0; setCntr < _numSets; setCntr++) { List <Point3D> points = pointSets[triangleIndex][setCntr]; if (points == null || points.Count == 0) { localSets.Add(null); continue; } Vector[] set = new Vector[points.Count]; localSets.Add(set); for (int cntr = 0; cntr < points.Count; cntr++) { set[cntr] = Math3D.ToBarycentric(this.Triangles[triangleIndex], points[cntr]); } } // Store these sets in this triangle this.Triangles[triangleIndex].StoreForcePointSets(localSets); } // Calculate the sample radius if (_triangles.Length > 0) { // The sum of the volumes of point samples needs to equal the volume of the hull: // N(4/3 pi r^3)=Vol // r=cube root((Vol/N)/(4/3 pi)) var aabb = Math3D.GetAABB(_triangles[0].AllPoints); // using AABB as a safe way to get the volume of the hull double volume = Math.Abs(aabb.Item2.X - aabb.Item1.X) * Math.Abs(aabb.Item2.Y - aabb.Item1.Y) * Math.Abs(aabb.Item2.Z - aabb.Item1.Z); double intermediate = (volume / _numPointsPerSet) / ((4d / 3d) * Math.PI); _sampleRadius = Math.Abs(Math.Pow(intermediate, 1d / 3d)); } else { _sampleRadius = 0d; } }