Exemple #1
0
        /// <summary>
        /// Hit-testing for stroke erase scenario.
        /// </summary>
        /// <param name="iterator">the stroke nodes to iterate</param>
        /// <returns>true if the strokes intersect, false otherwise</returns>
        internal bool HitTest(StrokeNodeIterator iterator)
        {
            System.Diagnostics.Debug.Assert(iterator != null);

            if ((_erasingStrokeNodes == null) || (_erasingStrokeNodes.Count == 0))
            {
                return(false);
            }

            Rect inkSegmentBounds = Rect.Empty;

            for (int i = 0; i < iterator.Count; i++)
            {
                StrokeNode inkStrokeNode = iterator[i];
                Rect       inkNodeBounds = inkStrokeNode.GetBounds();
                inkSegmentBounds.Union(inkNodeBounds);

                if (inkSegmentBounds.IntersectsWith(_bounds))
                {
                    //

                    foreach (StrokeNode erasingStrokeNode in _erasingStrokeNodes)
                    {
                        if (inkSegmentBounds.IntersectsWith(erasingStrokeNode.GetBoundsConnected()) &&
                            erasingStrokeNode.HitTest(inkStrokeNode))
                        {
                            return(true);
                        }
                    }
                }
            }
            return(false);
        }
Exemple #2
0
            /// <summary>
            /// Merge two crossings into one.
            /// </summary>
            /// <param name="crossing"></param>
            /// <returns>Return true if these two crossings are actually overlapping and merged; false otherwise</returns>
            public bool Merge(LassoCrossing crossing)
            {
                if (crossing.IsEmpty)
                {
                    return(false);
                }

                if (FIndices.IsEmpty && !crossing.IsEmpty)
                {
                    FIndices  = crossing.FIndices;
                    StartNode = crossing.StartNode;
                    EndNode   = crossing.EndNode;
                    return(true);
                }

                if (DoubleUtil.GreaterThanOrClose(crossing.FIndices.EndFIndex, FIndices.BeginFIndex) &&
                    DoubleUtil.GreaterThanOrClose(FIndices.EndFIndex, crossing.FIndices.BeginFIndex))
                {
                    if (DoubleUtil.LessThan(crossing.FIndices.BeginFIndex, FIndices.BeginFIndex))
                    {
                        FIndices.BeginFIndex = crossing.FIndices.BeginFIndex;
                        StartNode            = crossing.StartNode;
                    }

                    if (DoubleUtil.GreaterThan(crossing.FIndices.EndFIndex, FIndices.EndFIndex))
                    {
                        FIndices.EndFIndex = crossing.FIndices.EndFIndex;
                        EndNode            = crossing.EndNode;
                    }
                    return(true);
                }

                return(false);
            }
Exemple #3
0
        /// <summary>
        /// Generates stroke nodes along a given path.
        /// Drops any previously genererated nodes.
        /// </summary>
        /// <param name="path"></param>
        internal void MoveTo(IEnumerable <Point> path)
        {
            System.Diagnostics.Debug.Assert((path != null) && (IEnumerablePointHelper.GetCount(path) != 0));
            Point[] points = IEnumerablePointHelper.GetPointArray(path);

            if (_erasingStrokeNodes == null)
            {
                _erasingStrokeNodes = new List <StrokeNode>(points.Length);
            }
            else
            {
                _erasingStrokeNodes.Clear();
            }


            _bounds       = Rect.Empty;
            _nodeIterator = _nodeIterator.GetIteratorForNextSegment(points.Length > 1 ? FilterPoints(points) : points);
            for (int i = 0; i < _nodeIterator.Count; i++)
            {
                StrokeNode strokeNode = _nodeIterator[i];
                _bounds.Union(strokeNode.GetBoundsConnected());
                _erasingStrokeNodes.Add(strokeNode);
            }
#if POINTS_FILTER_TRACE
            _totalPointsAdded += path.Length;
            System.Diagnostics.Debug.WriteLine(String.Format("Total Points added: {0} screened: {1} collinear screened: {2}", _totalPointsAdded, _totalPointsScreened, _collinearPointsScreened));
#endif
        }
Exemple #4
0
        /// <summary>
        /// Hit-testing for stroke erase scenario.
        /// </summary>
        /// <param name="iterator">the stroke nodes to iterate</param>
        /// <returns>true if the strokes intersect, false otherwise</returns>
        internal bool HitTest(StrokeNodeIterator iterator)
        {
            System.Diagnostics.Debug.Assert(iterator != null);

            if ((_erasingStrokeNodes == null) || (_erasingStrokeNodes.Count == 0))
            {
                return(false);
            }

            Rect inkSegmentBounds = Rect.Empty;

            for (int i = 0; i < iterator.Count; i++)
            {
                StrokeNode inkStrokeNode = iterator[i];
                Rect       inkNodeBounds = inkStrokeNode.GetBounds();
                inkSegmentBounds.Union(inkNodeBounds);

                if (inkSegmentBounds.IntersectsWith(_bounds))
                {
                    for (var index = 0; index < _erasingStrokeNodes.Count; index++)
                    {
                        var erasingStrokeNodeBound = _erasingStrokeNodeBounds[index];
                        if (inkSegmentBounds.IntersectsWith(erasingStrokeNodeBound))
                        {
                            StrokeNode erasingStrokeNode = _erasingStrokeNodes[index];
                            if (erasingStrokeNode.HitTest(inkStrokeNode))
                            {
                                return(true);
                            }
                        }
                    }
                }
            }
            return(false);
        }
Exemple #5
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="newFIndices"></param>
 /// <param name="strokeNode"></param>
 public LassoCrossing(StrokeFIndices newFIndices, StrokeNode strokeNode)
 {
     System.Diagnostics.Debug.Assert(!newFIndices.IsEmpty);
     System.Diagnostics.Debug.Assert(strokeNode.IsValid);
     FIndices  = newFIndices;
     StartNode = EndNode = strokeNode;
 }
        /// <summary>
        /// This method tells whether the contour of a given stroke node
        /// intersects with the contour of this node. The contours of both nodes
        /// include their connecting quadrangles.
        /// </summary>
        /// <param name="hitNode"></param>
        /// <returns></returns>
        internal bool HitTest(StrokeNode hitNode)
        {
            if (!IsValid || !hitNode.IsValid)
            {
                return(false);
            }

            IEnumerable <ContourSegment> hittingContour = hitNode.GetContourSegments();

            return(_operations.HitTest(_lastNode, _thisNode, ConnectingQuad, hittingContour));
        }
        /// <summary>
        /// Finds out if a given node intersects with this one,
        /// and returns findices of the intersection.
        /// </summary>
        /// <param name="hitNode"></param>
        /// <returns></returns>
        internal StrokeFIndices CutTest(StrokeNode hitNode)
        {
            if ((IsValid == false) || (hitNode.IsValid == false))
            {
                return(StrokeFIndices.Empty);
            }

            IEnumerable <ContourSegment> hittingContour = hitNode.GetContourSegments();

            // If the node contours intersect, the result is a pair of findices
            // this segment should be cut at to let the hitNode's contour through it.
            StrokeFIndices cutAt = _operations.CutTest(_lastNode, _thisNode, ConnectingQuad, hittingContour);

            return((_index == 0) ? cutAt : BindFIndices(cutAt));
        }
