/// <summary> /// Gets the first intersection of a ray with a box (closest intersection to the /// ray origin)</summary> /// <param name="ray">Ray</param> /// <param name="box">Box</param> /// <param name="intersection">Intersection point</param> /// <returns>True iff ray hits box</returns> public static bool Intersect(Ray2F ray, Box2F box, ref Vec2F intersection) { // do X slab float tmin, tmax; SlabIntersect( ray.Direction.X, ray.Origin.X, box.Min.X, box.Max.X, out tmin, out tmax); // do Y slab float tminTemp, tmaxTemp; SlabIntersect( ray.Direction.Y, ray.Origin.Y, box.Min.Y, box.Max.Y, out tminTemp, out tmaxTemp); if ((tmin > tmaxTemp) || (tminTemp > tmax)) return false; tmin = Math.Max(tmin, tminTemp); tmax = Math.Min(tmax, tmaxTemp); // intersection at tmin, the maximal-minimal value if (tmin > 0) { intersection = ray.Origin + ray.Direction * tmin; return true; } return false; }
/// <summary> /// Projects a point onto a segment</summary> /// <param name="seg">Segment</param> /// <param name="p">Point to project</param> /// <returns>Point on the segment nearest to the point</returns> public static Vec2F Project(Seg2F seg, Vec2F p) { Vec2F result = new Vec2F(); Vec2F dir = seg.P2 - seg.P1; Vec2F vec = p - seg.P1; float lengthSquared = Vec2F.Dot(dir, dir); if (lengthSquared < DegenerateLength * DegenerateLength) // degenerate segment? { result = seg.P1; } else { float projection = Vec2F.Dot(dir, vec); if (projection < 0) { result = seg.P1; } else if (projection > lengthSquared) { result = seg.P2; } else { double scale = projection / lengthSquared; result.X = (float)(seg.P1.X + scale * dir.X); result.Y = (float)(seg.P1.Y + scale * dir.Y); } } return result; }
void IManipulator.Render(ViewControl vc) { TerrainGob terrain = m_terrainEditor.TerrainEditorControl.SelectedTerrain; TerrainBrush brush = m_terrainEditor.TerrainEditorControl.SelectedBrush; TerrainMap terrainMap = m_terrainEditor.TerrainEditorControl.SelectedTerrainMap; if (brush == null || (!brush.CanApplyTo(terrain) && !brush.CanApplyTo(terrainMap))) return; Vec2F drawscale = new Vec2F(1.0f,1.0f); if (brush.CanApplyTo(terrainMap)) { ImageData mapImg = terrainMap.GetSurface(); ImageData hmImg = terrain.GetSurface(); drawscale.X = (float)hmImg.Width / (float)mapImg.Width; drawscale.Y = (float)hmImg.Height / (float)mapImg.Height; } Point scrPt = vc.PointToClient(Control.MousePosition); if (!vc.ClientRectangle.Contains(scrPt)) return; Ray3F rayw = vc.GetWorldRay(scrPt); TerrainGob.RayPickRetVal retval; if (terrain.RayPick(rayw, out retval)) { terrain.DrawBrush(brush, drawscale, retval.hitpos); } }
/// <summary> /// Constructs curve from 4 control points as vectors</summary> /// <param name="p1">First control point (endpoint)</param> /// <param name="p2">Second control point (tangent)</param> /// <param name="p3">Third control point (tangent)</param> /// <param name="p4">Fourth control point (endpoint)</param> public BezierCurve2F(Vec2F p1, Vec2F p2, Vec2F p3, Vec2F p4) { P1 = p1; P2 = p2; P3 = p3; P4 = p4; }
private void TestToStringWithCulture(CultureInfo culture) { CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = culture; try { string listSeparator = culture.TextInfo.ListSeparator; string decimalSeparator = culture.NumberFormat.NumberDecimalSeparator; var o = new Vec2F(1.1f, 2.2f); string s = o.ToString(null, null); TestToStringResults(o, s, listSeparator, decimalSeparator); string s2 = o.ToString(); Assert.AreEqual(s, s2); s = o.ToString("G", culture); TestToStringResults(o, s, listSeparator, decimalSeparator); s = o.ToString("R", culture); TestToStringResults(o, s, listSeparator, decimalSeparator); } finally { Thread.CurrentThread.CurrentCulture = originalCulture; } }
/// <summary> /// Compute tangents for all control points in a given curve</summary> /// <param name="curve">Curve</param> public static void ComputeTangent(ICurve curve) { if (curve == null) { return; } ReadOnlyCollection <IControlPoint> points = curve.ControlPoints; if (points.Count == 0) { return; } if (points.Count == 1) { Vec2F tan = new Vec2F(1, 0); IControlPoint pt = points[0]; if (pt.TangentIn != tan) { pt.TangentIn = tan; } if (pt.TangentOut != tan) { pt.TangentOut = tan; } return; } for (int i = 0; i < points.Count; i++) { ComputeTangentIn(curve, i); ComputeTangentOut(curve, i); } }
/// <summary> /// Tests if a segment intersects a rectangle</summary> /// <param name="seg">The segment</param> /// <param name="rect">Rectangle, in Windows coordinates, such that the top y has /// a lower value than the bottom Y</param> /// <returns>True iff the segment intersects the rectangle</returns> public static bool Intersects(Seg2F seg, RectangleF rect) { // Quick acceptance if (rect.Contains(seg.P1) || rect.Contains(seg.P2)) { return(true); } // Check if both segment points are on same side of rectangle. if (seg.P1.X < rect.Left && seg.P2.X < rect.Left) { return(false); } if (seg.P1.Y < rect.Top && seg.P2.Y < rect.Top) { return(false); } if (seg.P1.X > rect.Right && seg.P2.X > rect.Right) { return(false); } if (seg.P1.Y > rect.Bottom && seg.P2.Y > rect.Bottom) { return(false); } // Check if all four rectangle points are on the same side of the line. Vec2F dir = new Vec2F(seg.P2.X - seg.P1.X, seg.P2.Y - seg.P1.Y); if (dir.LengthSquared > Seg2F.DegenerateLength * Seg2F.DegenerateLength) { Vec2F normal = dir.Perp; float dot1 = Vec2F.Dot(new Vec2F(rect.Left, rect.Top) - seg.P1, normal); float dot2 = Vec2F.Dot(new Vec2F(rect.Right, rect.Top) - seg.P1, normal); if (dot1 * dot2 > 0) // both are < 0 or both are > 0 { dot2 = Vec2F.Dot(new Vec2F(rect.Left, rect.Bottom) - seg.P1, normal); if (dot1 * dot2 > 0) // both are < 0 or both are > 0 { dot2 = Vec2F.Dot(new Vec2F(rect.Right, rect.Bottom) - seg.P1, normal); if (dot1 * dot2 > 0) // both are < 0 or both are > 0 { return(false); } } } } // Must intersect. return(true); }
public float ToTime(Vec2F point) { if (!Delta.X.ApproxZero()) { return((point.X - Start.X) * DeltaInverse.X); } return((point.Y - Start.Y) * DeltaInverse.Y); }
/* * The obstacle reminds a lot like the enemy of the Galaga game. * The Obstacles constructor is given a location and a picture. */ public Obstacle(DynamicShape shape, IBaseImage image, string fileName, char symbol) : base(shape, image) { this.shape = shape; vec2F = shape.Position; this.fileName = fileName; this.symbol = symbol; }
/// <summary> /// Transforms point from graph space to client space</summary> /// <param name="p">Point to be transformed</param> /// <returns>Vec2F representing transformed point in client space</returns> public Vec2F GraphToClient(Vec2F p) { Vec2F result = new Vec2F(); result.X = (float)(m_trans.X + p.X * m_scale.X); result.Y = (float)(m_trans.Y + p.Y * m_scale.Y); return(result); }
/// <summary> /// Transforms x and y-coordinates from graph space to client space</summary> /// <param name="x">X-coordinate to be transformed</param> /// <param name="y">Y-coordinate to be transformed</param> /// <returns>Vec2F representing transformed x and y-coordinates in client space</returns> public Vec2F GraphToClient(float x, float y) { Vec2F result = new Vec2F(); result.X = (float)(m_trans.X + x * m_scale.X); result.Y = (float)(m_trans.Y + y * m_scale.Y); return(result); }
/// <summary> /// Performs custom actions on MouseLeave event. Resets some internal variable on mouse leave.</summary> /// <param name="e">Event args</param> protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); CurrentPoint = new Vec2F(-1, -1); PreviousPoint = CurrentPoint; CurrentGraphPoint = ClientToGraph(CurrentPoint); PreviousGraphPoint = CurrentGraphPoint; }
/// <summary> /// Transforms x and y-coordinates from client space to graph space</summary> /// <param name="px">X-coordinate to convert</param> /// <param name="py">Y-coordinate to convert</param> /// <returns>Vec2F representing transformed point in graph space</returns> public Vec2F ClientToGraph(float px, float py) { Vec2F result = new Vec2F(); result.X = (float)((px - m_trans.X) / m_scale.X); result.Y = (float)((py - m_trans.Y) / m_scale.Y); return(result); }
/// <summary> /// Transforms point from client space to graph space</summary> /// <param name="p">Point to be transformed</param> /// <returns>Vec2F representing transformed point in graph space</returns> public Vec2F ClientToGraph(Vec2F p) { Vec2F result = new Vec2F(); result.X = (float)((p.X - m_trans.X) / m_scale.X); result.Y = (float)((p.Y - m_trans.Y) / m_scale.Y); return(result); }
public Player(Vec2F position, Vec2F extent) { player = new Entity( new DynamicShape(position, extent), new Image(Path.Combine("Assets", "Images", "Player.png"))); GalagaBus.GetBus().Subscribe(GameEventType.PlayerEvent, this); }
/// <summary> /// Gets the rotational index between 0 - 7 for which sprite rotation /// to use. /// </summary> /// <param name="entity">The entity we're looking at.</param> /// <param name="tickFraction">The fraction for interpolation.</param> /// <returns>The index to use as an offset in a sprite rotation object. /// </returns> public static int CalculateRotationIndex(Entity entity, float tickFraction) { Vec2F eye = Position.XZ; Vec2F other = entity.Position.Value(tickFraction).XZ; uint bits = BitAngle.ToDiamondAngle(eye, other); return((int)BitAngle.CalculateSpriteRotation(bits, entity.Angle.Bits)); }
private CircleF GetBoundary(TNode node) { Rectangle bounds = node.Bounds; float r = bounds.Width / 2; Vec2F c = new Vec2F(bounds.X + r, bounds.Y + r); return(new CircleF(c, r)); }
/// <summary> /// Tests whether two vectors are approximately equal given a tolerance value. /// </summary> /// <param name="v">A <see cref="Vec2F"/> instance.</param> /// <param name="u">A <see cref="Vec2F"/> instance.</param> /// <param name="tolerance">The tolerance value used to test approximate equality.</param> /// <returns>True if the two vectors are approximately equal; otherwise, False.</returns> public static bool ApproxEqual(Vec2F v, Vec2F u, float tolerance) { return ( (System.Math.Abs(v.x - u.x) <= tolerance) && (System.Math.Abs(v.y - u.y) <= tolerance) ); }
public void MoveEnemy(Enemy enemy) { Vec2F pos = enemy.Shape.Position; Vec2F sPos = enemy.StartPos; pos.Y -= s; pos.X = sPos.X + a * (float)Math.Sin((2 * Math.PI * (sPos.Y - pos.Y) / p)); }
private void TestToStringResults(Vec2F o, string s, string listSeparator, string decimalSeparator) { string[] results = s.Split(new[] { listSeparator }, StringSplitOptions.RemoveEmptyEntries); Assert.AreEqual(results.Length, 2); foreach (string oneFloatString in results) Assert.True(oneFloatString.Contains(decimalSeparator)); Assert.AreEqual(float.Parse(results[0]), o.X); Assert.AreEqual(float.Parse(results[1]), o.Y); }
public TestShapeConversion() { var pos = new Vec2F(5.0f, 3.0f); var ext = new Vec2F(10.0f, 6.0f); var dir = new Vec2F(1.0f, 1.0f); shape = new DynamicShape(pos, ext, dir); image = null; }
public void AddPlayer(Vec2F pos) { if (Player == null) { PlayerStartPosition = pos.Copy(); Player = new Player(); Player.Shape.Position = pos; } }
public void TestApplyForceRight() { Vec2F oldVelocity = physics.GetRawVelocity(); physics.ApplyForce(Physics.ForceDirection.Right, 4000); Vec2F newVelocity = physics.GetRawVelocity(); Assert.Greater(newVelocity.X, oldVelocity.X); }
public LevelCreator() { reader = new Reader(); Obstacles = new EntityContainer(); Platforms = new List <Platform>(); Exits = new EntityContainer(); playerpos = new Vec2F(); Customer = new List <string[]>(); }
/// <summary> /// Deletes all points of the snake /// </summary> public void deleteAllPoints() { // Clear everything m_points.Clear(); m_center = Vec2F.Zero; m_F_elasSum = Vec2F.Zero; m_F_curvSum = Vec2F.Zero; m_F_imgSum = Vec2F.Zero; }
public void TestApplyForceUp() { Vec2F oldVelocity = physics.GetRawVelocity(); physics.ApplyForce(Physics.ForceDirection.Up, 4000); Vec2F newVelocity = physics.GetRawVelocity(); Assert.Greater(newVelocity.Y, oldVelocity.Y); }
public QuadTree(int index, Vec2F position, float weight, Vec2F minPos, Vec2F maxPos) { this.mIndex = index; this.mPosition = position; this.mWeight = weight; this.mMinPos = minPos; this.mMaxPos = maxPos; }
public void TestGravity() { Vec2F oldVelocity = physics.GetRawVelocity(); physics.UpdateVelocity(); Vec2F newVelocity = physics.GetRawVelocity(); Assert.Greater(oldVelocity.Y, newVelocity.Y); }
public void MoveEnemy(Enemy enemy) { Vec2F start = enemy.Position; enemy.Shape.Position.Y += speed; float fraction = (2 * PI * (start.Y - enemy.Shape.Position.Y)) / 0.045f; enemy.Shape.Position.X = start.X + 0.05f * (float)Math.Sin(fraction); }
public Boss() { Enemies = new EntityContainer <Enemy>(); size = new Vec2F(0.3f, 0.3f); startPosition = new Vec2F( 0.5f - size.X / 2.0f, 1 - size.X); MovementStrategy = new ZigZagDown(); }
/// <remarks> /// This way of handling physics was inspired by/stolen from the YouTube video /// "Math for Game Developers - Jumping and Gravity (Time Delta, Game Loop)", by Jorge Rodriguez /// https://youtu.be/c4b9lCfSDQM /// 0.0015f is used as a placeholder for DeltaTime, as the deltaTime fields have been made /// inaccessible for some reason. /// </remarks> public Physics(float weight) { Gravity = new Vec2F(0, -weight); Velocity = new Vec2F(0, 0); DeltaTime = 0.0015f; IsGrounded = false; }
/// <summary> /// Returns a value indicating whether this instance is equal to /// the specified object. /// </summary> /// <param name="obj">An object to compare to this instance.</param> /// <returns>True if <paramref name="obj"/> is a <see cref="Vec2F"/> and has the same values as this instance; otherwise, False.</returns> public override bool Equals(object obj) { if (obj is Vec2F) { Vec2F v = (Vec2F)obj; return((this.x == v.x) && (this.y == v.y)); } return(false); }
public void RenderState() { player.RenderPlayer(); explosions.RenderAnimations(); if (!isOnPlatform) { currentVelocity = (gravity + player.thrust) * game.gameTimer.CapturedUpdates + currentVelocity; player.Entity.Shape.AsDynamicShape().ChangeDirection(new Vec2F(currentVelocity.X, currentVelocity.Y)); } if (!isOnPlatform) { player.Entity.Shape.Move(currentVelocity); } foreach (var obstacle in currentLevel.obstacles) { obstacle.RenderEntity(); } singletonScore.RenderScore(); if (first) { stopwatch = Stopwatch.StartNew(); first = false; } if (customer != null) { if (singletonTimer.stopwatch.Elapsed.Seconds > customer.droptime && customer != null) { singletonTimer.stopwatch.Reset(); ChoseLevel.GetInstance().Customer = null; //END GAME singletonScore.PointChanger("Reset"); SpaceTaxiBus.GetBus().RegisterEvent( GameEventFactory <object> .CreateGameEventForAllProcessors( GameEventType.GameStateEvent, this, "CHANGE_STATE", "GAME_OVER", "")); } } //renders customers in current level foreach (var cus in currentLevel.cusList) { if (stopwatch.Elapsed.Seconds + (stopwatch.Elapsed.Minutes * 60) >= cus.spawntime) { if (!cus.entity.IsDeleted()) { cus.RenderCustomer(); } } } }
public static TileDamageStatus FromStream(IStarboundStream stream) { TileDamageStatus status = new TileDamageStatus(); status.Parameters = TileDamageParameters.FromStream(stream); status.SourcePosition = Vec2F.FromStream(stream); status.Damage = TileDamage.FromStream(stream); return(status); }
public void MoveEnemy(Enemy enemy) { float p = 0.045f; float a = 0.05f; Vec2F start = enemy.startPos; float yi = enemy.Shape.Position.Y - speed; var xi = start.X + a * Math.Sin((2 * Math.PI * (start.Y - yi)) / p); enemy.Shape.SetPosition(new Vec2F((float)xi, yi)); }
public void TestGrounded() { Vec2F oldVelocity = physics.GetRawVelocity(); physics.IsGrounded = true; physics.UpdateVelocity(); Vec2F newVelocity = physics.GetRawVelocity(); Assert.AreEqual(newVelocity.Y, oldVelocity.Y); }
/// <summary> /// Projects a point onto a circle</summary> /// <param name="p">Point to project</param> /// <param name="c">Circle to project onto</param> /// <param name="projection">Projected point</param> /// <returns>True iff projection is well defined</returns> public static bool Project(Vec2F p, CircleF c, ref Vec2F projection) { Vec2F d = Vec2F.Sub(p, c.Center); float length = d.Length; bool wellDefined = false; if (length > 0.000001f * c.Radius) { wellDefined = true; float scale = c.Radius / length; projection = Vec2F.Add(c.Center, Vec2F.Mul(d, scale)); } return wellDefined; }
/// <summary> /// Extends box to contain the given point</summary> /// <param name="p">Point</param> /// <returns>Extended box</returns> public Box2F Extend(Vec2F p) { if (m_empty) { Min = Max = p; m_empty = false; } else { Min.X = Math.Min(Min.X, p.X); Min.Y = Math.Min(Min.Y, p.Y); Max.X = Math.Max(Max.X, p.X); Max.Y = Math.Max(Max.Y, p.Y); } return this; }
/// <summary> /// Constructs the circle containing 3 points</summary> /// <param name="p1">Point 1</param> /// <param name="p2">Point 2</param> /// <param name="p3">Point 3</param> public CircleF(Vec2F p1, Vec2F p2, Vec2F p3) { Vec2F o1 = Vec2F.Add(p1, p2); o1 *= 0.5f; Vec2F o2 = Vec2F.Add(p3, p2); o2 *= 0.5f; Vec2F d1 = Vec2F.Sub(p2, p1); d1 = d1.Perp; Vec2F d2 = Vec2F.Sub(p3, p2); d2 = d2.Perp; double t1 = 0; double t2 = 0; if (Ray2F.Intersect(new Ray2F(o1, d1), new Ray2F(o2, d2), ref t1, ref t2)) { Center = o1 + d1 * (float)t1; Radius = Vec2F.Distance(Center, p1); } else { Center = new Vec2F(float.PositiveInfinity, float.PositiveInfinity); Radius = float.PositiveInfinity; } }
/// <summary> /// Calculates the points of intersection between 2 CircleF objects</summary> /// <param name="c1">First CircleF</param> /// <param name="c2">Second CircleF</param> /// <param name="p1">First intersection point</param> /// <param name="p2">Second intersection point</param> /// <returns>True iff there are 1 or 2 intersection points; false if there are none or an infinite number</returns> public static bool Intersect(CircleF c1, CircleF c2, ref Vec2F p1, ref Vec2F p2) { Vec2F v1 = c2.Center - c1.Center; double d = v1.Length; const double EPS = 1.0e-6; if (d < EPS || d > c1.Radius + c2.Radius) return false; v1 *= (float)(1 / d); Vec2F v2 = v1.Perp; double cos = (d * d + c1.Radius * c1.Radius - c2.Radius * c2.Radius) / (2 * c1.Radius * d); double sin = Math.Sqrt(1 - cos * cos); Vec2F t1 = Vec2F.Mul(v1, (float)(c1.Radius * cos)); Vec2F t2 = Vec2F.Mul(v2, (float)(c1.Radius * sin)); p1 = c1.Center + t1 + t2; p2 = c1.Center + t1 - t2; return true; // From http://mathforum.org/library/drmath/view/51710.html // First, let C1 and C2 be the centers of the Circlefs with radii r1 and // r2, and let d be the distance between C1 and C2. // // Now let V1 be the unit vector from C1 to C2, and let V2 be the unit // vector perpendicular to V1. // // Also let V3 be the vector from C1 to one of the intersection points. // // Finally, let A be the angle between V1 and V3. // // From the law of cosines we know that // // r2^2 = r1^2 + d^2 - 2*r1*d*cos(A) // // With this equation we can solve for 'A'. // // The intersection points will be // // C1 + [r1*cos(A)]*V1 + [r1*sin(A)]*V2 // // C1 + [r1*cos(A)]*V1 - [r1*sin(A)]*V2 // a simple unit test // CircleF test1 = new CircleF(new Vec2F(-0.5f, 0), 1); // CircleF test2 = new CircleF(new Vec2F(0.5f, 0), 1); // Vec2F result1 = new Vec2F(); // Vec2F result2 = new Vec2F(); // CircleF.Intersect(test1, test2, ref result1, ref result2); }
/// <summary> /// Constructor with min and max</summary> /// <param name="min">Minima of extents</param> /// <param name="max">Maxima of extents</param> public Box2F(Vec2F min, Vec2F max) { Min = min; Max = max; m_empty = false; }
/// <summary> /// Transforms point from client space to graph space</summary> public Vec2F ClientToGraph(Vec2F p) { Vec2F result = new Vec2F(); result.X = (float)((p.X - m_trans.X) / m_scale.X); result.Y = (float)((p.Y - m_trans.Y) / m_scale.Y); return result; }
/// <summary> /// Frames to fit a given graph rectangle</summary> /// <param name="rect">Rectangle to frame</param> public void Frame(RectangleF rect) { float h = ClientSize.Height; float w = ClientSize.Width; float hh = h / 2; float hw = w / 2; if(rect.Width == 0 || rect.Height == 0 ) return; // uniform zoom //float z = Math.Min(w / rect.Width, h / rect.Height); //Zoom = new Vec2F(z, z); switch(m_lockorg) { case OriginLockMode.Free: { Zoom = new Vec2F(w / rect.Width, h / rect.Height); Vec2F center = new Vec2F(rect.X + rect.Width / 2, rect.Y + rect.Height / 2); Pan = new Vec2F(hw - center.X * Zoom.X, hh + center.Y * Zoom.Y); break; } case OriginLockMode.Center: { float absleft = Math.Abs(rect.Left); float absright = Math.Abs(rect.Right); float fx = Math.Max(absleft, absright); float abstop = Math.Abs(rect.Top); float absbottom = Math.Abs(rect.Bottom); float fy = Math.Max(abstop, absbottom); Zoom = new Vec2F(hw / fx, hh / fy); break; } case OriginLockMode.Left: { if (rect.Right > 0) { float left = Math.Max(0, rect.Left); float fx = (left > 0) ? rect.Width : rect.Right; Zoom = new Vec2F(w / fx, h / rect.Height); Vec2F center = new Vec2F(left + fx / 2, rect.Y + rect.Height / 2); Pan = new Vec2F(hw - center.X * Zoom.X, hh + center.Y * Zoom.Y); } break; } case OriginLockMode.LeftTop: if (rect.Right > 0 && rect.Bottom > 0) { float left = Math.Max(0, rect.Left); float fx = (left > 0) ? rect.Width : rect.Right; float top = Math.Max(0, rect.Top); float fy = (top > 0) ? rect.Height : rect.Bottom; Zoom = new Vec2F(w / fx, h / fy); Vec2F center = new Vec2F(left + fx / 2, top + fy / 2); Pan = new Vec2F(hw - center.X * Zoom.X, hh + center.Y * Zoom.Y); } break; case OriginLockMode.LeftBottom: { if (rect.Right > 0 && rect.Bottom > 0) { float left = Math.Max(0, rect.Left); float fx = (left > 0) ? rect.Width : rect.Right; float top = Math.Max(0, rect.Top); float fy = (top > 0) ? rect.Height : rect.Bottom; Zoom = new Vec2F(w / fx, h / fy); Vec2F center = new Vec2F(left + fx / 2, top + fy / 2); Pan = new Vec2F(hw - center.X * Zoom.X, hh + center.Y * Zoom.Y); } break; } case OriginLockMode.LeftMiddle: { if (rect.Right > 0) { float left = Math.Max(0, rect.Left); float fx = (left > 0) ? rect.Width : rect.Right; float abstop = Math.Abs(rect.Top); float absbottom = Math.Abs(rect.Bottom); float fy = Math.Max(abstop, absbottom); Zoom = new Vec2F(w / fx, hh / fy); Vec2F center = new Vec2F(left + fx / 2, 0); Pan = new Vec2F(hw - center.X * Zoom.X, Pan.Y); } break; } } }
/// <summary> /// Picks the specified curve</summary> /// <param name="curve">Curve</param> /// <param name="p">Picking point</param> /// <param name="tolerance">Pick tolerance</param> /// <param name="hitPoint">Hit point</param> /// <returns>True if curve found; false otherwise</returns> public static bool Pick(BezierCurve2F curve, Vec2F p, float tolerance, ref Vec2F hitPoint) { Queue<BezierCurve2F> curves = new Queue<BezierCurve2F>(); curves.Enqueue(curve); float dMin = float.MaxValue; Vec2F closestPoint = new Vec2F(); while (curves.Count > 0) { BezierCurve2F current = curves.Dequeue(); // project p onto segment connecting curve endpoints Seg2F seg = new Seg2F(current.P1, current.P4); Vec2F projection = Seg2F.Project(seg, p); float d = Vec2F.Distance(p, projection); // reject - point not near enough to segment, expanded by curve "thickness" float flatness = current.Flatness; if (d - flatness > tolerance) continue; // accept - point within tolerance of curve if (flatness <= tolerance) { if (d < dMin) { dMin = d; closestPoint = projection; } } else { BezierCurve2F left, right; current.Subdivide(0.5f, out left, out right); curves.Enqueue(left); curves.Enqueue(right); } } if (dMin < tolerance) { hitPoint = closestPoint; return true; } return false; }
/// <summary> /// Performs custom actions on MouseDown event. Sets data for performing pan and zoom.</summary> /// <param name="e">Event args</param> protected override void OnMouseDown(MouseEventArgs e) { Focus(); Capture = true; ClickPoint = new Vec2F(e.X, e.Y); ClickGraphPoint = ClientToGraph(ClickPoint); PreviousPoint = ClickPoint; PreviousGraphPoint = ClickGraphPoint; CurrentPoint = ClickPoint; CurrentGraphPoint = ClickGraphPoint; ClickPan_d = Pan_d; ClickZoom_d = Zoom_d; m_startDrag = true; SelectionRect.Location = ClickPoint; base.OnMouseDown(e); }
/// <summary> /// Performs custom actions on MouseMove event. Performs pan and zoom depending on m_editAction field.</summary> /// <param name="e">Event args</param> protected override void OnMouseMove(MouseEventArgs e) { PreviousPoint = CurrentPoint; PreviousGraphPoint = CurrentGraphPoint; CurrentPoint = new Vec2F(e.X, e.Y); CurrentGraphPoint = ClientToGraph(CurrentPoint); float dx = CurrentPoint.X - ClickPoint.X; float dy = CurrentPoint.Y - ClickPoint.Y; if (m_startDrag && !m_dragOverThreshold && (Math.Abs(dx) > m_dragThreshold || Math.Abs(dy) > m_dragThreshold)) { m_dragOverThreshold = true; } base.OnMouseMove(e); }
/// <summary> /// Performs custom actions on Paint events. Draws grid and curve.</summary> /// <param name="e">Paint event args</param> protected override void OnPaint(PaintEventArgs e) { DrawCartesianGrid(e.Graphics); if (m_curves.Count > 0) { float w = ClientSize.Width; float h = ClientSize.Height; e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; Pen pen = new Pen(Color.Black, 2); // draw axis labels foreach (ICurve curve in m_curves) DrawXYLabel(e.Graphics, curve.XLabel, curve.YLabel); // draw each curve. foreach (ICurve curve in m_curves) { if (!curve.Visible) continue; m_renderer.DrawCurve(curve, e.Graphics); } // draw control points. foreach (ICurve curve in m_curves) { if (!curve.Visible) continue; m_renderer.DrawControlPoints(curve, e.Graphics); } // draw curve limits for selected curves. foreach (ICurve curve in m_selectedCurves) { if (!curve.Visible) continue; Vec2F min = new Vec2F(curve.MinX, curve.MinY); Vec2F max = new Vec2F(curve.MaxX, curve.MaxY); min = GraphToClient(min); max = GraphToClient(max); float x1 = min.X; float x2 = max.X; float y1 = min.Y; float y2 = max.Y; // to avoind gdi+ overflow and drawing limit where the width or height // is less than one pixel. // can't use e.Graphics.DrawRectangle(pen, rect); // cull, clip and draw curve limit guide lines. pen.Color = curve.CurveColor; bool cull = (x1 < 0 && x2 < 0) || (x1 > w && x2 > w); if (!cull) { // clip float lx1 = Math.Max(0, x1); float lx2 = Math.Min(w, x2); // draw top line if (y2 > 0 && y2 < h) { e.Graphics.DrawLine(pen, lx1, y2, lx2, y2); } // draw bottom line if (y1 > 0 && y1 < h) { e.Graphics.DrawLine(pen, lx1, y1, lx2, y1); } } cull = (y1 < 0 && y2 < 0) || (y1 > h && y2 > h); if (!cull) { // clip float ly1 = Math.Min(h, y1); float ly2 = Math.Max(0, y2); // draw left line if (x1 > 0 && x1 < w) { e.Graphics.DrawLine(pen, x1, ly2, x1, ly1); } // draw right line if (x2 > 0 && x2 < w) { e.Graphics.DrawLine(pen, x2, ly2, x2, ly1); } } } if (m_drawScalePivot) { Vec2F scalePivot = GraphToClient(m_scalePivot); Rectangle pvRect = new Rectangle((int)scalePivot.X - 4, (int)scalePivot.Y - 4, 8, 8); e.Graphics.DrawLine(Pens.Black, 0, scalePivot.Y, Width, scalePivot.Y); e.Graphics.DrawLine(Pens.Black, scalePivot.X, 0, scalePivot.X, Height); e.Graphics.DrawRectangle(Pens.Black, pvRect); } else if (m_drawInsertLine) { Pen insertLinePen = new Pen(m_insertLineColor); e.Graphics.DrawLine(insertLinePen, CurrentPoint.X, 0, CurrentPoint.X, Height); foreach (ICurve curve in m_selectedCurves) { ICurveEvaluator cv = CurveUtils.CreateCurveEvaluator(curve); float y = cv.Evaluate(CurrentGraphPoint.X); Vec2F pt = new Vec2F(CurrentGraphPoint.X, y); pt = GraphToClient(pt); RectangleF ptRect = m_mouseRect; ptRect.X = pt.X - ptRect.Width / 2; ptRect.Y = pt.Y - ptRect.Height / 2; int index = CurveUtils.GetValidInsertionIndex(curve, CurrentGraphPoint.X); if (index >= 0) { e.Graphics.DrawRectangle(insertLinePen, Rectangle.Truncate(ptRect)); } else { pt.X = pt.X - s_noAction.Width / 2; pt.Y = pt.Y - s_noAction.Height / 2; e.Graphics.DrawImage(s_noAction, pt); } } insertLinePen.Dispose(); } pen.Dispose(); } //draw selection rect. if (SelectionRect != RectangleF.Empty) { e.Graphics.DrawRectangle(s_marqueePen, Rectangle.Truncate(SelectionRect)); } }
/// <summary> /// Transforms x and y-coordinates from client space to graph space</summary> public Vec2F ClientToGraph(float px, float py) { Vec2F result = new Vec2F(); result.X = (float)((px - m_trans.X) / m_scale.X); result.Y = (float)((py - m_trans.Y) / m_scale.Y); return result; }
private ICurve PickCurveLimits(out CurveLimitSides side) { side = CurveLimitSides.None; if (AutoComputeCurveLimitsEnabled) return null; ICurve hit = null; const float pickTol = 4; RectangleF rect = RectangleF.Empty; for (int i = SelectedCurves.Length - 1; i >= 0; i--) { ICurve curve = SelectedCurves[i]; Vec2F min = new Vec2F(curve.MinX, curve.MinY); Vec2F max = new Vec2F(curve.MaxX, curve.MaxY); min = GraphToClient(min); max = GraphToClient(max); RectangleF limitRect = MakeRect(min, max); RectangleF outerRect = RectangleF.Inflate(limitRect, pickTol, pickTol); if (!curve.Visible || !outerRect.Contains(CurrentPoint)) continue; RectangleF innerRect = RectangleF.Inflate(limitRect, -pickTol, -pickTol); if (!innerRect.Contains(CurrentPoint)) { m_limitHits.Clear(); m_limitHits[Math.Abs(min.X - CurrentPoint.X)] = CurveLimitSides.Left; m_limitHits[Math.Abs(max.X - CurrentPoint.X)] = CurveLimitSides.Right; m_limitHits[Math.Abs(min.Y - CurrentPoint.Y)] = CurveLimitSides.Bottom; m_limitHits[Math.Abs(max.Y - CurrentPoint.Y)] = CurveLimitSides.Top; foreach (KeyValuePair<float, CurveLimitSides> kv in m_limitHits) { if (kv.Key <= pickTol) { side = kv.Value; hit = curve; break; } } break; } } return hit; }
/// <summary> /// Gets the distance from a point p to its projection on a segment</summary> /// <param name="seg">Segment</param> /// <param name="p">Point to project</param> /// <returns>Distance from point p to its projection on segment</returns> public static float DistanceToSegment(Seg2F seg, Vec2F p) { Vec2F projection = Project(seg, p); float d = Vec2F.Distance(p, projection); return d; }
/// <summary> /// Draws an arc representing a portion of an ellipse specified by a D2dEllipse</summary> /// <param name="ellipse">Ellipse to draw</param> /// <param name="brush">The brush used to paint the arc's outline</param> /// <param name="startAngle">Starting angle in degrees measured clockwise from the x-axis /// to the starting point of the arc</param> /// <param name="sweepAngle">Sweep angle in degrees measured clockwise from the startAngle /// parameter to ending point of the arc</param> /// <param name="strokeWidth">The thickness of the ellipse's stroke. The stroke is centered /// on the ellipse's outline.</param> /// <param name="strokeStyle">The style of stroke to apply to the arc's outline or null to draw a solid line</param> public void DrawArc(D2dEllipse ellipse, D2dBrush brush, float startAngle, float sweepAngle, float strokeWidth = 1.0f, D2dStrokeStyle strokeStyle = null) { // compute steps float step = Tessellation / m_scale.X; float angle1 = startAngle * ToRadian; float angle2 = (startAngle + sweepAngle) * ToRadian; if (angle1 > angle2) { float temp = angle1; angle1 = angle2; angle2 = temp; } float cx = ellipse.Center.X; float cy = ellipse.Center.Y; var v1 = new Vec2F(); v1.X = ellipse.RadiusX * (float)Math.Cos(angle1); v1.Y = ellipse.RadiusY * (float)Math.Sin(angle1); var v2 = new Vec2F(); v2.X = ellipse.RadiusX * (float)Math.Cos(angle2); v2.Y = ellipse.RadiusY * (float)Math.Sin(angle2); float arcLen = (v2 - v1).Length; // approx arc len. float numSegs = arcLen / step; float dtheta = (angle2 - angle1) / numSegs; m_tempPoints.Clear(); for (float theta = angle1; theta < angle2; theta += dtheta) { var pt = new PointF(); pt.X = cx + ellipse.RadiusX * (float)Math.Cos(theta); pt.Y = cy + ellipse.RadiusY * (float)Math.Sin(theta); m_tempPoints.Add(pt); } DrawLines(m_tempPoints, brush, strokeWidth, strokeStyle); }
/// <summary> /// Constructor using end points</summary> /// <param name="p1">First endpoint</param> /// <param name="p2">Second endpoint</param> public Seg2F(Vec2F p1, Vec2F p2) { P1 = p1; P2 = p2; }
public void DrawBrush(TerrainBrush brush, Vec2F drawscale, Vec3F posW) { INativeObject nobj = this.As<INativeObject>(); DrawBrushArgs arg; arg.falloff = brush.Falloff; arg.radius = (float)brush.Radius; arg.posW = posW; arg.drawscale = drawscale; IntPtr argPtr = new IntPtr(&arg); IntPtr retval; nobj.InvokeFunction("DrawBrush", argPtr, out retval); }
/// <summary> /// Snaps p1 to v2</summary> private void SnapPoint(IControlPoint p1, Vec2F v2, float threshold) { Vec2F v1 = new Vec2F(p1.X, p1.Y); Vec2F v3 = v1 - v2; v3 = GraphToClientTangent(v3); float dist = v3.Length; if (dist > threshold) return; p1.Y = v2.Y; float s = v2.X; ICurve curve = p1.Parent; int index = curve.ControlPoints.IndexOf(p1); if (index == -1) throw new ArgumentException("p1"); IControlPoint neighbor = null; if (p1.X > s) // snap left. { neighbor = (index != 0) ? curve.ControlPoints[index - 1] : null; if (neighbor != null && Math.Abs(neighbor.X - s) <= CurveUtils.Epsilone) { p1.X = neighbor.X + CurveUtils.Epsilone; } else { p1.X = s; } } else if (p1.X < s) { neighbor = ((index + 1) < curve.ControlPoints.Count) ? curve.ControlPoints[index + 1] : null; if (neighbor != null && Math.Abs(neighbor.X - s) <= CurveUtils.Epsilone) { p1.X = neighbor.X - CurveUtils.Epsilone; } else { p1.X = s; } } }
/// <summary> /// Transforms the given world or local point into viewport (Windows) coordinates</summary> /// <param name="localPoint">World or local point to be transformed</param> /// <param name="localToScreen">Tranformation matrix composed of object-to-world times /// world-to-view times view-to-projection</param> /// <param name="viewportWidth">The viewport width, for example Control.Width or /// IRenderAction.ViewportWidth</param> /// <param name="viewportHeight">The viewport height, for example Control.Height or /// IRenderAction.ViewportHeight</param> /// <returns>The viewport or Window coordinate in the range [0,Width] and [0,Height] /// where the origin is the upper left corner of the viewport. The coordinate could be /// outside of this range if localPoint is not visible.</returns> /// <example> /// To calculate localToScreen using an object's local-to-world and a Camera: /// localToScreen = Matrix4F.Multiply(localToWorld, camera.ViewMatrix); /// localToScreen.Mul(localToScreen, camera.ProjectionMatrix); /// </example> public static Vec2F TransformToViewport( Vec3F localPoint, Matrix4F localToScreen, float viewportWidth, float viewportHeight) { // transform to clip space and do perspective divide. Result is in range of [-1, 1] Vec4F xScreen = new Vec4F(localPoint); localToScreen.Transform(xScreen, out xScreen); xScreen = Vec4F.Mul(xScreen, 1.0f / xScreen.W); // get viewport coordinates. Convert [-1, 1] to [0, view size] Vec2F xViewport = new Vec2F( (xScreen.X + 1) * 0.5f * viewportWidth, (1 - (xScreen.Y + 1) * 0.5f) * viewportHeight); return xViewport; }
/// <summary> /// Transforms point from graph space to client space</summary> public Vec2F GraphToClient(Vec2F p) { Vec2F result = new Vec2F(); result.X = (float)(m_trans.X + p.X * m_scale.X); result.Y = (float)(m_trans.Y + p.Y * m_scale.Y); return result; }
/// <summary> /// Transforms tangent from graph space to client space</summary> public Vec2F GraphToClientTangent(Vec2F tan) { return new Vec2F(tan.X * (float)m_scale.X, tan.Y * (float)m_scale.Y); }
private void ComputeHermiteCoeff(IControlPoint p1, IControlPoint p2, float[] Coeff) { Vec2F t1 = new Vec2F(p1.TangentOut.X, p1.TangentOut.Y); Vec2F t2 = new Vec2F(p2.TangentIn.X, p2.TangentIn.Y); float m1 = 0.0f; if (t1.X != 0.0f) { m1 = t1.Y / t1.X; } float m2 = 0.0f; if (t2.X != 0.0f) { m2 = t2.Y / t2.X; } float dx = p2.X - p1.X; float dy = p2.Y - p1.Y; float length = 1.0f / (dx * dx); float d1 = dx * m1; float d2 = dx * m2; Coeff[0] = (d1 + d2 - dy - dy) * length / dx; Coeff[1] = (dy + dy + dy - d1 - d1 - d2) * length; Coeff[2] = m1; Coeff[3] = p1.Y; }
/// <summary> /// Transforms x and y-coordinates from graph space to client space</summary> public Vec2F GraphToClient(float x, float y) { Vec2F result = new Vec2F(); result.X = (float)(m_trans.X + x * m_scale.X); result.Y = (float)(m_trans.Y + y * m_scale.Y); return result; }