471 lines
19 KiB
C#
471 lines
19 KiB
C#
using DrawFigureLibrary.Figures;
|
|
using System.Globalization;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
|
|
namespace DrawFigureLibrary
|
|
{
|
|
public class TerrainPointSunPointHumanPointText : Figure
|
|
{
|
|
private const double ReferenceWidth = 800.0 ;
|
|
private const double ReferenceHeight = 400.0 ;
|
|
private const double SunCenterX = 90 ;
|
|
private const double SunCenterY = 70 ;
|
|
private const double SunRadius = 28 ;
|
|
private const double SunRayLength = 26 ;
|
|
private const double SunRayBase = 24 ;
|
|
private const double GreetingFontBase = 28 ;
|
|
private const double GreetingFontScale = 0.9 ;
|
|
private const double GreetingTextX = 330 ;
|
|
private const double GreetingTextY = 20 ;
|
|
private const double FieldBaseY = 400 ;
|
|
private const double FieldCenterX = 400 ;
|
|
private const double FieldRadiusX = 400 ;
|
|
private const double FieldRadiusY = 150 ;
|
|
private const double PersonCenterX = 400 ;
|
|
private const double PersonScale = 0.80 ;
|
|
private const double HeadRadius = 18 ;
|
|
private const double BodyWidth = 46 ;
|
|
private const double BodyHeight = 72 ;
|
|
private const double ArmWidth = 78 ;
|
|
private const double ArmHeight = 12 ;
|
|
private const double LegWidth = 12 ;
|
|
private const double LegHeight = 56 ;
|
|
private const double LegGap = 8 ;
|
|
private readonly int _sceneX ;
|
|
private readonly int _sceneY ;
|
|
private int _humanDx ;
|
|
private int _humanDy ;
|
|
private readonly CircleFigure _sunCore ;
|
|
private readonly Triangle _rayUp ;
|
|
private readonly Triangle _rayRight ;
|
|
private readonly Triangle _rayDown ;
|
|
private readonly Triangle _rayLeft ;
|
|
private readonly CircleFigure _head ;
|
|
private readonly RectangleFigure _body ;
|
|
private readonly RectangleFigure _leftArm ;
|
|
private readonly RectangleFigure _rightArm ;
|
|
private readonly RectangleFigure _leftLeg ;
|
|
private readonly RectangleFigure _rightLeg ;
|
|
|
|
public TerrainPointSunPointHumanPointText(string name, int x, int y, int sceneWidth, int sceneHeight)
|
|
: base(name, x, y, sceneWidth, sceneHeight)
|
|
{
|
|
if (sceneWidth <= 0 || sceneHeight <= 0) throw new ArgumentException("sceneWidth/sceneHeight должны быть > 0");
|
|
|
|
_sceneX = x;
|
|
_sceneY = y;
|
|
|
|
_sunCore = new CircleFigure ( name + "_sun", 0, 0, 1 );
|
|
_rayUp = new Triangle ( name + "_rayUp", new Point(0, 0), new Point(0, 0), new Point(0, 0) );
|
|
_rayRight = new Triangle ( name + "_rayRight", new Point(0, 0), new Point(0, 0), new Point(0, 0) );
|
|
_rayDown = new Triangle ( name + "_rayDown", new Point(0, 0), new Point(0, 0), new Point(0, 0) );
|
|
_rayLeft = new Triangle ( name + "_rayLeft", new Point(0, 0), new Point(0, 0), new Point(0, 0) );
|
|
_head = new CircleFigure ( name + "_head", 0, 0, 1 );
|
|
_body = new RectangleFigure ( name + "_body", 0, 0, 1, 1 );
|
|
_leftArm = new RectangleFigure ( name + "_armL", 0, 0, 1, 1 );
|
|
_rightArm = new RectangleFigure ( name + "_armR", 0, 0, 1, 1 );
|
|
_leftLeg = new RectangleFigure ( name + "_legL", 0, 0, 1, 1 );
|
|
_rightLeg = new RectangleFigure ( name + "_legR", 0, 0, 1, 1 );
|
|
}
|
|
|
|
public override void Draw(DrawingContext dc)
|
|
{
|
|
double scaleX = w / ReferenceWidth;
|
|
double scaleY = h / ReferenceHeight;
|
|
|
|
UpdateSunPrimitives(scaleX, scaleY);
|
|
_sunCore .Draw(dc);
|
|
_rayUp .Draw(dc);
|
|
_rayRight .Draw(dc);
|
|
_rayDown .Draw(dc);
|
|
_rayLeft .Draw(dc);
|
|
|
|
DrawGreeting(dc, scaleX, scaleY);
|
|
|
|
DrawFieldArc(dc, scaleX, scaleY);
|
|
|
|
double groundYRef =
|
|
GetTopEllipseYAtX(
|
|
PersonCenterX,
|
|
FieldCenterX,
|
|
FieldBaseY,
|
|
FieldRadiusX,
|
|
FieldRadiusY
|
|
);
|
|
|
|
UpdatePersonPrimitives(PersonCenterX, groundYRef, scaleX, scaleY, _humanDx, _humanDy);
|
|
|
|
_leftLeg .Draw(dc);
|
|
_rightLeg .Draw(dc);
|
|
_body .Draw(dc);
|
|
_leftArm .Draw(dc);
|
|
_rightArm .Draw(dc);
|
|
_head .Draw(dc);
|
|
|
|
DrawCap(
|
|
dc,
|
|
PersonCenterX + _humanDx,
|
|
GetHeadCenterYRef(PersonCenterX, groundYRef) + _humanDy,
|
|
HeadRadius * PersonScale,
|
|
scaleX,
|
|
scaleY
|
|
);
|
|
}
|
|
|
|
public override void MoveBy(int deltaX, int deltaY)
|
|
{
|
|
if (Init.DrawingImage == null) return;
|
|
|
|
double scaleX = w / ReferenceWidth;
|
|
double scaleY = h / ReferenceHeight;
|
|
|
|
var bbox = GetPersonBoundingBox(scaleX, scaleY, _humanDx + deltaX, _humanDy + deltaY);
|
|
|
|
double maxW = Init.DrawingImage.ActualWidth;
|
|
double maxH = Init.DrawingImage.ActualHeight;
|
|
|
|
if (bbox.Left >= 0 &&
|
|
bbox.Top >= 0 &&
|
|
bbox.Right <= maxW &&
|
|
bbox.Bottom <= maxH)
|
|
{
|
|
_humanDx += deltaX;
|
|
_humanDy += deltaY;
|
|
}
|
|
}
|
|
|
|
private void UpdateSunPrimitives(double scaleX, double scaleY)
|
|
{
|
|
double cx = _sceneX + SunCenterX * scaleX;
|
|
double cy = _sceneY + SunCenterY * scaleY;
|
|
double rX = SunRadius * scaleX;
|
|
double rY = SunRadius * scaleY;
|
|
|
|
_sunCore.x = (int)Math.Round(cx - rX);
|
|
_sunCore.y = (int)Math.Round(cy - rY);
|
|
_sunCore.w = (int)Math.Round(2 * rX);
|
|
_sunCore.h = (int)Math.Round(2 * rY);
|
|
|
|
SetTrianglePoints(_rayUp,
|
|
ToScenePoint( SunCenterX, SunCenterY - SunRadius - SunRayLength, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX - SunRayBase / 2, SunCenterY - SunRadius, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX + SunRayBase / 2, SunCenterY - SunRadius, scaleX, scaleY )
|
|
);
|
|
|
|
SetTrianglePoints(_rayRight,
|
|
ToScenePoint( SunCenterX + SunRadius + SunRayLength, SunCenterY, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX + SunRadius, SunCenterY - SunRayBase / 2, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX + SunRadius, SunCenterY + SunRayBase / 2, scaleX, scaleY )
|
|
);
|
|
|
|
SetTrianglePoints(_rayDown,
|
|
ToScenePoint( SunCenterX, SunCenterY + SunRadius + SunRayLength, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX - SunRayBase / 2, SunCenterY + SunRadius, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX + SunRayBase / 2, SunCenterY + SunRadius, scaleX, scaleY )
|
|
);
|
|
|
|
SetTrianglePoints(_rayLeft,
|
|
ToScenePoint( SunCenterX - SunRadius - SunRayLength, SunCenterY, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX - SunRadius, SunCenterY - SunRayBase / 2, scaleX, scaleY ),
|
|
ToScenePoint( SunCenterX - SunRadius, SunCenterY + SunRayBase / 2, scaleX, scaleY )
|
|
);
|
|
}
|
|
|
|
private void UpdatePersonPrimitives(double centerXRef, double groundYRef, double scaleX, double scaleY, int dx, int dy)
|
|
{
|
|
double k = PersonScale;
|
|
|
|
double headRadius = HeadRadius * k;
|
|
|
|
double bodyWidth = BodyWidth * k;
|
|
double bodyHeight = BodyHeight * k;
|
|
|
|
double armWidth = ArmWidth * k;
|
|
double armHeight = ArmHeight * k;
|
|
|
|
double legWidth = LegWidth * k;
|
|
double legHeight = LegHeight * k;
|
|
double legGap = LegGap * k;
|
|
|
|
double gapFromGround = 4 / k;
|
|
double feetY = groundYRef - gapFromGround;
|
|
|
|
double legsTopY = feetY - legHeight;
|
|
|
|
SetRect(_leftLeg,
|
|
centerXRef - legGap / 2 - legWidth + dx,
|
|
legsTopY + dy,
|
|
legWidth,
|
|
legHeight,
|
|
scaleX, scaleY
|
|
);
|
|
|
|
SetRect(_rightLeg,
|
|
centerXRef + legGap / 2 + dx,
|
|
legsTopY + dy,
|
|
legWidth,
|
|
legHeight,
|
|
scaleX, scaleY
|
|
);
|
|
|
|
double bodyX = centerXRef - bodyWidth / 2;
|
|
double bodyY = legsTopY - bodyHeight;
|
|
|
|
SetRect(_body,
|
|
bodyX + dx,
|
|
bodyY + dy,
|
|
bodyWidth,
|
|
bodyHeight,
|
|
scaleX, scaleY
|
|
);
|
|
|
|
double armY = bodyY + bodyHeight * 0.30;
|
|
|
|
SetRect(_leftArm,
|
|
bodyX - armWidth + dx,
|
|
armY + dy,
|
|
armWidth,
|
|
armHeight,
|
|
scaleX, scaleY
|
|
);
|
|
|
|
SetRect(_rightArm,
|
|
bodyX + bodyWidth + dx,
|
|
armY + dy,
|
|
armWidth,
|
|
armHeight,
|
|
scaleX, scaleY
|
|
);
|
|
|
|
double headCenterY = bodyY - headRadius * 1.05;
|
|
|
|
SetCircle(_head,
|
|
centerXRef + dx,
|
|
headCenterY + dy,
|
|
headRadius,
|
|
scaleX, scaleY
|
|
);
|
|
}
|
|
|
|
private void DrawGreeting(DrawingContext dc, double scaleX, double scaleY)
|
|
{
|
|
string text = GetGreetingByTime();
|
|
|
|
double pixelsPerDip = 1.0;
|
|
try
|
|
{
|
|
if (Application.Current?.MainWindow != null) pixelsPerDip = VisualTreeHelper.GetDpi(Application.Current.MainWindow).PixelsPerDip;
|
|
}
|
|
catch { }
|
|
|
|
var formattedText = new FormattedText(
|
|
text,
|
|
CultureInfo.GetCultureInfo("ru-RU"),
|
|
FlowDirection.LeftToRight,
|
|
new Typeface("Segoe UI"),
|
|
(GreetingFontBase * GreetingFontScale) * scaleY,
|
|
Brushes.Black,
|
|
pixelsPerDip
|
|
);
|
|
|
|
dc.DrawText(formattedText, ToScenePoint(GreetingTextX, GreetingTextY, scaleX, scaleY));
|
|
}
|
|
|
|
private void DrawFieldArc(DrawingContext dc, double scaleX, double scaleY)
|
|
{
|
|
DrawTopHalfEllipseArc(dc, FieldCenterX, FieldBaseY, FieldRadiusX, FieldRadiusY, scaleX, scaleY);
|
|
}
|
|
|
|
private void DrawCap(DrawingContext dc, double headCenterXRef, double headCenterYRef, double headRadiusRef, double scaleX, double scaleY)
|
|
{
|
|
double capBaseY = headCenterYRef - headRadiusRef;
|
|
|
|
double capRadiusX = headRadiusRef * 1.10;
|
|
double capRadiusY = headRadiusRef * 0.85;
|
|
|
|
double leftX = headCenterXRef - capRadiusX;
|
|
double rightX = headCenterXRef + capRadiusX;
|
|
|
|
var geometry = new StreamGeometry();
|
|
using (var ctx = geometry.Open())
|
|
{
|
|
ctx.BeginFigure(ToScenePoint(leftX, capBaseY, scaleX, scaleY), isFilled: false, isClosed: false);
|
|
ctx.ArcTo(
|
|
ToScenePoint(rightX, capBaseY, scaleX, scaleY),
|
|
new Size(capRadiusX * scaleX, capRadiusY * scaleY),
|
|
rotationAngle: 0,
|
|
isLargeArc: false,
|
|
sweepDirection: SweepDirection.Clockwise,
|
|
isStroked: true,
|
|
isSmoothJoin: false
|
|
);
|
|
}
|
|
|
|
dc.DrawGeometry(null, Init.pen, geometry);
|
|
|
|
double brimLength = headRadiusRef * 3;
|
|
double brimStartX = headCenterXRef - capRadiusX;
|
|
|
|
dc.DrawLine(Init.pen,
|
|
ToScenePoint(brimStartX, capBaseY, scaleX, scaleY),
|
|
ToScenePoint(brimStartX + brimLength, capBaseY + headRadiusRef * 0.05, scaleX, scaleY)
|
|
);
|
|
}
|
|
|
|
|
|
private Rect GetPersonBoundingBox(double scaleX, double scaleY, int dx, int dy)
|
|
{
|
|
double k = PersonScale;
|
|
|
|
double headRadius = HeadRadius * k;
|
|
double bodyWidth = BodyWidth * k;
|
|
double bodyHeight = BodyHeight * k;
|
|
double armWidth = ArmWidth * k;
|
|
double legWidth = LegWidth * k;
|
|
double legHeight = LegHeight * k;
|
|
double legGap = LegGap * k;
|
|
|
|
double groundYRef = GetTopEllipseYAtX(PersonCenterX, FieldCenterX, FieldBaseY, FieldRadiusX, FieldRadiusY);
|
|
|
|
double gapFromGround = 4 / k;
|
|
double feetY = groundYRef - gapFromGround;
|
|
|
|
double legsTopY = feetY - legHeight;
|
|
double bodyY = legsTopY - bodyHeight;
|
|
|
|
double headCenterY = bodyY - headRadius * 1.05;
|
|
double capTopY = (headCenterY - headRadius) - (headRadius * 0.85);
|
|
|
|
double left = PersonCenterX - (bodyWidth / 2 + armWidth);
|
|
double right = PersonCenterX + (bodyWidth / 2 + armWidth);
|
|
double top = capTopY;
|
|
double bottom = feetY;
|
|
|
|
Point tl = ToScenePoint(left + dx, top + dy, scaleX, scaleY);
|
|
Point br = ToScenePoint(right + dx, bottom + dy, scaleX, scaleY);
|
|
|
|
return new Rect(tl, br);
|
|
}
|
|
|
|
private static double GetHeadCenterYRef(double centerXRef, double groundYRef)
|
|
{
|
|
double k = PersonScale;
|
|
double headRadius = HeadRadius * k;
|
|
double bodyHeight = BodyHeight * k;
|
|
double legHeight = LegHeight * k;
|
|
|
|
double gapFromGround = 4 / k;
|
|
double feetY = groundYRef - gapFromGround;
|
|
double legsTopY = feetY - legHeight;
|
|
double bodyY = legsTopY - bodyHeight;
|
|
|
|
return bodyY - headRadius * 1.05;
|
|
}
|
|
|
|
private void DrawTopHalfEllipseArc(DrawingContext dc, double centerX, double centerY, double radiusX, double radiusY, double scaleX, double scaleY)
|
|
{
|
|
Point start = ToScenePoint(centerX - radiusX, centerY, scaleX, scaleY);
|
|
Point end = ToScenePoint(centerX + radiusX, centerY, scaleX, scaleY);
|
|
|
|
var geometry = new StreamGeometry();
|
|
using (var ctx = geometry.Open())
|
|
{
|
|
ctx.BeginFigure(start, isFilled: false, isClosed: false);
|
|
ctx.ArcTo(
|
|
end,
|
|
new Size(radiusX * scaleX, radiusY * scaleY),
|
|
rotationAngle: 0,
|
|
isLargeArc: false,
|
|
sweepDirection: SweepDirection.Clockwise,
|
|
isStroked: true,
|
|
isSmoothJoin: false
|
|
);
|
|
}
|
|
|
|
dc.DrawGeometry(null, Init.pen, geometry);
|
|
}
|
|
|
|
private static double GetTopEllipseYAtX(double x, double centerX, double centerY, double radiusX, double radiusY)
|
|
{
|
|
double dx = (x - centerX) / radiusX;
|
|
double inside = 1.0 - dx * dx;
|
|
if (inside < 0) inside = 0;
|
|
return centerY - radiusY * Math.Sqrt(inside);
|
|
}
|
|
|
|
private Point ToScenePoint(double localX, double localY, double scaleX, double scaleY)
|
|
=> new Point(_sceneX + localX * scaleX, _sceneY + localY * scaleY);
|
|
|
|
private void SetRect(RectangleFigure rect, double localX, double localY, double localW, double localH, double scaleX, double scaleY)
|
|
{
|
|
rect.x = (int)Math.Round(_sceneX + localX * scaleX);
|
|
rect.y = (int)Math.Round(_sceneY + localY * scaleY);
|
|
rect.w = (int)Math.Round(localW * scaleX);
|
|
rect.h = (int)Math.Round(localH * scaleY);
|
|
}
|
|
|
|
private void SetCircle(CircleFigure circle, double centerXRef, double centerYRef, double radiusRef, double scaleX, double scaleY)
|
|
{
|
|
double cx = _sceneX + centerXRef * scaleX;
|
|
double cy = _sceneY + centerYRef * scaleY;
|
|
double rx = radiusRef * scaleX;
|
|
double ry = radiusRef * scaleY;
|
|
|
|
circle.x = (int)Math.Round(cx - rx);
|
|
circle.y = (int)Math.Round(cy - ry);
|
|
circle.w = (int)Math.Round(2 * rx);
|
|
circle.h = (int)Math.Round(2 * ry);
|
|
}
|
|
|
|
private static void SetTrianglePoints(Triangle t, Point a, Point b, Point c)
|
|
{
|
|
t.Points[0] = a;
|
|
t.Points[1] = b;
|
|
t.Points[2] = c;
|
|
}
|
|
|
|
private static string GetGreetingByTime()
|
|
{
|
|
int hour = DateTime.Now.Hour;
|
|
if (hour >= 5 && hour <= 11) return "Доброе утро!";
|
|
if (hour >= 12 && hour <= 17) return "Добрый день!";
|
|
return "Добрый вечер!";
|
|
}
|
|
|
|
public override bool MoveByChecked(int deltaX, int deltaY)
|
|
{
|
|
if (Init.DrawingImage == null) return false;
|
|
|
|
double scaleX = w / ReferenceWidth;
|
|
double scaleY = h / ReferenceHeight;
|
|
|
|
var bbox = GetPersonBoundingBox(scaleX, scaleY, _humanDx + deltaX, _humanDy + deltaY);
|
|
|
|
double maxW = Init.DrawingImage.ActualWidth;
|
|
double maxH = Init.DrawingImage.ActualHeight;
|
|
|
|
if (bbox.Left >= 0 &&
|
|
bbox.Top >= 0 &&
|
|
bbox.Right <= maxW &&
|
|
bbox.Bottom <= maxH)
|
|
{
|
|
_humanDx += deltaX;
|
|
_humanDy += deltaY;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override bool HitTest(Point p)
|
|
{
|
|
double scaleX = w / ReferenceWidth;
|
|
double scaleY = h / ReferenceHeight;
|
|
|
|
Rect bbox = GetPersonBoundingBox(scaleX, scaleY, _humanDx, _humanDy);
|
|
|
|
return bbox.Contains(p);
|
|
}
|
|
}
|
|
} |