public void AdvanceWithSample (StrokeSample incomingSample)
		{
			var sampleAfter = SampleAfter;
			if (sampleAfter != null) {
				SampleBefore = FromSample;
				FromSample = ToSample;
				ToSample = SampleAfter;
				SampleAfter = incomingSample;
				FromSampleIndex += 1;
			}
		}
示例#2
0
        public void AdvanceWithSample(StrokeSample incomingSample)
        {
            var sampleAfter = SampleAfter;

            if (sampleAfter != null)
            {
                SampleBefore     = FromSample;
                FromSample       = ToSample;
                ToSample         = SampleAfter;
                SampleAfter      = incomingSample;
                FromSampleIndex += 1;
            }
        }
示例#3
0
        public void Update(StrokeSample sample, int index)
        {
            if (index == 0)
            {
                hasUpdatesFromStartTo = 0;
            }
            else if (hasUpdatesFromStartTo.HasValue && index == hasUpdatesFromStartTo.Value + 1)
            {
                hasUpdatesFromStartTo = index;
            }
            else if (!hasUpdatesAtEndFrom.HasValue || hasUpdatesAtEndFrom.Value > index)
            {
                hasUpdatesAtEndFrom = index;
            }

            Samples [index] = sample;

            sampleIndicesExpectingUpdates.Remove(index);
            if (sampleIndicesExpectingUpdates.Count == 0)
            {
                ReceivedAllNeededUpdatesBlock?.Invoke();
                ReceivedAllNeededUpdatesBlock = null;
            }
        }
示例#4
0
        public int Add(StrokeSample sample)
        {
            var resultIndex = Samples.Count;

            if (!hasUpdatesAtEndFrom.HasValue)
            {
                hasUpdatesAtEndFrom = resultIndex;
            }

            Samples.Add(sample);

            if (PreviousPredictedSamples.Count == 0)
            {
                PreviousPredictedSamples.AddRange(PredictedSamples);
            }

            if (sample.EstimatedPropertiesExpectingUpdates != 0)
            {
                sampleIndicesExpectingUpdates.Add(resultIndex);
            }

            PredictedSamples.Clear();
            return(resultIndex);
        }
		void Collect (Stroke stroke, UITouch touch, UIView view, bool coalesced, bool predicted)
		{
			if (view == null)
				throw new ArgumentNullException ();

			// Only collect samples that actually moved in 2D space.
			var location = touch.GetPreciseLocation (view);
			var previousSample = stroke.Samples.LastOrDefault ();
			if (Distance (previousSample?.Location, location) < 0.003)
				return;

			var sample = new StrokeSample {
				Timestamp = touch.Timestamp,
				Location = location,
				Coalesced = coalesced,
				Predicted = predicted
			};
			bool collectForce = touch.Type == UITouchType.Stylus || view.TraitCollection.ForceTouchCapability == UIForceTouchCapability.Available;
			if (collectForce)
				sample.Force = touch.Force;

			if (touch.Type == UITouchType.Stylus) {
				var estimatedProperties = touch.EstimatedProperties;
				sample.EstimatedProperties = estimatedProperties;
				sample.EstimatedPropertiesExpectingUpdates = touch.EstimatedPropertiesExpectingUpdates;
				sample.Altitude = touch.AltitudeAngle;
				sample.Azimuth = touch.GetAzimuthAngle (view);

				if (stroke.Samples.Count == 0 && estimatedProperties.HasFlag (UITouchProperties.Azimuth)) {
					stroke.ExpectsAltitudeAzimuthBackfill = true;
				} else if (stroke.ExpectsAltitudeAzimuthBackfill &&
						   !estimatedProperties.HasFlag (UITouchProperties.Azimuth)) {
					for (int index = 0; index < stroke.Samples.Count; index++) {
						var priorSample = stroke.Samples [index];
						var updatedSample = priorSample;

						if (updatedSample.EstimatedProperties.HasFlag (UITouchProperties.Altitude)) {
							updatedSample.EstimatedProperties &= ~UITouchProperties.Altitude;
							updatedSample.Altitude = sample.Altitude;
						}
						if (updatedSample.EstimatedProperties.HasFlag (UITouchProperties.Azimuth)) {
							updatedSample.EstimatedProperties &= ~UITouchProperties.Azimuth;
							updatedSample.Azimuth = sample.Azimuth;
						}
						stroke.Update (updatedSample, index);
					}
					stroke.ExpectsAltitudeAzimuthBackfill = false;
				}
			}

			if (predicted) {
				stroke.AddPredicted (sample);
			} else {
				var index = stroke.Add (sample);
				if (touch.EstimatedPropertiesExpectingUpdates != 0) {
					outstandingUpdateIndexes [touch.EstimationUpdateIndex] = new StrokeIndex {
						Stroke = stroke,
						Index = index
					};
				}
			}
		}
