public static void AddLine(XGraphicsPath path, LineSegment segment, Random random, bool straightEdges) { if (Settings.HandDrawn && !straightEdges) { var dx = segment.End.X - segment.Start.X; var dy = segment.End.Y - segment.Start.Y; var distance = (float) Math.Sqrt(dx*dx + dy*dy); var points = random.Next(Math.Max(3, (int) (distance/15)), Math.Max(6, (int) (distance/8))); var lines = points - 1; var last = segment.Start; for (var line = 0; line < lines; ++line) { Vector next; if (line == 0) { next = last; } else if (line == lines - 1) { next = segment.End; } else { var fraction = line/(float) (lines - 1); var x = segment.Start.X + (segment.End.X - segment.Start.X)*fraction; var y = segment.Start.Y + (segment.End.Y - segment.Start.Y)*fraction; x += random.Next(-1, 2); y += random.Next(-1, 2); next = new Vector(x, y); } path.AddLine(last.ToPointF(), next.ToPointF()); last = next; } } else { path.AddLine(segment.Start.ToPointF(), segment.End.ToPointF()); } }
public float DistanceFromLineSegment(LineSegment line) { return DistanceFromLineSegment(line, this); }
public static float DistanceFromLineSegment(LineSegment line, Vector pos) { var delta = line.End - line.Start; var direction = Vector.Normalize(delta); float distanceAlongSegment = Vector.Dot(pos, direction) - Vector.Dot(line.Start, direction); Vector nearest; if (distanceAlongSegment < 0) { nearest = line.Start; } else if (distanceAlongSegment > delta.Length) { nearest = line.End; } else { nearest = line.Start + distanceAlongSegment * direction; } return Vector.Distance(nearest, pos); }
/// <summary> /// Split the given line segment if it crosses line segments we've already drawn. /// </summary> /// <param name="lineSegment">The line segment to consider.</param> /// <param name="context">The context in which we've been drawing line segments.</param> /// <param name="newSegments"> /// The results of splitting the given line segment, if any. Call with a reference to a null /// list. /// </param> /// <returns>True if the line segment was split and newSegments now exists and contains line segments; false otherwise.</returns> private bool Split(LineSegment lineSegment, DrawingContext context, ref List<LineSegment> newSegments) { foreach (var previousSegment in context.LinesDrawn) { var amount = Math.Max(1, Settings.LineWidth)*3; List<LineSegmentIntersect> intersects; if (lineSegment.Intersect(previousSegment, true, out intersects)) { foreach (var intersect in intersects) { switch (intersect.Type) { case LineSegmentIntersectType.MidPointA: var one = new LineSegment(lineSegment.Start, intersect.Position); if (one.Shorten(amount)) { if (!Split(one, context, ref newSegments)) { if (newSegments == null) { newSegments = new List<LineSegment>(); } newSegments.Add(one); } } var two = new LineSegment(intersect.Position, lineSegment.End); if (two.Forshorten(amount)) { if (!Split(two, context, ref newSegments)) { if (newSegments == null) { newSegments = new List<LineSegment>(); } newSegments.Add(two); } } break; case LineSegmentIntersectType.StartA: if (lineSegment.Forshorten(amount)) { if (!Split(lineSegment, context, ref newSegments)) { if (newSegments == null) { newSegments = new List<LineSegment>(); } newSegments.Add(lineSegment); } } break; case LineSegmentIntersectType.EndA: if (lineSegment.Shorten(amount)) { if (!Split(lineSegment, context, ref newSegments)) { if (newSegments == null) { newSegments = new List<LineSegment>(); } newSegments.Add(lineSegment); } } break; } // don't check other intersects; // we've already split this line, and tested the parts for further intersects. return newSegments != null; } } } return false; }
private void Annotate(XGraphics graphics, Palette palette, List<LineSegment> lineSegments) { if (lineSegments.Count == 0) return; if (!string.IsNullOrEmpty(StartText)) { Annotate(graphics, palette, lineSegments[0], m_startText, StringAlignment.Near, GetSourceRoom()?.Shape); } if (!string.IsNullOrEmpty(EndText)) { Annotate(graphics, palette, lineSegments[lineSegments.Count - 1], m_endText, StringAlignment.Far, GetTargetRoom()?.Shape); } if (!string.IsNullOrEmpty(MidText)) { var totalLength = lineSegments.Sum(lineSegment => lineSegment.Length); var middle = totalLength/2; foreach (var lineSegment in lineSegments) { var length = lineSegment.Length; if (middle > length) { middle -= length; } else { middle /= length; var pos = lineSegment.Start + lineSegment.Delta*middle; var fakeSegment = new LineSegment(pos - lineSegment.Delta*Numeric.Small, pos + lineSegment.Delta*Numeric.Small); Annotate(graphics, palette, fakeSegment, m_midText, StringAlignment.Center); break; } } } }
private static void Annotate(XGraphics graphics, Palette palette, LineSegment lineSegment, TextBlock text, StringAlignment alignment, RoomShape? shape = RoomShape.SquareCorners) { Vector point; var delta = lineSegment.Delta; switch (alignment) { case StringAlignment.Near: default: point = lineSegment.Start; delta.Negate(); break; case StringAlignment.Center: point = lineSegment.Mid; break; case StringAlignment.Far: point = lineSegment.End; break; } var bounds = new Rect(point, Vector.Zero); bounds.Inflate(Settings.TextOffsetFromConnection); var angle = (float) -(Math.Atan2(delta.Y, delta.X)/Math.PI*180.0); var compassPoint = CompassPoint.East; if (Numeric.InRange(angle, 0, 45)) { compassPoint = CompassPoint.NorthWest; } else if (Numeric.InRange(angle, 45, 90)) { compassPoint = CompassPoint.SouthEast; } else if (Numeric.InRange(angle, 90, 135)) { compassPoint = CompassPoint.SouthWest; } else if (Numeric.InRange(angle, 135, 180)) { compassPoint = CompassPoint.NorthEast; } else if (Numeric.InRange(angle, 0, -45)) { compassPoint = CompassPoint.NorthEast; } else if (Numeric.InRange(angle, -45, -90)) { compassPoint = CompassPoint.NorthEast; } else if (Numeric.InRange(angle, -90, -135)) { compassPoint = CompassPoint.NorthWest; } else if (Numeric.InRange(angle, -135, -180)) { compassPoint = CompassPoint.SouthEast; } var pos = bounds.GetCorner(compassPoint); var format = new XStringFormat(); Drawing.SetAlignmentFromCardinalOrOrdinalDirection(format, compassPoint); if (alignment == StringAlignment.Center && Numeric.InRange(angle, -10, 10)) { // HACK: if the line segment is pretty horizontal and we're drawing mid-line text, // move text below the line to get it out of the way of any labels at the ends, // and center the text so it fits onto a line between two proximal rooms. pos = bounds.GetCorner(CompassPoint.South); format.Alignment = XStringAlignment.Center; format.LineAlignment = XLineAlignment.Near; } if (!Settings.DebugDisableTextRendering) text.Draw(graphics, Settings.LineFont, palette.LineTextBrush, pos, Vector.Zero, format); }
public override void PreDraw(DrawingContext context) { var topLeft = InnerBounds.GetCorner(CompassPoint.NorthWest); var topRight = InnerBounds.GetCorner(CompassPoint.NorthEast); var bottomLeft = InnerBounds.GetCorner(CompassPoint.SouthWest); var bottomRight = InnerBounds.GetCorner(CompassPoint.SouthEast); var top = new LineSegment(topLeft, topRight); var right = new LineSegment(topRight, bottomRight); var bottom = new LineSegment(bottomRight, bottomLeft); var left = new LineSegment(bottomLeft, topLeft); context.LinesDrawn.Add(top); context.LinesDrawn.Add(right); context.LinesDrawn.Add(bottom); context.LinesDrawn.Add(left); }
public bool IntersectsWith(Rect rect) { if (rect.Contains(Start) || rect.Contains(End)) { return true; } var a = new LineSegment(new Vector(rect.Left, rect.Top), new Vector(rect.Right, rect.Top)); var b = new LineSegment(new Vector(rect.Right, rect.Top), new Vector(rect.Right, rect.Bottom)); var c = new LineSegment(new Vector(rect.Right, rect.Bottom), new Vector(rect.Left, rect.Bottom)); var d = new LineSegment(new Vector(rect.Left, rect.Bottom), new Vector(rect.Left, rect.Top)); List<LineSegmentIntersect> intersects; if (Intersect(a, false, out intersects) || Intersect(b, false, out intersects) || Intersect(c, false, out intersects) || Intersect(d, false, out intersects)) { return true; } return false; }
public override void Draw(XGraphics graphics, Palette palette, DrawingContext context) { Random random = new Random(Name.GetHashCode()); var topLeft = InnerBounds.GetCorner(CompassPoint.NorthWest); var topRight = InnerBounds.GetCorner(CompassPoint.NorthEast); var bottomLeft = InnerBounds.GetCorner(CompassPoint.SouthWest); var bottomRight = InnerBounds.GetCorner(CompassPoint.SouthEast); var top = new LineSegment(topLeft, topRight); var right = new LineSegment(topRight, bottomRight); var bottom = new LineSegment(bottomRight, bottomLeft); var left = new LineSegment(bottomLeft, topLeft); context.LinesDrawn.Add(top); context.LinesDrawn.Add(right); context.LinesDrawn.Add(bottom); context.LinesDrawn.Add(left); var brush = context.Selected ? palette.BorderBrush : palette.FillBrush; if (!Settings.DebugDisableLineRendering) { var path = palette.Path(); Drawing.AddLine(path, top, random); Drawing.AddLine(path, right, random); Drawing.AddLine(path, bottom, random); Drawing.AddLine(path, left, random); graphics.DrawPath(brush, path); if (IsDark) { var state = graphics.Save(); graphics.IntersectClip(path); brush = context.Selected ? palette.FillBrush : palette.BorderBrush; graphics.DrawPolygon(brush, new PointF[] { topRight.ToPointF(), new PointF(topRight.X - Settings.DarknessStripeSize, topRight.Y), new PointF(topRight.X, topRight.Y + Settings.DarknessStripeSize) }, XFillMode.Alternate); graphics.Restore(state); } graphics.DrawPath(palette.BorderPen, path); } var font = Settings.LargeFont; brush = context.Selected ? palette.FillBrush : palette.LargeTextBrush; Rect textBounds = InnerBounds; textBounds.Inflate(-5, -5); if (textBounds.Width > 0 && textBounds.Height > 0) { m_name.Draw(graphics, font, brush, textBounds.Position, textBounds.Size, XStringFormats.Center); } var expandedBounds = InnerBounds; expandedBounds.Inflate(Settings.ObjectListOffsetFromRoom, Settings.ObjectListOffsetFromRoom); var drawnObjectList = false; font = Settings.SmallFont; brush = palette.SmallTextBrush; if (!string.IsNullOrEmpty(Objects)) { XStringFormat format = new XStringFormat(); Vector pos = expandedBounds.GetCorner(m_objectsPosition); if (!Drawing.SetAlignmentFromCardinalOrOrdinalDirection(format, m_objectsPosition)) { // object list appears inside the room below its name format.LineAlignment = XLineAlignment.Far; format.Alignment = XStringAlignment.Near; //format.Trimming = StringTrimming.EllipsisCharacter; //format.FormatFlags = StringFormatFlags.LineLimit; var height = InnerBounds.Height / 2 - font.Height / 2; var bounds = new Rect(InnerBounds.Left + Settings.ObjectListOffsetFromRoom, InnerBounds.Bottom - height, InnerBounds.Width - Settings.ObjectListOffsetFromRoom, height - Settings.ObjectListOffsetFromRoom); brush = context.Selected ? palette.FillBrush : brush; if (bounds.Width > 0 && bounds.Height > 0) { m_objects.Draw(graphics, font, brush, bounds.Position, bounds.Size, format); } drawnObjectList = true; } else if (m_objectsPosition == CompassPoint.North || m_objectsPosition == CompassPoint.South) { pos.X += Settings.ObjectListOffsetFromRoom; } if (!drawnObjectList) { m_objects.Draw(graphics, font, brush, pos, Vector.Zero, format); } } }
public bool Intersect(LineSegment a, LineSegment b, bool ignoreEndPointIntersects, out List<LineSegmentIntersect> intersects) { float ua = (b.End.X - b.Start.X) * (a.Start.Y - b.Start.Y) - (b.End.Y - b.Start.Y) * (a.Start.X - b.Start.X); float ub = (a.End.X - a.Start.X) * (a.Start.Y - b.Start.Y) - (a.End.Y - a.Start.Y) * (a.Start.X - b.Start.X); float denominator = (b.End.Y - b.Start.Y) * (a.End.X - a.Start.X) - (b.End.X - b.Start.X) * (a.End.Y - a.Start.Y); intersects = null; const float small = 0.01f; if (Math.Abs(denominator) <= small) { if (Math.Abs(ua) <= small && Math.Abs(ub) <= small) { // lines are coincident: // lacking other algorithms which actually work, // roll some expensive distance tests to find intersection points if (a.Start.DistanceFromLineSegment(b) <= small) { intersects = new List<LineSegmentIntersect>(); intersects.Add(new LineSegmentIntersect(LineSegmentIntersectType.StartA, a.Start)); } if (a.End.DistanceFromLineSegment(b) <= small) { if (intersects == null) intersects = new List<LineSegmentIntersect>(); intersects.Add(new LineSegmentIntersect(LineSegmentIntersectType.EndA, a.End)); } if (b.Start.DistanceFromLineSegment(a) <= small) { if (intersects == null) intersects = new List<LineSegmentIntersect>(); intersects.Add(new LineSegmentIntersect(LineSegmentIntersectType.MidPointA, b.Start)); } if (b.End.DistanceFromLineSegment(a) <= small) { if (intersects == null) intersects = new List<LineSegmentIntersect>(); intersects.Add(new LineSegmentIntersect(LineSegmentIntersectType.MidPointA, b.End)); } } } else { ua /= denominator; ub /= denominator; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { LineSegmentIntersectType type = LineSegmentIntersectType.MidPointA; if (ua <= small) { type = LineSegmentIntersectType.StartA; } else if (1 - ua <= small) { type = LineSegmentIntersectType.EndA; } intersects = new List<LineSegmentIntersect>(); intersects.Add(new LineSegmentIntersect(type, a.Start + new Vector(ua * (a.End.X - a.Start.X), ua * (a.End.Y - a.Start.Y)))); } } if (intersects != null && ignoreEndPointIntersects) { for (int index = 0; index < intersects.Count; ++index) { var intersect = intersects[index]; if (intersect.Position.Distance(a.Start) <= small || intersect.Position.Distance(b.Start) <= small || intersect.Position.Distance(a.End) <= small || intersect.Position.Distance(b.End) <= small) { intersects.RemoveAt(index); --index; } } } return intersects != null; }
/// <summary> /// Find the intersection point(s) between two line segments, if any. /// </summary> /// <param name="other">The other line segment.</param> /// <returns>A list of one or more intersections, or null if the line segments do not intersect.</returns> public bool Intersect(LineSegment other, bool ignoreEndPointIntersects, out List<LineSegmentIntersect> intersects) { return Intersect(this, other, ignoreEndPointIntersects, out intersects); }
public static void AddLine(XGraphicsPath path, LineSegment segment, Random random) { if (Settings.HandDrawn) { float dx = segment.End.X - segment.Start.X; float dy = segment.End.Y - segment.Start.Y; float distance = (float)Math.Sqrt(dx * dx + dy * dy); int points = random.Next(Math.Max(3, (int)(distance / 15)), Math.Max(6, (int)(distance / 8))); int lines = points - 1; Vector last = segment.Start; for (int line = 0; line < lines; ++line) { Vector next; if (line == 0) { next = last; } else if (line == lines - 1) { next = segment.End; } else { float fraction = (float)line / (float)(lines - 1); float x = segment.Start.X + (segment.End.X - segment.Start.X) * fraction; float y = segment.Start.Y + (segment.End.Y - segment.Start.Y) * fraction; x += random.Next(-1, 2); y += random.Next(-1, 2); next = new Vector(x, y); } path.AddLine(last.ToPointF(), next.ToPointF()); last = next; } } else { path.AddLine(segment.Start.ToPointF(), segment.End.ToPointF()); } }
public override void PreDraw(DrawingContext context) { var topLeft = InnerBounds.GetCorner(CompassPoint.NorthWest); var topRight = InnerBounds.GetCorner(CompassPoint.NorthEast); var bottomLeft = InnerBounds.GetCorner(CompassPoint.SouthWest); var bottomRight = InnerBounds.GetCorner(CompassPoint.SouthEast); var topCenter = InnerBounds.GetCorner(CompassPoint.North); var rightCenter = InnerBounds.GetCorner(CompassPoint.East); var bottomCenter = InnerBounds.GetCorner(CompassPoint.South); var leftCenter = InnerBounds.GetCorner(CompassPoint.West); var top = new LineSegment(topLeft, topRight); var right = new LineSegment(topRight, bottomRight); var bottom = new LineSegment(bottomRight, bottomLeft); var left = new LineSegment(bottomLeft, topLeft); var halfTopRight = new LineSegment(topCenter, topRight); var halfBottomRight = new LineSegment(bottomRight, bottomCenter); var centerVertical = new LineSegment(bottomCenter, topCenter); var centerHorizontal = new LineSegment(leftCenter, rightCenter); var halfRightBottom = new LineSegment(rightCenter, bottomRight); var halfLeftBottom = new LineSegment(bottomLeft, leftCenter); var slantUp = new LineSegment(bottomLeft, topRight); var slantDown = new LineSegment(bottomRight, topLeft); context.LinesDrawn.Add(top); context.LinesDrawn.Add(right); context.LinesDrawn.Add(bottom); context.LinesDrawn.Add(left); }
public override void Draw(XGraphics graphics, Palette palette, DrawingContext context) { Random random = new Random(Name.GetHashCode()); var topLeft = InnerBounds.GetCorner(CompassPoint.NorthWest); var topRight = InnerBounds.GetCorner(CompassPoint.NorthEast); var bottomLeft = InnerBounds.GetCorner(CompassPoint.SouthWest); var bottomRight = InnerBounds.GetCorner(CompassPoint.SouthEast); var topCenter = InnerBounds.GetCorner(CompassPoint.North); var rightCenter = InnerBounds.GetCorner(CompassPoint.East); var bottomCenter = InnerBounds.GetCorner(CompassPoint.South); var leftCenter = InnerBounds.GetCorner(CompassPoint.West); var top = new LineSegment(topLeft, topRight); var right = new LineSegment(topRight, bottomRight); var bottom = new LineSegment(bottomRight, bottomLeft); var left = new LineSegment(bottomLeft, topLeft); var halfTopRight = new LineSegment(topCenter, topRight); var halfBottomRight = new LineSegment(bottomRight, bottomCenter); var centerVertical = new LineSegment(bottomCenter, topCenter); var centerHorizontal = new LineSegment(leftCenter, rightCenter); var halfRightBottom = new LineSegment(rightCenter, bottomRight); var halfLeftBottom = new LineSegment(bottomLeft, leftCenter); var slantUp = new LineSegment(bottomLeft, topRight); var slantDown = new LineSegment(bottomRight, topLeft); context.LinesDrawn.Add(top); context.LinesDrawn.Add(right); context.LinesDrawn.Add(bottom); context.LinesDrawn.Add(left); var brush = context.Selected ? palette.BorderBrush : palette.FillBrush; // Room specific fill brush (White shows global color) if (RoomFill != ColorTranslator.FromHtml("White") && RoomFill != ColorTranslator.FromHtml("#FFFFFF")) { brush = new SolidBrush(RoomFill); } if (!Settings.DebugDisableLineRendering) { var path = palette.Path(); Drawing.AddLine(path, top, random); Drawing.AddLine(path, right, random); Drawing.AddLine(path, bottom, random); Drawing.AddLine(path, left, random); graphics.DrawPath(brush, path); // Second fill for room specific colors with a split option if (SecondFill != ColorTranslator.FromHtml("White") && SecondFill != ColorTranslator.FromHtml("#FFFFFF")) { // Set the second fill color brush = new SolidBrush(SecondFill); // Define the second path based on the second fill location var secondPath = palette.Path(); switch (SecondFillLocation) { case "Bottom": Drawing.AddLine(secondPath, centerHorizontal, random); Drawing.AddLine(secondPath, halfRightBottom, random); Drawing.AddLine(secondPath, bottom, random); Drawing.AddLine(secondPath, halfLeftBottom, random); break; case "BottomRight": Drawing.AddLine(secondPath, slantUp, random); Drawing.AddLine(secondPath, right, random); Drawing.AddLine(secondPath, bottom, random); break; case "Right": Drawing.AddLine(secondPath, halfTopRight, random); Drawing.AddLine(secondPath, right, random); Drawing.AddLine(secondPath, halfBottomRight, random); Drawing.AddLine(secondPath, centerVertical, random); break; case "TopRight": Drawing.AddLine(secondPath, top, random); Drawing.AddLine(secondPath, right, random); Drawing.AddLine(secondPath, slantDown, random); break; default: break; } // Draw the second fill over the first graphics.DrawPath(brush, secondPath); } if (IsDark) { var state = graphics.Save(); graphics.IntersectClip(path); brush = context.Selected ? palette.FillBrush : palette.BorderBrush; // Room specific fill brush (White shows global color) if (RoomBorder != ColorTranslator.FromHtml("White") && RoomBorder != ColorTranslator.FromHtml("#FFFFFF")) { brush = new SolidBrush(RoomBorder); } graphics.DrawPolygon(brush, new PointF[] { topRight.ToPointF(), new PointF(topRight.X - Settings.DarknessStripeSize, topRight.Y), new PointF(topRight.X, topRight.Y + Settings.DarknessStripeSize) }, XFillMode.Alternate); graphics.Restore(state); } if (RoomBorder == ColorTranslator.FromHtml("White") || RoomBorder == ColorTranslator.FromHtml("#FFFFFF")) { graphics.DrawPath(palette.BorderPen, path); } else { var RoomBorderPen = new Pen(RoomBorder, Settings.LineWidth); RoomBorderPen.StartCap = LineCap.Round; RoomBorderPen.EndCap = LineCap.Round; graphics.DrawPath(RoomBorderPen, path); } } var font = Settings.LargeFont; brush = context.Selected ? palette.FillBrush : palette.LargeTextBrush; // Room specific fill brush (White shows global color) if (RoomLargeText != ColorTranslator.FromHtml("White") && RoomLargeText != ColorTranslator.FromHtml("#FFFFFF")) { brush = new SolidBrush(RoomLargeText); } Rect textBounds = InnerBounds; textBounds.Inflate(-5, -5); if (textBounds.Width > 0 && textBounds.Height > 0) { m_name.Draw(graphics, font, brush, textBounds.Position, textBounds.Size, XStringFormats.Center); } var expandedBounds = InnerBounds; expandedBounds.Inflate(Settings.ObjectListOffsetFromRoom, Settings.ObjectListOffsetFromRoom); var drawnObjectList = false; font = Settings.SmallFont; brush = palette.SmallTextBrush; // Room specific fill brush (White shows global color) if (RoomSmallText != ColorTranslator.FromHtml("White") && RoomSmallText != ColorTranslator.FromHtml("#FFFFFF")) { brush = new SolidBrush(RoomSmallText); } if (!string.IsNullOrEmpty(Objects)) { XStringFormat format = new XStringFormat(); Vector pos = expandedBounds.GetCorner(m_objectsPosition); if (!Drawing.SetAlignmentFromCardinalOrOrdinalDirection(format, m_objectsPosition)) { // object list appears inside the room below its name format.LineAlignment = XLineAlignment.Far; format.Alignment = XStringAlignment.Near; //format.Trimming = StringTrimming.EllipsisCharacter; //format.FormatFlags = StringFormatFlags.LineLimit; var height = InnerBounds.Height / 2 - font.Height / 2; var bounds = new Rect(InnerBounds.Left + Settings.ObjectListOffsetFromRoom, InnerBounds.Bottom - height, InnerBounds.Width - Settings.ObjectListOffsetFromRoom, height - Settings.ObjectListOffsetFromRoom); brush = context.Selected ? palette.FillBrush : brush; if (bounds.Width > 0 && bounds.Height > 0) { m_objects.Draw(graphics, font, brush, bounds.Position, bounds.Size, format); } drawnObjectList = true; } else if (m_objectsPosition == CompassPoint.North || m_objectsPosition == CompassPoint.South) { pos.X += Settings.ObjectListOffsetFromRoom; } if (!drawnObjectList) { m_objects.Draw(graphics, font, brush, pos, Vector.Zero, format); } } }