<commit_before>using Mapsui.Fetcher;
using Mapsui.Rendering.Skia;
using CoreFoundation;
using Foundation;
using UIKit;
using System;
using System.ComponentModel;
using System.Linq;
using CoreGraphics;
using Mapsui.Geometries;
using Mapsui.Geometries.Utilities;
using SkiaSharp.Views.iOS;

namespace Mapsui.UI.iOS
{
	[Register ("MapControl"), DesignTimeVisible (true)]
	public class MapControl: UIView, IMapControl
	{
		private Map _map;
		private readonly MapRenderer _renderer = new MapRenderer ();
		private readonly AttributionView _attributionPanel = new AttributionView ();
		private readonly SKGLView _canvas = new SKGLView ();
		private nuint _previousTouchCount = 0;
		private bool _viewportInitialized;
		private nfloat _previousX;
		private nfloat _previousY;
		private double _previousRadius;
		private double _previousRotation;
	    private float _skiaScale;

		public event EventHandler ViewportInitialized;

		public MapControl (CGRect frame)
			: base (frame)
		{
			Initialize ();
		}

		[Preserve]
		public MapControl (IntPtr handle) : base (handle) // used when initialized from storyboard
		{
			Initialize ();
		}

		public void Initialize ()
		{
			Map = new Map ();
			BackgroundColor = UIColor.White;

			_canvas.TranslatesAutoresizingMaskIntoConstraints = false;
			_canvas.ClipsToBounds = true;
			_canvas.MultipleTouchEnabled = true;

			_attributionPanel.TranslatesAutoresizingMaskIntoConstraints = false;

			AddSubview (_canvas);
			AddSubview (_attributionPanel);

			AddConstraints (new [] {
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Leading, 1.0f, 0.0f),
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Trailing, 1.0f, 0.0f),
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Top, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Top, 1.0f, 0.0f),
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Bottom, 1.0f, 0.0f),

				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1.0f, 0.0f),
				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1.0f, 26.0f),
				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this, NSLayoutAttribute.Right, 1.0f, 0.0f),
				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Bottom, NSLayoutRelation.LessThanOrEqual, this, NSLayoutAttribute.Bottom, 1.0f, 0.0f)
			});

			_attributionPanel.ClipsToBounds = true;
			_attributionPanel.BackgroundColor = UIColor.FromRGBA(255, 255, 255, 191);
			_attributionPanel.TintColor = UIColor.Black;
		    _attributionPanel.Layer.CornerRadius = 8.0f;
		    _attributionPanel.Layer.MasksToBounds = true;
		    _attributionPanel.LayoutIfNeeded();

            InitializeViewport ();

			ClipsToBounds = true;

			MultipleTouchEnabled = true;
			UserInteractionEnabled = true;

			_canvas.PaintSurface += OnPaintSurface;
		}

		public override void AddSubview (UIView view)
		{
			base.AddSubview (view);
			if (_attributionPanel != null && Subviews.Contains(_attributionPanel))
			{
				BringSubviewToFront (_attributionPanel);
			}
		}

		void OnPaintSurface (object sender, SKPaintGLSurfaceEventArgs skPaintSurfaceEventArgs)
		{
			if (!_viewportInitialized) InitializeViewport ();
			if (!_viewportInitialized) return;

			_map.Viewport.Width = _canvas.Frame.Width;
			_map.Viewport.Height = _canvas.Frame.Height;

			_skiaScale = (float)_canvas.ContentScaleFactor;
			skPaintSurfaceEventArgs.Surface.Canvas.Scale(_skiaScale, _skiaScale);

            _renderer.Render(skPaintSurfaceEventArgs.Surface.Canvas, _map.Viewport, _map.Layers, _map.BackColor);
		}

		private void InitializeViewport ()
		{
			if (ViewportHelper.TryInitializeViewport (_map, _canvas.Frame.Width, _canvas.Frame.Height))
			{
				_viewportInitialized = true;
				Map.ViewChanged (true);
				OnViewportInitialized ();
			}
		}

		private void OnViewportInitialized ()
		{
			ViewportInitialized?.Invoke (this, EventArgs.Empty);
		}

		public override void TouchesMoved (NSSet touches, UIEvent evt)
		{
			base.TouchesMoved (touches, evt);

			if (touches.Count == 1)
			{
				var touch = touches.AnyObject as UITouch;
				if (touch != null)
				{
					var currentPos = touch.LocationInView (this);
					var previousPos = touch.PreviousLocationInView (this);

					var cRect = new CGRect (new CGPoint ((int)currentPos.X, (int)currentPos.Y), new CGSize (5, 5));
					var pRect = new CGRect (new CGPoint ((int)previousPos.X, (int)previousPos.Y), new CGSize (5, 5));

					if (!cRect.IntersectsWith (pRect))
					{
						if (_previousTouchCount == touches.Count)
						{
							_map.Viewport.Transform (currentPos.X, currentPos.Y, previousPos.X, previousPos.Y);
							RefreshGraphics ();
						}
					}
				}
			}
			else if (touches.Count == 2)
			{
				nfloat centerX = 0;
				nfloat centerY = 0;

				var locations = touches.Select (t => ((UITouch)t).LocationInView (this)).ToList ();

				foreach (var location in locations)
				{
					centerX += location.X;
					centerY += location.Y;
				}

				centerX = centerX / touches.Count;
				centerY = centerY / touches.Count;

				var radius = Algorithms.Distance (centerX, centerY, locations [0].X, locations [0].Y);
				
				if (_previousTouchCount == touches.Count)
				{
					_map.Viewport.Transform (centerX, centerY, _previousX, _previousY, radius / _previousRadius);

				    if (AllowPinchRotation)
				    {
				        var rotation = Math.Atan2(locations[1].Y - locations[0].Y, 
                            locations[1].X - locations[0].X) * 180.0 / Math.PI;
				        _map.Viewport.Rotation += rotation - _previousRotation;
				        _previousRotation = rotation;
				    }

				    RefreshGraphics();
				}

				_previousX = centerX;
				_previousY = centerY;
				_previousRadius = radius;
			}
			_previousTouchCount = touches.Count;
		}

		public override void TouchesEnded (NSSet touches, UIEvent e)
		{
			Refresh ();
			HandleInfo (e.AllTouches);
		}

		private void HandleInfo (NSSet touches)
		{
		    var screenPosition = GetScreenPosition(touches);
		    if (screenPosition == null) return;
		    Map.InvokeInfo(screenPosition, _skiaScale, _renderer.SymbolCache);  
		}

        /// <returns>The screen position as Mapsui point. Can be null.</returns>
	    private Point GetScreenPosition(NSSet touches)
	    {
	        if (touches.Count != 1) return null;
	        var touch = touches.FirstOrDefault() as UITouch;<commit_msg>Fixing a glitch that could occur when repeating pinch zoom on iOS #243<commit_after>using Mapsui.Fetcher;
using Mapsui.Rendering.Skia;
using CoreFoundation;
using Foundation;
using UIKit;
using System;
using System.ComponentModel;
using System.Linq;
using CoreGraphics;
using Mapsui.Geometries;
using Mapsui.Geometries.Utilities;
using SkiaSharp.Views.iOS;

namespace Mapsui.UI.iOS
{
	[Register ("MapControl"), DesignTimeVisible (true)]
	public class MapControl: UIView, IMapControl
	{
		private Map _map;
		private readonly MapRenderer _renderer = new MapRenderer ();
		private readonly AttributionView _attributionPanel = new AttributionView ();
		private readonly SKGLView _canvas = new SKGLView ();
		private nuint _previousTouchCount = 0;
		private bool _viewportInitialized;
		private nfloat _previousX;
		private nfloat _previousY;
		private double _previousRadius;
		private double _previousRotation;
	    private float _skiaScale;

		public event EventHandler ViewportInitialized;

		public MapControl (CGRect frame)
			: base (frame)
		{
			Initialize ();
		}

		[Preserve]
		public MapControl (IntPtr handle) : base (handle) // used when initialized from storyboard
		{
			Initialize ();
		}

		public void Initialize ()
		{
			Map = new Map ();
			BackgroundColor = UIColor.White;

			_canvas.TranslatesAutoresizingMaskIntoConstraints = false;
			_canvas.ClipsToBounds = true;
			_canvas.MultipleTouchEnabled = true;

			_attributionPanel.TranslatesAutoresizingMaskIntoConstraints = false;

			AddSubview (_canvas);
			AddSubview (_attributionPanel);

			AddConstraints (new [] {
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Leading, 1.0f, 0.0f),
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Trailing, 1.0f, 0.0f),
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Top, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Top, 1.0f, 0.0f),
				NSLayoutConstraint.Create(this, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, _canvas, NSLayoutAttribute.Bottom, 1.0f, 0.0f),

				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1.0f, 0.0f),
				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this, NSLayoutAttribute.Top, 1.0f, 26.0f),
				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this, NSLayoutAttribute.Right, 1.0f, 0.0f),
				NSLayoutConstraint.Create(_attributionPanel, NSLayoutAttribute.Bottom, NSLayoutRelation.LessThanOrEqual, this, NSLayoutAttribute.Bottom, 1.0f, 0.0f)
			});

			_attributionPanel.ClipsToBounds = true;
			_attributionPanel.BackgroundColor = UIColor.FromRGBA(255, 255, 255, 191);
			_attributionPanel.TintColor = UIColor.Black;
		    _attributionPanel.Layer.CornerRadius = 8.0f;
		    _attributionPanel.Layer.MasksToBounds = true;
		    _attributionPanel.LayoutIfNeeded();

            InitializeViewport ();

			ClipsToBounds = true;

			MultipleTouchEnabled = true;
			UserInteractionEnabled = true;

			_canvas.PaintSurface += OnPaintSurface;
		}

		public override void AddSubview (UIView view)
		{
			base.AddSubview (view);
			if (_attributionPanel != null && Subviews.Contains(_attributionPanel))
			{
				BringSubviewToFront (_attributionPanel);
			}
		}

		void OnPaintSurface (object sender, SKPaintGLSurfaceEventArgs skPaintSurfaceEventArgs)
		{
			if (!_viewportInitialized) InitializeViewport ();
			if (!_viewportInitialized) return;

			_map.Viewport.Width = _canvas.Frame.Width;
			_map.Viewport.Height = _canvas.Frame.Height;

			_skiaScale = (float)_canvas.ContentScaleFactor;
			skPaintSurfaceEventArgs.Surface.Canvas.Scale(_skiaScale, _skiaScale);

            _renderer.Render(skPaintSurfaceEventArgs.Surface.Canvas, _map.Viewport, _map.Layers, _map.BackColor);
		}

		private void InitializeViewport ()
		{
			if (ViewportHelper.TryInitializeViewport (_map, _canvas.Frame.Width, _canvas.Frame.Height))
			{
				_viewportInitialized = true;
				Map.ViewChanged (true);
				OnViewportInitialized ();
			}
		}

		private void OnViewportInitialized ()
		{
			ViewportInitialized?.Invoke (this, EventArgs.Empty);
		}

		public override void TouchesMoved (NSSet touches, UIEvent evt)
		{
			base.TouchesMoved (touches, evt);

			if (touches.Count == 1)
			{
				var touch = touches.AnyObject as UITouch;
				if (touch != null)
				{
					var currentPos = touch.LocationInView (this);
					var previousPos = touch.PreviousLocationInView (this);

					var cRect = new CGRect (new CGPoint ((int)currentPos.X, (int)currentPos.Y), new CGSize (5, 5));
					var pRect = new CGRect (new CGPoint ((int)previousPos.X, (int)previousPos.Y), new CGSize (5, 5));

					if (!cRect.IntersectsWith (pRect))
					{
						if (_previousTouchCount == touches.Count)
						{
							_map.Viewport.Transform (currentPos.X, currentPos.Y, previousPos.X, previousPos.Y);
							RefreshGraphics ();
						}
					}
				}
			}
			else if (touches.Count == 2)
			{
				nfloat centerX = 0;
				nfloat centerY = 0;

				var locations = touches.Select (t => ((UITouch)t).LocationInView (this)).ToList ();

				foreach (var location in locations)
				{
					centerX += location.X;
					centerY += location.Y;
				}

				centerX = centerX / touches.Count;
				centerY = centerY / touches.Count;

				var radius = Algorithms.Distance (centerX, centerY, locations [0].X, locations [0].Y);
				
				if (_previousTouchCount == touches.Count)
				{
					_map.Viewport.Transform (centerX, centerY, _previousX, _previousY, radius / _previousRadius);

				    if (AllowPinchRotation)
				    {
				        var rotation = Math.Atan2(locations[1].Y - locations[0].Y, 
                            locations[1].X - locations[0].X) * 180.0 / Math.PI;
				        _map.Viewport.Rotation += rotation - _previousRotation;
				        _previousRotation = rotation;
				    }

				    RefreshGraphics();
				}

				_previousX = centerX;
				_previousY = centerY;
				_previousRadius = radius;
			}
			_previousTouchCount = touches.Count;
		}

		public override void TouchesEnded (NSSet touches, UIEvent e)
		{
			Refresh ();
			HandleInfo (e.AllTouches);
		    _previousTouchCount = 0;

		}

		private void HandleInfo (NSSet touches)
		{
		    var screenPosition = GetScreenPosition(touches);
		    if (screenPosition == null) return;
		    Map.InvokeInfo(screenPosition, _skiaScale, _renderer.SymbolCache);  
		}

        /// <returns>The screen position as Mapsui point. Can be null.</returns>
	    private Point GetScreenPosition(NSSet touches)
	    {
	        if (touches.Count != 1) return null;
	        var touch = touches.FirstOrDefault() as UITouch;
{'fraction_non_alphanumeric': 0.10072102876681781, 'fraction_numerical': 0.010406600758195198, 'mean_word_length': 3.7389926030292355, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '91727', 'n_tokens_mistral': 4979, 'n_tokens_neox': 4105, 'n_words': 911}