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); } } }