protected internal override void DoGrabMove(GrabMovement move) { if (move.GrabType == GrabTypes.SingleVertex || move.GrabType == GrabTypes.Radius) // not sure how I want to display them graphically yet, but the functionality will be the same { int index = move.ShapeIndex; int previous = (index + 3) % 4; int next = (index + 1) % 4; int opposite = (index + 2) % 4; PointF target = Geometry.ClosestPointOnLine(Vertices[opposite], Vertices[index], move.Current.Exact); if (target.ApproxEqual(Vertices[opposite])) { return; } Vertices[index] = target; SizeF vector = Vertices[opposite].VectorTo(Vertices[index]).MultiplyBy(0.5f); PointF centre = Vertices[opposite] + vector; vector = new SizeF(-vector.Height, vector.Width); Vertices[previous] = centre + vector; Vertices[next] = centre - vector; DiscardPath(); } else { base.DoGrabMove(move); } }
protected internal override void DoGrabMove(GrabMovement move) { switch (move.GrabType) { case GrabTypes.SingleVertex: PointF ptNew = Vertices[1]; move.Transform.TransformPoint(ref ptNew); if (ptNew.ApproxEqual(Vertices[0])) { return; } if (HasCaret) { CaretRealculateCoordinates(false); } Vertices[1] = ptNew; ClearTextCache(); m_Bounds = CalculateBounds(); break; default: base.DoGrabMove(move); break; } }
// let the base class take care of positioning the first line // however after choosing the end of that line becomes point 2 and point 1 is then moved (and point 3 is automatic) public override VerbResult Choose(EditableView.ClickPosition position) { PointF pt = position.Snapped; if (pt.ApproxEqual(LastDefined)) { return(VerbResult.Rejected); } DiscardPath(); switch (m_DefinedVertices) { case 1: SetLength(4); Vertices[2] = pt; Vertices[1] = Geometry.MidPoint(Vertices[0], Vertices[2]); m_DefinedVertices = 2; Float(position); return(VerbResult.Continuing); case 2: Float(position); if (Geometry.PointApproxOnLine(Vertices[0], Vertices[2], Vertices[1])) { return(VerbResult.Rejected); } return(VerbResult.Completed); default: throw new InvalidOperationException(); } }
/// <summary>like AngleSnap, but can use two origins, trying to find an intersection 15 degrees from each /// first origin has priority. If bolSecondOnlyIfClose then second only used if quite close to snapping angle - ignored if angle needs a lot of adjusting</summary> internal static PointF AngleSnapFromTwoPoints(PointF pt, PointF origin, PointF secondOrigin, bool secondOnlyIfClose) { Debug.Assert(!origin.IsEmpty); // usually means dont want snap if (pt.ApproxEqual(origin)) { return(origin); // usually happens as shape is being drawn - floated point starts on top of previous } float angle = VectorAngle(origin, pt); angle = (float)(Math.Round(angle / AngleStep()) * AngleStep()); float angle2 = VectorAngle(secondOrigin, pt); float angle2Rounded = (float)(Math.Round(angle2 / AngleStep()) * AngleStep()); // check if we need to ignore second - happens if both angles are the same (because we are v distant from 2 origins near each other // in which case there is no target 15deg from both), or if bolSecondOnlyIfClose if (Math.Abs(angle - angle2Rounded) < 0.05 || secondOnlyIfClose && Math.Abs(angle2 - angle2Rounded) > AngleStep() / 6) { // just return snapping to first origin return(origin + ScalarToVector(DistanceBetween(pt, origin), angle)); } // try to solve for both - generate two lines of arbitrary length in right directions and let intersection logic return the intersection PointF intersection = Intersection.Line_LineIntersection(origin, origin + ScalarToVector(100, angle), false, secondOrigin, secondOrigin + ScalarToVector(100, angle2Rounded), false, true, true, out _, out _); // can return Empty if failed. Ignore last Byref - only used if parallel which these aren't if (!intersection.IsEmpty) { return(intersection); } Debug.WriteLine("AngleSnapFromTwoPoint2 failed to resolve both angles"); // just use first origin return(origin + ScalarToVector(DistanceBetween(pt, origin), angle)); }
public override VerbResult Float(EditableView.ClickPosition position) { // let the base class take care of positioning the first line if (m_DefinedVertices < 2) { return(base.Float(position)); } // need to calculate the coordinates of the fourth point. We can add the 1>2 vector onto point 0 Debug.Assert(Vertices.Count == 4); PointF newPoint = Geometry.PerpendicularPoint(Vertices[0], Vertices[1], position.Snapped); if (newPoint.Equals(Vertices[2])) { return(VerbResult.Unchanged); } if (newPoint.ApproxEqual(Vertices[1])) { return(VerbResult.Rejected); } Vertices[2] = newPoint; SizeF vector = Vertices[1].VectorTo(Vertices[2]); Vertices[3] = PointF.Add(Vertices[0], vector); m_Bounds = CalculateBounds(); m_Acceptable = !VerticesFormLine(0); DiscardPath(); return(VerbResult.Continuing); }
public override VerbResult Choose(EditableView.ClickPosition position) { PointF point = position.Snapped; if (point.ApproxEqual(LastDefined)) { return(VerbResult.Rejected); } Vertices[Vertices.Count - 1] = point; FixVertex(); return(VerbResult.Continuing); }
/// <summary>as AngleSnap, but doesn't make angle a multiple of 15; instead difference between current vector and vector ptOrigin->ptRelative /// must be a multiple of 15</summary> internal static PointF AngleSnapPointRelative(PointF pt, PointF origin, PointF relative) { Debug.Assert(!origin.IsEmpty); // usually means dont want snap if (pt.ApproxEqual(origin)) { return(origin); // usually happens as shape is being drawn - floated point starts on top of previous } float angle = VectorAngle(origin, pt); float relativeAngle = VectorAngle(origin, relative); angle = (float)(Math.Round((angle - relativeAngle) / AngleStep()) * AngleStep() + relativeAngle); return(origin + ScalarToVector(DistanceBetween(pt, origin), angle)); }
internal static PointF AngleSnapPoint(PointF pt, PointF origin, float stepAngle = -1) { if (stepAngle <= 0) { stepAngle = AngleStep(); } Debug.Assert(!origin.IsEmpty); // usually means dont want snap if (pt.ApproxEqual(origin)) { return(origin); // usually happens as shape is being drawn - floated point starts on top of previous } float angle = VectorAngle(origin, pt); angle = (float)(Math.Round(angle / stepAngle) * stepAngle); return(origin + ScalarToVector(DistanceBetween(pt, origin), angle)); }
/// <summary>checks whether ptTest is within line A-B *ASSUMING* that we have already tested it lies on the line, /// *OR* we can assume it must. (Eg intersection on given Segment). Used especially for Ant/Segment where rounding</summary> internal static bool PointWithinLineExtent(PointF lineA, PointF lineB, PointF test) { // errors can actually put the point slightly off the official line, so we can't strictly check that test is within Rectangle(ptA, ptB) // just check in X or Y direction - wherever line is most distinct Debug.Assert(!lineA.ApproxEqual(lineB), "PointWithinLineExtent doesn\'t work correctly if points equal"); // not sure about the assert - why not??? And I was getting some errors on scissors, which might be due to this pausing the code in an invalid state if (Math.Abs(lineA.X - lineB.X) > Math.Abs(lineA.Y - lineB.Y)) { // X more useful return((test.X > lineA.X - NEGLIGIBLE || test.X > lineB.X - NEGLIGIBLE) && (test.X < lineA.X + NEGLIGIBLE || test.X < lineB.X + NEGLIGIBLE)); } else { return((test.Y > lineA.Y - NEGLIGIBLE || test.Y > lineB.Y - NEGLIGIBLE) && (test.Y < lineA.Y + NEGLIGIBLE || test.Y < lineB.Y + NEGLIGIBLE)); } }
public override VerbResult Float(EditableView.ClickPosition position) { PointF pt = position.Snapped; if (!m_BaseLineFixed) { if (pt.ApproxEqual(Vertices[0])) { return(VerbResult.Rejected); } Vertices[1] = pt; m_Bounds = CalculateBounds(); // otherwise we are still typing text, nothing actually moves ClearTextCache(); } return(VerbResult.Continuing); }
protected internal override void DoGrabMove(GrabMovement move) { if (move.GrabType == GrabTypes.Radius) { PointF centre = Centre; // because Centre function may not be all that fast if (centre.ApproxEqual(move.Current.Snapped)) { return; } // update the radius, changing all points float radius = Geometry.DistanceBetween(centre, move.Current.Snapped); for (int vertex = 0; vertex <= Vertices.Count - 1; vertex++) { Vertices[vertex] = centre + centre.VectorTo(Vertices[vertex]).ChangeLength(radius); } DiscardPath(); m_Bounds = RectangleF.Empty; } else { base.DoGrabMove(move); } }
public override VerbResult Choose(EditableView.ClickPosition position) { PointF pt = position.Snapped; if (pt.ApproxEqual(LastDefined)) { return(VerbResult.Rejected); } DiscardPath(); if (!UseBaseline()) { Debug.Assert(Vertices.Count == m_DefinedVertices + 1, "Sequential.Choose is only applicable when there is a single floating vertex if not UseBaseline - this function should have been overridden"); FixVertex(); return(VerbResult.Continuing); } // once we have two points forming a baseline, generate a complete set of points and float to set their initial positions if (m_DefinedVertices == 1) { // baseline now formed Vertices[1] = pt; SetLength(FixedVerticesLength()); m_DefinedVertices = 2; Float(position); // will set some sort of default position for all of the remaining vertices return(VerbResult.Continuing); } // presumably the shape is complete - however reject if trying to place pt2 on pt1 if (!m_Acceptable) { return(VerbResult.Rejected); } Float(position); m_DefinedVertices = FixedVerticesLength(); return(VerbResult.Completed); }
protected internal override void DoGrabMove(GrabMovement move) { // we must implement the functionality at the base class, because we cannot use the version of this in Curve // however if we disallow single vertex movement there is almost no base functionality to be fitted m_Targets = null; // not really needed; but at the moment I have been leaving the targets in place for diagnostics switch (move.GrabType) { case GrabTypes.Move: this.ApplyTransformation(move.Transform); // will transform the control points m_Bounds = RectangleF.Empty; break; case GrabTypes.SingleVertex: PointF newPoint = Vertices[move.ShapeIndex]; move.Transform.TransformPoint(ref newPoint); // we need to check that this point does not collide with the previous or next ones before actually storing it if (move.ShapeIndex > 0 && newPoint.ApproxEqual(Vertices[move.ShapeIndex - 1])) { return; } if (move.ShapeIndex < Vertices.Count - 1 && newPoint.ApproxEqual(Vertices[move.ShapeIndex + 1])) { return; } Vertices[move.ShapeIndex] = newPoint; if (move.GrabType == GrabTypes.SingleVertex && m_Closed && move.ShapeIndex == 0) { // if moving the initial point, we must also move the closing point Vertices[Vertices.Count - 1] = Vertices[0]; } // and we move the previous and next control points by a similar amount if (move.ShapeIndex > 0 || m_Closed) { int previous = move.ShapeIndex * 2 - 1; if (move.ShapeIndex == 0) { previous = m_ControlPoints.Count - 1; } PointF temp = m_ControlPoints[previous]; move.Transform.TransformPoint(ref temp); m_ControlPoints[previous] = temp; #if DEBUG if (m_ControlPoints[previous].ApproxEqual(Vertices[move.ShapeIndex])) { // splatter won't like this. Shouldn't have been created in first place, but Andrew's doc had this m_ControlPoints[previous] = Geometry.Interpolate(Vertices[move.ShapeIndex], Vertices[move.ShapeIndex - 1 % Vertices.Count], 0.25F); } #endif } if (move.ShapeIndex < m_DefinedVertices - 1 || m_Closed) { int control = move.ShapeIndex * 2 % (Vertices.Count * 2 - 2); PointF temp = m_ControlPoints[control]; move.Transform.TransformPoint(ref temp); m_ControlPoints[control] = temp; #if DEBUG if (m_ControlPoints[control].ApproxEqual(Vertices[move.ShapeIndex])) { m_ControlPoints[control] = Geometry.Interpolate(Vertices[move.ShapeIndex], Vertices[(move.ShapeIndex + 1) % Vertices.Count], 0.25F); } #endif } m_Bounds = RectangleF.Empty; break; case GrabTypes.Rotate: base.DoGrabMove(move); break; default: Debug.Fail("Unexpected GrabSpot movement type in Pencil curve: " + move.GrabType); break; } }