/* * ShapeSprite - A sprite that draws a shape * * Author: Phillip Piper * Date: 08/02/2010 6:18 PM * * Change log: * 2010-02-08 JPP - Initial version * * To do: * 2010-02-08 Given TextSprite more formatting options * 2010-01-18 Change ShapeSprite so it can use arbitrary Pens and Brushes * * Copyright (C) 2009-2014 Phillip Piper * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com. */ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Windows.Forms; namespace BrightIdeasSoftware { /// /// Instances of ShapeSprite draw a geometric shape within their bounds /// public class ShapeSprite : Sprite { protected enum Shapes { None, Rectangle, Triangle, RoundedRectangle, Square, Circle, Oval, IsoTriangle, EquiTriangle } #region Factories public static ShapeSprite Rectangle(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.Rectangle, penWidth, foreColor, Color.Empty); } public static ShapeSprite RoundedRectangle(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.RoundedRectangle, penWidth, foreColor, Color.Empty); } public static ShapeSprite Circle(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.Circle, penWidth, foreColor, Color.Empty); } public static ShapeSprite Oval(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.Oval, penWidth, foreColor, Color.Empty); } public static ShapeSprite Square(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.Square, penWidth, foreColor, Color.Empty); } public static ShapeSprite Triangle(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.Triangle, penWidth, foreColor, Color.Empty); } public static ShapeSprite IsoTriangle(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.IsoTriangle, penWidth, foreColor, Color.Empty); } public static ShapeSprite EquiTriangle(float penWidth, Color foreColor) { return new ShapeSprite(Shapes.EquiTriangle, penWidth, foreColor, Color.Empty); } public static ShapeSprite FilledRectangle(Color backColor) { return new ShapeSprite(Shapes.Rectangle, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledRoundedRectangle(Color backColor) { return new ShapeSprite(Shapes.RoundedRectangle, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledCircle(Color backColor) { return new ShapeSprite(Shapes.Circle, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledOval(Color backColor) { return new ShapeSprite(Shapes.Oval, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledSquare(Color backColor) { return new ShapeSprite(Shapes.Square, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledTriangle(Color backColor) { return new ShapeSprite(Shapes.Triangle, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledIsoTriangle(Color backColor) { return new ShapeSprite(Shapes.IsoTriangle, 0.0f, Color.Empty, backColor); } public static ShapeSprite FilledEquiTriangle(Color backColor) { return new ShapeSprite(Shapes.EquiTriangle, 0.0f, Color.Empty, backColor); } public static ShapeSprite Rectangle(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.Rectangle, penWidth, foreColor, backColor); } public static ShapeSprite RoundedRectangle(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.RoundedRectangle, penWidth, foreColor, backColor); } public static ShapeSprite Circle(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.Circle, penWidth, foreColor, backColor); } public static ShapeSprite Oval(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.Oval, penWidth, foreColor, backColor); } public static ShapeSprite Square(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.Square, penWidth, foreColor, backColor); } public static ShapeSprite Triangle(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.Triangle, penWidth, foreColor, backColor); } public static ShapeSprite IsoTriangle(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.IsoTriangle, penWidth, foreColor, backColor); } public static ShapeSprite EquiTriangle(float penWidth, Color foreColor, Color backColor) { return new ShapeSprite(Shapes.EquiTriangle, penWidth, foreColor, backColor); } #endregion #region Life and death public ShapeSprite() : this(Shapes.Rectangle, 2.0f, Color.DarkBlue, Color.CornflowerBlue) { } protected ShapeSprite(Shapes shape, float penWidth, Color foreColor, Color backColor) { this.Shape = shape; this.PenWidth = penWidth; this.ForeColor = foreColor; this.BackColor = backColor; this.CornerRounding = 16; } #endregion #region Public properties protected Shapes Shape ; public float PenWidth ; public Color ForeColor ; public Color BackColor ; /// /// How rounded should the corners of a rounded rectangle be? /// Has no impact on other shapes /// public float CornerRounding ; #endregion #region Implementation properties /// /// Gets whether the shape should be drawn filled in /// protected bool Filled { get { return !this.BackColor.IsEmpty; } } /// /// Gets whether the shape should be drawn with an outline /// protected bool Lined { get { return this.PenWidth > 0.0f && !this.ForeColor.IsEmpty; } } #endregion #region Sprite methods public override void Draw(Graphics g) { this.ApplyState(g); // We don't draw the shape into Bounds, since the location of the // shape is handled by a co-ordinate transformation. So we draw the // shape as if it was at origin 0,0 this.DrawShape(g, new Rectangle(Point.Empty, this.Size), this.Opacity); this.UnapplyState(g); } #endregion #region Implementation methods protected virtual void DrawShape(Graphics g, Rectangle r, float opacity) { using (GraphicsPath path = this.GetGraphicsPath(r)) { if (this.Filled) { using (Brush b = this.GetFillBrush(r, opacity)) { g.FillPath(b, path); } } if (this.Lined) { using (Pen p = this.GetLinePen(r, opacity)) { g.DrawPath(p, path); } } } } protected virtual Pen GetLinePen(Rectangle r, float opacity) { return new Pen(this.ApplyOpacityToColor(opacity, this.ForeColor), this.PenWidth); } protected virtual Brush GetFillBrush(Rectangle r, float opacity) { return new SolidBrush(this.ApplyOpacityToColor(opacity, this.BackColor)); } protected Color ApplyOpacityToColor(float opacity, Color c) { if (opacity < 1.0f) return Color.FromArgb((int)(opacity * c.A), this.BackColor); else return c; } protected virtual GraphicsPath GetGraphicsPath(Rectangle r) { GraphicsPath path = new GraphicsPath(); int minimumAxis = Math.Min(r.Width, r.Height); Point midPoint = new Point(r.X + r.Width / 2, r.Y + r.Height / 2); switch (this.Shape) { case Shapes.Rectangle: path.AddRectangle(new Rectangle(Point.Empty, r.Size)); break; case Shapes.RoundedRectangle: path = ShapeSprite.GetRoundedRect(r, this.CornerRounding); break; case Shapes.Circle: path.AddEllipse(midPoint.X - minimumAxis / 2, midPoint.Y - minimumAxis / 2, minimumAxis, minimumAxis); break; case Shapes.Oval: path.AddEllipse(r); break; case Shapes.Square: path.AddRectangle(new Rectangle(midPoint.X - minimumAxis / 2, midPoint.Y - minimumAxis / 2, minimumAxis, minimumAxis)); break; case Shapes.Triangle: path.AddLines(new Point[] { new Point(r.Left, r.Top), new Point(r.Left, r.Bottom), new Point(r.Right, r.Top), new Point(r.Left, r.Top) }); break; case Shapes.IsoTriangle: path.AddLines(new Point[] { new Point(r.Left, r.Bottom), new Point(r.Right, r.Bottom), new Point(midPoint.X, r.Top), new Point(r.Left, r.Bottom) }); break; case Shapes.EquiTriangle: //TODO break; default: break; } return path; } #endregion #region Utilities /// /// Return a GraphicPath that is a round cornered rectangle /// /// The rectangle /// The diameter of the corners /// A round cornered rectagle path /// If I could rely on people using C# 3.0+, this should be /// an extension method of GraphicsPath. public static GraphicsPath GetRoundedRect(Rectangle rect, float diameter) { GraphicsPath path = new GraphicsPath(); if (diameter > 0) { RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter); path.AddArc(arc, 180, 90); arc.X = rect.Right - diameter; path.AddArc(arc, 270, 90); arc.Y = rect.Bottom - diameter; path.AddArc(arc, 0, 90); arc.X = rect.Left; path.AddArc(arc, 90, 90); path.CloseFigure(); } else { path.AddRectangle(rect); } return path; } #endregion } public class ParallelogramSprite : ShapeSprite { public ParallelogramSprite(int horizontalSize, bool forwardSlope) { this.HorizontalSide = horizontalSize; this.SlopeForward = forwardSlope; } /// /// Gets or sets the length of the parallel size of the shape /// public int HorizontalSide ; /// /// Gets or sets if the slope of the parallelogram is forward /// (left edge of bottom is left of the left edge of the top /// public bool SlopeForward ; protected override GraphicsPath GetGraphicsPath(Rectangle r) { GraphicsPath path = new GraphicsPath(); if (this.SlopeForward) { path.AddLines(new Point[] { new Point(r.Left, r.Bottom), new Point(Math.Min(r.Left + this.HorizontalSide, r.Right), r.Bottom), new Point(r.Right, r.Top), new Point(Math.Max(r.Right - this.HorizontalSide, r.Left), r.Top), new Point(r.Left, r.Bottom) }); } else { path.AddLines(new Point[] { new Point(r.Left, r.Top), new Point(Math.Min(r.Left + this.HorizontalSide, r.Right), r.Top), new Point(r.Right, r.Bottom), new Point(Math.Max(r.Right - this.HorizontalSide, r.Left), r.Bottom), new Point(r.Left, r.Top) }); } return path; } } }