Exemple #8
0
        /// <summary>
        /// Helper function to find out whether a point is inside the lasso
        /// </summary>
        private bool SegmentWithinLasso(StrokeNode strokeNode, double fIndex)
        {
            bool currentSegmentWithinLasso;

            if (DoubleUtil.AreClose(fIndex, StrokeFIndices.BeforeFirst))
            {
                // This should check against the very first stroke node
                currentSegmentWithinLasso = this.Contains(strokeNode.GetPointAt(0f));
            }
            else if (DoubleUtil.AreClose(fIndex, StrokeFIndices.AfterLast))
            {
                // This should check against the last stroke node
                currentSegmentWithinLasso = this.Contains(strokeNode.Position);
            }
            else
            {
                currentSegmentWithinLasso = this.Contains(strokeNode.GetPointAt(fIndex));
            }

            return(currentSegmentWithinLasso);
        }
Exemple #9
0
        /// <summary>
        /// GetPointsAtMiddleSegment
        /// </summary>
        internal void GetPointsAtMiddleSegment( StrokeNode previous, 
                                                double angleBetweenNodes, 
                                                List<Point> abPoints, 
                                                List<Point> dcPoints,
                                                out bool missingIntersection
#if DEBUG_RENDERING_FEEDBACK
                                                , DrawingContext debugDC, double feedbackSize, bool showFeedback
#endif
                                                )
        {
            missingIntersection = false;
            if (IsValid && previous.IsValid)
            {
                Quad quad1 = previous.ConnectingQuad;
                if (!quad1.IsEmpty)
                {
                    Quad quad2 = ConnectingQuad;
                    if (!quad2.IsEmpty)
                    {
                        if (IsEllipse)
                        {
                            Rect node1Bounds = _operations.GetNodeBounds(previous._lastNode);
                            Rect node2Bounds = _operations.GetNodeBounds(_lastNode);
                            Rect node3Bounds = _operations.GetNodeBounds(_thisNode);
#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            {
                                debugDC.DrawEllipse(null, new Pen(Brushes.Pink, feedbackSize / 2), _lastNode.Position, node2Bounds.Width / 2, node2Bounds.Height / 2);
                            }
#endif
                            if (angleBetweenNodes == 0.0d || ((quad1.B == quad2.A) && (quad1.C == quad2.D)))
                            {
                                //quads connections are the same, just add them
                                abPoints.Add(quad1.B);
                                dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK
                                if (showFeedback)
                                {
                                    debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                }
#endif
                            }
                            else if (angleBetweenNodes > 0.0)
                            {
                                //the stroke angled towards the AB side
                                //this part is easy
                                if (quad1.B == quad2.A)
                                {
                                    abPoints.Add(quad1.B);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    Point intersection = GetIntersection(quad1.A, quad1.B, quad2.A, quad2.B);
                                    Rect union = Rect.Union(node1Bounds, node2Bounds);
                                    union.Inflate(1.0, 1.0);
                                    //make sure we're not off in space

#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize * 1.5, feedbackSize * 1.5);
                                        debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                    }
#endif

                                    if (union.Contains(intersection))
                                    {
                                        abPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                        if (showFeedback)
                                        {
                                            debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize);
                                        }
#endif
                                    }
                                    else
                                    {
                                        //if we missed the intersection we'll need to close the stroke segment
                                        //this work is done in StrokeRenderer
                                        missingIntersection = true;
                                        return; //we're done.
                                    }
                                }

                                if (quad1.C == quad2.D)
                                {
                                    dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    //add instructions to arc from quad1.C to quad2.D in reverse order (since we walk this array backwards to render)
                                    dcPoints.Add(quad1.C);
                                    dcPoints.Add(new Point(node2Bounds.Width, node2Bounds.Height));
                                    dcPoints.Add(StrokeRenderer.ArcToMarker);
                                    dcPoints.Add(quad2.D);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                        debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                            }
                            else
                            {
                                //the stroke angled towards the CD side
                                //this part is easy
                                if (quad1.C == quad2.D)
                                {
                                    dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    Point intersection = GetIntersection(quad1.D, quad1.C, quad2.D, quad2.C);
                                    Rect union = Rect.Union(node1Bounds, node2Bounds);
                                    union.Inflate(1.0, 1.0);
                                    //make sure we're not off in space

#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize * 1.5, feedbackSize * 1.5);
                                        debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                                    }
#endif

                                    if (union.Contains(intersection))
                                    {
                                        dcPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                        if (showFeedback)
                                        {
                                            debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize);
                                        }
#endif
                                    }
                                    else
                                    {
                                        //if we missed the intersection we'll need to close the stroke segment
                                        //this work is done in StrokeRenderer
                                        missingIntersection = true;
                                        return; //we're done.
                                    }
                                }

                                if (quad1.B == quad2.A)
                                {
                                    abPoints.Add(quad1.B);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    }
#endif

                                }
                                else
                                {
                                    //we need to arc between quad1.B and quad2.A along node2
                                    abPoints.Add(quad1.B);
                                    abPoints.Add(StrokeRenderer.ArcToMarker);
                                    abPoints.Add(new Point(node2Bounds.Width, node2Bounds.Height));
                                    abPoints.Add(quad2.A);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                        debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                            }
                        }
                        else
                        {
                            //rectangle
                            int indexA = -1;
                            int indexB = -1;
                            int indexC = -1;
                            int indexD = -1;

                            Vector[] vertices = _operations.GetVertices();
                            double pressureFactor = _lastNode.PressureFactor;
                            for (int i = 0; i < vertices.Length; i++)
                            {
                                Point point = _lastNode.Position + (vertices[i % vertices.Length] * pressureFactor);
                                if (point == quad2.A)
                                {
                                    indexA = i;
                                }
                                if (point == quad1.B)
                                {
                                    indexB = i;
                                }
                                if (point == quad1.C)
                                {
                                    indexC = i;
                                }
                                if (point == quad2.D)
                                {
                                    indexD = i;
                                }
                            }

                            if (indexA == -1 || indexB == -1 || indexC == -1 || indexD == -1)
                            {
                                Debug.Assert(false, "Couldn't find all 4 indexes in StrokeNodeOperations.GetPointsAtMiddleSegment");
                                return;
                            }

#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            {

                                debugDC.DrawRectangle(null, new Pen(Brushes.Pink, feedbackSize / 2), _operations.GetNodeBounds(_lastNode));
                                debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                            }
#endif

                            Rect node3Rect = _operations.GetNodeBounds(_thisNode);
                            //take care of a-b first
                            if (indexA == indexB)
                            {
                                //quad connection is the same, just add it
                                if (!node3Rect.Contains(quad1.B))
                                {
                                    abPoints.Add(quad1.B);
                                }
                            }
                            else if ((indexA == 0 && indexB == 3) || ((indexA != 3 || indexB != 0) && (indexA > indexB)))
                            {
                                if (!node3Rect.Contains(quad1.B))
                                {
                                    abPoints.Add(quad1.B);
                                }
                                if (!node3Rect.Contains(quad2.A))
                                {
                                    abPoints.Add(quad2.A);
                                }
                            }
                            else
                            {
                                Point intersection = GetIntersection(quad1.A, quad1.B, quad2.A, quad2.B);
                                Rect node12 = Rect.Union(_operations.GetNodeBounds(previous._lastNode), _operations.GetNodeBounds(_lastNode));
                                node12.Inflate(1.0, 1.0);
                                //make sure we're not off in space
                                if (node12.Contains(intersection))
                                {
                                    abPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize * 1.5);
                                    }
