зеркало из https://github.com/wieslawsoltes/SVG.git
204 строки
9.1 KiB
C#
204 строки
9.1 KiB
C#
#if !NO_SDC
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Linq;
|
|
using Svg.DataTypes;
|
|
|
|
namespace Svg
|
|
{
|
|
public partial class SvgMarker : SvgPathBasedElement, ISvgViewPort
|
|
{
|
|
public override GraphicsPath Path(ISvgRenderer renderer)
|
|
{
|
|
if (MarkerElement != null)
|
|
return MarkerElement.Path(renderer);
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Render this marker using the slope of the given line segment
|
|
/// </summary>
|
|
/// <param name="pRenderer"></param>
|
|
/// <param name="pOwner"></param>
|
|
/// <param name="pRefPoint"></param>
|
|
/// <param name="pMarkerPoint1"></param>
|
|
/// <param name="pMarkerPoint2"></param>
|
|
/// <param name="isStartMarker"></param>
|
|
public void RenderMarker(ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, bool isStartMarker)
|
|
{
|
|
float fAngle1 = 0f;
|
|
if (Orient.IsAuto)
|
|
{
|
|
// Only calculate this if needed.
|
|
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
|
|
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
|
|
fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
|
|
|
|
if (isStartMarker && Orient.IsAutoStartReverse)
|
|
{
|
|
fAngle1 += 180;
|
|
}
|
|
}
|
|
|
|
RenderPart2(fAngle1, pRenderer, pOwner, pRefPoint);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Render this marker using the average of the slopes of the two given line segments
|
|
/// </summary>
|
|
/// <param name="pRenderer"></param>
|
|
/// <param name="pOwner"></param>
|
|
/// <param name="pRefPoint"></param>
|
|
/// <param name="pMarkerPoint1"></param>
|
|
/// <param name="pMarkerPoint2"></param>
|
|
/// <param name="pMarkerPoint3"></param>
|
|
public void RenderMarker(ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pRefPoint, PointF pMarkerPoint1, PointF pMarkerPoint2, PointF pMarkerPoint3)
|
|
{
|
|
float xDiff = pMarkerPoint2.X - pMarkerPoint1.X;
|
|
float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y;
|
|
float fAngle1 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
|
|
|
|
xDiff = pMarkerPoint3.X - pMarkerPoint2.X;
|
|
yDiff = pMarkerPoint3.Y - pMarkerPoint2.Y;
|
|
float fAngle2 = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI);
|
|
|
|
RenderPart2((fAngle1 + fAngle2) / 2, pRenderer, pOwner, pRefPoint);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Common code for rendering a marker once the orientation angle has been calculated
|
|
/// </summary>
|
|
/// <param name="fAngle"></param>
|
|
/// <param name="pRenderer"></param>
|
|
/// <param name="pOwner"></param>
|
|
/// <param name="pMarkerPoint"></param>
|
|
private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pMarkerPoint)
|
|
{
|
|
using (var pRenderPen = CreatePen(pOwner, pRenderer))
|
|
{
|
|
using (var markerPath = GetClone(pOwner, pRenderer))
|
|
{
|
|
using (var transMatrix = new Matrix())
|
|
{
|
|
transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y);
|
|
if (Orient.IsAuto)
|
|
transMatrix.Rotate(fAngle);
|
|
else
|
|
transMatrix.Rotate(Orient.Angle);
|
|
switch (MarkerUnits)
|
|
{
|
|
case SvgMarkerUnits.StrokeWidth:
|
|
if (ViewBox.Width > 0 && ViewBox.Height > 0)
|
|
{
|
|
transMatrix.Scale(MarkerWidth, MarkerHeight);
|
|
var strokeWidth = pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this);
|
|
transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) *
|
|
strokeWidth),
|
|
AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) *
|
|
strokeWidth));
|
|
}
|
|
else
|
|
{
|
|
// SvgMarkerUnits.UserSpaceOnUse
|
|
// TODO: We know this isn't correct.
|
|
// But use this until the TODOs from AdjustForViewBoxWidth and AdjustForViewBoxHeight are done.
|
|
// MORE see Unit Test "MakerEndTest.TestArrowCodeCreation()"
|
|
transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
|
|
-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
|
|
}
|
|
break;
|
|
case SvgMarkerUnits.UserSpaceOnUse:
|
|
transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
|
|
-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
|
|
break;
|
|
}
|
|
|
|
if (MarkerElement != null && MarkerElement.Transforms != null)
|
|
using (var matrix = MarkerElement.Transforms.GetMatrix())
|
|
transMatrix.Multiply(matrix);
|
|
markerPath.Transform(transMatrix);
|
|
if (pRenderPen != null) pRenderer.DrawPath(pRenderPen, markerPath);
|
|
|
|
SvgPaintServer pFill = this.Children.First().Fill;
|
|
SvgFillRule pFillRule = FillRule; // TODO: What do we use the fill rule for?
|
|
|
|
if (pFill != null)
|
|
{
|
|
using (var pBrush = pFill.GetBrush(this, pRenderer, FixOpacityValue(FillOpacity)))
|
|
{
|
|
pRenderer.FillPath(pBrush, markerPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a pen that can be used to render this marker
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private Pen CreatePen(SvgVisualElement pPath, ISvgRenderer renderer)
|
|
{
|
|
if (this.Stroke == null) return null;
|
|
Brush pBrush = this.Stroke.GetBrush(this, renderer, FixOpacityValue(Opacity));
|
|
switch (MarkerUnits)
|
|
{
|
|
case SvgMarkerUnits.StrokeWidth:
|
|
// TODO: have to multiply with marker stroke width if it is not inherted from the
|
|
// same ancestor as owner path stroke width
|
|
return (new Pen(pBrush, pPath.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
|
|
case SvgMarkerUnits.UserSpaceOnUse:
|
|
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
|
|
}
|
|
return (new Pen(pBrush, StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, this)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a clone of the current path, scaled for the stroke width
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private GraphicsPath GetClone(SvgVisualElement pPath, ISvgRenderer renderer)
|
|
{
|
|
GraphicsPath pRet = Path(renderer).Clone() as GraphicsPath;
|
|
switch (MarkerUnits)
|
|
{
|
|
case SvgMarkerUnits.StrokeWidth:
|
|
using (var transMatrix = new Matrix())
|
|
{
|
|
transMatrix.Scale(AdjustForViewBoxWidth(pPath.StrokeWidth), AdjustForViewBoxHeight(pPath.StrokeWidth));
|
|
pRet.Transform(transMatrix);
|
|
}
|
|
break;
|
|
case SvgMarkerUnits.UserSpaceOnUse:
|
|
break;
|
|
}
|
|
return (pRet);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adjust the given value to account for the width of the viewbox in the viewport
|
|
/// </summary>
|
|
/// <param name="fWidth"></param>
|
|
/// <returns></returns>
|
|
private float AdjustForViewBoxWidth(float fWidth)
|
|
{
|
|
// TODO: We know this isn't correct
|
|
return (ViewBox.Width <= 0 ? 1 : fWidth / ViewBox.Width);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adjust the given value to account for the height of the viewbox in the viewport
|
|
/// </summary>
|
|
/// <param name="fHeight"></param>
|
|
/// <returns></returns>
|
|
private float AdjustForViewBoxHeight(float fHeight)
|
|
{
|
|
// TODO: We know this isn't correct
|
|
return (ViewBox.Height <= 0 ? 1 : fHeight / ViewBox.Height);
|
|
}
|
|
}
|
|
}
|
|
#endif
|