123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Text;
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Media;
- using Avalonia.Input;
- using Avalonia.Threading;
- using Avalonia.Controls.Shapes;
- namespace ControlCatalog.Pages
- {
- public class CustomDrawingExampleControl : Control
- {
- private Point _cursorPoint;
- public StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
- public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
- public StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation));
- /// <summary>
- /// Rotation, measured in Radians!
- /// </summary>
- public double Rotation
- {
- get => GetValue(RotationProperty);
- set
- {
- double valueToUse = value % (Math.PI * 2);
- SetValue(RotationProperty, valueToUse);
- }
- }
- public StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
- public double ViewportCenterY { get => GetValue(ViewportCenterYProperty); set => SetValue(ViewportCenterYProperty, value); }
- public StyledProperty<double> ViewportCenterXProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterX), 0.0d);
- public double ViewportCenterX { get => GetValue(ViewportCenterXProperty); set => SetValue(ViewportCenterXProperty, value); }
- private IPen _pen;
- private System.Diagnostics.Stopwatch _timeKeeper = System.Diagnostics.Stopwatch.StartNew();
- private bool _isPointerCaptured = false;
- public CustomDrawingExampleControl()
- {
- _pen = new Pen(new SolidColorBrush(Colors.Black), lineCap: PenLineCap.Round);
- var _arc = new ArcSegment()
- {
- IsLargeArc = false,
- Point = new Point(0, 0),
- RotationAngle = 0,
- Size = new Size(25, 25),
- SweepDirection = SweepDirection.Clockwise,
- };
- StreamGeometry sg = new StreamGeometry();
- var cntx = sg.Open();
- cntx.BeginFigure(new Point(-25.0d, -10.0d), false);
- cntx.ArcTo(new Point(25.0d, -10.0d), new Size(10.0d, 10.0d), 0.0d, false, SweepDirection.Clockwise);
- cntx.EndFigure(true);
- _smileGeometry = sg.Clone();
- }
- private Geometry _smileGeometry;
- protected override void OnPointerMoved(PointerEventArgs e)
- {
- base.OnPointerMoved(e);
- Point previousPoint = _cursorPoint;
- _cursorPoint = e.GetPosition(this);
- if (_isPointerCaptured)
- {
- Point oldWorldPoint = UIPointToWorldPoint(previousPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
- Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
- Vector diff = newWorldPoint - oldWorldPoint;
- ViewportCenterX -= diff.X;
- ViewportCenterY -= diff.Y;
- }
- }
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- e.Handled = true;
- e.Pointer.Capture(this);
- _isPointerCaptured = true;
- base.OnPointerPressed(e);
- }
- protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
- {
- base.OnPointerWheelChanged(e);
- var oldScale = Scale;
- Scale *= (1.0d + e.Delta.Y / 12.0d);
- Point oldWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, oldScale, Rotation);
- Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
- Vector diff = newWorldPoint - oldWorldPoint;
- ViewportCenterX -= diff.X;
- ViewportCenterY -= diff.Y;
- }
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
- {
- e.Pointer.Capture(null);
- _isPointerCaptured = false;
- base.OnPointerReleased(e);
- }
- public override void Render(DrawingContext context)
- {
- var localBounds = new Rect(new Size(this.Bounds.Width, this.Bounds.Height));
- var clip = context.PushClip(this.Bounds);
- context.DrawRectangle(Brushes.White, _pen, localBounds, 1.0d);
- var halfMax = Math.Max(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) * Math.Sqrt(2.0d);
- var halfMin = Math.Min(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) / 1.3d;
- var halfWidth = this.Bounds.Width / 2.0d;
- var halfHeight = this.Bounds.Height / 2.0d;
- // 0,0 refers to the top-left of the control now. It is not prime time to draw gui stuff because it'll be under the world
- var translateModifier = context.PushPreTransform(Avalonia.Matrix.CreateTranslation(new Avalonia.Vector(halfWidth, halfHeight)));
- // now 0,0 refers to the ViewportCenter(X,Y).
- var rotationMatrix = Avalonia.Matrix.CreateRotation(Rotation);
- var rotationModifier = context.PushPreTransform(rotationMatrix);
- // everything is rotated but not scaled
- var scaleModifier = context.PushPreTransform(Avalonia.Matrix.CreateScale(Scale, -Scale));
- var mapPositionModifier = context.PushPreTransform(Matrix.CreateTranslation(new Vector(-ViewportCenterX, -ViewportCenterY)));
- // now everything is rotated and scaled, and at the right position, now we're drawing strictly in world coordinates
- context.DrawEllipse(Brushes.White, _pen, new Point(0.0d, 0.0d), 50.0d, 50.0d);
- context.DrawLine(_pen, new Point(-25.0d, -5.0d), new Point(-25.0d, 15.0d));
- context.DrawLine(_pen, new Point(25.0d, -5.0d), new Point(25.0d, 15.0d));
- context.DrawGeometry(null, _pen, _smileGeometry);
- Point cursorInWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
- context.DrawEllipse(Brushes.Gray, _pen, cursorInWorldPoint, 20.0d, 20.0d);
-
- for (int i = 0; i < 10; i++)
- {
- double orbitRadius = i * 100 + 200;
- var orbitInput = ((_timeKeeper.Elapsed.TotalMilliseconds + 987654d) / orbitRadius) / 10.0d;
- if (i % 3 == 0)
- orbitInput *= -1;
- Point orbitPosition = new Point(Math.Sin(orbitInput) * orbitRadius, Math.Cos(orbitInput) * orbitRadius);
- context.DrawEllipse(Brushes.Gray, _pen, orbitPosition, 20.0d, 20.0d);
- }
- // end drawing the world
- mapPositionModifier.Dispose();
- scaleModifier.Dispose();
- rotationModifier.Dispose();
- translateModifier.Dispose();
- // this is prime time to draw gui stuff
- context.DrawLine(_pen, _cursorPoint + new Vector(-20, 0), _cursorPoint + new Vector(20, 0));
- context.DrawLine(_pen, _cursorPoint + new Vector(0, -20), _cursorPoint + new Vector(0, 20));
- clip.Dispose();
- // oh and draw again when you can, no rush, right?
- Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
- }
- private Point UIPointToWorldPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
- {
- Point workingPoint = new Point(inPoint.X, -inPoint.Y);
- workingPoint += new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
- workingPoint /= scale;
- workingPoint = Matrix.CreateRotation(rotation).Transform(workingPoint);
- workingPoint += new Vector(viewportCenterX, viewportCenterY);
- return workingPoint;
- }
- private Point WorldPointToUIPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
- {
- Point workingPoint = new Point(inPoint.X, inPoint.Y);
- workingPoint -= new Vector(viewportCenterX, viewportCenterY);
- // undo rotation
- workingPoint = Matrix.CreateRotation(-rotation).Transform(workingPoint);
- workingPoint *= scale;
- workingPoint -= new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
- workingPoint = new Point(workingPoint.X, -workingPoint.Y);
- return workingPoint;
- }
- }
- }
|