#endif
                                }
                                else
                                {
                                    //if we missed the intersection we'll need to close the stroke segment
                                    //this work is done in StrokeRenderer.
                                    missingIntersection = true;
                                    return; //we're done.
                                }
                            }
                            
                            // now take care of c-d.  
                            if (indexC == indexD)
                            {
                                //quad connection is the same, just add it
                                if (!node3Rect.Contains(quad1.C))
                                {
                                    dcPoints.Add(quad1.C);
                                }
                            }
                            else if ((indexC == 0 && indexD == 3) || ((indexC != 3 || indexD != 0) && (indexC > indexD)))
                            {
                                if (!node3Rect.Contains(quad1.C))
                                {
                                    dcPoints.Add(quad1.C);
                                }
                                if (!node3Rect.Contains(quad2.D))
                                {
                                    dcPoints.Add(quad2.D);
                                }
                            }
                            else
                            {
                                Point intersection = GetIntersection(quad1.D, quad1.C, quad2.D, quad2.C);
                                Rect node12 = Rect.Union(_operations.GetNodeBounds(previous._lastNode), _operations.GetNodeBounds(_lastNode));
                                node12.Inflate(1.0, 1.0);
                                //make sure we're not off in space
                                if (node12.Contains(intersection))
                                {
                                    dcPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK 
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize * 1.5);
                                    }
#endif
                                }
                                else 
                                {
                                    //if we missed the intersection we'll need to close the stroke segment
                                    //this work is done in StrokeRenderer.
                                    missingIntersection = true;
                                    return; //we're done.
                                }
                            }
                        }
                    }
                }
            }
        }
Exemple #10
0
        /// <summary>
        /// Hit-testing for point erase.
        /// </summary>
        /// <param name="iterator"></param>
        /// <param name="intersections"></param>
        /// <returns></returns>
        internal bool EraseTest(StrokeNodeIterator iterator, List <StrokeIntersection> intersections)
        {
            System.Diagnostics.Debug.Assert(iterator != null);
            System.Diagnostics.Debug.Assert(intersections != null);
            intersections.Clear();

            List <StrokeFIndices> eraseAt = new List <StrokeFIndices>();

            if ((_erasingStrokeNodes == null) || (_erasingStrokeNodes.Count == 0))
            {
                return(false);
            }

            Rect inkSegmentBounds = Rect.Empty;

            for (int x = 0; x < iterator.Count; x++)
            {
                StrokeNode inkStrokeNode = iterator[x];
                Rect       inkNodeBounds = inkStrokeNode.GetBounds();
                inkSegmentBounds.Union(inkNodeBounds);

                if (inkSegmentBounds.IntersectsWith(_bounds))
                {
                    //

                    int index = eraseAt.Count;
                    foreach (StrokeNode erasingStrokeNode in _erasingStrokeNodes)
                    {
                        if (false == inkSegmentBounds.IntersectsWith(erasingStrokeNode.GetBoundsConnected()))
                        {
                            continue;
                        }

                        StrokeFIndices fragment = inkStrokeNode.CutTest(erasingStrokeNode);
                        if (fragment.IsEmpty)
                        {
                            continue;
                        }

                        // Merge it with the other results for this ink segment
                        bool inserted = false;
                        for (int i = index; i < eraseAt.Count; i++)
                        {
                            StrokeFIndices lastFragment = eraseAt[i];
                            if (fragment.BeginFIndex < lastFragment.EndFIndex)
                            {
                                // If the fragments overlap, merge them
                                if (fragment.EndFIndex > lastFragment.BeginFIndex)
                                {
                                    fragment = new StrokeFIndices(
                                        Math.Min(lastFragment.BeginFIndex, fragment.BeginFIndex),
                                        Math.Max(lastFragment.EndFIndex, fragment.EndFIndex));

                                    // If the fragment doesn't go beyond lastFragment, break
                                    if ((fragment.EndFIndex <= lastFragment.EndFIndex) || ((i + 1) == eraseAt.Count))
                                    {
                                        inserted   = true;
                                        eraseAt[i] = fragment;
                                        break;
                                    }
                                    else
                                    {
                                        eraseAt.RemoveAt(i);
                                        i--;
                                    }
                                }
                                // insert otherwise
                                else
                                {
                                    eraseAt.Insert(i, fragment);
                                    inserted = true;
                                    break;
                                }
                            }
                        }

                        // If not merged nor inserted, add it to the end of the list
                        if (false == inserted)
                        {
                            eraseAt.Add(fragment);
                        }
                        // Break out if the entire ink segment is hit - {BeforeFirst, AfterLast}
                        if (eraseAt[eraseAt.Count - 1].IsFull)
                        {
                            break;
                        }
                    }
                    // Merge inter-segment overlapping fragments
                    if ((index > 0) && (index < eraseAt.Count))
                    {
                        StrokeFIndices lastFragment = eraseAt[index - 1];
                        if (DoubleUtil.AreClose(lastFragment.EndFIndex, StrokeFIndices.AfterLast))
                        {
                            if (DoubleUtil.AreClose(eraseAt[index].BeginFIndex, StrokeFIndices.BeforeFirst))
                            {
                                lastFragment.EndFIndex = eraseAt[index].EndFIndex;
                                eraseAt[index - 1]     = lastFragment;
                                eraseAt.RemoveAt(index);
                            }
                            else
                            {
                                lastFragment.EndFIndex = inkStrokeNode.Index;
                                eraseAt[index - 1]     = lastFragment;
                            }
                        }
                    }
                }
                // Start next ink segment
                inkSegmentBounds = inkNodeBounds;
            }
            if (eraseAt.Count != 0)
            {
                foreach (StrokeFIndices segment in eraseAt)
                {
                    intersections.Add(new StrokeIntersection(segment.BeginFIndex, StrokeFIndices.AfterLast,
                                                             StrokeFIndices.BeforeFirst, segment.EndFIndex));
                }
            }
            return(eraseAt.Count != 0);
        }
