/*
* 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;
}
}
}