private static Model3D GetAxeCylinder(double radius, double height, double scale, double yOffset, MaterialGroup material) { GeometryModel3D retVal = new GeometryModel3D(); retVal.Material = material; retVal.BackMaterial = material; double bevel = radius * .2; List <TubeRingBase> tubes = new List <TubeRingBase>(); tubes.Add(new TubeRingRegularPolygon(0, false, radius - bevel, radius - bevel, true)); tubes.Add(new TubeRingRegularPolygon(bevel, false, radius, radius, false)); tubes.Add(new TubeRingRegularPolygon(height - (bevel * 2), false, radius, radius, false)); tubes.Add(new TubeRingRegularPolygon(bevel, false, radius - bevel, radius - bevel, true)); retVal.Geometry = UtilityWPF.GetMultiRingedTube(10, tubes, true, true); Transform3DGroup transform = new Transform3DGroup(); transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), 90))); // the tube is built along z, rotate so it's along y if (!Math1D.IsNearZero(yOffset)) { transform.Children.Add(new TranslateTransform3D(0, yOffset, 0)); } transform.Children.Add(new ScaleTransform3D(scale, scale, scale)); retVal.Transform = transform; return(retVal); }
private static Model3D GetAxeSpike(double radius, double length, double scale, double yOffset, MaterialGroup materialMiddle, MaterialGroup materialEdge) { GeometryModel3D retVal = new GeometryModel3D(); retVal.Material = materialEdge; retVal.BackMaterial = materialEdge; double bevel = radius * .2; List <TubeRingBase> tubes = new List <TubeRingBase>(); tubes.Add(new TubeRingRegularPolygon(0, false, radius, radius * 2, true)); tubes.Add(new TubeRingPoint(length, false)); retVal.Geometry = UtilityWPF.GetMultiRingedTube(10, tubes, true, false); Transform3DGroup transform = new Transform3DGroup(); transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), -90))); // the tube is built along z, rotate so it's along x if (!Math1D.IsNearZero(yOffset)) { transform.Children.Add(new TranslateTransform3D(0, yOffset, 0)); } transform.Children.Add(new ScaleTransform3D(scale, scale, scale)); retVal.Transform = transform; return(retVal); }
private void TransitionToFinal() { List <TrackingCandidate> finalists = new List <TrackingCandidate>(); #region Find finished finalists // Get all the finalists that are finished int index = 0; while (index < _finalists.Count) { TrackingCandidate finalist = _finalists[index]; int numStarted = finalist.NumStarted; if (numStarted >= this.FinalistCount && numStarted == finalist.NumFinished) { if (!Math1D.IsNearZero(finalist.GetAverage())) // don't let zero scores into the final (the way to get a zero score is to become a candidate, but then each attempt resulted in a zero score) { finalists.Add(finalist); } _finalists.RemoveAt(index); } else { index++; } } #endregion if (finalists.Count > 0) { this.Final.StoreWinners(finalists.Select(o => new WinnerList.WinningBean(o.DNA, o.GetAverage(), 0d)).ToArray()); } }
public DamageProps CalculateDamage(MaterialCollision[] collisions) { if (!_isFullySetUp || collisions.Length == 0) { return(null); } double damangeMult = GetDamageMultiplier(); if (Math1D.IsNearZero(damangeMult)) { return(null); } var avgCollision = MaterialCollision.GetAverageCollision(collisions, _bot.PhysicsBody); //TODO: See if this position is along the ramming direction double speed = avgCollision.Item2 / 10; // a speed of 10 is considered an average impact speed double damage = speed * damangeMult; DamageProps retVal = new DamageProps(avgCollision.Item1, damage); // The act of hitting something needs to stop the ram, otherwise, they just keep pushing against the item // and continue to do damage StopRamming(); return(retVal); }
private bool AreForcesZero() { if (this.Field == null || _triangles == null || _triangles.Length == 0) { return(true); } bool isZeroForce = false; if (this.Field is FluidFieldUniform) { var velocities = this.Field.GetForce(new[] { new Point3D(0, 0, 0) }, 1); isZeroForce = Math1D.IsNearZero(velocities[0].Item1.LengthSquared); } if (isZeroForce && (this.Body == null || (Math1D.IsNearZero(this.Body.Velocity.LengthSquared) && Math1D.IsNearZero(this.Body.AngularVelocity.LengthSquared)))) { return(true); } else { return(false); } }
private Vector3D AvoidWorker() { Vector3D retVal = new Vector3D(0, 0, 0); Vector3D myPositionWorld = this.PhysicsBody.PositionToWorld(this.PhysicsBody.CenterOfMass).ToVector(); // center mass should always be at zero, but I want to be consistent foreach (SwarmBot1 bot in _otherBots) { // Get its position relative to me Vector3D botPositionWorld = bot.PhysicsBody.PositionToWorld(bot.PhysicsBody.CenterOfMass).ToVector(); Vector3D offsetLocal = this.PhysicsBody.DirectionFromWorld(botPositionWorld - myPositionWorld); // I need to invert this, because the greater the distance, the less the influence double offsetLength = offsetLocal.Length; if (Math1D.IsNearZero(offsetLength)) { // Can't divide by zero. For now, I'll just skip this bot continue; } double awayForce = 1 / offsetLength; offsetLocal /= offsetLength; // normalize offsetLocal *= -awayForce; // point away from the bot, with the away force strength // Now add this to the return vector retVal += offsetLocal; } // Exit Function return(retVal); }
/// <summary> /// a1 is line1 start, a2 is line1 end, b1 is line2 start, b2 is line2 end /// </summary> /// <remarks> /// Got this here: /// http://stackoverflow.com/questions/14480124/how-do-i-detect-triangle-and-rectangle-intersection /// /// A similar article: /// http://www.flipcode.com/archives/Point-Plane_Collision.shtml /// </remarks> private static Vector?Intersects(Vector a1, Vector a2, Vector b1, Vector b2) { Vector b = Vector.Subtract(a2, a1); Vector d = Vector.Subtract(b2, b1); double bDotDPerp = (b.X * d.Y) - (b.Y * d.X); // if b dot d == 0, it means the lines are parallel so have infinite intersection points if (Math1D.IsNearZero(bDotDPerp)) { return(null); } Vector c = Vector.Subtract(b1, a1); double t = (c.X * d.Y - c.Y * d.X) / bDotDPerp; if (t < 0 || t > 1) { return(null); } double u = (c.X * b.Y - c.Y * b.X) / bDotDPerp; if (u < 0 || u > 1) { return(null); } // Return the intersection point return(Vector.Add(a1, Vector.Multiply(b, t))); }
private void PhysicsBody_BodyMoved(object sender, EventArgs e) { if (!_isVisualAdded) { return; } // Graphics need to be updated here. If I wait until world update, this graphic will be one frame // behind, which is very noticable when the bot has a high velocity double damageMult = GetDamageMultiplier(); Vector3D velocity = _bot.VelocityWorld; var dna = this.DNA; // Light size double lightSize = UtilityCore.GetScaledValue_Capped(0, _bot.Radius * 4, 0, dna.DamageMult * 20, damageMult); UtilityWPF.SetAttenuation(_light, lightSize, .1d); _light.Color = Color.FromArgb(Convert.ToByte(UtilityCore.GetScaledValue_Capped(0, 192, 0, dna.DamageMult * 10, damageMult)), 255, 0, 0); // Scale double scale; if (damageMult < dna.DamageMult * 4) { scale = 0; } else { scale = UtilityCore.GetScaledValue_Capped(0, _bot.Radius, dna.DamageMult * 4, dna.DamageMult * 25, damageMult); } _scale.ScaleX = scale; _scale.ScaleY = scale; _scale.ScaleZ = scale; // Translate Point3D position = _bot.PositionWorld + (velocity.ToUnit() * (_bot.Radius * UtilityCore.GetScaledValue_Capped(1.05, 1.3, 0, dna.DamageMult * 25, damageMult))); _translate.OffsetX = position.X; _translate.OffsetY = position.Y; _translate.OffsetZ = position.Z; // Rotate Vector3D xAxis = new Vector3D(1, 0, 0); Vector3D axis = Vector3D.CrossProduct(xAxis, velocity); double angle = Vector3D.AngleBetween(xAxis, velocity); if (Math3D.IsNearZero(axis) || Math1D.IsNearZero(angle)) { _rotate.Quaternion = Quaternion.Identity; } else { _rotate.Quaternion = new Quaternion(axis, angle); } }
private void Brain_StraightToTarget_VelocityAware2() { // Velocity should be zero when touching the chase point //TODO: Implement this (when not attacking, velocity should slow to a max speed the closer to the chase point) // Convert everything to local coords Point3D position = new Point3D(0, 0, 0); Point3D chasePoint = this.PhysicsBody.PositionFromWorld(_chasePoint); Vector3D directionToGo = chasePoint.ToVector() - position.ToVector(); Quaternion rotation; #region Adjust for current velocity attempt1a Vector3D currentVelocity = this.VelocityWorld; if (!Math1D.IsNearZero(currentVelocity.LengthSquared)) { currentVelocity = this.PhysicsBody.DirectionFromWorld(currentVelocity); rotation = Math3D.GetRotation(directionToGo, currentVelocity); // This is how much to rotate direction to align with current velocity, I want to go against the current velocity (if aligned, // the angle will be zero, so negating won't make a difference) rotation = rotation.ToReverse(); // If it's greater than 90 degrees, then just use the original direction (because it will pull the velocity in line // eventually) I don't multiply by .5, because when it is very close to 90 degrees, the bot will thrash a bit if (Math.Abs(Math1D.DegreesToRadians(rotation.Angle)) < Math.PI * .4d) { // Change the direction by the angle directionToGo = rotation.GetRotatedVector(directionToGo); } } #endregion // Now that I know where to go, rotate the original thruster direction (0,1,0) to line up with the desired direction rotation = Math3D.GetRotation(_origThrustDirection, directionToGo); // Thrust Direction _thrustTransform = new RotateTransform3D(new QuaternionRotation3D(rotation)); // Thrust Strength if (_isAttacking) { _thrustPercent = 1d; } else { _thrustPercent = .5d; } }
private static RayHitTestParameters CastRay_PlaneLimited_GetSnapLine(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); }
public BoundryField(double startPercent, double strengthHalf, double exponent, Point3D mapMin, Point3D mapMax) { this.StartPercent = startPercent; this.StrengthHalf = strengthHalf; this.Exponent = exponent; this.MapMin = mapMin; this.MapMax = mapMax; #region Initialize // Center point _center = new Point3D((this.MapMin.X + this.MapMax.X) / 2d, (this.MapMin.Y + this.MapMax.Y) / 2d, (this.MapMin.Z + this.MapMax.Z) / 2d); Vector3D offset = this.MapMax - _center; double maxValue = Math.Max(offset.X, Math.Max(offset.Y, offset.Z)); // Boundries _boundryStop = maxValue; _boundryStart = _boundryStop * this.StartPercent; _boundryStartSquared = _boundryStart * _boundryStart; // force = c * x^2 // c = force / x^2 _equationConstant = this.StrengthHalf / Math.Pow((_boundryStop - _boundryStart) * .5d, this.Exponent); // Ratios if (Math1D.IsNearZero(offset.X)) { _ratioX = 1d; } else { _ratioX = maxValue / offset.X; } if (Math1D.IsNearZero(offset.Y)) { _ratioY = 1d; } else { _ratioY = maxValue / offset.Y; } if (Math1D.IsNearZero(offset.Z)) { _ratioZ = 1d; } else { _ratioZ = maxValue / offset.Z; } #endregion }
// These are just crude test methods private void Update_Simplest() { var top = _neurons.OrderByDescending(o => o.Value).FirstOrDefault(); if (top != null && !Math1D.IsNearZero(top.Value)) { // Map this to the mouse plate Point point = new Point(top.Position.X * _distanceMult, top.Position.Y * _distanceMult); // no need to look at Z. The neurons should have been laid out on the XY plane _mousePlate.CurrentPoint2D = point; } }
/// <summary> /// This will emulate sounds coming from locations relative to the listener /// </summary> /// <remarks> /// Note that sound intensity doesn't diminish linearly from by distance, but by: /// I = P/(4*pi*r^2) /// /// I is final intensity /// P is intensity at the source /// r is distance from the listener /// </remarks> /// <param name="volume">0 (none) to 1 (full)</param> /// <param name="balance">-1 (all left) to 1 (all right)</param> /// <param name="offset">The location of the sound relative to the listener</param> /// <param name="sourceVolume">0 (none) to 1 (full) - this is how loud the source would be at the listener</param> private void GetPositionalSoundSettings(out double volume, out double balance, Vector3D offset, double sourceVolume) { #region Calculate Volume // I won't range check the input volume - I'll leave it up to the media player to either bomb or cap it double intensityRatio = 1d; double offsetLength = offset.Length; if (_distanceFalloffRatio == 0d || Math1D.IsNearZero(offsetLength)) { volume = sourceVolume; } else { double distance = offsetLength * _distanceFalloffRatio; intensityRatio = 1d / (4d * Math.PI * distance * distance); // I need this intensity when calculating balance volume = sourceVolume * intensityRatio; } #endregion #region Calculate Balance // This means that if a sound is a distance of very near zero, and all the way to the left or right, then instead of +-1, it is +-.5 const double MAXOPPOSITE_EAR = .5d; // When the intensity would be 75% of max, then I won't add anything to the opposite ear const double MAXOPPOSITE_DISTANCEINTENSITY = .75d; // Getting the angle (I don't want Z) double angle = Vector3D.AngleBetween(new Vector3D(1, 0, 0), new Vector3D(offset.X, offset.Y, 0)); // cos(0) is 1, cos(90) is 0, cos(180) is -1. Exactly what I need balance = Math.Cos(Math1D.DegreesToRadians(angle)); //NOTE: The problem with a pure cosine is that if a loud sound is sitting very near the person, but on the left, then in reality, the right // ear would hear something, but a simple cosine would be all the way -1 // // So, I'm going to approach .5 for objects that are near (still on the left, but can be heard on the right) if (intensityRatio > MAXOPPOSITE_DISTANCEINTENSITY) { // So when the intensity is 1 (right next to the head), then I will give back .5 // When the intensity is at the limit, then balance will be the pure cosine value // Anywhere in between is a linear interpelation balance = UtilityCore.GetScaledValue_Capped(balance, MAXOPPOSITE_EAR * balance, MAXOPPOSITE_DISTANCEINTENSITY, 1d, intensityRatio); } #endregion //TODO: Take in relative velocity, then manipulate playback speed to emulate doppler (unless there is a way to change pitch directly) }
/// <summary> /// This takes the click point, and returns a point from 0 to 1 /// </summary> private static Point GetScaledPoint(MouseEventArgs e, FrameworkElement relativeTo) { Point clickPoint = e.GetPosition(relativeTo); double width = relativeTo.ActualWidth; double height = relativeTo.ActualHeight; if (Math1D.IsNearZero(width) || Math1D.IsNearZero(height)) { return(new Point(0, 0)); } return(new Point(clickPoint.X / width, clickPoint.Y / height)); }
public bool Transfer() { //NOTE: This doesn't call the converter's update, that is done by the ship (it randomizes the order that they are updated) lock (_lock) { if (_converters.Length == 0) { return(false); } // See how much volume all of the converters contain Tuple <double, double>[] used = _converters.Select(o => Tuple.Create(o.UsedVolume, o.MaxVolume)).ToArray(); // If they are all greater than 50% capacity, then just leave now if (!used.Any(o => o.Item1 / o.Item2 < .5d)) { return(false); } // See how much quantity is needed double needFull = used.Sum(o => o.Item2 - o.Item1); double needScaled = needFull * .99d; // do this to avoid rounding errors causing overflow when distributing to multiple converters // Try to pull this much volume out of the cargo bays var cargo = _cargoBays.RemoveMineral_Volume(needScaled); if (Math1D.IsNearZero(cargo.Item1)) { // The cargo bays are empty return(false); } if (_converters.Length == 1) { // No need to distribute to multiple converters, just give everything to the one Transfer_One(cargo.Item2); } else { // Distriube evenly across the converters //NOTE: Passing in needFull, because that's what used is based off of. Transfer_Many calculates percents, and if the scaled value is passed //in, the percents will be off. Transfer_Many(cargo, used, needFull); } return(true); } }
private static double BuildTerrainSprtRadius(Point[] points) { // Get the average min distance between the points List <Tuple <int, int, double> > distances = new List <Tuple <int, int, double> >(); for (int outer = 0; outer < points.Length - 1; outer++) { for (int inner = outer + 1; inner < points.Length; inner++) { double distance = (points[outer] - points[inner]).LengthSquared; distances.Add(Tuple.Create(outer, inner, distance)); } } double avgDist; if (distances.Count == 0) { avgDist = points[0].ToVector().Length; if (Math1D.IsNearZero(avgDist)) { avgDist = .1; } } else { avgDist = Enumerable.Range(0, points.Length). Select(o => distances. Where(p => p.Item1 == o || p.Item2 == o). // get the disances that mention this index Min(p => p.Item3)). // only keep the smallest of those distances Average(); // get the average of all the mins avgDist = Math.Sqrt(avgDist); } // Get the distance of the farthest out neuron double maxDist = points.Max(o => o.ToVector().LengthSquared); maxDist = Math.Sqrt(maxDist); // Radius of the extra points will be the avg distance beyond that max return(maxDist + avgDist); }
private void Mineral_ApplyForceAndTorque(object sender, BodyApplyForceAndTorqueArgs e) { const double POW = 1d / 2.5d; Vector3D force = _gravityField.GetForce(e.Body.Position); double origLength = force.Length; if (Math1D.IsNearZero(origLength)) { return; } //double newLength = Math.Sqrt(origLength); double newLength = Math.Pow(origLength, POW); double multiplier = newLength / origLength; e.Body.AddForce(new Vector3D(force.X * multiplier, force.Y * multiplier, force.Z * multiplier)); }
private void RedrawCanvas() { if (Math1D.IsNearZero(canvasMap.ActualWidth) || Math1D.IsNearZero(canvasMap.ActualHeight)) { return; } Transform transform = GetMapToCanvasTransform(); Effect effect = new DropShadowEffect() { Color = Colors.Black, Opacity = .25, BlurRadius = 10, Direction = 0, ShadowDepth = 0 }; canvasMap.Children.Clear(); foreach (SpaceStation2D station in _map.GetItems <SpaceStation2D>(false)) { Point3D position = station.PositionWorld; Point positionView = transform.Transform(new Point(position.X, -position.Y)); DrawFlag(positionView, 30, 20, station.Flag, effect); } foreach (ShipPlayer ship in _map.GetItems <ShipPlayer>(false)) { Point3D position = ship.PositionWorld; Point positionView = transform.Transform(new Point(position.X, -position.Y)); Vector3D dir3D = ship.PhysicsBody.DirectionToWorld(new Vector3D(0, 1, 0)); double angle = Vector.AngleBetween(new Vector(dir3D.X, dir3D.Y), new Vector(0, 1)); DrawTriangle(positionView, 10, 20, angle, _playerBrush, _playerStroke, 1, effect); } DrawAsteroidsMinerals(transform, true); ShowCameraView(); }
/// <summary> /// This takes the click point, and returns a point from 0 to 1 /// </summary> private static Point3D GetScaledPoint(MouseEventArgs e, FrameworkElement relativeTo, double depth, ViewDirection direction) { Point clickPoint = e.GetPosition(relativeTo); double width = relativeTo.ActualWidth; double height = relativeTo.ActualHeight; if (Math1D.IsNearZero(width) || Math1D.IsNearZero(height)) { // The window has no size return(new Point3D(0, 0, 0)); } Point3D point = new Point3D(clickPoint.X / width, clickPoint.Y / height, depth); switch (direction) { case ViewDirection.Front: return(new Point3D(point.X, point.Y, point.Z)); case ViewDirection.Right: return(new Point3D(1d - point.Z, point.Y, point.X)); case ViewDirection.Left: return(new Point3D(point.Z, point.Y, 1d - point.X)); case ViewDirection.Top: return(new Point3D(point.X, point.Z, 1d - point.Y)); case ViewDirection.Bottom: return(new Point3D(point.X, 1d - point.Z, point.Y)); case ViewDirection.Back: return(new Point3D(1d - point.X, point.Y, 1d - point.Z)); default: throw new ApplicationException("Unknown ViewDirection: " + direction.ToString()); } }
private Vector3D StraightToTarget_VelocityAware1Worker(Point3D chasePointWorld) { // Convert everything to local coords Point3D position = new Point3D(0, 0, 0); Point3D chasePointLocal = this.PhysicsBody.PositionFromWorld(chasePointWorld); Vector3D directionToGo = chasePointLocal.ToVector() - position.ToVector(); Vector3D axis; double radians; #region Adjust for current velocity attempt1a Vector3D currentVelocity = this.VelocityWorld; if (!Math1D.IsNearZero(currentVelocity.LengthSquared)) { currentVelocity = this.PhysicsBody.DirectionFromWorld(currentVelocity); Quaternion rotation = Math3D.GetRotation(directionToGo, currentVelocity); // This is how much to rotate direction to align with current velocity, I want to go against the current velocity (if aligned, // the angle will be zero, so negating won't make a difference) rotation = rotation.ToReverse(); // If it's greater than 90 degrees, then just use the original direction (because it will pull the velocity in line // eventually) I don't multiply by .5, because when it is very close to 90 degrees, the bot will thrash a bit if (Math.Abs(Math1D.DegreesToRadians(rotation.Angle)) < Math.PI * .4d) { // Change the direction by the angle directionToGo = rotation.GetRotatedVector(directionToGo); } } #endregion // Exit Function return(directionToGo); }
internal static double CalculateMagnitudePercent(double current, double max) { if (current > max) { return(1d); } else if (Math1D.IsNearZero(max)) // can't divide by zero { if (Math1D.IsNearZero(current)) { return(0d); } else { return(1d); } } else { return(current / max); } }
private static bool AddPoisonSprtContainer(ref double mass, IContainer container, double ratio) { if (container == null) { return(false); } double needed = mass * ratio; double unmet = container.RemoveQuantity(needed, false); // If unmet is zero, then there was enough in this container if (Math1D.IsNearZero(unmet)) { mass = 0d; return(true); } // Reduce mass by the amount removed double removed = needed - unmet; mass -= removed * (1d / ratio); return(false); }
/// <summary> /// If this fluid hull is tied to a physics body, then this method must be called from within that body's apply force/torque callback /// </summary> public void Update() { if (AreForcesZero()) { // All forces are zero, so don't waste the processor #region Clear Forces if (_triangles != null && !_areForcesSet) // added this so I don't waste the processor clearing forces on a dead object each frame { foreach (FluidTriangle triangle in _triangles) { triangle.NextFrame(null); } _areForcesSet = true; } #endregion return; } if (!_areForcePointsChosen) { // First time init ChooseForcePoints(); _areForcePointsChosen = true; } // Transform the points to world coords Point3D[] worldPoints = GetTrianglePointsWorld(); foreach (FluidTriangle triangle in _triangles) // AreForcesZero verified that _triangles isn't null { triangle.NextFrame(worldPoints); Point3D[] forcesAt = triangle.GetForceAt(); if (forcesAt == null) { continue; } Vector3D[] forces = new Vector3D[forcesAt.Length]; // Can't use triangle.Normal, because it's still in model coords Vector3D normal = Vector3D.CrossProduct(worldPoints[triangle.Index2] - worldPoints[triangle.Index1], worldPoints[triangle.Index0] - worldPoints[triangle.Index1]); //double avgNormalLength = triangle.Normal.Length / forcesAt.Length; double avgNormalLength = normal.Length / forcesAt.Length; Tuple <Vector3D, double>[] flowsAt = this.Field.GetForce(forcesAt, _sampleRadius); for (int cntr = 0; cntr < forcesAt.Length; cntr++) { #region Sample Point // Calculate the velocity of the triangle at this point Vector3D velocityAtPoint = new Vector3D(0, 0, 0); if (this.Body != null) { velocityAtPoint = this.Body.GetVelocityAtPoint(forcesAt[cntr]); } Vector3D flowAtPoint = flowsAt[cntr].Item1 - velocityAtPoint; // If it's convex/uniform, only allow forces that flow into the hull if (!this.IsClosedConvexInUniformField || Vector3D.DotProduct(flowAtPoint, normal) < 0) { // Scale the normal based on how much of the flow is along it forces[cntr] = flowAtPoint.GetProjectedVector(normal); // the length returned is from zero to flowAtPoint.Length. avgNormal's length is ignored, only its direction // Scale the force by the area of the triangle (cross gives area of parallelagram, so taking half) // Also scale by viscocity forces[cntr] *= avgNormalLength * (.5d * flowsAt[cntr].Item2); // Apply the force to the body if (this.Body != null && !Math1D.IsNearZero(forces[cntr].LengthSquared)) { this.Body.AddForceAtPoint(forces[cntr], forcesAt[cntr]); } } #endregion } triangle.SetForces(forces); // this is only stored here for debugging, etc (physics doesn't look at it) } }
private void InvalidateGeometry() { Vector3D direction = _toPoint - _fromPoint; double directionLength = direction.Length; if (Math1D.IsInvalid(directionLength) || Math1D.IsNearZero(directionLength)) { _lineModel.Transform = new ScaleTransform3D(0, 0, 0); if (_fromArrowModel != null) { _fromArrowModel.Transform = new ScaleTransform3D(0, 0, 0); } if (_toArrowModel != null) { _toArrowModel.Transform = new ScaleTransform3D(0, 0, 0); } return; } Vector3D directionUnit = new Vector3D(); double arrowWidth = 0; double arrowLength = 0; double halfArrowLength = 0; if ((_fromArrowModel != null && _fromArrowPercent != null) || (_toArrowModel != null && _toArrowPercent != null)) // they should both be null or not null together, just being safe { directionUnit = direction.ToUnit(); arrowWidth = _thickness * _arrowBaseMult * _arrowSizeMult; arrowLength = _thickness * _arrowHeightMult * _arrowSizeMult; halfArrowLength = arrowLength / 2; } #region line double length = directionLength; // Reduce the length if the arrow is at the end so that the line graphic doesn't poke through the arrow graphic double?fromOffset = null; if (_fromArrowModel != null && _fromArrowPercent != null && _fromArrowPercent.Value.IsNearValue(1)) { fromOffset = halfArrowLength; length -= halfArrowLength; } if (_toArrowModel != null && _toArrowPercent != null && _toArrowPercent.Value.IsNearValue(1)) { length -= halfArrowLength; } Transform3DGroup transform = new Transform3DGroup(); if (length > Math1D.NEARZERO) { transform.Children.Add(new ScaleTransform3D(_thickness, _thickness, length)); if (fromOffset != null) { transform.Children.Add(new TranslateTransform3D(0, 0, fromOffset.Value)); } transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(_initialOrientation, StaticRandom.NextDouble(360d)))); // spin transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(_initialOrientation, direction)))); transform.Children.Add(new TranslateTransform3D(_fromPoint.ToVector())); } else { // The arrows are visible, but there's not enough length to show the line graphic transform.Children.Add(new ScaleTransform3D(0, 0, 0)); } _lineModel.Transform = transform; #endregion #region from arrow if (_fromArrowModel != null && _fromArrowPercent != null) { Vector3D arrowOffset = directionUnit * arrowLength; Point3D arrowPosition = _fromPoint + (direction * (1d - _fromArrowPercent.Value)); arrowPosition += arrowOffset; // this is because the tip of the arrow should be that the percent, not the base transform = new Transform3DGroup(); transform.Children.Add(new ScaleTransform3D(arrowWidth, arrowWidth, arrowLength)); transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(_initialOrientation, StaticRandom.NextDouble(360d)))); // spin transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(_initialOrientation, -direction)))); transform.Children.Add(new TranslateTransform3D(arrowPosition.ToVector())); _fromArrowModel.Transform = transform; } #endregion #region to arrow if (_toArrowModel != null && _toArrowPercent != null) { Vector3D arrowOffset = directionUnit * arrowLength; Point3D arrowPosition = _fromPoint + (direction * _toArrowPercent.Value); arrowPosition -= arrowOffset; // this is because the tip of the arrow should be that the percent, not the base transform = new Transform3DGroup(); transform.Children.Add(new ScaleTransform3D(arrowWidth, arrowWidth, arrowLength)); transform.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(_initialOrientation, StaticRandom.NextDouble(360d)))); // spin transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(_initialOrientation, direction)))); transform.Children.Add(new TranslateTransform3D(arrowPosition.ToVector())); _toArrowModel.Transform = transform; } #endregion }
/// <summary> /// This returns a visual of a graph that can be added to a canvas /// </summary> public static IEnumerable <UIElement> GetGradientGraph(double width, double height, GradientEntry[] gradient, Color fill, Color stroke) { if (gradient == null || gradient.Length <= 1) // need at least two for a gradient { return(new UIElement[0]); } else if (width.IsNearZero() || height.IsNearZero()) { return(new UIElement[0]); } List <UIElement> retVal = new List <UIElement>(); double maxPercent = gradient.Max(o => o.Percent); if (maxPercent > 1) { #region 100% line // Draw a dashed line at 1 (showing the 100% mark) Color color100 = UtilityWPF.AlphaBlend(UtilityWPF.AlphaBlend(stroke, Colors.Gray, .85), Colors.Transparent, .66); double y100 = ((maxPercent - 1) / maxPercent) * height; Line line100 = new Line() { Stroke = new SolidColorBrush(color100), StrokeThickness = 1, StrokeDashArray = new DoubleCollection(new[] { 4d, 2d }), X1 = 0, X2 = width, Y1 = y100, Y2 = y100, }; retVal.Add(line100); #endregion } if (maxPercent < 1) { // Do this so the graph is to scale (doesn't always go to the top) maxPercent = 1; } double lastGradX = gradient[gradient.Length - 1].Distance; if (!Math1D.IsNearZero(lastGradX) && lastGradX > 0) { Polyline polyLine = new Polyline(); Polygon polyFill = new Polygon(); polyLine.Stroke = new SolidColorBrush(stroke); polyLine.StrokeThickness = 2; polyFill.Fill = new SolidColorBrush(fill); //NOTE: gradient must be sorted on Item1 double xScale = width / lastGradX; for (int cntr = 0; cntr < gradient.Length; cntr++) { double x = gradient[cntr].Distance * xScale; double y = ((maxPercent - gradient[cntr].Percent) / maxPercent) * height; polyLine.Points.Add(new Point(x, y)); polyFill.Points.Add(new Point(x, y)); } // Circle back fill to make a polygon polyFill.Points.Add(new Point(polyFill.Points[polyFill.Points.Count - 1].X, height)); polyFill.Points.Add(new Point(polyFill.Points[0].X, height)); retVal.Add(polyFill); retVal.Add(polyLine); } return(retVal); }
private static void DrawFieldSprtDoIt(byte[] pixels, double[] ink, bool[] blocked, int size, byte[] colorZFront, byte[] colorZBack, AxisFor pixelX, AxisFor pixelY, AxisFor pixelZ) { List <Mapping_2D_1D> flattened = new List <Mapping_2D_1D>(); // Setup the pixel array //for (int y2D = pixelY.Start; pixelY.IsPos ? y2D <= pixelY.Stop : y2D >= pixelY.Stop; y2D += pixelY.Increment) foreach (int y2D in pixelY.Iterate()) { int offsetY = pixelY.GetValueForOffset(y2D) * size; //for (int x2D = pixelX.Start; pixelX.IsPos ? x2D <= pixelX.Stop : x2D >= pixelX.Stop; x2D += pixelX.Increment) foreach (int x2D in pixelX.Iterate()) { int offset = offsetY + pixelX.GetValueForOffset(x2D); flattened.Add(new Mapping_2D_1D(x2D, y2D, offset)); } } // Each pixel of the output bitmap can be added up independently, so employ some threading flattened.AsParallel().ForAll(o => { //TODO: Color is 6.5 times slower than byte array List <byte[]> colorColumn = new List <byte[]>(); for (int z2D = pixelZ.Start; pixelZ.IsPos?z2D <= pixelZ.Stop : z2D >= pixelZ.Stop; z2D += pixelZ.Increment) { int x = -1, y = -1, z = -1; pixelX.Set3DIndex(ref x, ref y, ref z, o.X); pixelY.Set3DIndex(ref x, ref y, ref z, o.Y); pixelZ.Set3DIndex(ref x, ref y, ref z, z2D); int index = FluidField3D.Get1DIndex(x, y, z, size); if (blocked[index]) { // Blocked cells are all white, so save the overlay method a bit of work, and throw out everything behind this colorColumn.Clear(); colorColumn.Add(new byte[] { 255, 255, 255, 255 }); continue; } double inkCell = ink[index]; if (Math1D.IsNearZero(inkCell)) { continue; } byte[] depthColor = UtilityWPF.AlphaBlend(colorZBack, colorZFront, UtilityCore.GetScaledValue_Capped(0, 1, 0, size - 1, z)); int alpha = Convert.ToInt32(Math.Round(inkCell * 255)); if (alpha < 0) { alpha = 0; } else if (alpha > 255) { alpha = 255; } colorColumn.Add(new byte[] { Convert.ToByte(alpha), depthColor[1], depthColor[2], depthColor[3] }); } byte[] color = colorColumn.Count > 0 ? UtilityWPF.OverlayColors(colorColumn) : new byte[] { 0, 0, 0, 0 }; pixels[o.Offset1D * 4 + 0] = color[3]; // Blue pixels[o.Offset1D * 4 + 1] = color[2]; // Green pixels[o.Offset1D * 4 + 2] = color[1]; // Red pixels[o.Offset1D * 4 + 3] = color[0]; // Alpha }); }
private static Model3DGroup GetModel_WoodIron(WeaponSpikeBallDNA dna, WeaponSpikeBallDNA finalDNA, WeaponMaterialCache materials) { Model3DGroup retVal = new Model3DGroup(); Random rand = StaticRandom.GetRandomForThread(); var from = dna.KeyValues; var to = finalDNA.KeyValues; double spikeLength = dna.Radius * 1.4d; double ballRadius = dna.Radius * 1d; double spikeRadius = dna.Radius * .2; #region Ball System.Windows.Media.Media3D.Material material = materials.Ball_Wood; // the property get returns a slightly random color GeometryModel3D geometry = new GeometryModel3D(); geometry.Material = material; geometry.BackMaterial = material; // Create a convex hull out of semi evenly distibuted points int numHullPoints = Convert.ToInt32(WeaponDNA.GetKeyValue("numHullPoints", from, to, rand.Next(20, 50))); TriangleIndexed[] ball = Math3D.GetConvexHull(Math3D.GetRandomVectors_SphericalShell_EvenDist(numHullPoints, ballRadius, .03, 10).Select(o => o.ToPoint()).ToArray()); geometry.Geometry = UtilityWPF.GetMeshFromTriangles(ball); retVal.Children.Add(geometry); #endregion // These are placed where the rings are, to push spikes away (so that spikes don't poke through the rings) List <Vector3D> staticPoints = new List <Vector3D>(); #region Rings material = materials.Spike_Iron; // the property get returns a slightly random color // 0, 1 or 2 rings. Higher chance of 0 than 2 int numRings = Convert.ToInt32(WeaponDNA.GetKeyValue("numRings", from, to, Math.Floor(rand.NextPow(2, 2.3)))); double[] zs = new double[0]; switch (numRings) { case 0: break; case 1: zs = new double[] { WeaponDNA.GetKeyValue("ringZ1", from, to, Math1D.GetNearZeroValue(ballRadius * .75)) }; break; case 2: double z1 = WeaponDNA.GetKeyValue("ringZ1", from, to, Math1D.GetNearZeroValue(ballRadius * .75)); double z2 = 0; if (from == null || !from.TryGetValue("ringZ2", out z2)) { do { z2 = Math1D.GetNearZeroValue(ballRadius * .75); } while (Math.Abs(z1 - z2) < ballRadius * .4); to.Add("ringZ2", z2); } zs = new double[] { z1, z2 }; break; default: throw new ApplicationException("Unexpected number of rings: " + numRings.ToString()); } // Build the rings at the z offsets that were calculated above for (int cntr = 0; cntr < zs.Length; cntr++) { retVal.Children.Add(GetModel_WoodIron_Ring_Band(ballRadius, zs[cntr], material, ball, from, to, "ringZ" + cntr.ToString())); // Store points at the rings double ringRadiusAvg = Math.Sqrt((ballRadius * ballRadius) - (zs[cntr] * zs[cntr])); staticPoints.AddRange(Math2D.GetCircle_Cached(7).Select(o => (o.ToVector() * ringRadiusAvg).ToVector3D(zs[cntr]))); } #endregion #region Spikes Vector3D[] staticPointsArr = staticPoints.Count == 0 ? null : staticPoints.ToArray(); double[] staticRepulse = staticPoints.Count == 0 ? null : Enumerable.Range(0, staticPoints.Count).Select(o => .005d).ToArray(); int numSpikes = Convert.ToInt32(WeaponDNA.GetKeyValue("numSpikes", from, to, rand.Next(8, 14))); Vector3D[] spikeLocations; if (from != null && from.ContainsKey("spikeLoc0X")) { spikeLocations = new Vector3D[numSpikes]; for (int cntr = 0; cntr < numSpikes; cntr++) { string prefix = "spikeLoc" + cntr.ToString(); spikeLocations[cntr] = new Vector3D(to[prefix + "X"], to[prefix + "Y"], to[prefix + "Z"]); } } else { spikeLocations = Math3D.GetRandomVectors_SphericalShell_EvenDist(numSpikes, ballRadius, .03, 10, null, staticPointsArr, staticRepulse); for (int cntr = 0; cntr < numSpikes; cntr++) { string prefix = "spikeLoc" + cntr.ToString(); to.Add(prefix + "X", spikeLocations[cntr].X); to.Add(prefix + "Y", spikeLocations[cntr].Y); to.Add(prefix + "Z", spikeLocations[cntr].Z); } } for (int cntr = 0; cntr < spikeLocations.Length; cntr++) { material = materials.Spike_Iron; // the property get returns a slightly random color geometry = new GeometryModel3D(); geometry.Material = material; geometry.BackMaterial = material; RotateTransform3D transform = new RotateTransform3D(new QuaternionRotation3D(Math3D.GetRotation(new Vector3D(0, 0, 1), spikeLocations[cntr]))); // the tube builds along z List <TubeRingBase> rings = new List <TubeRingBase>(); double spikeRadiusA = WeaponDNA.GetKeyValue("spikeRad" + cntr.ToString(), from, to, rand.NextPercent(spikeRadius, .33)); rings.Add(new TubeRingRegularPolygon(0, false, spikeRadiusA, spikeRadiusA, false)); rings.Add(new TubeRingPoint(WeaponDNA.GetKeyValue("spikeLen" + cntr.ToString(), from, to, rand.NextDouble(spikeLength * .9, spikeLength * 1.1)), false)); int numSegments = Convert.ToInt32(WeaponDNA.GetKeyValue("spikeSegs" + cntr.ToString(), from, to, rand.Next(3, 6))); bool isSoft = Math1D.IsNearZero(WeaponDNA.GetKeyValue("spikeSoft" + cntr.ToString(), from, to, rand.Next(2))); geometry.Geometry = UtilityWPF.GetMultiRingedTube(numSegments, rings, isSoft, false, transform); retVal.Children.Add(geometry); } #endregion return(retVal); }
private static Tuple <Dot, Vector3D>[] GetForces(DotVisuals dots, bool useOutput, double mult) { // Figure out which set of distances to use var distances = useOutput ? dots.Distances_Output : dots.Distances_Input; #region Calculate forces Tuple <Dot, Vector3D>[] forces = distances. //AsParallel(). //TODO: if distances.Length > threshold, do this in parallel SelectMany(o => { // Spring from 1 to 2 Vector3D spring = o.Item2.Position - o.Item1.Position; double springLength = spring.Length; double difference = o.Item3 - springLength; difference *= mult; if (Math1D.IsNearZero(springLength)) { spring = Math3D.GetRandomVector_Spherical_Shell(Math.Abs(difference)); } else { spring = spring.ToUnit() * Math.Abs(difference); } if (difference > 0) { // Gap needs to be bigger, push them away (default is closing the gap) spring = -spring; } return(new[] { Tuple.Create(o.Item1, spring), Tuple.Create(o.Item2, -spring) }); }). ToArray(); #endregion // Give them a very slight pull toward the origin so that the cloud doesn't drift away Point3D center = Math3D.GetCenter(dots.Dots.Select(o => o.Position)); double centerMult = mult * -5; Vector3D centerPullForce = center.ToVector() * centerMult; // Group by dot var grouped = forces. GroupBy(o => o.Item1.Token); return(grouped. Select(o => { Vector3D sum = centerPullForce; Dot dot = null; foreach (var force in o) { dot = force.Item1; sum += force.Item2; } return Tuple.Create(dot, sum); }). ToArray()); }
private void Transfer_Many_DistributeGroup(List <Cargo> cargo, double[] percents) { double sumVolume = cargo.Sum(o => o.Volume); // This is used to keep track of how much each converter has left to go double[] remaining = percents.Select(o => sumVolume * o).ToArray(); int infiniteLoopCntr = 0; while (cargo.Count > 0) { bool foundOne = false; #region Whole matches // Shoot through the cargo, looking for ones that will fit exactly int index = 0; while (index < cargo.Count) { bool wasRemoved = false; for (int cntr = 0; cntr < _converters.Length; cntr++) { if (remaining[cntr] >= cargo[index].Volume) { if (_converters[cntr].Add(cargo[index])) // This should never come back false unless there is a rounding error (or something else added to the converter outside of this class) { remaining[cntr] -= cargo[index].Volume; cargo.RemoveAt(index); wasRemoved = true; foundOne = true; break; } } } if (!wasRemoved) { index++; } } #endregion if (!foundOne) { #region Divide one cargo // Nothing was moved during the previous pass, so find a converter that still needs to be filled, and fill it exactly if (cargo.Count == 0) { throw new ApplicationException("Execution shouldn't have gotten into this if statement if there is no more cargo to distribute"); } for (int cntr = 0; cntr < remaining.Length; cntr++) { if (remaining[cntr] > 0d && !Math1D.IsNearZero(remaining[cntr])) { if (cargo[0].Volume < remaining[cntr]) { throw new ApplicationException("The previous pass should have caught this"); } Cargo splitCargo = cargo[0].Clone(); splitCargo.Volume = remaining[cntr]; cargo[0].Volume -= remaining[cntr]; if (cargo[0].Volume.IsNearZero()) { cargo.RemoveAt(0); } if (!_converters[cntr].Add(splitCargo)) { throw new ApplicationException(string.Format("Couldn't add cargo to the converter: {0}, {1}", remaining[cntr].ToString(), (_converters[cntr].MaxVolume - _converters[cntr].UsedVolume).ToString())); } remaining[cntr] = 0d; break; } } #endregion } infiniteLoopCntr++; if (infiniteLoopCntr > 100) { // This happens when the cargo volumes are almost zero (but not quite below the NEARZERO threshold. Don't bother trying to // distribute such a tiny amount, just exit //if(cargo.All(o => o.Volume.IsNearZero())) if (cargo.All(o => Math.Abs(o.Volume) < UtilityCore.NEARZERO * 100)) { return; } throw new ApplicationException("Infinite loop detected"); } } }