Exemple #11
0
        internal static void CalcGeometryAndBounds(StrokeNodeIterator iterator,
                                                   DrawingAttributes drawingAttributes,
#if DEBUG_RENDERING_FEEDBACK
                                                   DrawingContext debugDC,
                                                   double feedbackSize,
                                                   bool showFeedback,
#endif
                                                   bool calculateBounds,
                                                   out Geometry geometry,
                                                   out Rect bounds)
        {

            Debug.Assert(iterator != null && drawingAttributes != null);

            //we can use our new algorithm for identity only.
            Matrix stylusTipTransform = drawingAttributes.StylusTipTransform;
            if (stylusTipTransform != Matrix.Identity && stylusTipTransform._type != MatrixTypes.TRANSFORM_IS_SCALING)
            {
                //second best optimization
                CalcGeometryAndBoundsWithTransform(iterator, drawingAttributes, stylusTipTransform._type, calculateBounds, out geometry, out bounds);
            }
            else
            {

                StreamGeometry streamGeometry = new StreamGeometry();
                streamGeometry.FillRule = FillRule.Nonzero;

                StreamGeometryContext context = streamGeometry.Open();
                geometry = streamGeometry;
                Rect empty = Rect.Empty;
                bounds = empty;
                try
                {
                    //
                    // We keep track of three StrokeNodes as we iterate across
                    // the Stroke. Since these are structs, the default ctor will
                    // be called and .IsValid will be false until we initialize them
                    //
                    StrokeNode emptyStrokeNode = new StrokeNode();
                    StrokeNode prevPrevStrokeNode = new StrokeNode();
                    StrokeNode prevStrokeNode = new StrokeNode();
                    StrokeNode strokeNode = new StrokeNode();

                    Rect prevPrevStrokeNodeBounds = empty;
                    Rect prevStrokeNodeBounds = empty;
                    Rect strokeNodeBounds = empty;

                    //percentIntersect is a function of drawingAttributes height / width
                    double percentIntersect = 95d;
                    double maxExtent = Math.Max(drawingAttributes.Height, drawingAttributes.Width);
                    percentIntersect += Math.Min(4.99999d, ((maxExtent / 20d) * 5d));

                    double prevAngle = double.MinValue;
                    bool isStartOfSegment = true;
                    bool isEllipse = drawingAttributes.StylusTip == StylusTip.Ellipse;
                    bool ignorePressure = drawingAttributes.IgnorePressure;
                    //
                    // Two List<Point>'s that get reused for adding figures
                    // to the streamgeometry.
                    //
                    List<Point> pathFigureABSide = new List<Point>();//don't prealloc.  It causes Gen2 collections to rise and doesn't help execution time
                    List<Point> pathFigureDCSide = new List<Point>();
                    List<Point> polyLinePoints =  new List<Point>(4);

                    int iteratorCount = iterator.Count;
                    for (int index = 0, previousIndex = -1; index < iteratorCount; )
                    {
                        if (!prevPrevStrokeNode.IsValid)
                        {
                            if (prevStrokeNode.IsValid)
                            {
                                //we're sliding our pointers forward
                                prevPrevStrokeNode = prevStrokeNode;
                                prevPrevStrokeNodeBounds = prevStrokeNodeBounds;
                                prevStrokeNode = emptyStrokeNode;
                            }
                            else
                            {
                                prevPrevStrokeNode = iterator[index++, previousIndex++];
                                prevPrevStrokeNodeBounds = prevPrevStrokeNode.GetBounds();
                                continue; //so we always check if index < iterator.Count
                            }
                        }

                        //we know prevPrevStrokeNode is valid
                        if (!prevStrokeNode.IsValid)
                        {
                            if (strokeNode.IsValid)
                            {
                                //we're sliding our pointers forward
                                prevStrokeNode = strokeNode;
                                prevStrokeNodeBounds = strokeNodeBounds;
                                strokeNode = emptyStrokeNode;
                            }
                            else
                            {
                                //get the next strokeNode, but don't automatically update previousIndex
                                prevStrokeNode = iterator[index++, previousIndex];
                                prevStrokeNodeBounds = prevStrokeNode.GetBounds();

                                RectCompareResult result = 
                                    FuzzyContains(  prevStrokeNodeBounds, 
                                                    prevPrevStrokeNodeBounds,
                                                    isStartOfSegment ? 99.99999d : percentIntersect);

                                if (result == RectCompareResult.Rect1ContainsRect2)
                                {
                                    // this node already contains the prevPrevStrokeNodeBounds (PP):
                                    //
                                    //  |------------|
                                    //  | |----|     |
                                    //  | | PP |  P  |                            
                                    //  | |----|     |
                                    //  |------------|
                                    //
                                    prevPrevStrokeNode = iterator[index - 1, prevPrevStrokeNode.Index - 1]; ;
                                    prevPrevStrokeNodeBounds = Rect.Union(prevStrokeNodeBounds, prevPrevStrokeNodeBounds);

                                    // at this point prevPrevStrokeNodeBounds already contains this node
                                    // we can just ignore this node
                                    prevStrokeNode = emptyStrokeNode;

                                    // update previousIndex to point to this node
                                    previousIndex = index - 1;

                                    // go back to our main loop
                                    continue;
                                }
                                else if (result == RectCompareResult.Rect2ContainsRect1)
                                {
                                    // this prevPrevStrokeNodeBounds (PP) already contains this node:
                                    //
                                    //  |------------|
                                    //  |      |----||
                                    //  |  PP  | P  ||                            
                                    //  |      |----||
                                    //  |------------|
                                    //

                                    //prevPrevStrokeNodeBounds already contains this node
                                    //we can just ignore this node
                                    prevStrokeNode = emptyStrokeNode;

                                    // go back to our main loop, but do not update previousIndex
                                    // because it should continue to point to previousPrevious
                                    continue;
                                }

                                Debug.Assert(!prevStrokeNode.GetConnectingQuad().IsEmpty, "prevStrokeNode.GetConnectingQuad() is Empty!");
                                
                                // if neither was true, we now have two of our three nodes required to 
                                // start our computation, we need to update previousIndex to point
                                // to our current, valid prevStrokeNode
                                previousIndex = index - 1;
                                continue; //so we always check if index < iterator.Count
                            }
                        }

                        //we know prevPrevStrokeNode and prevStrokeNode are both valid 
                        if (!strokeNode.IsValid)
                        {
                            strokeNode = iterator[index++, previousIndex];
                            strokeNodeBounds = strokeNode.GetBounds();

                            RectCompareResult result =
                                    FuzzyContains(  strokeNodeBounds,
                                                    prevStrokeNodeBounds,
                                                    isStartOfSegment ? 99.99999 : percentIntersect);

                            RectCompareResult result2 =
                                    FuzzyContains(  strokeNodeBounds,
                                                    prevPrevStrokeNodeBounds,
                                                    isStartOfSegment ? 99.99999 : percentIntersect);

                            if ( isStartOfSegment &&
                                 result == RectCompareResult.Rect1ContainsRect2 &&
                                 result2 == RectCompareResult.Rect1ContainsRect2)
                            {
                                if (pathFigureABSide.Count > 0)
                                {
                                    //we've started a stroke, we need to end it before resetting
                                    //prevPrev
#if DEBUG_RENDERING_FEEDBACK
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                    //render
                                    ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/);
                                }
                                //we're resetting
                                //prevPrevStrokeNode.  We need to gen one
                                //without a connecting quad
                                prevPrevStrokeNode = iterator[index - 1, prevPrevStrokeNode.Index - 1];
                                prevPrevStrokeNodeBounds = prevPrevStrokeNode.GetBounds();
                                prevStrokeNode = emptyStrokeNode;
                                strokeNode = emptyStrokeNode;

                                // increment previousIndex to to point to this node
                                previousIndex = index - 1;
                                continue;

                            }
                            else if (result == RectCompareResult.Rect1ContainsRect2)
                            {
                                // this node (C) already contains the prevStrokeNodeBounds (P):
                                //
                                //          |------------|
                                //  |----|  | |----|     |
                                //  | PP |  | | P  |  C  |                            
                                //  |----|  | |----|     |
                                //          |------------|
                                //
                                //we have to generate a new stroke node that points
                                //to pp since the connecting quad from C to P could be empty
                                //if they have the same point
                                strokeNode = iterator[index - 1, prevStrokeNode.Index - 1];
                                if (!strokeNode.GetConnectingQuad().IsEmpty)
                                {
                                    //only update prevStrokeNode if we have a valid connecting quad
                                    prevStrokeNode = strokeNode;
                                    prevStrokeNodeBounds = Rect.Union(strokeNodeBounds, prevStrokeNodeBounds);

                                    // update previousIndex, since it should point to this node now
                                    previousIndex = index - 1;
                                }

                                // at this point we can just ignore this node
                                strokeNode = emptyStrokeNode;
                                //strokeNodeBounds = empty;

                                prevAngle = double.MinValue; //invalidate
                                
                                // go back to our main loop
                                continue;
                            }
                            else if (result == RectCompareResult.Rect2ContainsRect1)
                            {
                                // this prevStrokeNodeBounds (P) already contains this node (C):
                                //
                                //          |------------|
                                // |----|   |      |----||
                                // | PP |   |  P   | C  ||                            
                                // |----|   |      |----||
                                //          |------------|
                                //
                                //prevStrokeNodeBounds already contains this node
                                //we can just ignore this node
                                strokeNode = emptyStrokeNode;

                                // go back to our main loop, but do not update previousIndex
                                // because it should continue to point to previous
                                continue;
                            }

                            Debug.Assert(!strokeNode.GetConnectingQuad().IsEmpty, "strokeNode.GetConnectingQuad was empty, this is unexpected");

                            //
                            // NOTE: we do not check if C contains PP, or PP contains C because
                            // that indicates a change in direction, which we handle below
                            //
                            // if neither was true P and C are separate, 
                            // we now have all three nodes required to 
                            // start our computation, we need to update previousIndex to point
                            // to our current, valid prevStrokeNode
                            previousIndex = index - 1;
                        }


                        // see if we have an overlap between the first and third node
                        bool overlap = prevPrevStrokeNodeBounds.IntersectsWith(strokeNodeBounds);

                        // prevPrevStrokeNode, prevStrokeNode and strokeNode are all 
                        // valid nodes now.  Now we need to figure out what do add to our 
                        // PathFigure.  First calc bounds on the strokeNode we know we need to render
                        if (calculateBounds)
                        {
                            bounds.Union(prevStrokeNodeBounds);
                        }

                        // determine what points to add to pathFigureABSide and pathFigureDCSide
                        // from prevPrevStrokeNode
                        if (pathFigureABSide.Count == 0)
                        {
                            Debug.Assert(pathFigureDCSide.Count == 0);
                            if (calculateBounds)
                            {
                                bounds.Union(prevPrevStrokeNodeBounds);
                            }

                            if (isStartOfSegment && overlap)
                            {
                                //render a complete first stroke node or we can get artifacts
                                prevPrevStrokeNode.GetContourPoints(polyLinePoints);
                                AddFigureToStreamGeometryContext(context, polyLinePoints, prevPrevStrokeNode.IsEllipse/*isBezierFigure*/);
                                polyLinePoints.Clear();
                            }

                            // we're starting a new pathfigure
                            // we need to add parts of the prevPrevStrokeNode contour
                            // to pathFigureABSide and pathFigureDCSide
#if DEBUG_RENDERING_FEEDBACK
                            prevStrokeNode.GetPointsAtStartOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                            prevStrokeNode.GetPointsAtStartOfSegment(pathFigureABSide, pathFigureDCSide);
#endif

                            //set our marker, we're no longer at the start of the stroke
                            isStartOfSegment = false;
                        }

                        

                        if (prevAngle == double.MinValue)
                        {
                            //prevAngle is no longer valid
                            prevAngle = GetAngleBetween(prevPrevStrokeNode.Position, prevStrokeNode.Position);
                        }
                        double delta = GetAngleDeltaFromLast(prevStrokeNode.Position, strokeNode.Position, ref prevAngle);
                        bool directionChangedOverAbsoluteThreshold = Math.Abs(delta) > 90d && Math.Abs(delta) < (360d - 90d);
                        bool directionChangedOverOverlapThreshold = overlap && !(ignorePressure || strokeNode.PressureFactor == 1f) && Math.Abs(delta) > 30d && Math.Abs(delta) < (360d - 30d);

                        double prevArea = prevStrokeNodeBounds.Height * prevStrokeNodeBounds.Width;
                        double currArea = strokeNodeBounds.Height * strokeNodeBounds.Width;

                        bool areaChanged = !(prevArea == currArea && prevArea == (prevPrevStrokeNodeBounds.Height * prevPrevStrokeNodeBounds.Width));
                        bool areaChangeOverThreshold = false;
                        if (overlap && areaChanged)
                        {
                            if ((Math.Min(prevArea, currArea) / Math.Max(prevArea, currArea)) <= 0.90d)
                            {
                                //the min area is < 70% of the max area
                                areaChangeOverThreshold = true;
                            }
                        }

                        if (areaChanged || delta != 0.0d || index >= iteratorCount)
                        {
                            //the area changed between the three nodes OR there was an angle delta OR we're at the end 
                            //of the stroke...  either way, this is a significant node.  If not, we're going to drop it.
                            if ((overlap && (directionChangedOverOverlapThreshold || areaChangeOverThreshold)) ||
                                directionChangedOverAbsoluteThreshold)
                            {
                                //
                                // we need to stop the pathfigure at P
                                // and render the pathfigure
                                //
                                //  |--|      |--|    |--||--|   |------|
                                //  |PP|------|P |    |PP||P |   |PP P C| 
                                //  |--|      |--|    |--||--|   |------|
                                //           /           |C |          
                                //      |--|             |--|         
                                //      |C |               
                                //      |--|               


#if DEBUG_RENDERING_FEEDBACK
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                //end the figure
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                //render
                                ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/);

                                if (areaChangeOverThreshold)
                                {
                                    //render a complete stroke node or we can get artifacts
                                    prevStrokeNode.GetContourPoints(polyLinePoints);
                                    AddFigureToStreamGeometryContext(context, polyLinePoints, prevStrokeNode.IsEllipse/*isBezierFigure*/);
                                    polyLinePoints.Clear();
                                }
                            }
                            else
                            {
                                //
                                // direction didn't change over the threshold, add the midpoint data
                                //  |--|      |--|
                                //  |PP|------|P | 
                                //  |--|      |--|
                                //                \
                                //                  |--| 
                                //                  |C | 
                                //                  |--| 
                                bool endSegment; //flag that tell us if we missed an intersection
#if DEBUG_RENDERING_FEEDBACK
                                strokeNode.GetPointsAtMiddleSegment(prevStrokeNode, delta, pathFigureABSide, pathFigureDCSide, out endSegment, debugDC, feedbackSize, showFeedback);
#else
                                strokeNode.GetPointsAtMiddleSegment(prevStrokeNode, delta, pathFigureABSide, pathFigureDCSide, out endSegment);
#endif
                                if (endSegment)
                                {
                                    //we have a missing intersection, we need to end the 
                                    //segment at P
#if DEBUG_RENDERING_FEEDBACK
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                    //end the figure
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                    //render
                                    ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/);
                                }
                             }
                        }

                        //
                        // either way... slide our pointers forward, to do this, we simply mark 
                        // our first pointer as 'empty'
                        //
                        prevPrevStrokeNode = emptyStrokeNode;
                        prevPrevStrokeNodeBounds = empty;

                        
                    }

                    //
                    // anything left to render?
                    //
                    if (prevPrevStrokeNode.IsValid)
                    {
                        if (prevStrokeNode.IsValid)
                        {
                            if (calculateBounds)
                            {
                                bounds.Union(prevPrevStrokeNodeBounds);
                                bounds.Union(prevStrokeNodeBounds);
                            }
                            Debug.Assert(!strokeNode.IsValid);
                            //
                            // we never made it to strokeNode, render two points, OR 
                            // strokeNode was a dupe
                            //
                            if (pathFigureABSide.Count > 0)
                            {
#if DEBUG_RENDERING_FEEDBACK
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                //
                                // strokeNode was a dupe, we just need to render the end of the stroke
                                // which is at prevStrokeNode
                                //
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                //render
                                ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, false/*clear the point collections*/);
                            }
                            else
                            {
                                // we've only seen two points to render
                                Debug.Assert(pathFigureDCSide.Count == 0);
                                //contains all the logic to render two stroke nodes
                                RenderTwoStrokeNodes(   context,
                                                        prevPrevStrokeNode,
                                                        prevPrevStrokeNodeBounds,
                                                        prevStrokeNode,
                                                        prevStrokeNodeBounds,
                                                        pathFigureABSide,
                                                        pathFigureDCSide,
                                                        polyLinePoints
#if DEBUG_RENDERING_FEEDBACK
                                                       ,debugDC,
                                                       feedbackSize,
                                                       showFeedback
#endif
                                                    );

                            }
                        }
                        else
                        {
                            if (calculateBounds)
                            {
                                bounds.Union(prevPrevStrokeNodeBounds);
                            }

                            // we only have a single point to render
                            Debug.Assert(pathFigureABSide.Count == 0);
                            prevPrevStrokeNode.GetContourPoints(pathFigureABSide);
                            AddFigureToStreamGeometryContext(context, pathFigureABSide, prevPrevStrokeNode.IsEllipse/*isBezierFigure*/);

                        }
                    }
                    else if (prevStrokeNode.IsValid && strokeNode.IsValid)
                    {
                        if (calculateBounds)
                        {
                            bounds.Union(prevStrokeNodeBounds);
                            bounds.Union(strokeNodeBounds);
                        }

                        // typical case, we hit the end of the stroke
                        // see if we need to start a stroke, or just end one
                        if (pathFigureABSide.Count > 0)
                        {
#if DEBUG_RENDERING_FEEDBACK
                            strokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                            strokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif

                            //render
                            ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, false/*clear the point collections*/);

                            if (FuzzyContains(strokeNodeBounds, prevStrokeNodeBounds, 70d) != RectCompareResult.NoItersection)
                            {
                                //render a complete stroke node or we can get artifacts
                                strokeNode.GetContourPoints(polyLinePoints);
                                AddFigureToStreamGeometryContext(context, polyLinePoints, strokeNode.IsEllipse/*isBezierFigure*/);
                            }
                        }
                        else
                        {
                            Debug.Assert(pathFigureDCSide.Count == 0);
                            //contains all the logic to render two stroke nodes
                            RenderTwoStrokeNodes(   context,
                                                    prevStrokeNode,
                                                    prevStrokeNodeBounds,
                                                    strokeNode,
                                                    strokeNodeBounds,
                                                    pathFigureABSide,
                                                    pathFigureDCSide,
                                                    polyLinePoints
#if DEBUG_RENDERING_FEEDBACK
                                                   ,debugDC,
                                                   feedbackSize,
                                                   showFeedback
#endif
                                                );

                        } 
                    }
                }
                finally
                {
                    context.Close();
                    geometry.Freeze();
                }
            }
        }
