//NOTE: Radius isn't the radius of the ball, it's how far that ball is from the center public DraggableModifierSphere(Vector3D offset, EditorColors editorColors) : base(editorColors) { _radius = offset.Length; _initialOffset = offset.ToUnit(); this.Model = new ModelVisual3D(); this.Model.Content = GetBall(); }
private static Vector3D[] DistributeForces_UNECESSARRILYCOMLEX(Point3D[] bodyCenters, Point3D hitStart, Vector3D hitDirection, double mainBodyRadius, bool useDistDot = true, double distDotPow = .4) { Vector3D hitDirectionUnit = hitDirection.ToUnit(); double hitForceBase = hitDirection.Length / mainBodyRadius; var vectors = bodyCenters. Select(o => { Vector3D toPoint = o - hitStart; Vector3D toPointUnit = toPoint.ToUnit(); double dot = Vector3D.DotProduct(hitDirectionUnit, toPointUnit); double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, toPointUnit); Vector3D along = toPoint.GetProjectedVector(hitDirection).ToUnit(false); Vector3D orth = (o - Math3D.GetClosestPoint_Line_Point(hitStart, hitDirection, o)).ToUnit(false); along *= linearDot; orth *= (1 - linearDot); double distance = toPoint.Length; // Exaggerate the distance based on dot product. That way, points in line with the hit will get more of the impact double scale = (1 - Math.Abs(linearDot)); scale = Math.Pow(scale, distDotPow); double scaledDistance = scale * distance; return new { ForcePoint = o, Distance = distance, ScaledDistance = scaledDistance, Along = along, Orth = orth, }; }). ToArray(); double[] distances = useDistDot ? vectors.Select(o => o.ScaledDistance).ToArray() : vectors.Select(o => o.Distance).ToArray(); double[] percents = ExplosionForceWorker.GetPercentOfProjForce(distances); Vector3D[] retVal = new Vector3D[vectors.Length]; for (int cntr = 0; cntr < vectors.Length; cntr++) { Vector3D along = vectors[cntr].Along * (hitForceBase * percents[cntr]); Vector3D orth = vectors[cntr].Orth * (hitForceBase * percents[cntr]); retVal[cntr] = along + orth; } return retVal; }
private void RedrawProjForce() { double linearDotPow = .4; if (chkProjForceUseDistDot.IsChecked.Value && !double.TryParse(txtProjForceDistDotPow.Text, out linearDotPow)) { txtProjForceDistDotPow.Effect = _errorEffect; return; } else { txtProjForceDistDotPow.Effect = null; } Point3D hitStart = new Point3D(0, MAXRADIUS * -.6, 0); Vector3D hitDirection = new Vector3D(0, MAXRADIUS / 2, 0); double asteroidSize = MAXRADIUS * .8; Vector3D hitDirectionUnit = hitDirection.ToUnit(); double hitForceBase = hitDirection.Length / asteroidSize; ClearAll(); AddDot(hitStart, DOTRADIUS * 4, _colors.Shot); AddLine_Billboard(hitStart, hitStart + hitDirection, LINETHICKNESS_BILLBOARD, _colors.Shot); if (_projForcePoints != null) { AddDots(_projForcePoints, DOTRADIUS, _colors.ControlPoint); var vectors = _projForcePoints. Select(o => { Vector3D toPoint = o - hitStart; Vector3D toPointUnit = toPoint.ToUnit(); double dot = Vector3D.DotProduct(hitDirectionUnit, toPointUnit); double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, toPointUnit); Vector3D along = toPoint.GetProjectedVector(hitDirection).ToUnit(false); Vector3D orth = (o - Math3D.GetClosestPoint_Line_Point(hitStart, hitDirection, o)).ToUnit(false); along *= linearDot * trkProjForceAlong.Value; orth *= (1 - linearDot) * trkProjForceOrth.Value; double distance = toPoint.Length; // Exaggerate the distance based on dot product. That way, points in line with the hit will get more of the impact double scale = (1 - Math.Abs(linearDot)); scale = Math.Pow(scale, linearDotPow); double scaledDistance = scale * distance; return new { ForcePoint = o, Distance = distance, ScaledDistance = scaledDistance, LinearDot = linearDot, Dot = dot, Along = along, Orth = orth, }; }). ToArray(); double[] distances = chkProjForceUseDistDot.IsChecked.Value ? vectors.Select(o => o.ScaledDistance).ToArray() : vectors.Select(o => o.Distance).ToArray(); double[] percents = ExplosionForceWorker.GetPercentOfProjForce(distances); for (int cntr = 0; cntr < vectors.Length; cntr++) { Vector3D along = vectors[cntr].Along * (hitForceBase * percents[cntr] * trkProjForceOverall.Value); Vector3D orth = vectors[cntr].Orth * (hitForceBase * percents[cntr] * trkProjForceOverall.Value); AddLine_Billboard(vectors[cntr].ForcePoint, vectors[cntr].ForcePoint + along, LINETHICKNESS_BILLBOARD / 3, UtilityWPF.ColorFromHex("808080")); AddLine_Billboard(vectors[cntr].ForcePoint, vectors[cntr].ForcePoint + orth, LINETHICKNESS_BILLBOARD / 3, UtilityWPF.ColorFromHex("808080")); } } }
private static Vector3D? GetScaledDirection(Tuple<Point3D, Vector3D>[] nearby) { Vector3D retVal = new Vector3D(0, 0, 0); for (int cntr = 0; cntr < nearby.Length; cntr++) { retVal += nearby[cntr].Item2; } if (Math3D.IsNearZero(retVal)) { return null; } return retVal.ToUnit(); }
private void ShadowMultiStepSprtFinish(Point3D cameraPos, Vector3D cameraLook, TriangleIndexed[] triangles) { // Order by distance to plane ITriangleIndexed[] sorted = Math3D.SortByPlaneDistance(triangles, cameraPos, cameraLook); SortedList<int, List<Point3D[]>> clipPolygons = new SortedList<int, List<Point3D[]>>(); Vector3D hullDepth = cameraLook.ToUnit() * 50d; //for (int outer = 0; outer < sorted.Length - 1; outer++) for (int outer = 0; outer < sorted.Length; outer++) { // Create a volume out of the this triangle ITriangleIndexed[] hull = GetHullFromTriangle(sorted[outer], hullDepth); if (hull != null && hull.Length > 6) // if the triangle is super thin, then a 2D hull will be created { //for (int inner = outer + 1; inner < sorted.Length; inner++) for (int inner = 0; inner < sorted.Length; inner++) { if (outer == inner) { continue; } Point3D[] poly = HullTriangleIntersect2.GetIntersection_Hull_Triangle(hull, sorted[inner]); if (poly != null) { if (!clipPolygons.ContainsKey(inner)) { clipPolygons.Add(inner, new List<Point3D[]>()); } clipPolygons[inner].Add(poly); } } } } double[] percents = new double[sorted.Length]; for (int cntr = 0; cntr < sorted.Length; cntr++) { if (clipPolygons.ContainsKey(cntr)) { percents[cntr] = GetPercentVisible(new Point3D[] { sorted[cntr].Point0, sorted[cntr].Point1, sorted[cntr].Point2 }, clipPolygons[cntr].ToArray()); } else { percents[cntr] = 1d; } } #region Draw Visual3D visual; Color color; // % in shadow for (int cntr = 0; cntr < sorted.Length; cntr++) { color = UtilityWPF.AlphaBlend(Colors.White, Colors.Black, percents[cntr]); visual = GetVisual_PolygonLines(new Point3D[] { sorted[cntr].Point0, sorted[cntr].Point1, sorted[cntr].Point2 }, color, 2d); _debugVisuals.Add(visual); _viewport.Children.Add(visual); } // Clip polygons foreach (Point3D[] polygon in clipPolygons.Values.SelectMany(o => o)) { visual = GetVisual_PolygonHull(polygon, Colors.DodgerBlue, .005); _debugVisuals.Add(visual); _viewport.Children.Add(visual); } // Triangles color = UtilityWPF.ColorFromHex("50E0E0E0"); for (int cntr = 0; cntr < sorted.Length; cntr++) { visual = GetVisual_Triangle(new ITriangle[] { sorted[cntr] }, color); _debugVisuals.Add(visual); _viewport.Children.Add(visual); } //// Draw the plane //visual = GetTriangleVisual(GetPlane(_camera.Position, _camera.LookDirection, 60), UtilityWPF.ColorFromHex("20B0B0B0")); //_debugVisuals.Add(visual); //_viewport.Children.Add(visual); #endregion }
//TODO: These two are aspects of the same thing. Make a single method for this private static double GetScore_UnderPowered(Vector3D? objective, Vector3D actual, double maxPossible) { if (objective == null || objective.Value.LengthSquared.IsNearZero()) { // The underpowered check doesn't care about this condition return 0; } double lenSqr = actual.LengthSquared; double maxSqr = maxPossible * maxPossible; double dot = Vector3D.DotProduct(objective.Value.ToUnit(false), actual.ToUnit(false)); // This makes sure it's actually pushing the bot, and not just balancing forces double underPower = 0; if (lenSqr * dot < maxSqr) { if (dot > 0) { underPower = maxSqr - (lenSqr * dot); } else { underPower = maxSqr; } } return underPower; }
//TODO: Have a way to determine which props will be needed up front, and only populate those /// <param name="item">NOTE: This is the item chasing, NOT the item being chased</param> public ChasePoint_GetForceArgs(IMapObject item, Vector3D direction) { this.Item = item; this.ItemMass = item.PhysicsBody.Mass; this.DirectionLength = direction.Length; this.DirectionUnit = direction.ToUnit(false); // Velocity Vector3D velocity = this.Item.PhysicsBody.Velocity; this.VelocityLength = velocity.Length; this.VelocityUnit = velocity.ToUnit(false); // Along Vector3D velocityAlong = velocity.GetProjectedVector(direction); this.VelocityAlongLength = velocityAlong.Length; this.VelocityAlongUnit = velocityAlong.ToUnit(false); this.IsVelocityAlongTowards = Vector3D.DotProduct(direction, velocity) > 0d; // Orth Vector3D orth = Vector3D.CrossProduct(direction, velocity); // the first cross is orth to both (outside the plane) orth = Vector3D.CrossProduct(orth, direction); // the second cross is in the plane, but orth to distance Vector3D velocityOrth = velocity.GetProjectedVector(orth); this.VelocityOrthLength = velocityOrth.Length; this.VelocityOrthUnit = velocityOrth.ToUnit(false); }
private static Vector3D CapVector(Vector3D vector, double maxLength) { if (vector.LengthSquared <= maxLength * maxLength) { return vector; } return vector.ToUnit(false) * maxLength; }
private static bool CanCounterVectors2(Vector3D[] tests, Vector3D[] others) { Vector3D cumulative = new Vector3D(); foreach (Vector3D test in tests) { cumulative += test; } // Make the cumulative point the opposite way (that's what all the tests need) cumulative = cumulative * -1d; // Check for 0D, 1D, 2D, 3D opposition if (others.Length == 0) { #region 0D // Nothing to oppose. The only thing that works is if the test self cancel return Math3D.IsNearZero(cumulative); #endregion } else if (others.Length == 1) { #region 1D // Opposition is a single line // If cumulative's length is longer, then the other is overwhelmed // If the dot product isn't one, then they aren't lined up (cumulative has already been negated) return cumulative.LengthSquared <= others[0].LengthSquared && Math1D.IsNearValue(Vector3D.DotProduct(cumulative.ToUnit(), others[0].ToUnit()), 1d); #endregion } else if (others.Length == 2) { #region 2D // Opposition forms a parallelagram Triangle triangle = new Triangle(new Point3D(0, 0, 0), others[0].ToPoint(), others[1].ToPoint()); if (!Math1D.IsNearZero(Vector3D.DotProduct(triangle.Normal, cumulative))) { return false; } Vector bary = Math3D.ToBarycentric(triangle, cumulative.ToPoint()); return bary.X >= 0d && bary.Y >= 0d && bary.X + bary.Y <= 2d; #endregion } else { #region 3D //NOTE: The reason there is no need to check for 4D, 5D, etc is because cumulative is a 3D vector // Build the hull out of others Point3D[] extremes = GetRemainingExtremes(others, new int[0]); TriangleIndexed[] hull = Math3D.GetConvexHull(extremes); if (hull != null) { return Math3D.IsInside_Planes(hull, cumulative.ToPoint()); } // If hull is null, the points could be coplanar, so try 2D var perimiter = Math2D.GetConvexHull(extremes); if (perimiter != null) { // These are coplanar, see if the cumulative is inside Point? cumulative2D = perimiter.GetTransformedPoint(cumulative.ToPoint()); if (cumulative2D == null) { return false; } else { return perimiter.IsInside(cumulative2D.Value); } } return false; #endregion } }
private static Vector3D GetThrustLine(Vector3D force, double strengthRatio) { const double MULT = -1d; // the desired length when force.length / strengthRatio is one double ratio = MULT * force.Length / strengthRatio; return force.ToUnit() * MULT; }
private static IEnumerable<ThrusterSetting> EnsureThrustKeysBuilt_Rotate(Vector3D axis, ThrustContribution[] contributions, double maxAcceleration, MassMatrix inertia) { axis = axis.ToUnit(); // doing this so the dot product can be used as a percent List<Tuple<ThrustContribution, double>> retVal = new List<Tuple<ThrustContribution, double>>(); // Get a list of thrusters that will contribute to the direction foreach (ThrustContribution contribution in contributions) { double dot = Vector3D.DotProduct(contribution.TorqueUnit, axis); if (dot > .5d) { //retVal.Add(new ThrusterSetting(contribution.Thruster, contribution.Index, 1)); // for now, just do 100% retVal.Add(Tuple.Create(contribution, contribution.TorqueLength * dot)); } } retVal = FilterThrusts(retVal); #region Reduce Percent double percent = 1; if (retVal.Count > 0) { double torque = retVal.Sum(o => o.Item2); // A=F/M //double accel = torque / (Vector3D.DotProduct(inertia.Inertia, axis) * inertia.Mass); double accel = torque / Math.Abs(Vector3D.DotProduct(inertia.Inertia, axis)); if (accel > maxAcceleration) { percent = maxAcceleration / accel; } } #endregion return retVal.Select(o => new ThrusterSetting(o.Item1.Thruster, o.Item1.Index, percent)).ToArray(); // commiting to array so that this linq isn't rerun each time it's iterated over }
/// <summary> /// This overload will cut back the returned percents if the thrusters exceed the acceleration passed in /// </summary> private static IEnumerable<ThrusterSetting> EnsureThrustKeysBuilt_Linear(Vector3D direction, ThrustContribution[] contributions, double maxAcceleration, double mass) { direction = direction.ToUnit(); // doing this so the dot product can be used as a percent List<Tuple<ThrustContribution, double>> retVal = new List<Tuple<ThrustContribution, double>>(); // Get a list of thrusters that will contribute to the direction foreach (ThrustContribution contribution in contributions) { double dot = Vector3D.DotProduct(contribution.TranslationForceUnit, direction); if (dot > .05d) { retVal.Add(Tuple.Create(contribution, contribution.TranslationForceLength * dot)); } } retVal = FilterThrusts(retVal); #region Reduce Percent double percent = 1; if (retVal.Count > 0) { double force = retVal.Sum(o => o.Item2); //F=MA, A=F/M double accel = force / mass; if (accel > maxAcceleration) { percent = maxAcceleration / accel; } } #endregion return retVal.Select(o => new ThrusterSetting(o.Item1.Thruster, o.Item1.Index, percent)).ToArray(); // commiting to array so that this linq isn't rerun each time it's iterated over }
private static IEnumerable<ThrusterSetting> EnsureThrustKeysBuilt_Rotate(Vector3D torque, ThrustContribution[] contributions) { torque = torque.ToUnit(); // doing this so the dot product can be used as a percent List<Tuple<ThrustContribution, double>> retVal = new List<Tuple<ThrustContribution, double>>(); // Get a list of thrusters that will contribute to the direction foreach (ThrustContribution contribution in contributions) { double dot = Vector3D.DotProduct(contribution.TorqueUnit, torque); if (dot > .5d) { retVal.Add(Tuple.Create(contribution, contribution.TorqueLength * dot)); } //if (dot > .05d) //{ // retVal.Add(new ThrusterSetting(thruster, cntr, dot)); // for now, use the dot as the percent //} } retVal = FilterThrusts(retVal, .05); return retVal.Select(o => new ThrusterSetting(o.Item1.Thruster, o.Item1.Index, 1)).ToArray(); // commiting to array so that this linq isn't rerun each time it's iterated over }
private void UpdateNeurons_fixed(Vector3D gravity, double magnitude) { const double MINDOT = 1d; if (Math3D.IsNearZero(gravity)) { // There is no gravity to report for (int cntr = 0; cntr < _neurons.Length; cntr++) { _neurons[cntr].Value = 0d; } return; } Vector3D gravityUnit = gravity.ToUnit(); for (int cntr = 0; cntr < _neurons.Length; cntr++) { if (_neurons[cntr].PositionUnit == null) { // This neuron is sitting at 0,0,0 _neurons[cntr].Value = magnitude; } else { double dot = Vector3D.DotProduct(gravityUnit, _neurons[cntr].PositionUnit.Value); dot += 1d; // get this to scale from 0 to 2 instead of -1 to 1 if (dot < MINDOT) { _neurons[cntr].Value = 0d; } else { _neurons[cntr].Value = UtilityCore.GetScaledValue_Capped(0d, 1d, MINDOT, 2d, dot); } } } }
/// <summary> /// This sets all the neurons from 0 to magnitude. Magnitude needs to be from 0 to 1, and is based on the currently felt gravity compared /// to what the sensor has seen as the max felt /// </summary> internal static void UpdateNeurons(Neuron_SensorPosition[] neurons, double neuronMaxRadius, Vector3D vector, double magnitude) { const double MINDOT = 1.25d; if (Math3D.IsNearZero(vector)) { // There is no gravity to report for (int cntr = 0; cntr < neurons.Length; cntr++) { neurons[cntr].Value = 0d; } return; } Vector3D gravityUnit = vector.ToUnit(); for (int cntr = 0; cntr < neurons.Length; cntr++) { if (neurons[cntr].PositionUnit == null) { // This neuron is sitting at 0,0,0 neurons[cntr].Value = magnitude; } else { // Figure out how aligned this neuron is with the gravity vector double dot = Vector3D.DotProduct(gravityUnit, neurons[cntr].PositionUnit.Value); dot += 1d; // get this to scale from 0 to 2 instead of -1 to 1 // Scale minDot double radiusPercent = neurons[cntr].PositionLength / neuronMaxRadius; double revisedMinDot = UtilityCore.GetScaledValue_Capped(0d, MINDOT, 0d, 1d, radiusPercent); // Rig it so that the lower radius neurons will fire stronger double minReturn = 1d - radiusPercent; if (minReturn < 0d) { minReturn = 0d; } // Figure out what percentage of magnitude to use double percent; if (dot < revisedMinDot) { percent = UtilityCore.GetScaledValue_Capped(0d, minReturn, 0d, revisedMinDot, dot); } else { percent = UtilityCore.GetScaledValue_Capped(minReturn, 1d, revisedMinDot, 2d, dot); } // Set the neuron neurons[cntr].Value = percent * magnitude; } } }
/// <summary> /// This will figure how much velocity to apply to each bodyCenter /// TODO: Also calculate angular velocities /// </summary> /// <param name="bodyCenters">The bodies to apply velocity to</param> /// <param name="hitStart">The point of impact</param> /// <param name="hitDirection">The direction and force of the impact</param> /// <param name="mainBodyRadius">The avg radius of the body that is getting blown apart</param> private static Vector3D[] DistributeForces(Point3D[] bodyCenters, Point3D hitStart, Vector3D hitDirection, double mainBodyRadius, HullVoronoiExploder_Options options) { Vector3D hitDirectionUnit = hitDirection.ToUnit(); double hitForceBase = hitDirection.Length / mainBodyRadius; var vectors = bodyCenters. Select(o => { Vector3D direction = o - hitStart; Vector3D directionUnit = direction.ToUnit(); double distance = direction.Length; double scaledDistance = distance; if (options.DistanceDot_Power != null) { double linearDot = Math3D.GetLinearDotProduct(hitDirectionUnit, directionUnit); // making it linear so the power function is more predictable // Exaggerate the distance based on dot product. That way, points in line with the hit will get more of the impact double scale = (1 - Math.Abs(linearDot)); scale = Math.Pow(scale, options.DistanceDot_Power.Value); scaledDistance = scale * distance; } return new { ForcePoint = o, Distance = distance, ScaledDistance = scaledDistance, DirectionUnit = directionUnit, }; }). ToArray(); double[] percents = GetPercentOfProjForce(vectors.Select(o => o.ScaledDistance).ToArray()); Vector3D[] retVal = new Vector3D[vectors.Length]; for (int cntr = 0; cntr < vectors.Length; cntr++) { retVal[cntr] = vectors[cntr].DirectionUnit * (hitForceBase * percents[cntr]); } return retVal; }
public static double GetMaximumPossible_Rotation(ThrustContributionModel model, Vector3D direction) { direction = direction.ToUnit(); // doing this so the dot product can be used as a percent double retVal = 0d; foreach (var contribution in model.Contributions) { retVal += contribution.Item3.Torque.GetProjectedVector(direction, false).Length; } return retVal; }
// These return error as a square (so the numbers can get big quick) private static double GetScore_Balance(Vector3D? objective, Vector3D actual) { if (objective == null || objective.Value.LengthSquared.IsNearZero()) { // When null, the objective is zero. So any length is an error return actual.LengthSquared; } double dot = Vector3D.DotProduct(objective.Value.ToUnit(false), actual.ToUnit(false)); // Ideal dot is 1, so 1-1 is 0. // 1 - 0 is 1 // 1 - -1 is 2 // So divide by 2 to get difference in a range of 0 to 1 double difference = (1d - dot) / 2d; // Now that difference is scaled 0 to 1, use the length squared as max error double dotScale = actual.LengthSquared * difference; return dotScale; }
private static RayHitTestParameters CastRay_PlaneLimitedSprtGetSnapLine(ITriangle plane, Vector3D lookDirection) { const double THRESHOLD = .33d; #region Get orthogonal vectors Point3D centerPoint = new Point3D(); DoubleVector testVectors = new DoubleVector(); //This assumes that the plane was set up with orthogonal vectors, and that these are the ones to use in this case for (int cntr = 0; cntr < 3; cntr++) { switch (cntr) { case 0: centerPoint = plane.Point0; testVectors = new DoubleVector((plane.Point1 - plane.Point0).ToUnit(), (plane.Point2 - plane.Point0).ToUnit()); break; case 1: centerPoint = plane.Point1; testVectors = new DoubleVector((plane.Point2 - plane.Point1).ToUnit(), (plane.Point0 - plane.Point1).ToUnit()); break; case 2: centerPoint = plane.Point2; testVectors = new DoubleVector((plane.Point0 - plane.Point2).ToUnit(), (plane.Point1 - plane.Point2).ToUnit()); break; default: throw new ApplicationException("Unexpected cntr: " + cntr.ToString()); } if (Math1D.IsNearZero(Vector3D.DotProduct(testVectors.Standard, testVectors.Orth))) { break; } } #endregion double dot = Vector3D.DotProduct(testVectors.Standard, lookDirection.ToUnit()); if (Math.Abs(dot) < THRESHOLD) { return new RayHitTestParameters(centerPoint, testVectors.Standard); // standard is fairly orhogonal to the camera's look direction, so only allow the part to slide along this axis } dot = Vector3D.DotProduct(testVectors.Orth, lookDirection.ToUnit()); if (Math.Abs(dot) < THRESHOLD) { return new RayHitTestParameters(centerPoint, testVectors.Orth); } return null; }
private void FireRay(Point clickPoint) { // This is how microsoft recomends doing hit tests, but I don't care about wpf models. I just want the world coords of the mouse //HitTestResult result = VisualTreeHelper.HitTest(grdViewPort, clickPoint, ); // Project the mouse click into world coords RayHitTestParameters hitParam = UtilityWPF.RayFromViewportPoint(_camera, _viewport, clickPoint); _rayPoint = hitParam.Origin; _rayDirection = hitParam.Direction; if (_leftClickAction == LeftClickAction.ShootBall) { FireRaySprtShootBall(); return; } List<WorldBase.IntersectionPoint> hits = _world.RayCast(_rayPoint, _rayPoint + (_rayDirection.ToUnit() * 1000d)); switch (_leftClickAction) { case LeftClickAction.Remove: #region Remove if (hits.Count > 0) { RemoveBrick(hits[0].Body); } #endregion break; case LeftClickAction.RemoveLine: #region RemoveLine foreach (WorldBase.IntersectionPoint hit in hits) { RemoveBrick(hit.Body); } #endregion break; case LeftClickAction.Explode: #region Explode if (hits.Count > 0 && hits[0].Body.MaterialGroupID == _material_Brick) { ExplodeBody(hits[0].Body, true, trkLeftExplodePower.Value); // Remove from brick list _bricks.Remove(hits[0].Body); } #endregion break; case LeftClickAction.ExplodeLine: #region ExplodeLine foreach (WorldBase.IntersectionPoint hit in hits) { if (hit.Body.MaterialGroupID == _material_Brick) { ExplodeBody(hit.Body, true, trkLeftExplodePower.Value); // Remove from brick list _bricks.Remove(hit.Body); } } #endregion break; case LeftClickAction.Implode: #region Implode if (hits.Count > 0 && hits[0].Body.MaterialGroupID == _material_Brick) { ExplodeBody(hits[0].Body, false, trkLeftExplodePower.Value); // Remove from brick list _bricks.Remove(hits[0].Body); } #endregion break; case LeftClickAction.ImplodeLine: #region ImplodeLine foreach (WorldBase.IntersectionPoint hit in hits) { if (hit.Body.MaterialGroupID == _material_Brick) { ExplodeBody(hit.Body, false, trkLeftExplodePower.Value); // Remove from brick list _bricks.Remove(hit.Body); } } #endregion break; case LeftClickAction.ForceBeam: // Nothing to do here, it's all in the apply force/torque callback break; default: // Paint them green for now foreach (WorldBase.IntersectionPoint hit in hits) { if (_bodyMaterials.ContainsKey(hit.Body)) // the terrain isn't in the list { _bodyMaterials[hit.Body].Material.Brush = new SolidColorBrush(Colors.Chartreuse); } } break; } }