示例#6
0
 public void AddPredicted(StrokeSample sample)
 {
     PredictedSamples.Add(sample);
 }
        void Collect(Stroke stroke, UITouch touch, UIView view, bool coalesced, bool predicted)
        {
            if (view == null)
            {
                throw new ArgumentNullException();
            }

            // Only collect samples that actually moved in 2D space.
            var location       = touch.GetPreciseLocation(view);
            var previousSample = stroke.Samples.LastOrDefault();

            if (Distance(previousSample?.Location, location) < 0.003)
            {
                return;
            }

            var sample = new StrokeSample {
                Timestamp = touch.Timestamp,
                Location  = location,
                Coalesced = coalesced,
                Predicted = predicted
            };
            bool collectForce = touch.Type == UITouchType.Stylus || view.TraitCollection.ForceTouchCapability == UIForceTouchCapability.Available;

            if (collectForce)
            {
                sample.Force = touch.Force;
            }

            if (touch.Type == UITouchType.Stylus)
            {
                var estimatedProperties = touch.EstimatedProperties;
                sample.EstimatedProperties = estimatedProperties;
                sample.EstimatedPropertiesExpectingUpdates = touch.EstimatedPropertiesExpectingUpdates;
                sample.Altitude = touch.AltitudeAngle;
                sample.Azimuth  = touch.GetAzimuthAngle(view);

                if (stroke.Samples.Count == 0 && estimatedProperties.HasFlag(UITouchProperties.Azimuth))
                {
                    stroke.ExpectsAltitudeAzimuthBackfill = true;
                }
                else if (stroke.ExpectsAltitudeAzimuthBackfill &&
                         !estimatedProperties.HasFlag(UITouchProperties.Azimuth))
                {
                    for (int index = 0; index < stroke.Samples.Count; index++)
                    {
                        var priorSample   = stroke.Samples [index];
                        var updatedSample = priorSample;

                        if (updatedSample.EstimatedProperties.HasFlag(UITouchProperties.Altitude))
                        {
                            updatedSample.EstimatedProperties &= ~UITouchProperties.Altitude;
                            updatedSample.Altitude             = sample.Altitude;
                        }
                        if (updatedSample.EstimatedProperties.HasFlag(UITouchProperties.Azimuth))
                        {
                            updatedSample.EstimatedProperties &= ~UITouchProperties.Azimuth;
                            updatedSample.Azimuth              = sample.Azimuth;
                        }
                        stroke.Update(updatedSample, index);
                    }
                    stroke.ExpectsAltitudeAzimuthBackfill = false;
                }
            }

            if (predicted)
            {
                stroke.AddPredicted(sample);
            }
            else
            {
                var index = stroke.Add(sample);
                if (touch.EstimatedPropertiesExpectingUpdates != 0)
                {
                    outstandingUpdateIndexes [touch.EstimationUpdateIndex] = new StrokeIndex {
                        Stroke = stroke,
                        Index  = index
                    };
                }
            }
        }
		// Note: this is not a particularily efficient way to draw a great stroke path
		// with CoreGraphics.It is just a way to produce an interesting looking result.
		// For a real world example you would reuse and cache CGPaths and draw longer
		// paths instead of an aweful lot of tiny ones, etc.You would also respect the
		// draw rect to cull your draw requests.And you would use bezier paths to
		// interpolate between the points to get a smooother curve.
		void Draw (Stroke stroke)
		{
			var updateRanges = stroke.UpdatedRanges ();
			if (DisplayOptions == StrokeViewDisplayOptions.Debug) {
				for (int index = 0; index < DirtyRectViews.Count; index++) {
					var dirtyRectView = DirtyRectViews [index];
					dirtyRectView.Alpha = 0;
					if (index < updateRanges.Length) {
						dirtyRectView.Alpha = 1;
						var range = updateRanges [index];
						var strokes = stroke.Samples.Skip (range.LowerBound)
											.Take (range.UpperBound - range.LowerBound + 1);
						dirtyRectView.Frame = DirtyRectForSampleStride (strokes);
					}
				}
			}

			lastEstimatedSample = null;
			stroke.ClearUpdateInfo ();
			var sampleCount = stroke.Samples.Count;
			if (sampleCount <= 0)
				return;
			var context = UIGraphics.GetCurrentContext ();
			if (context == null)
				return;
			var strokeColor = UIColor.Black;

			Action lineSettings;
			Action forceEstimatedLineSettings;
			if (displayOptions == StrokeViewDisplayOptions.Debug) {
				lineSettings = () => {
					context.SetLineWidth (0.5f);
					context.SetStrokeColor (UIColor.White.CGColor);
				};
				forceEstimatedLineSettings = () => {
					context.SetLineWidth (0.5f);
					context.SetStrokeColor (UIColor.Blue.CGColor);
				};
			} else {
				lineSettings = () => {
					context.SetLineWidth (0.25f);
					context.SetStrokeColor (strokeColor.CGColor);
				};
				forceEstimatedLineSettings = lineSettings;
			}

			Action azimuthSettings = () => {
				context.SetLineWidth (1.5f);
				context.SetStrokeColor (UIColor.Orange.CGColor);
			};

			Action altitudeSettings = () => {
				context.SetLineWidth (0.5f);
				context.SetStrokeColor (strokeColor.CGColor);
			};
			var forceMultiplier = 2f;
			var forceOffset = 0.1f;

			var fillColorRegular = UIColor.Black.CGColor;
			var fillColorCoalesced = UIColor.LightGray.CGColor;
			var fillColorPredicted = UIColor.Red.CGColor;

			CGVector? lockedAzimuthUnitVector = null;
			var azimuthLockAltitudeThreshold = NMath.PI / 2 * 0.8f; // locking azimuth at 80% altitude

			lineSettings ();

			Func<StrokeSample, nfloat> forceAccessBlock = sample => {
				return sample.ForceWithDefault ();
			};

			if (DisplayOptions == StrokeViewDisplayOptions.Ink) {
				forceAccessBlock = sample => {
					return sample.PerpendicularForce ();
				};
			}

			// Make the force influence less pronounced for the calligraphy pen.
			if (DisplayOptions == StrokeViewDisplayOptions.Calligraphy) {
				var prevGetter = forceAccessBlock;
				forceAccessBlock = sample => {
					return NMath.Max (prevGetter (sample), 1);
				};
				// make force value less pronounced
				forceMultiplier = 1;
				forceOffset = 10;
			}

			var previousGetter = forceAccessBlock;
			forceAccessBlock = sample => {
				return previousGetter (sample) * forceMultiplier + forceOffset;
			};

			StrokeSample heldFromSample = null;
			CGVector? heldFromSampleUnitVector = null;

			Action<StrokeSegment> draw = segment => {
				var toSample = segment.ToSample;
				if (toSample != null) {
					StrokeSample fromSample = heldFromSample ?? segment.FromSample;

					// Skip line segments that are too short.
					var dist = Vector (fromSample.Location, toSample.Location).Quadrance ();
					if (dist < 0.003f) {
						if (heldFromSample == null) {
							heldFromSample = fromSample;
							heldFromSampleUnitVector = segment.FromSampleUnitNormal;
						}
						return;
					}

					if (toSample.Predicted) {
						if (displayOptions == StrokeViewDisplayOptions.Debug)
							context.SetFillColor (fillColorPredicted);
					} else {
						bool coalesced = displayOptions == StrokeViewDisplayOptions.Debug && fromSample.Coalesced;
						context.SetFillColor (coalesced ? fillColorCoalesced : fillColorRegular);
					}

					if (displayOptions == StrokeViewDisplayOptions.Calligraphy) {
						var fromAzimuthUnitVector = Stroke.CalligraphyFallbackAzimuthUnitVector;
						var toAzimuthUnitVector = Stroke.CalligraphyFallbackAzimuthUnitVector;

						if (fromSample.Azimuth.HasValue) {
							if (!lockedAzimuthUnitVector.HasValue)
								lockedAzimuthUnitVector = fromSample.GetAzimuthUnitVector ();
							fromAzimuthUnitVector = fromSample.GetAzimuthUnitVector ();
							toAzimuthUnitVector = toSample.GetAzimuthUnitVector ();

							if (fromSample.Altitude.Value > azimuthLockAltitudeThreshold)
								fromAzimuthUnitVector = lockedAzimuthUnitVector.Value;

							if (toSample.Altitude.Value > azimuthLockAltitudeThreshold)
								toAzimuthUnitVector = lockedAzimuthUnitVector.Value;
							else
								lockedAzimuthUnitVector = toAzimuthUnitVector;
						}

						// Rotate 90 degrees
						var calligraphyTransform = CGAffineTransform.MakeRotation (NMath.PI / 2);
						fromAzimuthUnitVector = fromAzimuthUnitVector.Apply (calligraphyTransform);
						toAzimuthUnitVector = toAzimuthUnitVector.Apply (calligraphyTransform);

						var fromUnitVector = fromAzimuthUnitVector.Mult (forceAccessBlock (fromSample));
						var toUnitVector = toAzimuthUnitVector.Mult (forceAccessBlock (toSample));

						context.BeginPath ();
						context.Move (fromSample.Location.Add (fromUnitVector));
						context.AddLine (toSample.Location.Add (toUnitVector));
						context.AddLine (toSample.Location.Sub (toUnitVector));
						context.AddLine (fromSample.Location.Sub (fromUnitVector));
						context.ClosePath ();

						context.DrawPath (CGPathDrawingMode.FillStroke);
					} else {
						var fromUnitVector = (heldFromSampleUnitVector.HasValue ? heldFromSampleUnitVector.Value : segment.FromSampleUnitNormal).Mult (forceAccessBlock (fromSample));

						var toUnitVector = segment.ToSampleUnitNormal.Mult (forceAccessBlock (toSample));
						var isForceEstimated = fromSample.EstimatedProperties.HasFlag (UITouchProperties.Force)
						                                 || toSample.EstimatedProperties.HasFlag (UITouchProperties.Force);

						if (isForceEstimated) {
							if (lastEstimatedSample == null)
								lastEstimatedSample = new EstimatedSample { Index = segment.FromSampleIndex + 1, Sample = toSample };
							forceEstimatedLineSettings ();
						} else {
							lineSettings ();
						}

						context.BeginPath ();
						context.Move (fromSample.Location.Add (fromUnitVector));
						context.AddLine (toSample.Location.Add (toUnitVector));
						context.AddLine (toSample.Location.Sub (toUnitVector));
						context.AddLine (fromSample.Location.Sub (fromUnitVector));
						context.ClosePath ();
						context.DrawPath (CGPathDrawingMode.FillStroke);
					}

					var isEstimated = fromSample.EstimatedProperties.HasFlag (UITouchProperties.Azimuth);
					if (fromSample.Azimuth.HasValue && (!fromSample.Coalesced || isEstimated) && !fromSample.Predicted && displayOptions == StrokeViewDisplayOptions.Debug) {
						var length = 20f;
						var azimuthUnitVector = fromSample.GetAzimuthUnitVector ();
						var azimuthTarget = fromSample.Location.Add (azimuthUnitVector.Mult (length));

						var altitudeStart = azimuthTarget.Add (azimuthUnitVector.Mult (length / -2));
						var altitudeTarget = altitudeStart.Add ((azimuthUnitVector.Mult (length / 2)).Apply (CGAffineTransform.MakeRotation (fromSample.Altitude.Value)));

						// Draw altitude as black line coming from the center of the azimuth.
						altitudeSettings ();

						context.BeginPath ();
						context.Move (altitudeStart);
						context.AddLine (altitudeTarget);
						context.StrokePath ();

						// Draw azimuth as orange (or blue if estimated) line.
						azimuthSettings ();

						if (isEstimated)
							context.SetStrokeColor (UIColor.Blue.CGColor);
						context.BeginPath ();
						context.Move (fromSample.Location);
						context.AddLine (azimuthTarget);
						context.StrokePath ();
					}

					heldFromSample = null;
					heldFromSampleUnitVector = null;
				}
			};

			if (stroke.Samples.Count == 1) {
				// Construct a face segment to draw for a stroke that is only one point.
				var sample = stroke.Samples [0];

				var tempSampleFrom = new StrokeSample {
					Timestamp = sample.Timestamp,
					Location = sample.Location.Add (new CGVector (-0.5f, 0)),
					Coalesced = false,
					Predicted = false,
					Force = sample.Force,
					Azimuth = sample.Azimuth,
					Altitude = sample.Altitude,
					EstimatedProperties = sample.EstimatedProperties
				};

				var tempSampleTo = new StrokeSample {
					Timestamp = sample.Timestamp,
					Location = sample.Location.Add (new CGVector (0.5f, 0)),
					Coalesced = false,
					Predicted = false,
					Force = sample.Force,
					Azimuth = sample.Azimuth,
					Altitude = sample.Altitude,
					EstimatedProperties = sample.EstimatedProperties
				};

				var segment = new StrokeSegment (tempSampleFrom);
				segment.AdvanceWithSample (tempSampleTo);
				segment.AdvanceWithSample (null);

				draw (segment);
			} else {
				foreach (var segment in stroke)
					draw (segment);
			}
		}