Exemple #12
0
        /// <summary>
        /// Helper function to find out whether a point is inside the lasso
        /// </summary>
        private bool SegmentWithinLasso(StrokeNode strokeNode, double fIndex)
        {
            bool currentSegmentWithinLasso;
            if (DoubleUtil.AreClose(fIndex, StrokeFIndices.BeforeFirst))
            {
                // This should check against the very first stroke node
                currentSegmentWithinLasso = this.Contains(strokeNode.GetPointAt(0f));
            }
            else if (DoubleUtil.AreClose(fIndex, StrokeFIndices.AfterLast))
            {
                // This should check against the last stroke node
                currentSegmentWithinLasso = this.Contains(strokeNode.Position);
            }
            else
            {
                currentSegmentWithinLasso = this.Contains(strokeNode.GetPointAt(fIndex));
            }

            return currentSegmentWithinLasso;
        }
        /// <summary>
        /// GetPointsAtMiddleSegment
        /// </summary>
        internal void GetPointsAtMiddleSegment(StrokeNode previous,
                                               double angleBetweenNodes,
                                               List <Point> abPoints,
                                               List <Point> dcPoints,
                                               out bool missingIntersection
#if DEBUG_RENDERING_FEEDBACK
                                               , DrawingContext debugDC, double feedbackSize, bool showFeedback
#endif
                                               )
        {
            missingIntersection = false;
            if (IsValid && previous.IsValid)
            {
                Quad quad1 = previous.ConnectingQuad;
                if (!quad1.IsEmpty)
                {
                    Quad quad2 = ConnectingQuad;
                    if (!quad2.IsEmpty)
                    {
                        if (IsEllipse)
                        {
                            Rect node1Bounds = _operations.GetNodeBounds(previous._lastNode);
                            Rect node2Bounds = _operations.GetNodeBounds(_lastNode);
                            Rect node3Bounds = _operations.GetNodeBounds(_thisNode);
#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            {
                                debugDC.DrawEllipse(null, new Pen(Brushes.Pink, feedbackSize / 2), _lastNode.Position, node2Bounds.Width / 2, node2Bounds.Height / 2);
                            }
#endif
                            if (angleBetweenNodes == 0.0d || ((quad1.B == quad2.A) && (quad1.C == quad2.D)))
                            {
                                //quads connections are the same, just add them
                                abPoints.Add(quad1.B);
                                dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK
                                if (showFeedback)
                                {
                                    debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                }
#endif
                            }
                            else if (angleBetweenNodes > 0.0)
                            {
                                //the stroke angled towards the AB side
                                //this part is easy
                                if (quad1.B == quad2.A)
                                {
                                    abPoints.Add(quad1.B);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    Point intersection = GetIntersection(quad1.A, quad1.B, quad2.A, quad2.B);
                                    Rect  union        = Rect.Union(node1Bounds, node2Bounds);
                                    union.Inflate(1.0, 1.0);
                                    //make sure we're not off in space

#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize * 1.5, feedbackSize * 1.5);
                                        debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                    }
#endif

                                    if (union.Contains(intersection))
                                    {
                                        abPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                        if (showFeedback)
                                        {
                                            debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize);
                                        }
#endif
                                    }
                                    else
                                    {
                                        //if we missed the intersection we'll need to close the stroke segment
                                        //this work is done in StrokeRenderer
                                        missingIntersection = true;
                                        return; //we're done.
                                    }
                                }

                                if (quad1.C == quad2.D)
                                {
                                    dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    //add instructions to arc from quad1.C to quad2.D in reverse order (since we walk this array backwards to render)
                                    dcPoints.Add(quad1.C);
                                    dcPoints.Add(new Point(node2Bounds.Width, node2Bounds.Height));
                                    dcPoints.Add(StrokeRenderer.ArcToMarker);
                                    dcPoints.Add(quad2.D);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                        debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                            }
                            else
                            {
                                //the stroke angled towards the CD side
                                //this part is easy
                                if (quad1.C == quad2.D)
                                {
                                    dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    Point intersection = GetIntersection(quad1.D, quad1.C, quad2.D, quad2.C);
                                    Rect  union        = Rect.Union(node1Bounds, node2Bounds);
                                    union.Inflate(1.0, 1.0);
                                    //make sure we're not off in space

#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize * 1.5, feedbackSize * 1.5);
                                        debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                                    }
#endif

                                    if (union.Contains(intersection))
                                    {
                                        dcPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                        if (showFeedback)
                                        {
                                            debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize);
                                        }
#endif
                                    }
                                    else
                                    {
                                        //if we missed the intersection we'll need to close the stroke segment
                                        //this work is done in StrokeRenderer
                                        missingIntersection = true;
                                        return; //we're done.
                                    }
                                }

                                if (quad1.B == quad2.A)
                                {
                                    abPoints.Add(quad1.B);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                                else
                                {
                                    //we need to arc between quad1.B and quad2.A along node2
                                    abPoints.Add(quad1.B);
                                    abPoints.Add(StrokeRenderer.ArcToMarker);
                                    abPoints.Add(new Point(node2Bounds.Width, node2Bounds.Height));
                                    abPoints.Add(quad2.A);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                        debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                    }
#endif
                                }
                            }
                        }
                        else
                        {
                            //rectangle
                            int indexA = -1;
                            int indexB = -1;
                            int indexC = -1;
                            int indexD = -1;

                            Vector[] vertices       = _operations.GetVertices();
                            double   pressureFactor = _lastNode.PressureFactor;
                            for (int i = 0; i < vertices.Length; i++)
                            {
                                Point point = _lastNode.Position + (vertices[i % vertices.Length] * pressureFactor);
                                if (point == quad2.A)
                                {
                                    indexA = i;
                                }
                                if (point == quad1.B)
                                {
                                    indexB = i;
                                }
                                if (point == quad1.C)
                                {
                                    indexC = i;
                                }
                                if (point == quad2.D)
                                {
                                    indexD = i;
                                }
                            }

                            if (indexA == -1 || indexB == -1 || indexC == -1 || indexD == -1)
                            {
                                Debug.Assert(false, "Couldn't find all 4 indexes in StrokeNodeOperations.GetPointsAtMiddleSegment");
                                return;
                            }

#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            {
                                debugDC.DrawRectangle(null, new Pen(Brushes.Pink, feedbackSize / 2), _operations.GetNodeBounds(_lastNode));
                                debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                            }
