From 6176901ceb5a299a8792cf8f8d8aefcd6df50839 Mon Sep 17 00:00:00 2001 From: Debug_pro Date: Thu, 26 Feb 2026 01:33:47 +0300 Subject: [PATCH] Create TerrainPointSunPointHumanPointText.cs --- .../TerrainPointSunPointHumanPointText.cs | 471 ++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs diff --git a/DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs b/DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs new file mode 100644 index 0000000..e2971b3 --- /dev/null +++ b/DrawFigureLibrary/TerrainPointSunPointHumanPointText.cs @@ -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); + } + } +} \ No newline at end of file