Custom Shape

Shape Geometry Customization (Shape Points Editing)

Overview

Customization of the shape geometry assumes editing points of an existing shape.

overview_image

To provide the mentioned functionality GeometryPath class and IGeometryPath interface have been added. GeometryPath instance represents a geometry path of the IGeometryShape object.

To retrieve GeometryPath from the IGeometryShape instance IGeometryShape.GetGeometryPaths method has been added. Shapes may be built from a few smaller shapes (e.g. an “equal” sign) so this method returns an array of GeometryPath objects.

To set GeometryPath to the shape two methods have been added: IGeometryShape.SetGeometryPath for solid shapes and IGeometryShape.SetGeometryPaths for composite shapes.

IGeometryPath provides methods for adding segments of various types:

Adds line to the end of the path

void LineTo(PointF point);
void LineTo(float x, float y);

Adds line to the specified place of the path:

void LineTo(PointF point, uint index);
void LineTo(float x, float y, uint index);

Adds cubic Bezier curve at the end the path:

void CubicBezierTo(PointF point1, PointF point2, PointF point3);
void CubicBezierTo(float x1, float y1, float x2, float y2, float x3, float y3);

Adds cubic Bezier curve to the specified place of the path:

void CubicBezierTo(PointF point1, PointF point2, PointF point3, uint index);
void CubicBezierTo(float x1, float y1, float x2, float y2, float x3, float y3, uint index);

Adds quadratic Bezier curve at the end the path:

void QuadraticBezierTo(PointF point1, PointF point2);
void QuadraticBezierTo(float x1, float y1, float x2, float y2);

Adds quadratic Bezier curve to the specified place of the path:

void QuadraticBezierTo(PointF point1, PointF point2, uint index);
void QuadraticBezierTo(float x1, float y1, float x2, float y2, uint index);

Appends the specified arc to the path:

void ArcTo(float width, float heigth, float startAngle, float sweepAngle);

Closes the current figure of this path:

void CloseFigure();

Sets next point position:

void MoveTo(PointF point);
void MoveTo(float x, float y);

Removes path segment at the specified index:

void RemoveAt(int index);

Properties IGeometryPath.Stroke and IGeometryPath.FillMode set an appearance of the geometry path.

Property IGeometryPath.PathData returns the geometry path of GeometryShape as an array of path segments.

To provide more options of shape geometry customization ShapeUtil class has been added. Methods of this class allow to convert GeometryPath to GraphicsPath back and forth.

Examples and Use Cases

Add Custom Points to Shape

  • Create an instance of the GeometryShape class of type ShapeType.Rectangle
  • Retrieve an instance of the GeometryPath class from the shape.
  • Add a new point between two top points of the path.
  • Add a new point between two bottom points of the path.
  • Apply the path to the shape.
using (Presentation pres = new Presentation())
{
    GeometryShape shape = pres.Slides[0].Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 200, 100) as GeometryShape;
    IGeometryPath geometryPath = shape.GetGeometryPaths()[0];

    geometryPath.LineTo(100, 50, 1);
    geometryPath.LineTo(100, 50, 4);
    shape.SetGeometryPath(geometryPath);
}

example1_image

Remove Points from Shape

using (Presentation pres = new Presentation())
{
	GeometryShape shape = pres.Slides[0].Shapes.AddAutoShape(ShapeType.Heart, 100, 100, 300, 300) as GeometryShape;

	IGeometryPath path = shape.GetGeometryPaths()[0];
	path.RemoveAt(2);
	shape.SetGeometryPath(path);
}

example2_image

Create Custom Shape

  • Calculate points of the shape.
  • Create an instance of the GeometryPath class.
  • Fill the path with the points.
  • Create an instance of the GeometryShape class.
  • Apply the path to the shape.
List<PointF> points = new List<PointF>();

float R = 100, r = 50;
int step = 72;

for (int angle = -90; angle < 270; angle += step)
{
    double radians = angle * (Math.PI / 180f);
    double x = R * Math.Cos(radians);
    double y = R * Math.Sin(radians);
    points.Add(new PointF((float)x + R, (float)y + R));

    radians = Math.PI * (angle + step / 2) / 180.0;
    x = r * Math.Cos(radians);
    y = r * Math.Sin(radians);
    points.Add(new PointF((float)x + R, (float)y + R));
}

GeometryPath starPath = new GeometryPath();
starPath.MoveTo(points[0]);

for (int i = 1; i < points.Count; i++)
{
    starPath.LineTo(points[i]);
}

starPath.CloseFigure();

using (Presentation pres = new Presentation())
{
    GeometryShape shape = pres.Slides[0].Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, R * 2, R * 2) as GeometryShape;

    shape.SetGeometryPath(starPath);
}

example3_image

Create Composite Custom Shape

using (Presentation pres = new Presentation())
{
    GeometryShape shape = pres.Slides[0].Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 200, 100) as GeometryShape;

    GeometryPath geometryPath0 = new GeometryPath();
    geometryPath0.MoveTo(0, 0);
    geometryPath0.LineTo(shape.Width, 0);
    geometryPath0.LineTo(shape.Width, shape.Height/3);
    geometryPath0.LineTo(0, shape.Height / 3);
    geometryPath0.CloseFigure();

    GeometryPath geometryPath1 = new GeometryPath();
    geometryPath1.MoveTo(0, shape.Height/3 * 2);
    geometryPath1.LineTo(shape.Width, shape.Height / 3 * 2);
    geometryPath1.LineTo(shape.Width, shape.Height);
    geometryPath1.LineTo(0, shape.Height);
    geometryPath1.CloseFigure();

    shape.SetGeometryPaths(new GeometryPath[] { geometryPath0, geometryPath1});
}

example4_image

Conversion of GeometryPath to GraphicsPath (System.Drawing.Drawing2D)

using (Presentation pres = new Presentation())
{
    GeometryShape shape = pres.Slides[0].Shapes.AddAutoShape(ShapeType.Rectangle, 100, 100, 300, 100) as GeometryShape;

    IGeometryPath originalPath = shape.GetGeometryPaths()[0];
    originalPath.FillMode = PathFillModeType.None;

    GraphicsPath gPath = new GraphicsPath();

    gPath.AddString("Text in shape", new FontFamily("Arial"), 1, 40, new PointF(10, 10), StringFormat.GenericDefault);

    IGeometryPath textPath = ShapeUtil.GraphicsPathToGeometryPath(gPath);
    textPath.FillMode = PathFillModeType.Normal;

    shape.SetGeometryPaths(new[] {originalPath, textPath}) ;
}

example5_image