#endif

                            Rect node3Rect = _operations.GetNodeBounds(_thisNode);
                            //take care of a-b first
                            if (indexA == indexB)
                            {
                                //quad connection is the same, just add it
                                if (!node3Rect.Contains(quad1.B))
                                {
                                    abPoints.Add(quad1.B);
                                }
                            }
                            else if ((indexA == 0 && indexB == 3) || ((indexA != 3 || indexB != 0) && (indexA > indexB)))
                            {
                                if (!node3Rect.Contains(quad1.B))
                                {
                                    abPoints.Add(quad1.B);
                                }
                                if (!node3Rect.Contains(quad2.A))
                                {
                                    abPoints.Add(quad2.A);
                                }
                            }
                            else
                            {
                                Point intersection = GetIntersection(quad1.A, quad1.B, quad2.A, quad2.B);
                                Rect  node12       = Rect.Union(_operations.GetNodeBounds(previous._lastNode), _operations.GetNodeBounds(_lastNode));
                                node12.Inflate(1.0, 1.0);
                                //make sure we're not off in space
                                if (node12.Contains(intersection))
                                {
                                    abPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize * 1.5);
                                    }
#endif
                                }
                                else
                                {
                                    //if we missed the intersection we'll need to close the stroke segment
                                    //this work is done in StrokeRenderer.
                                    missingIntersection = true;
                                    return; //we're done.
                                }
                            }

                            // now take care of c-d.
                            if (indexC == indexD)
                            {
                                //quad connection is the same, just add it
                                if (!node3Rect.Contains(quad1.C))
                                {
                                    dcPoints.Add(quad1.C);
                                }
                            }
                            else if ((indexC == 0 && indexD == 3) || ((indexC != 3 || indexD != 0) && (indexC > indexD)))
                            {
                                if (!node3Rect.Contains(quad1.C))
                                {
                                    dcPoints.Add(quad1.C);
                                }
                                if (!node3Rect.Contains(quad2.D))
                                {
                                    dcPoints.Add(quad2.D);
                                }
                            }
                            else
                            {
                                Point intersection = GetIntersection(quad1.D, quad1.C, quad2.D, quad2.C);
                                Rect  node12       = Rect.Union(_operations.GetNodeBounds(previous._lastNode), _operations.GetNodeBounds(_lastNode));
                                node12.Inflate(1.0, 1.0);
                                //make sure we're not off in space
                                if (node12.Contains(intersection))
                                {
                                    dcPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize * 1.5);
                                    }
