Create TerrainPointSunPointHumanPointText.cs
This commit is contained in:
471
DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs
Normal file
471
DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs
Normal file
@@ -0,0 +1,471 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user