示例#9
0
        // Note: this is not a particularily efficient way to draw a great stroke path
        // with CoreGraphics.It is just a way to produce an interesting looking result.
        // For a real world example you would reuse and cache CGPaths and draw longer
        // paths instead of an aweful lot of tiny ones, etc.You would also respect the
        // draw rect to cull your draw requests.And you would use bezier paths to
        // interpolate between the points to get a smooother curve.
        void Draw(Stroke stroke)
        {
            var updateRanges = stroke.UpdatedRanges();

            if (DisplayOptions == StrokeViewDisplayOptions.Debug)
            {
                for (int index = 0; index < DirtyRectViews.Count; index++)
                {
                    var dirtyRectView = DirtyRectViews [index];
                    dirtyRectView.Alpha = 0;
                    if (index < updateRanges.Length)
                    {
                        dirtyRectView.Alpha = 1;
                        var range   = updateRanges [index];
                        var strokes = stroke.Samples.Skip(range.LowerBound)
                                      .Take(range.UpperBound - range.LowerBound + 1);
                        dirtyRectView.Frame = DirtyRectForSampleStride(strokes);
                    }
                }
            }

            lastEstimatedSample = null;
            stroke.ClearUpdateInfo();
            var sampleCount = stroke.Samples.Count;

            if (sampleCount <= 0)
            {
                return;
            }
            var context = UIGraphics.GetCurrentContext();

            if (context == null)
            {
                return;
            }
            var strokeColor = UIColor.Black;

            Action lineSettings;
            Action forceEstimatedLineSettings;

            if (displayOptions == StrokeViewDisplayOptions.Debug)
            {
                lineSettings = () => {
                    context.SetLineWidth(0.5f);
                    context.SetStrokeColor(UIColor.White.CGColor);
                };
                forceEstimatedLineSettings = () => {
                    context.SetLineWidth(0.5f);
                    context.SetStrokeColor(UIColor.Blue.CGColor);
                };
            }
            else
            {
                lineSettings = () => {
                    context.SetLineWidth(0.25f);
                    context.SetStrokeColor(strokeColor.CGColor);
                };
                forceEstimatedLineSettings = lineSettings;
            }

            Action azimuthSettings = () => {
                context.SetLineWidth(1.5f);
                context.SetStrokeColor(UIColor.Orange.CGColor);
            };

            Action altitudeSettings = () => {
                context.SetLineWidth(0.5f);
                context.SetStrokeColor(strokeColor.CGColor);
            };
            var forceMultiplier = 2f;
            var forceOffset     = 0.1f;

            var fillColorRegular   = UIColor.Black.CGColor;
            var fillColorCoalesced = UIColor.LightGray.CGColor;
            var fillColorPredicted = UIColor.Red.CGColor;

            CGVector?lockedAzimuthUnitVector      = null;
            var      azimuthLockAltitudeThreshold = NMath.PI / 2 * 0.8f;        // locking azimuth at 80% altitude

            lineSettings();

            Func <StrokeSample, nfloat> forceAccessBlock = sample => {
                return(sample.ForceWithDefault());
            };

            if (DisplayOptions == StrokeViewDisplayOptions.Ink)
            {
                forceAccessBlock = sample => {
                    return(sample.PerpendicularForce());
                };
            }

            // Make the force influence less pronounced for the calligraphy pen.
            if (DisplayOptions == StrokeViewDisplayOptions.Calligraphy)
            {
                var prevGetter = forceAccessBlock;
                forceAccessBlock = sample => {
                    return(NMath.Max(prevGetter(sample), 1));
                };
                // make force value less pronounced
                forceMultiplier = 1;
                forceOffset     = 10;
            }

            var previousGetter = forceAccessBlock;

            forceAccessBlock = sample => {
                return(previousGetter(sample) * forceMultiplier + forceOffset);
            };

            StrokeSample heldFromSample           = null;
            CGVector?    heldFromSampleUnitVector = null;

            Action <StrokeSegment> draw = segment => {
                var toSample = segment.ToSample;
                if (toSample != null)
                {
                    StrokeSample fromSample = heldFromSample ?? segment.FromSample;

                    // Skip line segments that are too short.
                    var dist = Vector(fromSample.Location, toSample.Location).Quadrance();
                    if (dist < 0.003f)
                    {
                        if (heldFromSample == null)
                        {
                            heldFromSample           = fromSample;
                            heldFromSampleUnitVector = segment.FromSampleUnitNormal;
                        }
                        return;
                    }

                    if (toSample.Predicted)
                    {
                        if (displayOptions == StrokeViewDisplayOptions.Debug)
                        {
                            context.SetFillColor(fillColorPredicted);
                        }
                    }
                    else
                    {
                        bool coalesced = displayOptions == StrokeViewDisplayOptions.Debug && fromSample.Coalesced;
                        context.SetFillColor(coalesced ? fillColorCoalesced : fillColorRegular);
                    }

                    if (displayOptions == StrokeViewDisplayOptions.Calligraphy)
                    {
                        var fromAzimuthUnitVector = Stroke.CalligraphyFallbackAzimuthUnitVector;
                        var toAzimuthUnitVector   = Stroke.CalligraphyFallbackAzimuthUnitVector;

                        if (fromSample.Azimuth.HasValue)
                        {
                            if (!lockedAzimuthUnitVector.HasValue)
                            {
                                lockedAzimuthUnitVector = fromSample.GetAzimuthUnitVector();
                            }
                            fromAzimuthUnitVector = fromSample.GetAzimuthUnitVector();
                            toAzimuthUnitVector   = toSample.GetAzimuthUnitVector();

                            if (fromSample.Altitude.Value > azimuthLockAltitudeThreshold)
                            {
                                fromAzimuthUnitVector = lockedAzimuthUnitVector.Value;
                            }

                            if (toSample.Altitude.Value > azimuthLockAltitudeThreshold)
                            {
                                toAzimuthUnitVector = lockedAzimuthUnitVector.Value;
                            }
                            else
                            {
                                lockedAzimuthUnitVector = toAzimuthUnitVector;
                            }
                        }

                        // Rotate 90 degrees
                        var calligraphyTransform = CGAffineTransform.MakeRotation(NMath.PI / 2);
                        fromAzimuthUnitVector = fromAzimuthUnitVector.Apply(calligraphyTransform);
                        toAzimuthUnitVector   = toAzimuthUnitVector.Apply(calligraphyTransform);

                        var fromUnitVector = fromAzimuthUnitVector.Mult(forceAccessBlock(fromSample));
                        var toUnitVector   = toAzimuthUnitVector.Mult(forceAccessBlock(toSample));

                        context.BeginPath();
                        context.Move(fromSample.Location.Add(fromUnitVector));
                        context.AddLine(toSample.Location.Add(toUnitVector));
                        context.AddLine(toSample.Location.Sub(toUnitVector));
                        context.AddLine(fromSample.Location.Sub(fromUnitVector));
                        context.ClosePath();

                        context.DrawPath(CGPathDrawingMode.FillStroke);
                    }
                    else
                    {
                        var fromUnitVector = (heldFromSampleUnitVector.HasValue ? heldFromSampleUnitVector.Value : segment.FromSampleUnitNormal).Mult(forceAccessBlock(fromSample));

                        var toUnitVector     = segment.ToSampleUnitNormal.Mult(forceAccessBlock(toSample));
                        var isForceEstimated = fromSample.EstimatedProperties.HasFlag(UITouchProperties.Force) ||
                                               toSample.EstimatedProperties.HasFlag(UITouchProperties.Force);

                        if (isForceEstimated)
                        {
                            if (lastEstimatedSample == null)
                            {
                                lastEstimatedSample = new EstimatedSample {
                                    Index = segment.FromSampleIndex + 1, Sample = toSample
                                }
                            }
                            ;
                            forceEstimatedLineSettings();
                        }
                        else
                        {
                            lineSettings();
                        }

                        context.BeginPath();
                        context.Move(fromSample.Location.Add(fromUnitVector));
                        context.AddLine(toSample.Location.Add(toUnitVector));
                        context.AddLine(toSample.Location.Sub(toUnitVector));
                        context.AddLine(fromSample.Location.Sub(fromUnitVector));
                        context.ClosePath();
                        context.DrawPath(CGPathDrawingMode.FillStroke);
                    }

                    var isEstimated = fromSample.EstimatedProperties.HasFlag(UITouchProperties.Azimuth);
                    if (fromSample.Azimuth.HasValue && (!fromSample.Coalesced || isEstimated) && !fromSample.Predicted && displayOptions == StrokeViewDisplayOptions.Debug)
                    {
                        var length            = 20f;
                        var azimuthUnitVector = fromSample.GetAzimuthUnitVector();
                        var azimuthTarget     = fromSample.Location.Add(azimuthUnitVector.Mult(length));

                        var altitudeStart  = azimuthTarget.Add(azimuthUnitVector.Mult(length / -2));
                        var altitudeTarget = altitudeStart.Add((azimuthUnitVector.Mult(length / 2)).Apply(CGAffineTransform.MakeRotation(fromSample.Altitude.Value)));

                        // Draw altitude as black line coming from the center of the azimuth.
                        altitudeSettings();

                        context.BeginPath();
                        context.Move(altitudeStart);
                        context.AddLine(altitudeTarget);
                        context.StrokePath();

                        // Draw azimuth as orange (or blue if estimated) line.
                        azimuthSettings();

                        if (isEstimated)
                        {
                            context.SetStrokeColor(UIColor.Blue.CGColor);
                        }
                        context.BeginPath();
                        context.Move(fromSample.Location);
                        context.AddLine(azimuthTarget);
                        context.StrokePath();
                    }

                    heldFromSample           = null;
                    heldFromSampleUnitVector = null;
                }
            };

            if (stroke.Samples.Count == 1)
            {
                // Construct a face segment to draw for a stroke that is only one point.
                var sample = stroke.Samples [0];

                var tempSampleFrom = new StrokeSample {
                    Timestamp           = sample.Timestamp,
                    Location            = sample.Location.Add(new CGVector(-0.5f, 0)),
                    Coalesced           = false,
                    Predicted           = false,
                    Force               = sample.Force,
                    Azimuth             = sample.Azimuth,
                    Altitude            = sample.Altitude,
                    EstimatedProperties = sample.EstimatedProperties
                };

                var tempSampleTo = new StrokeSample {
                    Timestamp           = sample.Timestamp,
                    Location            = sample.Location.Add(new CGVector(0.5f, 0)),
                    Coalesced           = false,
                    Predicted           = false,
                    Force               = sample.Force,
                    Azimuth             = sample.Azimuth,
                    Altitude            = sample.Altitude,
                    EstimatedProperties = sample.EstimatedProperties
                };

                var segment = new StrokeSegment(tempSampleFrom);
                segment.AdvanceWithSample(tempSampleTo);
                segment.AdvanceWithSample(null);

                draw(segment);
            }
            else
            {
                foreach (var segment in stroke)
                {
                    draw(segment);
                }
            }
        }
示例#10
0
 public StrokeSegment(StrokeSample sample)
 {
     SampleAfter     = sample;
     FromSampleIndex = -2;
 }
示例#11
0
		public StrokeSegment (StrokeSample sample)
		{
			SampleAfter = sample;
			FromSampleIndex = -2;
		}