#endif
                                }
                                else
                                {
                                    //if we missed the intersection we'll need to close the stroke segment
                                    //this work is done in StrokeRenderer.
                                    missingIntersection = true;
                                    return; //we're done.
                                }
                            }
                        }
                    }
                }
            }
        }
Exemple #14
0
        /// <summary>
        /// This method tells whether the contour of a given stroke node
        /// intersects with the contour of this node. The contours of both nodes
        /// include their connecting quadrangles.
        /// </summary>
        /// <param name="hitNode"></param>
        /// <returns></returns>
        internal bool HitTest(StrokeNode hitNode)
        {
            if (!IsValid || !hitNode.IsValid)
            {
                return false;
            }

            IEnumerable<ContourSegment> hittingContour = hitNode.GetContourSegments();

            return _operations.HitTest(_lastNode, _thisNode, ConnectingQuad, hittingContour);
        }
Exemple #15
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="newFIndices"></param>
 /// <param name="strokeNode"></param>
 public LassoCrossing(StrokeFIndices newFIndices, StrokeNode strokeNode)
 {
     System.Diagnostics.Debug.Assert(!newFIndices.IsEmpty);
     System.Diagnostics.Debug.Assert(strokeNode.IsValid);
     FIndices = newFIndices;
     StartNode = EndNode = strokeNode;
 }
Exemple #16
0
        /// <summary>
        /// Finds out if a given node intersects with this one,
        /// and returns findices of the intersection.
        /// </summary>
        /// <param name="hitNode"></param>
        /// <returns></returns>
        internal StrokeFIndices CutTest(StrokeNode hitNode)
        {
            if ((IsValid == false) || (hitNode.IsValid == false))
            {
                return StrokeFIndices.Empty;
            }

            IEnumerable<ContourSegment> hittingContour = hitNode.GetContourSegments();

            // If the node contours intersect, the result is a pair of findices
            // this segment should be cut at to let the hitNode's contour through it.
            StrokeFIndices cutAt = _operations.CutTest(_lastNode, _thisNode, ConnectingQuad, hittingContour);

            return (_index == 0) ? cutAt : BindFIndices(cutAt);
        }
Exemple #17
0
        /// <summary>
        /// Helper routine to render two distinct stroke nodes
        /// </summary>
        private static void RenderTwoStrokeNodes(   StreamGeometryContext context,
                                                    StrokeNode strokeNodePrevious,
                                                    Rect strokeNodePreviousBounds,
                                                    StrokeNode strokeNodeCurrent,
                                                    Rect strokeNodeCurrentBounds,
                                                    List<Point> pointBuffer1,
                                                    List<Point> pointBuffer2,
                                                    List<Point> pointBuffer3
#if DEBUG_RENDERING_FEEDBACK
                                                   ,DrawingContext debugDC,
                                                   double feedbackSize,
                                                   bool showFeedback
#endif
                                                    )
        {
            Debug.Assert(pointBuffer1 != null);
            Debug.Assert(pointBuffer2 != null);
            Debug.Assert(pointBuffer3 != null);
            Debug.Assert(context != null);
            
            
            //see if we need to render a quad - if there is not at least a 70% overlap
            if (FuzzyContains(strokeNodePreviousBounds, strokeNodeCurrentBounds, 70d) != RectCompareResult.NoItersection)
            {
                //we're between 100% and 70% overlapped
                //just render two distinct figures with a connecting quad (if needed)
                strokeNodePrevious.GetContourPoints(pointBuffer1);
                AddFigureToStreamGeometryContext(context, pointBuffer1, strokeNodePrevious.IsEllipse/*isBezierFigure*/);

                Quad quad = strokeNodeCurrent.GetConnectingQuad();
                if (!quad.IsEmpty)
                {
                    pointBuffer3.Add(quad.A);
                    pointBuffer3.Add(quad.B);
                    pointBuffer3.Add(quad.C);
                    pointBuffer3.Add(quad.D);
                    AddFigureToStreamGeometryContext(context, pointBuffer3, false/*isBezierFigure*/);
                }

                strokeNodeCurrent.GetContourPoints(pointBuffer2);
                AddFigureToStreamGeometryContext(context, pointBuffer2, strokeNodeCurrent.IsEllipse/*isBezierFigure*/);
            }
            else
            {
                //we're less than 70% overlapped, it's safe to run our optimization
#if DEBUG_RENDERING_FEEDBACK
                strokeNodeCurrent.GetPointsAtStartOfSegment(pointBuffer1, pointBuffer2, debugDC, feedbackSize, showFeedback);
                strokeNodeCurrent.GetPointsAtEndOfSegment(pointBuffer1, pointBuffer2, debugDC, feedbackSize, showFeedback);
#else
                strokeNodeCurrent.GetPointsAtStartOfSegment(pointBuffer1, pointBuffer2);
                strokeNodeCurrent.GetPointsAtEndOfSegment(pointBuffer1, pointBuffer2);
#endif
                //render
                ReverseDCPointsRenderAndClear(context, pointBuffer1, pointBuffer2, pointBuffer3, strokeNodeCurrent.IsEllipse, false/*clear the point collections*/);
            }
        }
