private Vector2D GetCircumcenter(List <Vector3D> points) { float u_ray; Line2D line1 = new Line2D(points[0], points[1]); Line2D line2 = new Line2D(points[2], points[0]); // Perpendicular bisectors Line2D bisector1 = new Line2D(line1.GetCoordinatesAt(0.5f), line1.GetCoordinatesAt(0.5f) + line1.GetPerpendicular()); Line2D bisector2 = new Line2D(line2.GetCoordinatesAt(0.5f), line2.GetCoordinatesAt(0.5f) + line2.GetPerpendicular()); bisector1.GetIntersection(bisector2, out u_ray); return(bisector1.GetCoordinatesAt(u_ray)); }
/// <summary> /// Generates a label position given a start and end point of a line. Taken (with modifications) from LineLengthLabel.Move() /// </summary> /// <param name="start">Start of the line</param> /// <param name="end">End of the line</param> /// <returns></returns> private Vector2D GetLabelPosition(Vector2D start, Vector2D end) { // Check if start/end point is on screen... Vector2D lt = General.Map.Renderer2D.DisplayToMap(new Vector2D(0.0, General.Interface.Display.Size.Height)); Vector2D rb = General.Map.Renderer2D.DisplayToMap(new Vector2D(General.Interface.Display.Size.Width, 0.0)); RectangleF viewport = new RectangleF((float)lt.x, (float)lt.y, (float)(rb.x - lt.x), (float)(rb.y - lt.y)); bool startvisible = viewport.Contains((float)start.x, (float)start.y); bool endvisible = viewport.Contains((float)end.x, (float)end.y); // Do this only when one point is visible, an the other isn't if ((!startvisible && endvisible) || (startvisible && !endvisible)) { Line2D drawnline = new Line2D(start, end); Line2D[] viewportsides = new[] { new Line2D(lt, rb.x, lt.y), // top new Line2D(lt.x, rb.y, rb.x, rb.y), // bottom new Line2D(lt, lt.x, rb.y), // left new Line2D(rb.x, lt.y, rb.x, rb.y), // right }; foreach (Line2D side in viewportsides) { // Modify the start point so it stays on screen double u; if (!startvisible && side.GetIntersection(drawnline, out u)) { start = drawnline.GetCoordinatesAt(u); break; } // Modify the end point so it stays on screen if (!endvisible && side.GetIntersection(drawnline, out u)) { end = drawnline.GetCoordinatesAt(u); break; } } } // Create position Vector2D delta = end - start; return(new Vector2D(start.x + delta.x * 0.5, start.y + delta.y * 0.5)); }
//mxd public void DrawLine3DFloor(int x1, int y1, int x2, int y2, ref PixelColor c, PixelColor c2) { Line2D line = new Line2D(x1, y1, x2, y2); float length = line.GetLength(); if (length < DASH_INTERVAL * 2) { DrawLineSolid(x1, y1, x2, y2, ref c2); } else { float d1 = DASH_INTERVAL / length; float d2 = 1.0f - d1; Vector2D p1 = line.GetCoordinatesAt(d1); Vector2D p2 = line.GetCoordinatesAt(d2); DrawLineSolid(x1, y1, (int)p1.x, (int)p1.y, ref c2); DrawLineSolid((int)p1.x, (int)p1.y, (int)p2.x, (int)p2.y, ref c); DrawLineSolid((int)p2.x, (int)p2.y, x2, y2, ref c2); } }
// This returns the closest coordinates ON the line public Vector2D NearestOnLine(Vector2D pos) { float u = Line2D.GetNearestOnLine(start.Position, end.Position, pos); if (u < 0f) { u = 0f; } else if (u > 1f) { u = 1f; } return(Line2D.GetCoordinatesAt(start.Position, end.Position, u)); }
public LuaVector2D GetCoordinatesAt(float u) { Vector2D output = new Vector2D(l2d.GetCoordinatesAt(u)); if (output.IsFinite()) { return(new LuaVector2D(output)); } else { throw new ScriptRuntimeException("GetCoordinatesAt() called with bad u value of " + u + ", (maybe you had a division by 0 somewhere?)"); } }
// This returns the aligned and snapped draw position public static DrawnVertex GetCurrentPosition(Vector2D mousemappos, bool snaptonearest, bool snaptogrid, IRenderer2D renderer, List <DrawnVertex> points) { DrawnVertex p = new DrawnVertex(); Vector2D vm = mousemappos; 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(mousemappos, v.pos) < (vrange * vrange)) { p.pos = v.pos; p.stitch = true; p.stitchline = true; return(p); } } // Try the nearest vertex Vertex nv = General.Map.Map.NearestVertexSquareRange(mousemappos, vrange); if (nv != null) { p.pos = nv.Position; p.stitch = true; p.stitchline = true; return(p); } // Try the nearest linedef Linedef nl = General.Map.Map.NearestLinedefRange(mousemappos, BuilderPlug.Me.StitchRange / renderer.Scale); if (nl != null) { // Snap to grid? if (snaptogrid) { // Get grid intersection coordinates List <Vector2D> coords = nl.GetGridIntersections(); // Find nearest grid intersection bool found = false; float found_distance = float.MaxValue; Vector2D found_coord = new Vector2D(); foreach (Vector2D v in coords) { Vector2D delta = mousemappos - 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; p.stitch = true; p.stitchline = true; return(p); } } else { // Aligned to line p.pos = nl.NearestOnLine(mousemappos); p.stitch = true; p.stitchline = true; 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(mousemappos, points[0].pos) < (vrange * vrange)) { p.pos = points[0].pos; p.stitch = true; p.stitchline = false; 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 bondaries 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); // 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; } p.stitch = snaptonearest; p.stitchline = snaptonearest; return(p); } else { // Normal position p.pos = vm; p.stitch = snaptonearest; p.stitchline = snaptonearest; return(p); } }
// 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); } }
/// <summary> /// Applies the slopes to the sectors. /// /// We have: /// - theta /// - offset angle ("offset") /// - horizontal line length ("length") /// /// What we need to compute: /// - x coordinate where the line starts in the circle ("left", this is cos(theta + offset angle)) /// - x coordinate where the line ends in the circle ("middle", this is cos(offset angle)) /// /// With this data we can calculate some more required variables: /// - radius: length / (middle - left) /// - left delimiter: cos(offset + theta) * radius /// - right delimiter: cos(rotation) * radius (should be same as left delimiter + length) /// - section start, in map units: cos(offset + theta) * radius /// - base height offset (where the slope starts) /// /// Then we can simply use pythagoras to compute the y position for an x position on the length /// </summary> public void ApplySlope() { double left = Math.Cos(theta + offsetangle); double middle = Math.Cos(offsetangle); double radius = length / (middle - left); double leftdelimiter = Math.Cos(offsetangle + theta); double rightdelimiter = Math.Cos(offsetangle); double sectionstart = Math.Cos(offsetangle + theta) * radius; baseheightoffset = Math.Sqrt(radius * radius - sectionstart * sectionstart) * scale; foreach (BaseVisualGeometrySector bvgs in sectors) { HashSet <Vertex> vertices = new HashSet <Vertex>(bvgs.Sector.Sides.Count * 2); double u1 = 1.0; double u2 = 0.0; foreach (Sidedef sd in bvgs.Sector.Sides.Keys) { vertices.Add(sd.Line.Start); vertices.Add(sd.Line.End); } // Get the two points that are the furthest apart on the line between the slope handles foreach (Vertex v in vertices) { double intersection = handleline.GetNearestOnLine(v.Position); if (intersection < u1) { u1 = intersection; } if (intersection > u2) { u2 = intersection; } } // Compute the x position and the corrosponding height of the coordinates double xpos1 = sectionstart + (u1 * length); double xpos2 = sectionstart + (u2 * length); double height1 = Math.Sqrt(radius * radius - xpos1 * xpos1) * scale; double height2 = Math.Sqrt(radius * radius - xpos2 * xpos2) * scale; if (double.IsNaN(height1)) { height1 = 0.0; } if (double.IsNaN(height2)) { height2 = 0.0; } // Adjust the heights height1 = height1 - baseheightoffset + baseheight + heightoffset; height2 = height2 - baseheightoffset + baseheight + heightoffset; // Get the angle of the slope. We cheat a bit and substitute the y value of the vectors with the height of the points double slopeangle = Vector2D.GetAngle(new Vector2D(xpos1, height1), new Vector2D(xpos2, height2)); // Always let the plane point up, VisualSidedefSlope.ApplySlope will invert it if necessary Plane plane = new Plane(new Vector3D(handleline.GetCoordinatesAt(u1), height1), handleline.GetAngle() + Angle2D.PIHALF, slopeangle, true); VisualSidedefSlope.ApplySlope(bvgs.Level, plane, mode); bvgs.Sector.UpdateSectorGeometry(true); } }
//mxd. This moves the label so it stays on screen and offsets it vertically so it doesn't overlap the line public virtual void Move(Vector2D start, Vector2D end) { // Store before making any adjustments to start/end... this.start = start; this.end = end; // Update text label UpdateText(); // Check if start/end point is on screen... Vector2D lt = General.Map.Renderer2D.DisplayToMap(new Vector2D(0.0f, General.Interface.Display.Size.Height)); Vector2D rb = General.Map.Renderer2D.DisplayToMap(new Vector2D(General.Interface.Display.Size.Width, 0.0f)); RectangleF viewport = new RectangleF(lt.x, lt.y, rb.x - lt.x, rb.y - lt.y); bool startvisible = viewport.Contains(start.x, start.y); bool endvisible = viewport.Contains(end.x, end.y); // Do this only when one point is visible, an the other isn't if ((!startvisible && endvisible) || (startvisible && !endvisible)) { Line2D drawnline = new Line2D(start, end); Line2D[] viewportsides = new[] { new Line2D(lt, rb.x, lt.y), // top new Line2D(lt.x, rb.y, rb.x, rb.y), // bottom new Line2D(lt, lt.x, rb.y), // left new Line2D(rb.x, lt.y, rb.x, rb.y), // right }; foreach (Line2D side in viewportsides) { // Modify the start point so it stays on screen float u; if (!startvisible && side.GetIntersection(drawnline, out u)) { start = drawnline.GetCoordinatesAt(u); break; } // Modify the end point so it stays on screen if (!endvisible && side.GetIntersection(drawnline, out u)) { end = drawnline.GetCoordinatesAt(u); break; } } } // Update label position if (offsetposition) { Vector2D perpendicular = (end - start).GetPerpendicular(); float angle = perpendicular.GetAngle(); SizeF textsize = General.Interface.MeasureString(label.Text, label.Font); float offset = textsize.Width * Math.Abs((float)Math.Sin(angle)) + textsize.Height * Math.Abs((float)Math.Cos(angle)); perpendicular = perpendicular.GetNormal().GetScaled(offset / 2.0f / General.Map.Renderer2D.Scale); start += perpendicular; end += perpendicular; } // Apply changes Vector2D delta = end - start; label.Location = new Vector2D(start.x + delta.x * 0.5f, start.y + delta.y * 0.5f); }
//mxd public override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); // Anything to do? if ((!selectpressed && !editpressed) || closestline == null) { hintlabel.Text = string.Empty; return; } // Do something... Vector2D perpendicular = closestline.Line.GetPerpendicular().GetNormal(); if (panel.Distance != 0) { perpendicular *= panel.Distance; // Special cases... } Vector2D center = closestline.GetCenterPoint(); Line2D radius = new Line2D(center, center - perpendicular); float u = radius.GetNearestOnLine(mousemappos - mousedownoffset); int dist = (panel.Distance == 0 ? 1 : panel.Distance); // Special cases... int offset = (int)Math.Round(dist * u - dist); bool updaterequired = false; // Clamp values? bool clampvalue = !General.Interface.ShiftState; // Change verts amount if (selectpressed && editpressed) { if (prevoffset != 0) { // Set new verts count without triggering the update... panel.SetValues(panel.Vertices + Math.Sign(prevoffset - offset), panel.Distance, panel.Angle, panel.FixedCurve); // Update hint text hintlabel.Text = "Vertices: " + panel.Vertices; updaterequired = true; } } // Change distance else if (selectpressed && !panel.FixedCurve) { if (float.IsNaN(u)) { // Set new distance without triggering the update... panel.SetValues(panel.Vertices, 0, panel.Angle, panel.FixedCurve); // Special cases... } else { int newoffset; if (clampvalue) { newoffset = (panel.Distance + offset) / panel.DistanceIncrement * panel.DistanceIncrement; // Clamp to 8 mu increments } else { newoffset = panel.Distance + offset; } // Set new distance without triggering the update... panel.SetValues(panel.Vertices, newoffset, panel.Angle, panel.FixedCurve); } // Update hint text hintlabel.Text = "Distance: " + panel.Distance; updaterequired = true; } // Change angle else if (editpressed && prevoffset != 0) { int newangle = 0; if (panel.FixedCurve) { // Flip required? if (panel.Angle == 0 && (Math.Sign(offset - prevoffset) != Math.Sign(panel.Distance))) { // Set new distance without triggering the update... panel.SetValues(panel.Vertices, -panel.Distance, panel.Angle, panel.FixedCurve); // Recalculate affected values... perpendicular *= -1; radius.v2 = center - perpendicular; u = radius.GetNearestOnLine(mousemappos - mousedownoffset); } //TODO: there surely is a way to get new angle without iteration... float targetoffset = radius.GetLength() * u; float prevdiff = float.MaxValue; int increment = (clampvalue ? panel.AngleIncrement : 1); for (int i = 1; i < panel.MaximumAngle; i += increment) { // Calculate diameter for current angle... float ma = Angle2D.DegToRad(i); float d = (closestline.Length / (float)Math.Tan(ma / 2f)) / 2; float D = d / (float)Math.Cos(ma / 2f); float h = D - d; float curdiff = Math.Abs(h - targetoffset); // This one matches better... if (curdiff < prevdiff) { newangle = i; } prevdiff = curdiff; } // Clamp to 5 deg increments if (clampvalue) { newangle = (newangle / panel.AngleIncrement) * panel.AngleIncrement; } } else { int diff = (int)Math.Round((offset - prevoffset) * renderer.Scale); if (panel.Angle + diff > 0) { if (clampvalue) { newangle = (panel.Angle / panel.AngleIncrement + Math.Sign(diff)) * panel.AngleIncrement; // Clamp to 5 deg increments } else { newangle = panel.Angle + diff; } } } // Set new angle without triggering the update... panel.SetValues(panel.Vertices, panel.Distance, newangle, panel.FixedCurve); // Update hint text hintlabel.Text = "Angle: " + panel.Angle; updaterequired = true; } // Update UI if (updaterequired) { // Update label position float labeldistance; if (panel.Angle == 0) { labeldistance = 0; // Special cases! } else if (panel.FixedCurve) { float ma = Angle2D.DegToRad(panel.Angle); float d = (closestline.Length / (float)Math.Tan(ma / 2f)) / 2; float D = d / (float)Math.Cos(ma / 2f); labeldistance = D - d; } else { labeldistance = Math.Abs(panel.Distance); } labeldistance += 16 / renderer.Scale; Vector2D labelpos = radius.GetCoordinatesAt(labeldistance / radius.GetLength()); hintlabel.Move(labelpos, labelpos); // Trigger update OnValuesChanged(null, EventArgs.Empty); } // Store current offset prevoffset = offset; }
public Vector2 GetCoordinatesAt(float u) { return(Line2D.GetCoordinatesAt(v1, v2, u)); }
// This finds the cut coordinates and splits the other poly with inner vertices private static void SplitOuterWithInner(LinkedListNode <EarClipVertex> start, EarClipPolygon p) { LinkedListNode <EarClipVertex> insertbefore = null; float foundu = float.MaxValue; Vector2D foundpos = new Vector2D(); // Create a line from start that goes beyond the right most vertex of p LinkedListNode <EarClipVertex> pr = FindRightMostVertex(p); float startx = start.Value.Position.x; float endx = pr.Value.Position.x + 10.0f; Line2D starttoright = new Line2D(start.Value.Position, new Vector2D(endx, start.Value.Position.y)); // Calculate a small bonus (0.1 mappixel) float bonus = starttoright.GetNearestOnLine(new Vector2D(start.Value.Position.x + 0.1f, start.Value.Position.y)); // Go for all lines in the outer polygon LinkedListNode <EarClipVertex> v1 = p.Last; LinkedListNode <EarClipVertex> v2 = p.First; while (v2 != null) { // Check if the line goes between startx and endx if ((v1.Value.Position.x > startx || v2.Value.Position.x > startx) && (v1.Value.Position.x < endx || v2.Value.Position.x < endx)) { // Find intersection Line2D pl = new Line2D(v1.Value.Position, v2.Value.Position); float u, ul; pl.GetIntersection(starttoright, out u, out ul); if (float.IsNaN(u)) { // We have found a line that is perfectly horizontal // (parallel to the cut scan line) Check if the line // is overlapping the cut scan line. if (v1.Value.Position.y == start.Value.Position.y) { // This is an exceptional situation which causes a bit of a problem, because // this could be a previously made cut, which overlaps another line from the // same cut and we have to determine which of the two we will join with. If we // pick the wrong one, the polygon is no longer valid and triangulation will fail. // Calculate distance of each vertex in units u = starttoright.GetNearestOnLine(v1.Value.Position); ul = starttoright.GetNearestOnLine(v2.Value.Position); // Rule out vertices before the scan line if (u < 0.0f) { u = float.MaxValue; } if (ul < 0.0f) { ul = float.MaxValue; } float insert_u = Math.Min(u, ul); Vector2D inserpos = starttoright.GetCoordinatesAt(insert_u); // Check in which direction the line goes. if (v1.Value.Position.x > v2.Value.Position.x) { // The line goes from right to left (towards our start point) // so we must always insert our cut after this line. // If the next line goes up, we consider this a better candidate than // a horizontal line that goes from left to right (the other cut line) // so we give it a small bonus. LinkedListNode <EarClipVertex> v3 = v2.Next ?? v2.List.First; if (v3.Value.Position.y < v2.Value.Position.y) { insert_u -= bonus; } // Remember this when it is a closer match if (insert_u <= foundu) { insertbefore = v2.Next ?? v2.List.First; foundu = insert_u; foundpos = inserpos; } } else { // The line goes from left to right (away from our start point) // so we must always insert our cut before this line. // If the previous line goes down, we consider this a better candidate than // a horizontal line that goes from right to left (the other cut line) // so we give it a small bonus. LinkedListNode <EarClipVertex> v3 = v1.Previous ?? v1.List.Last; if (v3.Value.Position.y > v1.Value.Position.y) { insert_u -= bonus; } // Remember this when it is a closer match if (insert_u <= foundu) { insertbefore = v2; foundu = insert_u; foundpos = inserpos; } } } } // Found a closer match? else if ((ul >= 0.0f) && (ul <= 1.0f) && (u > 0.0f) && (u <= foundu)) { // Found a closer intersection insertbefore = v2; foundu = u; foundpos = starttoright.GetCoordinatesAt(u); } } // Next v1 = v2; v2 = v2.Next; } // Found anything? if (insertbefore != null) { Sidedef sd = (insertbefore.Previous == null) ? insertbefore.List.Last.Value.Sidedef : insertbefore.Previous.Value.Sidedef; // Find the position where we have to split the outer polygon EarClipVertex split = new EarClipVertex(foundpos, null); // Insert manual split vertices p.AddBefore(insertbefore, new EarClipVertex(split, sd)); // Start inserting from the start (do I make sense this time?) v1 = start; do { // Insert inner polygon vertex p.AddBefore(insertbefore, new EarClipVertex(v1.Value)); v1 = (v1.Next ?? v1.List.First); } while (v1 != start); // Insert manual split vertices p.AddBefore(insertbefore, new EarClipVertex(start.Value, sd)); if (split.Position != insertbefore.Value.Position) { p.AddBefore(insertbefore, new EarClipVertex(split, sd)); } } }
// This generates the vertices to split the line with, from start to end private List<Vector2D> GenerateCurve(Line2D line, int vertices, float angle, bool backwards, float distance, bool fixedcurve) { // Make list List<Vector2D> points = new List<Vector2D>(vertices); //Added by Anders Åstrand 2008-05-18 //The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html //c and theta are known (length of line and angle parameter). d, R and h are //calculated from those two //If the curve is not supposed to be a circular segment it's simply deformed to fit //the value set for distance. //The vertices are generated to be evenly distributed (by angle) along the curve //and lastly they are rotated and moved to fit with the original line //calculate some identities of a circle segment (refer to the graph in the url above) double c = line.GetLength(); double theta = angle; double d = (c / Math.Tan(theta / 2)) / 2; double R = d / Math.Cos(theta / 2); double h = R - d; double yDeform = fixedcurve ? 1 : distance / h; if (backwards) yDeform = -yDeform; double a, x, y; Vector2D vertex; for (int v = 1; v <= vertices; v++) { //calculate the angle for this vertex //the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments //this assumes the line is horisontal and on y = 0, the point is rotated and moved later a = (Math.PI - theta) / 2 + v * (theta / (vertices + 1)); //calculate the coordinates of the point, and distort the y coordinate //using the deform factor calculated above x = Math.Cos(a) * R; y = (Math.Sin(a) * R - d) * yDeform; //rotate and transform to fit original line vertex = new Vector2D((float)x, (float)y).GetRotated(line.GetAngle() + Angle2D.PIHALF); vertex = vertex.GetTransformed(line.GetCoordinatesAt(0.5f).x, line.GetCoordinatesAt(0.5f).y, 1, 1); points.Add(vertex); } // Done return points; }
private void InitializeCatmullRomSplines() { List<Linedef> sourceld = new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true)); Line2D innerline, outerline; CatmullRomSplineData innerspline, outerspline; CatmullRomSplineGroup splinegroup; numcontrolpoints = stairsectorbuilderform.NumControlPoints; if (General.Map.Map.GetSelectedLinedefs(true).Count <= 0) { return; } if (catmullromsplinegroups == null) catmullromsplinegroups = new List<CatmullRomSplineGroup>(); else catmullromsplinegroups.Clear(); for (int l1 = 0; l1 < sourceld.Count - 1; l1++) { int l2 = l1 + 1; Vector2D s1, s2, e1, e2; s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position; e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position; s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position; e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position; //innerline = new Line2D(sourceld[l2].Start.Position, sourceld[l1].Start.Position); //outerline = new Line2D(sourceld[l1].End.Position, sourceld[l2].End.Position); innerline = new Line2D(s2, s1); outerline = new Line2D(e1, e2); innerspline = new CatmullRomSplineData(); innerspline.controlpoints = new List<Vector2D>(); innerspline.line = innerline; innerspline.controlpoints.Add(new Vector2D(innerline.v1)); for (int k = 1; k <= numcontrolpoints - 2; k++) innerspline.controlpoints.Add(new Vector2D(innerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k))); innerspline.controlpoints.Add(new Vector2D(innerline.v2)); ComputeTangents(ref innerspline); outerspline = new CatmullRomSplineData(); outerspline.controlpoints = new List<Vector2D>(); outerspline.line = outerline; outerspline.controlpoints.Add(new Vector2D(outerline.v1)); for (int k = 1; k <= numcontrolpoints - 2; k++) outerspline.controlpoints.Add(new Vector2D(outerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k))); outerspline.controlpoints.Add(new Vector2D(outerline.v2)); ComputeTangents(ref outerspline); splinegroup = new CatmullRomSplineGroup(); splinegroup.splines = new CatmullRomSplineData[2]; splinegroup.splines[INNER_SPLINE] = innerspline; splinegroup.splines[OUTER_SPLINE] = outerspline; splinegroup.sourcelinedef = sourceld[l1]; catmullromsplinegroups.Add(splinegroup); } }