1
0
Files
OAiP-Presnyakov_Ilya-Labora…/DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs
2026-02-26 01:46:31 +03:00

477 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;
int refDx = (int)(deltaX / scaleX);
int refDy = (int)(deltaY / scaleY);
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 += refDx;
_humanDy += refDy;
}
}
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;
int refDx = (int)(deltaX / scaleX);
int refDy = (int)(deltaY / scaleY);
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 += refDx;
_humanDy += refDy;
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);
}
}
}