Exemple #18
0
        internal StrokeIntersection[] HitTest(StrokeNodeIterator iterator)
        {
            System.Diagnostics.Debug.Assert(_points != null);
            System.Diagnostics.Debug.Assert(iterator != null);

            if (_points.Count < 3)
            {
                //
                // it takes at least 3 points to create a lasso
                //
                return(Array.Empty <StrokeIntersection>());
            }

            //
            // We're about to perform hit testing with a lasso.
            // To do so we need to iterate through each StrokeNode.
            // As we do, we calculate the bounding rect between it
            // and the previous StrokeNode and store this in 'currentStrokeSegmentBounds'
            //
            // Next, we check to see if that StrokeNode pair's bounding box intersects
            // with the bounding box of the Lasso points.  If not, we continue iterating through
            // StrokeNode pairs.
            //
            // If it does, we do a more granular hit test by pairing points in the Lasso, getting
            // their bounding box and seeing if that bounding box intersects our current StrokeNode
            // pair
            //

            Point lastNodePosition           = new Point();
            Point lassoLastPoint             = _points[_points.Count - 1];
            Rect  currentStrokeSegmentBounds = Rect.Empty;

            // Initilize the current crossing to be an empty one
            LassoCrossing currentCrossing = LassoCrossing.EmptyCrossing;

            // Creat a list to hold all the crossings
            List <LassoCrossing> crossingList = new List <LassoCrossing>();

            for (int i = 0; i < iterator.Count; i++)
            {
                StrokeNode strokeNode = iterator[i];
                Rect       nodeBounds = strokeNode.GetBounds();
                currentStrokeSegmentBounds.Union(nodeBounds);

                // Skip the node if it's outside of the lasso's bounds
                if (currentStrokeSegmentBounds.IntersectsWith(_bounds) == true)
                {
                    // currentStrokeSegmentBounds, made up of the bounding box of
                    // this StrokeNode unioned with the last StrokeNode,
                    // intersects the lasso bounding box.
                    //
                    // Now we need to iterate through the lasso points and find out where they cross
                    //
                    Point lastPoint = lassoLastPoint;
                    foreach (Point point in _points)
                    {
                        //
                        // calculate a segment of the lasso from the last point
                        // to the current point
                        //
                        Rect lassoSegmentBounds = new Rect(lastPoint, point);

                        //
                        // see if this lasso segment intersects with the current stroke segment
                        //
                        if (!currentStrokeSegmentBounds.IntersectsWith(lassoSegmentBounds))
                        {
                            lastPoint = point;
                            continue;
                        }

                        //
                        // the lasso segment DOES intersect with the current stroke segment
                        // find out precisely where
                        //
                        StrokeFIndices strokeFIndices = strokeNode.CutTest(lastPoint, point);

                        lastPoint = point;
                        if (strokeFIndices.IsEmpty)
                        {
                            // current lasso segment does not hit the stroke segment, continue with the next lasso point
                            continue;
                        }

                        // Create a potentially new crossing for the current hit testing result.
                        LassoCrossing potentialNewCrossing = new LassoCrossing(strokeFIndices, strokeNode);

                        // Try to merge with the current crossing. If the merge is succussful (return true), the new crossing is actually
                        // continueing the current crossing, so do not start a new crossing. Otherwise, start a new one and add the existing
                        // one to the list.
                        if (!currentCrossing.Merge(potentialNewCrossing))
                        {
                            // start a new crossing and add the existing on to the list
                            crossingList.Add(currentCrossing);
                            currentCrossing = potentialNewCrossing;
                        }
                    }
                }

                // Continue with the next node
                currentStrokeSegmentBounds = nodeBounds;
                lastNodePosition           = strokeNode.Position;
            }


            // Adding the last crossing to the list, if valid
            if (!currentCrossing.IsEmpty)
            {
                crossingList.Add(currentCrossing);
            }

            // Handle the special case of no intersection at all
            if (crossingList.Count == 0)
            {
                // the stroke was either completely inside the lasso
                // or outside the lasso
                if (this.Contains(lastNodePosition))
                {
                    StrokeIntersection[] strokeIntersections = new StrokeIntersection[1];
                    strokeIntersections[0] = StrokeIntersection.Full;
                    return(strokeIntersections);
                }
                else
                {
                    return(Array.Empty <StrokeIntersection>());
                }
            }

            // It is still possible that the current crossing list is not sorted or overlapping.
            // Sort the list and merge the overlapping ones.
            SortAndMerge(ref crossingList);

            // Produce the hit test results and store them in a list
            List <StrokeIntersection> strokeIntersectionList = new List <StrokeIntersection>();

            ProduceHitTestResults(crossingList, strokeIntersectionList);

            return(strokeIntersectionList.ToArray());
        }
Exemple #19
-2
            /// <summary>
            /// Merge two crossings into one.
            /// </summary>
            /// <param name="crossing"></param>
            /// <returns>Return true if these two crossings are actually overlapping and merged; false otherwise</returns>
            public bool Merge(LassoCrossing crossing)
            {
                if (crossing.IsEmpty)
                {
                    return false;
                }

                if (FIndices.IsEmpty && !crossing.IsEmpty)
                {
                    FIndices = crossing.FIndices;
                    StartNode = crossing.StartNode;
                    EndNode = crossing.EndNode;
                    return true;
                }

                if(DoubleUtil.GreaterThanOrClose(crossing.FIndices.EndFIndex, FIndices.BeginFIndex) &&
                    DoubleUtil.GreaterThanOrClose(FIndices.EndFIndex, crossing.FIndices.BeginFIndex))
                {
                    if (DoubleUtil.LessThan(crossing.FIndices.BeginFIndex, FIndices.BeginFIndex))
                    {
                        FIndices.BeginFIndex = crossing.FIndices.BeginFIndex;
                        StartNode = crossing.StartNode;
                    }

                    if (DoubleUtil.GreaterThan(crossing.FIndices.EndFIndex, FIndices.EndFIndex))
                    {
                        FIndices.EndFIndex =  crossing.FIndices.EndFIndex;
                        EndNode = crossing.EndNode;
                    }
                    return true;
                }

                return false;
            }