rj-action-library/Runtime/XML/SVG/SVGPathExtractor.cs

407 lines
9.8 KiB
C#

using Godot;
using System;
using System.Collections.Generic;
namespace Rokojori
{
public class SVGPathExtractor
{
Vector2 currentPoint = new Vector2();
Vector2 lastControlPoint = new Vector2();
Vector2 startPoint = new Vector2();
SVGPathInstruction instruction = new SVGPathInstruction();
public readonly EventSlot<SVGPathInstruction> onInstruction = new EventSlot<SVGPathInstruction>();
void DispatchInstruction()
{
onInstruction.DispatchEvent( instruction );
}
public void Process( List<SVGPathCommand> commands )
{
commands.ForEach( c => ProcessNext( c ) );
}
public void ProcessNext( SVGPathCommand command )
{
switch ( command.type)
{
case "m": case "M":
{
ProcessMoveTo( command );
}
break;
case "l": case "L":
{
ProcessLineTo( command );
}
break;
case "v": case "V":
{
ProcessVerticalTo( command );
}
break;
case "h": case "H":
{
ProcessHorizontalTo( command );
}
break;
case "c": case "C":
{
ProcessCubicTo( command );
}
break;
case "q": case "Q":
{
ProcessQuadraticTo( command );
}
break;
case "a": case "A":
{
ProcessArcTo( command );
}
break;
case "z": case "Z":
{
ProcessClose( command );
}
break;
}
}
void ProcessClose( SVGPathCommand command )
{
instruction.sourceCommand = command;
instruction.sourceCommandIndex = 0;
instruction.type = SVGPathInstructionType.Close;
instruction.startPoint = startPoint;
instruction.endPoint = startPoint;
currentPoint = startPoint;
DispatchInstruction();
}
void ProcessMoveTo( SVGPathCommand command )
{
var relative = command.type == "m";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 2 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
if ( i == 0 )
{
ProcessMoveToStartPoint( relative, SVGPathInstructionType.MoveTo, parameters[ i ], parameters[ i + 1 ] );
}
else
{
ProcessToEndpoint( relative, SVGPathInstructionType.LineTo, parameters[ i ], parameters[ i + 1 ] );
}
}
}
void ProcessLineTo( SVGPathCommand command )
{
var relative = command.type == "l";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 2 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
ProcessToEndpoint( relative, SVGPathInstructionType.LineTo, parameters[ i ], parameters[ i + 1 ] );
}
}
void ProcessVerticalTo( SVGPathCommand command )
{
var relative = command.type == "v";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i++ )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
var x = relative ? 0 : currentPoint.X;
var y = parameters[ i ];
ProcessToEndpoint( relative, SVGPathInstructionType.LineTo, x, y );
}
}
void ProcessHorizontalTo( SVGPathCommand command )
{
var relative = command.type == "h";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i++ )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
var x = parameters[ i ];
var y = relative ? 0 : currentPoint.Y;
ProcessToEndpoint( relative, SVGPathInstructionType.LineTo, x, y );
}
}
void ProcessArcTo( SVGPathCommand command )
{
var relative = command.type == "a";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 7 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
ProcessArc( relative,
parameters[ i ], parameters[ i + 1 ],
parameters[ i + 2 ], parameters[ i + 3 ],
parameters[ i + 4 ], parameters[ i + 5 ],
parameters[ i + 6 ]
);
}
}
void ProcessArc( bool relative, float rx, float ry, float angle, float largeArc, float sweepArc, float x, float y )
{
instruction.type = SVGPathInstructionType.ArcTo;
instruction.startPoint = currentPoint;
instruction.radius.X = rx;
instruction.radius.Y = ry;
instruction.angle = angle;
instruction.largeArcFlag = largeArc > 0;
instruction.sweepFlag = sweepArc > 0;
instruction.endPoint.X = x;
instruction.endPoint.Y = y;
if ( relative )
{
instruction.endPoint += currentPoint;
}
currentPoint = instruction.endPoint;
DispatchInstruction();
}
void ProcessCubicTo( SVGPathCommand command )
{
var relative = command.type == "c";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 6 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
ProcessCubic( relative,
parameters[ i ], parameters[ i + 1 ],
parameters[ i + 2 ], parameters[ i + 3 ],
parameters[ i + 4 ], parameters[ i + 5 ]
);
}
}
void ProcessSCubicTo( SVGPathCommand command )
{
var relative = command.type == "s";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 4 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
var cpDiff = currentPoint - lastControlPoint;
var cpNext = relative ? cpDiff : ( currentPoint + cpDiff );
ProcessCubic( relative,
cpNext.X, cpNext.Y,
parameters[ i ], parameters[ i + 1 ],
parameters[ i + 2 ], parameters[ i + 3 ]
);
}
}
void ProcessCubic( bool relative, float cx1, float cy1, float cx2, float cy2, float x, float y )
{
instruction.type = SVGPathInstructionType.CubicBezierTo;
instruction.startPoint = currentPoint;
instruction.controlPoint1.X = cx1;
instruction.controlPoint1.Y = cy1;
instruction.controlPoint2.X = cx2;
instruction.controlPoint2.Y = cy2;
instruction.endPoint.X = x;
instruction.endPoint.Y = y;
lastControlPoint = instruction.controlPoint2;
if ( relative )
{
instruction.controlPoint1 += currentPoint;
instruction.controlPoint2 += currentPoint;
instruction.endPoint += currentPoint;
}
currentPoint = instruction.endPoint;
DispatchInstruction();
}
void ProcessQuadraticTo( SVGPathCommand command )
{
var relative = command.type == "q";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 4 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
ProcessQuadratic( relative,
parameters[ i ], parameters[ i + 1 ],
parameters[ i + 2 ], parameters[ i + 3 ]
);
}
}
void ProcessTQuadraticTo( SVGPathCommand command )
{
var relative = command.type == "t";
var parameters = command.paramaters;
var sourceCommandIndex = 0;
instruction.sourceCommand = command;
for ( int i = 0; i < parameters.Count; i+= 2 )
{
instruction.sourceCommandIndex = sourceCommandIndex ++;
var cpDiff = currentPoint - lastControlPoint;
var cpNext = relative ? cpDiff : ( currentPoint + cpDiff );
ProcessQuadratic( relative,
cpNext.X, cpNext.Y,
parameters[ i ], parameters[ i + 1 ]
);
}
}
void ProcessQuadratic( bool relative, float cx1, float cy1, float x, float y )
{
instruction.type = SVGPathInstructionType.QuadraticBezierTo;
instruction.startPoint = currentPoint;
instruction.controlPoint1.X = cx1;
instruction.controlPoint1.Y = cy1;
instruction.endPoint.X = x;
instruction.endPoint.Y = y;
lastControlPoint = instruction.controlPoint1;
if ( relative )
{
instruction.controlPoint1 += currentPoint;
instruction.endPoint += currentPoint;
}
currentPoint = instruction.endPoint;
DispatchInstruction();
}
void ProcessMoveToStartPoint( bool relative, SVGPathInstructionType type, float x, float y )
{
instruction.type = type;
instruction.endPoint.X = x;
instruction.endPoint.Y = y;
if ( relative )
{
instruction.endPoint += currentPoint;
}
currentPoint = instruction.endPoint;
instruction.startPoint = currentPoint;
startPoint = currentPoint;
DispatchInstruction();
}
void ProcessToEndpoint( bool relative, SVGPathInstructionType type, float x, float y )
{
instruction.type = type;
instruction.startPoint = currentPoint;
instruction.endPoint.X = x;
instruction.endPoint.Y = y;
if ( relative )
{
instruction.endPoint += currentPoint;
}
currentPoint = instruction.endPoint;
DispatchInstruction();
}
}
}