当前位置:网站首页>C SVG path parser of xamarin version
C SVG path parser of xamarin version
2022-04-23 07:48:00 【Dake mountain man】
Xamarin Version of C# SVG Path parser , Yes SVG Of Path Path analysis , These include :
The main program SvgPathParser.cs,
Relevant interface definitions :ISourceFormatter.cs,
Auxiliary class :FormatterRocks.cs,
Derived from interface CSharpCoreGraphicsFormatter.cs.
// The main program SvgPathParser.cs:
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012-2013 Xamarin Inc.
//
// This file is mostly based on the C++ code from once magnificent Moonlight
// https://github.com/mono/moon/blob/master/src/xaml.cpp
// Copyright 2007 Novell, Inc. (http://www.novell.com)
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
namespace Poupou.SvgPathConverter {
public class SvgPathParser {
static int i;
public ISourceFormatter Formatter { get; set; }
public void Parse (string svgPath, string name = null)
{
if (Formatter == null)
throw new InvalidOperationException ("Missing formatter");
if (name == null)
name = "Unnamed_" + (++i).ToString ();
Parse (svgPath, name, Formatter);
}
static void Advance (string s, ref int pos)
{
if (pos >= s.Length)
return;
char c = s [pos];
while (!Char.IsLetterOrDigit (c) && c != '.' && c!= '-' && c != '+') {
if (++pos == s.Length)
return;
c = s [pos];
}
}
static int FindNonFloat (string s, int pos)
{
char c = s [pos];
while ((Char.IsNumber (c) || c == '.' || c == '-' || c == '+')) {
if (++pos == s.Length)
return pos;
c = s [pos];
}
return pos;
}
static bool MorePointsAvailable (string s, int pos)
{
if (pos >= s.Length)
return false;
char c = s [pos];
while (Char.IsWhiteSpace (c) || c == ',')
c = s [++pos];
return Char.IsDigit (c) || c == '.' || c == '-' || c == '+';
}
static float GetFloat (string svg, ref int pos)
{
int end = FindNonFloat (svg, pos);
string s = svg.Substring (pos, end - pos);
float f = Single.Parse (s, CultureInfo.InvariantCulture);
pos = end;
return f;
}
static PointF GetPoint (string svg, ref int pos)
{
while (Char.IsWhiteSpace (svg [pos]))
pos++;
float x = GetFloat (svg, ref pos);
while (Char.IsWhiteSpace (svg [pos]))
pos++;
if (svg [pos] == ',')
pos++;
while (Char.IsWhiteSpace (svg [pos]))
pos++;
float y = GetFloat (svg, ref pos);
return new PointF (x, y);
}
static PointF MakeRelative (PointF c, PointF m)
{
return new PointF (m.X + c.X, m.Y + c.Y);
}
static void Parse (string svg, string name, ISourceFormatter formatter)
{
formatter.Prologue (name);
PointF start;
PointF cp = new PointF (0, 0);
PointF cp1, cp2, cp3;
PointF qbzp, cbzp;
int fill_rule = 0;
int pos = 0;
bool cbz = false;
bool qbz = false;
while (pos < svg.Length) {
char c = svg [pos++];
if (Char.IsWhiteSpace (c))
continue;
bool relative = false;
switch (c) {
case 'f':
case 'F':
c = svg [pos++];
if (c == '0')
fill_rule = 0;
else if (c == '1')
fill_rule = 1;
else
throw new FormatException ();
break;
case 'h':
relative = true;
goto case 'H';
case 'H':
float x = GetFloat (svg, ref pos);
if (relative)
x += cp.X;
cp = new PointF (x, cp.Y);
formatter.LineTo (cp);
cbz = qbz = false;
break;
case 'm':
relative = true;
goto case 'M';
case 'M':
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
formatter.MoveTo (cp1);
start = cp = cp1;
Advance (svg, ref pos);
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
formatter.LineTo (cp1);
}
cp = cp1;
cbz = qbz = false;
break;
case 'l':
relative = true;
goto case 'L';
case 'L':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
Advance (svg, ref pos);
formatter.LineTo (cp1);
cp = cp1;
}
cbz = qbz = false;
break;
case 'a':
relative = true;
goto case 'A';
case 'A':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
// this is a width and height so it's not made relative to cp
Advance (svg, ref pos);
float angle = GetFloat (svg, ref pos);
Advance (svg, ref pos);
bool is_large = GetFloat (svg, ref pos) != 0.0f;
Advance (svg, ref pos);
bool positive_sweep = GetFloat (svg, ref pos) != 0.0f;
Advance (svg, ref pos);
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
formatter.ArcTo (cp1, angle, is_large, positive_sweep, cp2, cp);
cp = cp2;
Advance (svg, ref pos);
}
qbz = false;
cbz = false;
break;
case 'q':
relative = true;
goto case 'Q';
case 'Q':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
Advance (svg, ref pos);
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
formatter.QuadCurveTo (cp1, cp2);
cp = cp2;
Advance (svg, ref pos);
}
qbz = true;
qbzp = cp1;
cbz = false;
break;
case 'c':
relative = true;
goto case 'C';
case 'C':
while (MorePointsAvailable (svg, pos)) {
cp1 = GetPoint (svg, ref pos);
if (relative)
cp1 = MakeRelative (cp, cp1);
Advance (svg, ref pos);
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
cp3 = GetPoint (svg, ref pos);
if (relative)
cp3 = MakeRelative (cp, cp3);
Advance (svg, ref pos);
formatter.CurveTo (cp1, cp2, cp3);
cp1 = cp3;
}
cp = cp3;
cbz = true;
cbzp = cp2;
qbz = false;
break;
case 't':
relative = true;
goto case 'T';
case 'T':
while (MorePointsAvailable (svg, pos)) {
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
if (qbz) {
cp1.X = 2 * cp.X - qbzp.X;
cp1.Y = 2 * cp.Y - qbzp.Y;
} else {
cp1 = cp;
}
formatter.QuadCurveTo (cp1, cp2);
qbz = true;
qbzp = cp1;
cp = cp2;
Advance (svg, ref pos);
}
cbz = false;
break;
case 's':
relative = true;
goto case 'S';
case 'S':
while (MorePointsAvailable (svg, pos)) {
cp2 = GetPoint (svg, ref pos);
if (relative)
cp2 = MakeRelative (cp, cp2);
Advance (svg, ref pos);
cp3 = GetPoint (svg, ref pos);
if (relative)
cp3 = MakeRelative (cp, cp3);
if (cbz) {
cp1.X = 2 * cp.X - cbzp.X;
cp1.Y = 2 * cp.Y - cbzp.Y;
} else {
cp1 = cp;
}
formatter.CurveTo (cp1, cp2, cp3);
cbz = true;
cbzp = cp2;
cp = cp3;
Advance (svg, ref pos);
}
qbz = false;
break;
case 'v':
relative = true;
goto case 'V';
case 'V':
float y = GetFloat (svg, ref pos);
if (relative)
y += cp.Y;
cp = new PointF (cp.X, y);
formatter.LineTo (cp);
cbz = qbz = false;
break;
case 'z':
case 'Z':
formatter.ClosePath ();
formatter.MoveTo (start);
cp = start;
cbz = qbz = false;
break;
default:
throw new FormatException (c.ToString ());
}
}
formatter.Epilogue ();
}
}
}
// Relevant interface definitions :ISourceFormatter.cs
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012 Xamarin Inc.
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Drawing;
namespace Poupou.SvgPathConverter {
public interface ISourceFormatter {
void Prologue (string name);
void Epilogue ();
void MoveTo (PointF pt);
void LineTo (PointF pt);
void QuadCurveTo (PointF pt1, PointF pt2);
void CurveTo (PointF pt1, PointF pt2, PointF pt3);
void ArcTo (PointF size, float angle, bool isLarge, bool sweep, PointF ep, PointF sp);
void ClosePath ();
}
}
// Auxiliary class :FormatterRocks.cs
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012 Xamarin Inc.
//
// This file is mostly based on the C++ code from once magnificent Moonlight
// https://github.com/mono/moon/blob/master/src/moon-path.cpp
// Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Drawing;
using System.IO;
namespace Poupou.SvgPathConverter {
public static class FormatterRocks {
static bool IsNearZero (float value)
{
return Math.Abs (value) < 0.000019;
}
// The SVG Arc is a bit more complex than others - and also quite different than many existing API
// This implementation will use a ISourceFormatter's CurveTo method to draw the arc
public static void ArcHelper (this ISourceFormatter formatter, PointF size, float anglef,
bool isLarge, bool sweep, PointF endPoint, PointF startPoint)
{
if (IsNearZero (endPoint.X - startPoint.X) && IsNearZero (endPoint.Y - startPoint.Y))
return;
// Correction of out-of-range radii, see F6.6 (step 1)
if (IsNearZero (size.X) || IsNearZero (size.Y)) {
// treat this as a straight line (to end point)
formatter.LineTo (endPoint);
return;
}
// Correction of out-of-range radii, see F6.6.1 (step 2)
float rx = Math.Abs (size.X);
float ry = Math.Abs (size.Y);
// convert angle into radians
double angle = anglef * Math.PI / 180.0f;
// variables required for F6.3.1
double cos_phi = Math.Cos (angle);
double sin_phi = Math.Sin (angle);
double dx2 = (startPoint.X - endPoint.X) / 2.0;
double dy2 = (startPoint.Y - endPoint.Y) / 2.0;
double x1p = cos_phi * dx2 + sin_phi * dy2;
double y1p = cos_phi * dy2 - sin_phi * dx2;
double x1p2 = x1p * x1p;
double y1p2 = y1p * y1p;
float rx2 = rx * rx;
float ry2 = ry * ry;
// Correction of out-of-range radii, see F6.6.2 (step 4)
double lambda = (x1p2 / rx2) + (y1p2 / ry2);
if (lambda > 1.0) {
// see F6.6.3
float lambda_root = (float) Math.Sqrt (lambda);
rx *= lambda_root;
ry *= lambda_root;
// update rx2 and ry2
rx2 = rx * rx;
ry2 = ry * ry;
}
double cxp, cyp, cx, cy;
double c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
// check if there is no possible solution (i.e. we can't do a square root of a negative value)
if (c < 0.0) {
// scale uniformly until we have a single solution (see F6.2) i.e. when c == 0.0
float scale = (float) Math.Sqrt (1.0 - c / (rx2 * ry2));
rx *= scale;
ry *= scale;
// update rx2 and ry2
rx2 = rx * rx;
ry2 = ry * ry;
// step 2 (F6.5.2) - simplified since c == 0.0
cxp = 0.0;
cyp = 0.0;
// step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
cx = 0.0;
cy = 0.0;
} else {
// complete c calculation
c = Math.Sqrt (c / ((rx2 * y1p2) + (ry2 * x1p2)));
// inverse sign if Fa == Fs
if (isLarge == sweep)
c = -c;
// step 2 (F6.5.2)
cxp = c * ( rx * y1p / ry);
cyp = c * (-ry * x1p / rx);
// step 3 (F6.5.3 first part)
cx = cos_phi * cxp - sin_phi * cyp;
cy = sin_phi * cxp + cos_phi * cyp;
}
// step 3 (F6.5.3 second part) we now have the center point of the ellipse
cx += (startPoint.X + endPoint.X) / 2.0;
cy += (startPoint.Y + endPoint.Y) / 2.0;
// step 4 (F6.5.4)
// we dont' use arccos (as per w3c doc), see http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
// note: atan2 (0.0, 1.0) == 0.0
double at = Math.Atan2 (((y1p - cyp) / ry), ((x1p - cxp) / rx));
double theta1 = (at < 0.0) ? 2.0 * Math.PI + at : at;
double nat = Math.Atan2 (((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
double delta_theta = (nat < at) ? 2.0 * Math.PI - at + nat : nat - at;
if (sweep) {
// ensure delta theta < 0 or else add 360 degrees
if (delta_theta < 0.0)
delta_theta += 2.0 * Math.PI;
} else {
// ensure delta theta > 0 or else substract 360 degrees
if (delta_theta > 0.0)
delta_theta -= 2.0 * Math.PI;
}
// add several cubic bezier to approximate the arc (smaller than 90 degrees)
// we add one extra segment because we want something smaller than 90deg (i.e. not 90 itself)
int segments = (int) (Math.Abs (delta_theta / Math.PI)) + 1;
double delta = delta_theta / segments;
// http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
float bcp = (float) (4.0 / 3 * (1 - Math.Cos (delta / 2)) / Math.Sin (delta / 2));
double cos_phi_rx = cos_phi * rx;
double cos_phi_ry = cos_phi * ry;
double sin_phi_rx = sin_phi * rx;
double sin_phi_ry = sin_phi * ry;
double cos_theta1 = Math.Cos (theta1);
double sin_theta1 = Math.Sin (theta1);
PointF c1, c2;
int i;
for (i = 0; i < segments; ++i) {
// end angle (for this segment) = current + delta
double theta2 = theta1 + delta;
double cos_theta2 = Math.Cos (theta2);
double sin_theta2 = Math.Sin (theta2);
// first control point (based on start point sx,sy)
c1.X = startPoint.X - bcp * (float) (cos_phi_rx * sin_theta1 + sin_phi_ry * cos_theta1);
c1.Y = startPoint.Y + bcp * (float) (cos_phi_ry * cos_theta1 - sin_phi_rx * sin_theta1);
// end point (for this segment)
endPoint.X = (float) (cx + (cos_phi_rx * cos_theta2 - sin_phi_ry * sin_theta2));
endPoint.Y = (float) (cy + (sin_phi_rx * cos_theta2 + cos_phi_ry * sin_theta2));
// second control point (based on end point ex,ey)
c2.X = endPoint.X + bcp * (float) (cos_phi_rx * sin_theta2 + sin_phi_ry * cos_theta2);
c2.Y = endPoint.Y + bcp * (float) (sin_phi_rx * sin_theta2 - cos_phi_ry * cos_theta2);
formatter.CurveTo (c1, c2, endPoint);
// next start point is the current end point (same for angle)
startPoint = endPoint;
theta1 = theta2;
// avoid recomputations
cos_theta1 = cos_theta2;
sin_theta1 = sin_theta2;
}
}
}
}
// Derived from interface CSharpCoreGraphicsFormatter.cs
// Authors:
// Sebastien Pouliot <[email protected]>
//
// Copyright 2012-2013 Xamarin Inc.
//
// Licensed under the GNU LGPL 2 license only (no "later versions")
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
namespace Poupou.SvgPathConverter {
public class CSharpCoreGraphicsFormatter : ISourceFormatter {
TextWriter writer;
public CSharpCoreGraphicsFormatter (TextWriter textWriter)
{
writer = textWriter;
}
public void Prologue (string name)
{
writer.WriteLine ("\tstatic void {0} (CGContext c)", name);
writer.WriteLine ("\t{");
}
public void Epilogue ()
{
writer.WriteLine ("\t\tc.FillPath ();");
writer.WriteLine ("\t\tc.StrokePath ();");
writer.WriteLine ("\t}");
writer.WriteLine ();
}
public void MoveTo (PointF pt)
{
writer.WriteLine ("\t\tc.MoveTo ({0}f, {1}f);", pt.X.ToString (CultureInfo.InvariantCulture),
pt.Y.ToString (CultureInfo.InvariantCulture));
}
public void LineTo (PointF pt)
{
writer.WriteLine ("\t\tc.AddLineToPoint ({0}f, {1}f);", pt.X.ToString (CultureInfo.InvariantCulture),
pt.Y.ToString (CultureInfo.InvariantCulture));
}
public void ClosePath ()
{
writer.WriteLine ("\t\tc.ClosePath ();");
}
public void QuadCurveTo (PointF pt1, PointF pt2)
{
writer.WriteLine ("\t\tc.AddQuadCurveToPoint ({0}f, {1}f, {2}f, {3}f);",
pt1.X.ToString (CultureInfo.InvariantCulture), pt1.Y.ToString (CultureInfo.InvariantCulture),
pt2.X.ToString (CultureInfo.InvariantCulture), pt2.Y.ToString (CultureInfo.InvariantCulture));
}
public void CurveTo (PointF pt1, PointF pt2, PointF pt3)
{
writer.WriteLine ("\t\tc.AddCurveToPoint ({0}f, {1}f, {2}f, {3}f, {4}f, {5}f);",
pt1.X.ToString (CultureInfo.InvariantCulture), pt1.Y.ToString (CultureInfo.InvariantCulture),
pt2.X.ToString (CultureInfo.InvariantCulture), pt2.Y.ToString (CultureInfo.InvariantCulture),
pt3.X.ToString (CultureInfo.InvariantCulture), pt3.Y.ToString (CultureInfo.InvariantCulture));
}
public void ArcTo (PointF size, float angle, bool isLarge, bool sweep, PointF endPoint, PointF startPoint)
{
this.ArcHelper (size, angle, isLarge, sweep, endPoint, startPoint);
}
}
}
版权声明
本文为[Dake mountain man]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230625378796.html
边栏推荐
- Scrapy modifies the time in the statistics at the end of the crawler as the current system time
- c#读取INI文件和向ini文件写入数据
- BTREE, B + tree and hash index
- ES6 uses recursion to implement deep copy
- State synchronization and frame synchronization
- King glory - unity learning journey
- 平面定义-平面方程
- Ogldev reading notes
- 中间人环境mitmproxy搭建
- 4. Multi table query
猜你喜欢
SAP pi / PO rfc2soap publishes RFC interface as WS example
Understanding the Role of Individual Units in a Deep Neural Networks(了解各个卷积核在神经网络中的作用)
Dropping Pixels for Adversarial Robustness
Use of command line parameter passing library argparse
Django uses MySQL database to solve error reporting
ABAP CDS VIEW WITH ASSOCIATION示例
How to judge whether a point is within a polygon (including complex polygons or a large number of polygons)
FSM finite state machine
基于NLP的软件安全研究(一)
Protobuf 使用
随机推荐
Encapsulate the debug function of unity
unity 屏幕自适应
Hot change scheme and dynamic update strategy of mobile game
MySQL in window10 version does not work after setting remote access permission
Dropping Pixels for Adversarial Robustness
new的实现
4. Multi table query
Unable to process jar entry [module info. Class]
js之DOM学习三种创建元素的方式
SampleCameraFilter
electron-builder打包报错:proxyconnect tcp: dial tcp :0: connectex
Nodejs (four) character reading
利用Lambda表达式解决c#文件名排序问题(是100大还是11大的问题)
BTREE, B + tree and hash index
事件管理之一
解决在docker中部署mysql8, 密码正确但无法登陆MySQL问题
Use of typescript dictionary
Robust and Efficient Quadrotor Trajectory Generation for Fast Autonomous Flight
命令行参数传递库argparse的使用
数组扁平化