public void CanGetIntersectionPoint() { var line = new Line2D(2, 0); var secondLine = new Line2D(3, 1); Assert.AreEqual(new Point2D(-1, -2), line.GetIntersectionPoint(secondLine)); }
public void CanFindIntersectionPoint() { // 0.5x - 1 var firstLine = new Line2D(0.5, -1); // -2x - 6 var secondLine = new Line2D(-2, -6); var intersectionPoint = firstLine.GetIntersectionPoint(secondLine); Assert.AreEqual(-2, intersectionPoint.X); Assert.AreEqual(-2, intersectionPoint.Y); }
// This returns the aligned and snapped draw position public static DrawnVertex GetCurrentPosition(Vector2D mousemappos, bool snaptonearest, bool snaptogrid, bool snaptocardinal, bool usefourcardinaldirections, IRenderer2D renderer, List <DrawnVertex> points) { DrawnVertex p = new DrawnVertex(); p.stitch = true; //mxd. Setting these to false seems to be a good way to create invalid geometry... p.stitchline = true; //mxd snaptocardinal = (snaptocardinal && points.Count > 0); //mxd. Don't snap to cardinal when there are no points //mxd. If snap to cardinal directions is enabled and we have points, modify mouse position Vector2D vm, gridoffset; if (snaptocardinal) { Vector2D offset = mousemappos - points[points.Count - 1].pos; float angle; if (usefourcardinaldirections) { angle = Angle2D.DegToRad((General.ClampAngle((int)Angle2D.RadToDeg(offset.GetAngle()))) / 90 * 90 + 45); } else { angle = Angle2D.DegToRad((General.ClampAngle((int)Angle2D.RadToDeg(offset.GetAngle()) + 22)) / 45 * 45); } offset = new Vector2D(0, -offset.GetLength()).GetRotated(angle); vm = points[points.Count - 1].pos + offset; //mxd. We need to be snapped relative to initial position Vector2D prev = points[points.Count - 1].pos; gridoffset = prev - General.Map.Grid.SnappedToGrid(prev); } else { vm = mousemappos; gridoffset = new Vector2D(); } float vrange = BuilderPlug.Me.StitchRange / renderer.Scale; // Snap to nearest? if (snaptonearest) { // Go for all drawn points foreach (DrawnVertex v in points) { if (Vector2D.DistanceSq(vm, v.pos) < (vrange * vrange)) { p.pos = v.pos; return(p); } } // Try the nearest vertex Vertex nv = General.Map.Map.NearestVertexSquareRange(vm, vrange); if (nv != null) { //mxd. Line angle must stay the same if (snaptocardinal) { Line2D ourline = new Line2D(points[points.Count - 1].pos, vm); if (Math.Round(ourline.GetSideOfLine(nv.Position), 1) == 0) { p.pos = nv.Position; return(p); } } else { p.pos = nv.Position; return(p); } } // Try the nearest linedef. mxd. We'll need much bigger stitch distance when snapping to cardinal directions Linedef nl = General.Map.Map.NearestLinedefRange(vm, BuilderPlug.Me.StitchRange / renderer.Scale); if (nl != null) { //mxd. Line angle must stay the same if (snaptocardinal) { Line2D ourline = new Line2D(points[points.Count - 1].pos, vm); Line2D nearestline = new Line2D(nl.Start.Position, nl.End.Position); Vector2D intersection = Line2D.GetIntersectionPoint(nearestline, ourline, false); if (!float.IsNaN(intersection.x)) { // Intersection is on nearestline? float u = Line2D.GetNearestOnLine(nearestline.v1, nearestline.v2, intersection); if (u < 0f || u > 1f) { } else { p.pos = new Vector2D((float)Math.Round(intersection.x, General.Map.FormatInterface.VertexDecimals), (float)Math.Round(intersection.y, General.Map.FormatInterface.VertexDecimals)); return(p); } } } // Snap to grid? else if (snaptogrid) { // Get grid intersection coordinates List <Vector2D> coords = nl.GetGridIntersections(General.Map.Grid.GridRotate, General.Map.Grid.GridOriginX, General.Map.Grid.GridOriginY); // Find nearest grid intersection bool found = false; float found_distance = float.MaxValue; Vector2D found_coord = new Vector2D(); foreach (Vector2D v in coords) { Vector2D delta = vm - v; if (delta.GetLengthSq() < found_distance) { found_distance = delta.GetLengthSq(); found_coord = v; found = true; } } if (found) { // Align to the closest grid intersection p.pos = found_coord; return(p); } } else { // Aligned to line p.pos = nl.NearestOnLine(vm); return(p); } } } else { // Always snap to the first drawn vertex so that the user can finish a complete sector without stitching if (points.Count > 0) { if (Vector2D.DistanceSq(vm, points[0].pos) < (vrange * vrange)) { p.pos = points[0].pos; return(p); } } } // if the mouse cursor is outside the map bondaries check if the line between the last set point and the // mouse cursor intersect any of the boundary lines. If it does, set the position to this intersection if (points.Count > 0 && (mousemappos.x < General.Map.Config.LeftBoundary || mousemappos.x > General.Map.Config.RightBoundary || mousemappos.y > General.Map.Config.TopBoundary || mousemappos.y < General.Map.Config.BottomBoundary)) { Line2D dline = new Line2D(mousemappos, points[points.Count - 1].pos); bool foundintersection = false; float u = 0.0f; List <Line2D> blines = new List <Line2D>(); // lines for left, top, right and bottom boundaries blines.Add(new Line2D(General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary, General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary)); blines.Add(new Line2D(General.Map.Config.LeftBoundary, General.Map.Config.TopBoundary, General.Map.Config.RightBoundary, General.Map.Config.TopBoundary)); blines.Add(new Line2D(General.Map.Config.RightBoundary, General.Map.Config.TopBoundary, General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary)); blines.Add(new Line2D(General.Map.Config.RightBoundary, General.Map.Config.BottomBoundary, General.Map.Config.LeftBoundary, General.Map.Config.BottomBoundary)); // check for intersections with boundaries for (int i = 0; i < blines.Count; i++) { if (!foundintersection) { // only check for intersection if the last set point is not on the // line we are checking against if (blines[i].GetSideOfLine(points[points.Count - 1].pos) != 0.0f) { foundintersection = blines[i].GetIntersection(dline, out u); } } } // if there was no intersection set the position to the last set point if (!foundintersection) { vm = points[points.Count - 1].pos; } else { vm = dline.GetCoordinatesAt(u); } } // Snap to grid? if (snaptogrid) { // Aligned to grid p.pos = General.Map.Grid.SnappedToGrid(vm - gridoffset) + gridoffset; // special handling if (p.pos.x > General.Map.Config.RightBoundary) { p.pos.x = General.Map.Config.RightBoundary; } if (p.pos.y < General.Map.Config.BottomBoundary) { p.pos.y = General.Map.Config.BottomBoundary; } return(p); } else { // Normal position p.pos.x = (float)Math.Round(vm.x); //mxd p.pos.y = (float)Math.Round(vm.y); //mxd return(p); } }
//mxd protected void RenderGuidelines(Vector2D start, Vector2D end, PixelColor c) { if (end.x != start.x && end.y != start.y) { Vector2D tr = new Vector2D(Math.Max(end.x, start.x), Math.Max(end.y, start.y)); Vector2D bl = new Vector2D(Math.Min(end.x, start.x), Math.Min(end.y, start.y)); // Create guidelines Line3D[] lines = new Line3D[5]; lines[0] = new Line3D(new Vector2D(tr.x, General.Map.Config.TopBoundary), new Vector2D(tr.x, General.Map.Config.BottomBoundary), c, false); lines[1] = new Line3D(new Vector2D(bl.x, General.Map.Config.TopBoundary), new Vector2D(bl.x, General.Map.Config.BottomBoundary), c, false); lines[2] = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, tr.y), new Vector2D(General.Map.Config.RightBoundary, tr.y), c, false); lines[3] = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, bl.y), new Vector2D(General.Map.Config.RightBoundary, bl.y), c, false); // Create current line extent. Make sure v1 is to the left of v2 Line2D current = (end.x < start.x ? new Line2D(end, start) : new Line2D(start, end)); Vector2D extentstart, extentend; if (current.v1.y < current.v2.y) // Start is lower { // Start point can hit left or bottom boundaries extentstart = Line2D.GetIntersectionPoint(left, current, false); if (extentstart.y < General.Map.Config.BottomBoundary) { extentstart = Line2D.GetIntersectionPoint(bottom, current, false); } // End point can hit right or top boundaries extentend = Line2D.GetIntersectionPoint(right, current, false); if (extentend.y > General.Map.Config.TopBoundary) { extentend = Line2D.GetIntersectionPoint(top, current, false); } } else // Start is higher { // Start point can hit left or top boundaries extentstart = Line2D.GetIntersectionPoint(left, current, false); if (extentstart.y > General.Map.Config.TopBoundary) { extentstart = Line2D.GetIntersectionPoint(top, current, false); } // End point can hit right or bottom boundaries extentend = Line2D.GetIntersectionPoint(right, current, false); if (extentend.y < General.Map.Config.BottomBoundary) { extentend = Line2D.GetIntersectionPoint(bottom, current, false); } } lines[4] = new Line3D(extentstart, extentend, c, false); // Render them renderer.RenderArrows(lines); // Update horiz/vert length labels if (guidelabels != null) { guidelabels[0].Move(tr, new Vector2D(tr.x, bl.y)); guidelabels[1].Move(new Vector2D(bl.x, tr.y), tr); guidelabels[2].Move(new Vector2D(tr.x, bl.y), bl); guidelabels[3].Move(bl, new Vector2D(bl.x, tr.y)); } } // Render horizontal line + 2 vertical guidelines else if (end.x != start.x) { Line3D l = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, end.y), new Vector2D(General.Map.Config.RightBoundary, end.y), c, false); Line3D gs = new Line3D(new Vector2D(start.x, General.Map.Config.TopBoundary), new Vector2D(start.x, General.Map.Config.BottomBoundary), c, false); Line3D ge = new Line3D(new Vector2D(end.x, General.Map.Config.TopBoundary), new Vector2D(end.x, General.Map.Config.BottomBoundary), c, false); renderer.RenderArrows(new List <Line3D> { l, gs, ge }); } // Render vertical line + 2 horizontal guidelines else if (end.y != start.y) { Line3D l = new Line3D(new Vector2D(end.x, General.Map.Config.TopBoundary), new Vector2D(end.x, General.Map.Config.BottomBoundary), c, false); Line3D gs = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, start.y), new Vector2D(General.Map.Config.RightBoundary, start.y), c, false); Line3D ge = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, end.y), new Vector2D(General.Map.Config.RightBoundary, end.y), c, false); renderer.RenderArrows(new List <Line3D> { l, gs, ge }); } // Start and end match. Render a cross else { Line3D gs = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, start.y), new Vector2D(General.Map.Config.RightBoundary, start.y), c, false); Line3D ge = new Line3D(new Vector2D(start.x, General.Map.Config.TopBoundary), new Vector2D(start.x, General.Map.Config.BottomBoundary), c, false); renderer.RenderArrows(new List <Line3D> { gs, ge }); } }
// This checks if the view offset/zoom changed and updates the check (never used. mxd) /*protected bool CheckViewChanged() * { * // View changed? * bool viewchanged = (renderer.OffsetX != lastoffsetx || renderer.OffsetY != lastoffsety || renderer.Scale != lastscale); * * // Keep view information * lastoffsetx = renderer.OffsetX; * lastoffsety = renderer.OffsetY; * lastscale = renderer.Scale; * * // Return result * return viewchanged; * }*/ // This updates the dragging protected virtual void Update() { PixelColor stitchcolor = General.Colors.Highlight; PixelColor losecolor = General.Colors.Selection; snaptocardinaldirection = General.Interface.ShiftState && General.Interface.AltState; //mxd snaptogrid = (snaptocardinaldirection || General.Interface.ShiftState ^ General.Interface.SnapToGrid); snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge; DrawnVertex curp = GetCurrentPosition(); float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale; // Update label positions (mxd) if (labels.Count > 0) { // Update labels for already drawn lines for (int i = 0; i < labels.Count - 1; i++) { labels[i].ShowAngle = showguidelines; labels[i].Move(points[i].pos, points[i + 1].pos); } // Update label for active line labels[labels.Count - 1].ShowAngle = showguidelines; labels[labels.Count - 1].Move(points[points.Count - 1].pos, curp.pos); } // Render drawing lines if (renderer.StartOverlay(true)) { // Go for all points to draw lines PixelColor color; if (points.Count > 0) { //mxd bool renderguidelabels = false; if (showguidelines) { Vector2D prevp = points[points.Count - 1].pos; PixelColor c = General.Colors.InfoLine.WithAlpha(80); if (curp.pos.x != prevp.x && curp.pos.y != prevp.y) { renderguidelabels = true; Vector2D tr = new Vector2D(Math.Max(curp.pos.x, prevp.x), Math.Max(curp.pos.y, prevp.y)); Vector2D bl = new Vector2D(Math.Min(curp.pos.x, prevp.x), Math.Min(curp.pos.y, prevp.y)); // Create guidelines Line3D[] lines = new Line3D[5]; lines[0] = new Line3D(new Vector2D(tr.x, General.Map.Config.TopBoundary), new Vector2D(tr.x, General.Map.Config.BottomBoundary), c, false); lines[1] = new Line3D(new Vector2D(bl.x, General.Map.Config.TopBoundary), new Vector2D(bl.x, General.Map.Config.BottomBoundary), c, false); lines[2] = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, tr.y), new Vector2D(General.Map.Config.RightBoundary, tr.y), c, false); lines[3] = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, bl.y), new Vector2D(General.Map.Config.RightBoundary, bl.y), c, false); // Create current line extent. Make sure v1 is to the left of v2 Line2D current = (curp.pos.x < prevp.x ? new Line2D(curp.pos, prevp) : new Line2D(prevp, curp.pos)); Vector2D extentstart, extentend; if (current.v1.y < current.v2.y) // Start is lower { // Start point can hit left or bottom boundaries extentstart = Line2D.GetIntersectionPoint(left, current, false); if (extentstart.y < General.Map.Config.BottomBoundary) { extentstart = Line2D.GetIntersectionPoint(bottom, current, false); } // End point can hit right or top boundaries extentend = Line2D.GetIntersectionPoint(right, current, false); if (extentend.y > General.Map.Config.TopBoundary) { extentend = Line2D.GetIntersectionPoint(top, current, false); } } else // Start is higher { // Start point can hit left or top boundaries extentstart = Line2D.GetIntersectionPoint(left, current, false); if (extentstart.y > General.Map.Config.TopBoundary) { extentstart = Line2D.GetIntersectionPoint(top, current, false); } // End point can hit right or bottom boundaries extentend = Line2D.GetIntersectionPoint(right, current, false); if (extentend.y < General.Map.Config.BottomBoundary) { extentend = Line2D.GetIntersectionPoint(bottom, current, false); } } lines[4] = new Line3D(extentstart, extentend, c, false); // Render them renderer.RenderArrows(lines); // Update horiz/vert length labels guidelabels[0].Move(tr, new Vector2D(tr.x, bl.y)); guidelabels[1].Move(new Vector2D(bl.x, tr.y), tr); guidelabels[2].Move(new Vector2D(tr.x, bl.y), bl); guidelabels[3].Move(bl, new Vector2D(bl.x, tr.y)); } // Render horizontal line else if (curp.pos.x != prevp.x) { Line3D l = new Line3D(new Vector2D(General.Map.Config.LeftBoundary, curp.pos.y), new Vector2D(General.Map.Config.RightBoundary, curp.pos.y), c, false); renderer.RenderArrows(new List <Line3D> { l }); } // Render vertical line else if (curp.pos.y != prevp.y) { Line3D l = new Line3D(new Vector2D(curp.pos.x, General.Map.Config.TopBoundary), new Vector2D(curp.pos.x, General.Map.Config.BottomBoundary), c, false); renderer.RenderArrows(new List <Line3D> { l }); } } // Render lines DrawnVertex lastp = points[0]; for (int i = 1; i < points.Count; i++) { // Determine line color if (lastp.stitchline && points[i].stitchline) { color = stitchcolor; } else { color = losecolor; } // Render line renderer.RenderLine(lastp.pos, points[i].pos, LINE_THICKNESS, color, true); RenderLinedefDirectionIndicator(lastp.pos, points[i].pos, color); //mxd lastp = points[i]; } // Determine line color color = (lastp.stitchline && snaptonearest ? stitchcolor : losecolor); // Render line to cursor renderer.RenderLine(lastp.pos, curp.pos, LINE_THICKNESS, color, true); RenderLinedefDirectionIndicator(lastp.pos, curp.pos, color); //mxd // Render vertices for (int i = 0; i < points.Count; i++) { // Determine vertex color color = points[i].stitch ? stitchcolor : losecolor; // Render vertex renderer.RenderRectangleFilled(new RectangleF(points[i].pos.x - vsize, points[i].pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); } //mxd. Render guide labels? if (renderguidelabels) { renderer.RenderText(guidelabels); } // Render labels renderer.RenderText(labels.ToArray()); } // Determine point color color = snaptonearest ? stitchcolor : losecolor; // Render vertex at cursor renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true); // Done renderer.Finish(); } // Done renderer.Present(); }