コード例 #1
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);
			}
		}
コード例 #2
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);
                }
            }
        }