Commit 773803a4 authored by davescriven's avatar davescriven
Browse files

- Added arc path support (#6455 Arc Segments in SvgPathBuilder)

- Added SvgMatrix, SvgShear and SvgSkew missing files from the previous check-in.
parent c97ef156
...@@ -127,9 +127,13 @@ namespace Svg ...@@ -127,9 +127,13 @@ namespace Svg
{ {
SvgClipPath clipPath = this.OwnerDocument.GetElementById<SvgClipPath>(this.ClipPath.ToString()); SvgClipPath clipPath = this.OwnerDocument.GetElementById<SvgClipPath>(this.ClipPath.ToString());
this._previousClip = renderer.Clip; this._previousClip = renderer.Clip;
if (clipPath != null)
{
renderer.SetClip(clipPath.GetClipRegion()); renderer.SetClip(clipPath.GetClipRegion());
} }
} }
}
protected internal virtual void ResetClip(SvgRenderer renderer) protected internal virtual void ResetClip(SvgRenderer renderer)
{ {
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Drawing.Drawing2D;
using System.Drawing;
namespace Svg.Pathing namespace Svg.Pathing
{ {
public class SvgArcSegment : SvgPathSegment public sealed class SvgArcSegment : SvgPathSegment
{ {
public override void AddToPath(System.Drawing.Drawing2D.GraphicsPath graphicsPath) private const double RadiansPerDegree = Math.PI / 180.0;
private const double DoublePI = Math.PI * 2;
public float RadiusX
{
get;
set;
}
public float RadiusY
{
get;
set;
}
public float Angle
{
get;
set;
}
public SvgArcSweep Sweep
{
get;
set;
}
public SvgArcSize Size
{
get;
set;
}
public SvgArcSegment(PointF start, float radiusX, float radiusY, float angle, SvgArcSize size, SvgArcSweep sweep, PointF end)
: base(start, end)
{
this.RadiusX = Math.Abs(radiusX);
this.RadiusY = Math.Abs(radiusY);
this.Angle = angle;
this.Sweep = sweep;
this.Size = size;
}
private static double CalculateVectorAngle(double ux, double uy, double vx, double vy)
{
double ta = Math.Atan2(uy, ux);
double tb = Math.Atan2(vy, vx);
if (tb >= ta)
{
return tb - ta;
}
return SvgArcSegment.DoublePI - (ta - tb);
}
public override void AddToPath(GraphicsPath graphicsPath)
{
if (this.Start == this.End)
{
return;
}
if (this.RadiusX == 0.0f && this.RadiusY == 0.0f)
{
graphicsPath.AddLine(this.Start, this.End);
return;
}
double sinPhi = Math.Sin(this.Angle * SvgArcSegment.RadiansPerDegree);
double cosPhi = Math.Cos(this.Angle * SvgArcSegment.RadiansPerDegree);
double x1dash = cosPhi * (this.Start.X - this.End.X) / 2.0 + sinPhi * (this.Start.Y - this.End.Y) / 2.0;
double y1dash = -sinPhi * (this.Start.X - this.End.X) / 2.0 + cosPhi * (this.Start.Y - this.End.Y) / 2.0;
double root;
double numerator = this.RadiusX * this.RadiusX * this.RadiusY * this.RadiusY - this.RadiusX * this.RadiusX * y1dash * y1dash - this.RadiusY * this.RadiusY * x1dash * x1dash;
float rx = this.RadiusX;
float ry = this.RadiusY;
if (numerator < 0.0)
{
float s = (float)Math.Sqrt(1.0 - numerator / (this.RadiusX * this.RadiusX * this.RadiusY * this.RadiusY));
rx *= s;
ry *= s;
root = 0.0;
}
else
{
root = (this.Size == SvgArcSize.Large && this.Sweep == SvgArcSweep.Positive ? -1.0 : 1.0) * Math.Sqrt(numerator / (this.RadiusX * this.RadiusX * y1dash * y1dash + this.RadiusY * this.RadiusY * x1dash * x1dash));
}
double cxdash = root * rx * y1dash / ry;
double cydash = -root * ry * x1dash / rx;
double cx = cosPhi * cxdash - sinPhi * cydash + (this.Start.X + this.End.X) / 2.0;
double cy = sinPhi * cxdash + cosPhi * cydash + (this.Start.Y + this.End.Y) / 2.0;
double theta1 = SvgArcSegment.CalculateVectorAngle(1.0, 0.0, (x1dash - cxdash) / rx, (y1dash - cydash) / ry);
double dtheta = SvgArcSegment.CalculateVectorAngle((x1dash - cxdash) / rx, (y1dash - cydash) / ry, (-x1dash - cxdash) / rx, (-y1dash - cydash) / ry);
if (this.Sweep == SvgArcSweep.Negative && dtheta > 0)
{
dtheta -= 2.0 * Math.PI;
}
else if (this.Sweep == SvgArcSweep.Positive && dtheta < 0)
{ {
dtheta += 2.0 * Math.PI;
}
int segments = (int)Math.Ceiling((double)Math.Abs(dtheta / (Math.PI / 2.0)));
double delta = dtheta / segments;
double t = 8.0 / 3.0 * Math.Sin(delta / 4.0) * Math.Sin(delta / 4.0) / Math.Sin(delta / 2.0);
double startX = this.Start.X;
double startY = this.Start.Y;
for (int i = 0; i < segments; ++i)
{
double cosTheta1 = Math.Cos(theta1);
double sinTheta1 = Math.Sin(theta1);
double theta2 = theta1 + delta;
double cosTheta2 = Math.Cos(theta2);
double sinTheta2 = Math.Sin(theta2);
double endpointX = cosPhi * rx * cosTheta2 - sinPhi * ry * sinTheta2 + cx;
double endpointY = sinPhi * rx * cosTheta2 + cosPhi * ry * sinTheta2 + cy;
double dx1 = t * (-cosPhi * rx * sinTheta1 - sinPhi * ry * cosTheta1);
double dy1 = t * (-sinPhi * rx * sinTheta1 + cosPhi * ry * cosTheta1);
double dxe = t * (cosPhi * rx * sinTheta2 + sinPhi * ry * cosTheta2);
double dye = t * (sinPhi * rx * sinTheta2 - cosPhi * ry * cosTheta2);
graphicsPath.AddBezier((float)startX, (float)startY, (float)(startX + dx1), (float)(startY + dy1),
(float)(endpointX + dxe), (float)(endpointY + dye), (float)endpointX, (float)endpointY);
theta1 = theta2;
startX = (float)endpointX;
startY = (float)endpointY;
}
}
}
[Flags]
public enum SvgArcSweep
{
Negative = 0,
Positive = 1
} }
[Flags]
public enum SvgArcSize
{
Small = 0,
Large = 1
} }
} }
...@@ -26,11 +26,15 @@ namespace Svg ...@@ -26,11 +26,15 @@ namespace Svg
try try
{ {
List<float> coords;
char command;
bool isRelative;
foreach (var commandSet in SplitCommands(path.TrimEnd(null))) foreach (var commandSet in SplitCommands(path.TrimEnd(null)))
{ {
var coords = new List<float>(ParseCoordinates(commandSet)); coords = new List<float>(ParseCoordinates(commandSet));
var command = commandSet[0]; command = commandSet[0];
var isRelative = char.IsLower(command); isRelative = char.IsLower(command);
// http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation // http://www.w3.org/TR/SVG11/paths.html#PathDataGeneralInformation
switch (command) switch (command)
...@@ -48,7 +52,19 @@ namespace Svg ...@@ -48,7 +52,19 @@ namespace Svg
break; break;
case 'a': case 'a':
case 'A': case 'A':
throw new NotImplementedException("Arc segments are not yet implemented"); SvgArcSize size;
SvgArcSweep sweep;
for (var i = 0; i < coords.Count; i += 7)
{
size = (coords[i + 3] == 1.0f) ? SvgArcSize.Large : SvgArcSize.Small;
sweep = (coords[i + 4] == 1.0f) ? SvgArcSweep.Positive : SvgArcSweep.Negative;
// A|a rx ry x-axis-rotation large-arc-flag sweep-flag x y
segments.Add(new SvgArcSegment(segments.Last.End, coords[i], coords[i + 1], coords[i + 2],
size, sweep, ToAbsolute(coords[i + 5], coords[i + 6], segments, isRelative)));
}
break;
case 'l': // relative lineto case 'l': // relative lineto
case 'L': // lineto case 'L': // lineto
for (var i = 0; i < coords.Count; i += 2) for (var i = 0; i < coords.Count; i += 2)
...@@ -213,17 +229,17 @@ namespace Svg ...@@ -213,17 +229,17 @@ namespace Svg
private static IEnumerable<float> ParseCoordinates(string coords) private static IEnumerable<float> ParseCoordinates(string coords)
{ {
// TODO: Handle "1-1" (new PointF(1, -1); // TODO: Handle "1-1" (new PointF(1, -1);
var parts = coords.Remove(0, 1).Replace("-", " -").Split(new[] {',', ' '}, var parts = coords.Remove(0, 1).Replace("-", " -").Split(new[] { ',', ' ' },
StringSplitOptions.RemoveEmptyEntries); StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < parts.Length; i ++) for (var i = 0; i < parts.Length; i++)
yield return float.Parse(parts[i], NumberStyles.Float, CultureInfo.InvariantCulture); yield return float.Parse(parts[i], NumberStyles.Float, CultureInfo.InvariantCulture);
} }
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{ {
if (value is string) if (value is string)
return Parse((string) value); return Parse((string)value);
return base.ConvertFrom(context, culture, value); return base.ConvertFrom(context, culture, value);
} }
......
...@@ -23,6 +23,16 @@ namespace Svg.Pathing ...@@ -23,6 +23,16 @@ namespace Svg.Pathing
set { this._end = value; } set { this._end = value; }
} }
protected SvgPathSegment()
{
}
protected SvgPathSegment(PointF start, PointF end)
{
this.Start = start;
this.End = end;
}
public abstract void AddToPath(GraphicsPath graphicsPath); public abstract void AddToPath(GraphicsPath graphicsPath);
} }
} }
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Drawing2D;
namespace Svg.Transforms
{
/// <summary>
/// The class which applies custom transform to this Matrix (Required for projects created by the Inkscape).
/// </summary>
public sealed class SvgMatrix : SvgTransform
{
private List<float> points;
public List<float> Points
{
get { return this.points; }
set { this.points = value; }
}
public override System.Drawing.Drawing2D.Matrix Matrix
{
get
{
Matrix matrix = new Matrix(
this.points[0],
this.points[1],
this.points[2],
this.points[3],
this.points[4],
this.points[5]
);
return matrix;
}
}
public SvgMatrix(List<float> m)
{
this.points = m;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Drawing2D;
namespace Svg.Transforms
{
/// <summary>
/// The class which applies the specified shear vector to this Matrix.
/// </summary>
public sealed class SvgShear : SvgTransform
{
private float shearFactorX;
private float shearFactorY;
public float X
{
get { return this.shearFactorX; }
set { this.shearFactorX = value; }
}
public float Y
{
get { return this.shearFactorY; }
set { this.shearFactorY = value; }
}
public override Matrix Matrix
{
get
{
Matrix matrix = new Matrix();
matrix.Shear(this.X, this.Y);
return matrix;
}
}
public SvgShear(float x) : this(x, x) { }
public SvgShear(float x, float y)
{
this.shearFactorX = x;
this.shearFactorY = y;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Drawing2D;
namespace Svg.Transforms
{
/// <summary>
/// The class which applies the specified skew vector to this Matrix.
/// </summary>
public sealed class SvgSkew : SvgTransform
{
private float angleX, angleY;
public float AngleX
{
get { return this.angleX; }
set { this.angleX = value; }
}
public float AngleY
{
get { return this.angleY; }
set { this.angleY = value; }
}
public override Matrix Matrix
{
get
{
Matrix matrix = new Matrix();
matrix.Shear(this.AngleX, this.AngleY);
return matrix;
}
}
public SvgSkew(float x, float y)
{
this.angleX = x;
this.angleY = y;
}
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment