/// <summary> /// Build new polygon from ellipse. The ellipse is /// defined by the specified bounding rectangle. The /// second parameter specifies how close the /// polygon approximates the ellipse. /// Factor of 0 specifies simplest polygon, while /// factor 100 specifies the most complex polygon. /// </summary> /// <param name="bounds"></param> /// <param name="factor"></param> public Polygon(RectangleF bounds, int factor) { if (factor < 0) { factor = 0; } if (factor > 100) { factor = 100; } GraphicsPath path = new GraphicsPath(); path.AddEllipse(bounds); path.Flatten(new Matrix(), (float)factor / 100f); _points = new PointList(path.PathPoints); path.Dispose(); Complete(); }
/// <summary> /// Creates single h-line at the specified vertical offset. /// </summary> private PointList BuildLine(float yOff) { PointF p11 = new PointF(_bounds.Left - 1, 0); PointF p12 = new PointF(_bounds.Right + 1, 0); PointF p21 = new PointF(_bounds.Left - 1, 0); PointF p22 = new PointF(_bounds.Right + 1, 0); p11.Y = p12.Y = yOff; yOff += _text.Height; if (yOff > _bounds.Bottom) { return(null); } p21.Y = p22.Y = yOff; ArrayList merge = new ArrayList(); PointList result = new PointList(); PointList row1 = PolygonIntersect(p11, p12); PointList row2 = PolygonIntersect(p21, p22); int i; for (i = 0; i < row1.Count; i++) { merge.Add(new DirPt(row1[i], i % 2 == 1 ? 2 : 0)); } for (i = 0; i < row2.Count; i++) { merge.Add(new DirPt(row2[i], i % 2 == 1 ? 3 : 1)); } merge.Sort(); PointF pt = PointF.Empty; int inter = -1; // 0 - for first line, 2 for second line, 4 - for both for (i = 0; i < merge.Count; i++) { DirPt temp = merge[i] as DirPt; if (temp.Direction == 2 || temp.Direction == 3) // out { if (inter != 4) { if (inter == 0 && temp.Direction == 2) { inter = -1; } else if (inter == 2 && temp.Direction == 3) { inter = -1; } continue; } pt.Y = yOff - _text.Height; temp.Point = new PointF(temp.Point.X, yOff); // Make points relative to the upper-left point of the polygon pt.X -= _bounds.Left; pt.Y -= _bounds.Top; temp.Point = new PointF( temp.Point.X - _bounds.Left, temp.Point.Y - _bounds.Top); result.Add(pt); result.Add(temp.Point); if (temp.Direction == 2) { inter = 2; } else { inter = 0; } } else { pt = temp.Point; if (temp.Direction == 0) { if (inter == -1) { inter = 0; } else if (inter == 2) { inter = 4; } } else { if (inter == -1) { inter = 2; } else if (inter == 0) { inter = 4; } } } } // Check if the center point of each // rectangle lies within the polygon for (i = 0; i < result.Count;) { PointF pt1 = result[i]; PointF pt2 = result[i + 1]; PointF ptc = PointF.Empty; ptc.X = (pt1.X + pt2.X) / 2 + _bounds.Left; ptc.Y = (pt1.Y + pt2.Y) / 2 + _bounds.Top; if (!_polygon.Contains(ptc)) { result.RemoveAt(i); result.RemoveAt(i); } else { i += 2; } } return(result); }
/// <summary> /// Draws a previously laid-out text at a specified offset. /// </summary> public void Draw(float xOff, float yOff, RenderTextCallback renderCallback, DrawTextHint hint) { if (GetHLines(_totalLines) == null) { throw new Exception("Draw invoked on a non-laid-out text."); } if (GetHLines(_totalLines).Count == 0) { return; } bool styled = (_text is StyledText); bool is_LastWord = false; // Original brush and font System.Drawing.Brush brushOriginal = hint.Brush; Font fontOriginal = hint.Font; // We now have the starting line and the number // of total lines to layout the text in. Go for it int iword = 0; for (int i = _startLine; i < _startLine + _totalLines; i++) { PointList hLine = GetHLines(_totalLines)[i] as PointList; for (int j = 0; j < hLine.Count; j += 2) { PointF pt1 = hLine[j]; PointF pt2 = hLine[j + 1]; RectangleF rc = new RectangleF( _bounds.X + xOff + pt1.X, _bounds.Y + yOff + pt1.Y, pt2.X - pt1.X, pt2.Y - pt1.Y); bool newLine = false; int newword = FitTextInRect(iword, rc, ref newLine); if (newword > iword) { // Calculate the total width of the words // which are about to be rendered, but skip // the whitespaces at the front and the rear int ifront = iword; int irear = newword - 1; /*DON'T SKIP WHITESPACES ( next 2 lines ) * */ while (ifront < newword && _text.Words[ifront].IsWhitespace) { ifront++; } while (irear >= ifront && _text.Words[irear].IsWhitespace) { irear--; } int w; float total = 0; for (w = ifront; w <= irear; w++) { total += _text.Words[w].Width; } // Adjust the left side of the destination // rectangle according to the specified alignment switch (_options.Alignment) { case StringAlignment.Near: // Do nothing break; case StringAlignment.Center: rc.X = rc.X + (rc.Width - total) / 2; break; case StringAlignment.Far: rc.X = rc.Right - total; break; } // Render the words in the range [ifront, irear] for (w = ifront; w <= irear; w++) { Word word = _text.Words[w]; if (w == _text.Words.Count - 1) { is_LastWord = true; } else { is_LastWord = false; } if (!_fits && hint.AddEllipsis && styled) { if (i == _startLine + _totalLines - 1) { if (j == hLine.Count - 2) { if (w == irear) { // Append the last word with ellipsis StyledText.StyledWord sword = word as StyledText.StyledWord; StyledText.StyledWord newWord = null; int chars = sword.Value.Length; float width = sword.Width; do { newWord = new StyledText.StyledWord( sword.Value.Substring(0, chars) + "...", sword.Format, sword.Color); newWord.UpdateMeasures(hint.Graphics, hint.Font); chars--; }while (chars > 0 && newWord.Width > width); word = newWord; } } } } /*DON'T SKIP WHITESPACES ( next line ) * */ if (!word.IsLineBreak && !word.IsWhitespace) { if (styled) { // In case of styled text formatting, // apply fonts and colors StyledText.StyledWord sword = word as StyledText.StyledWord; hint.Font = sword.CreateFont(hint.Font); hint.Brush = sword.CreateBrush(hint.Brush); rc.Y += sword.YOffset; } // Add 10 to width and height becaus GDI+ stupid // and clips the text hint.IsLastLine = is_LastWord; hint.CurrentWord = word; renderCallback(word.Value, new RectangleF( rc.X, rc.Y, word.Width + 10, rc.Height + 10), hint); if (styled) { // Restore font and brush StyledText.StyledWord sword = word as StyledText.StyledWord; sword.DisposeFont(hint.Font); sword.DisposeBrush(hint.Brush); hint.Font = fontOriginal; hint.Brush = brushOriginal; rc.Y -= sword.YOffset; } } rc.X += _text.Words[w].Width; } iword = newword; } if (newLine) { break; } } } }
private bool DoLayout(Text text, Polygon polygon, LayoutOptions options) { // Initialize internal variables _text = text; _polygon = polygon; _options = options; _bounds = _polygon.Bounds; _fits = false; // Build the h-lines according to the layout settings BuildLines(); // Find out the starting line and the total number // of lines needed to layout the text. _totalLines = 1; _startLine = FirstLine(_totalLines); if (GetHLines(_totalLines).Count == 0) { return(false); } do { // Check if the text fits within the h-lines // in the range [_startLine, _startLine + _totalLines) int iword = 0; PointList hLine = null; for (int i = _startLine; i < _startLine + _totalLines; i++) { hLine = GetHLines(_totalLines)[i] as PointList; for (int j = 0; j < hLine.Count; j += 2) { PointF pt1 = hLine[j]; PointF pt2 = hLine[j + 1]; RectangleF rc = new RectangleF( pt1.X, pt1.Y, pt2.X - pt1.X, pt2.Y - pt1.Y); bool newLine = false; iword = FitTextInRect(iword, rc, ref newLine); if (newLine) { break; } if (iword >= _text.Words.Count) { _fits = true; return(true); } } } // If the text does not fit, increase the total // number of lines and recalculate the starting // line depending on v-alignment. _totalLines++; if (_totalLines > GetHLines(_totalLines).Count) { _totalLines--; return(false); } _startLine = FirstLine(_totalLines); ArrayList hLines = GetHLines(_totalLines); if (_startLine + _totalLines > hLines.Count) { ArrayList hLines2 = GetHLines(_totalLines + 1); if (_totalLines > hLines2.Count) { // The text would not fit in the // polygon, so use all of the available // space to layout as much text as possible _totalLines = Math.Max(hLines.Count, hLines2.Count); _startLine = 0; return(false); } } }while ((_startLine = FirstLine(_totalLines)) != -1); return(false); }
/// <summary> /// Finds a point inside the polygon. /// </summary> public PointF GetInternalPoint() { int cvi = FindConvexVertex(); int prev = cvi > 0 ? cvi - 1 : _points.Count - 1; int next = (cvi + 1) % _points.Count; PointF v = _points[cvi]; PointF a = _points[prev]; PointF b = _points[next]; PointList pl = new PointList(); pl.Add(a); pl.Add(v); pl.Add(b); Polygon avb = new Polygon(pl); float minDist = float.MaxValue; PointF intPt = v; for (int i = 0; i < _points.Count; ++i) { if (i == cvi) continue; if (i == prev) continue; if (i == next) continue; PointF q = _points[i]; if (avb.Contains(q)) { float dist = (float)Math.Sqrt((q.X-v.X)*(q.X-v.X) + (q.Y-v.Y)*(q.Y-v.Y)); if (dist < minDist) { minDist = dist; intPt = q; } } } if (intPt == v) return new PointF((a.X + b.X) / 2, (a.Y + b.Y) / 2); else return new PointF((v.X + intPt.X)/ 2, (v.Y + intPt.Y) / 2); }
/// <summary> /// Calculates the intersections between the /// polygon and the line defined by the given points. /// The result is a list containing all points of intersection. /// </summary> public PointList IntersectLine(PointF a, PointF b) { PointList result = new PointList(); PointF point; for (int i = 0; i < _points.Count - 1; i++) { point = new Line(_points[i], _points[i + 1]). IntersectLine(a, b); if (!float.IsPositiveInfinity(point.X)) result.Add(point); } return result; }
/// <summary> /// Build new polygon from ellipse. The ellipse is /// defined by the specified bounding rectangle. The /// second parameter specifies how close the /// polygon approximates the ellipse. /// Factor of 0 specifies simplest polygon, while /// factor 100 specifies the most complex polygon. /// </summary> /// <param name="bounds"></param> /// <param name="factor"></param> public Polygon(RectangleF bounds, int factor) { if (factor < 0) factor = 0; if (factor > 100) factor = 100; GraphicsPath path = new GraphicsPath(); path.AddEllipse(bounds); path.Flatten(new Matrix(), (float)factor / 100f); _points = new PointList(path.PathPoints); path.Dispose(); Complete(); }
/// <summary> /// Builds new polygon object from the specified rectangle. /// </summary> /// <param name="rect"></param> public Polygon(RectangleF rect) { float l = Math.Min(rect.Left, rect.Right); float r = Math.Max(rect.Left, rect.Right); float t = Math.Min(rect.Top, rect.Bottom); float b = Math.Max(rect.Top, rect.Bottom); _points = new PointList( new PointF[] { new PointF(l, t), new PointF(r, t), new PointF(r, b), new PointF(l, b) }); Complete(); }
/// <summary> /// Builds new polygon object from a given points list. /// </summary> public Polygon(PointList points) { if (points.Count < 3) throw new Exception("Polygons need at least 3 points."); _points = new PointList(points); Complete(); }