Пример #1
0
    private void inkoverlay_Stroke(object sender, InkCollectorStrokeEventArgs e)
    {
        dbg.WriteLine("----- inkoverlay_Stroke -----");

        // Ensure we're on the UI thread.
        dbg.Assert(!this.InvokeRequired);

        // Check to make sure we're not erasing.
        if (inkoverlay.EditingMode == InkOverlayEditingMode.Delete)
        {
            return;
        }

        try         // To prevent exceptions from propagating back to ink runtime.
        {
            // Hook for tap-to-select feature, in lasso-mode.
            if (inkoverlay.EditingMode == InkOverlayEditingMode.Select)
            {
                TryTapToSelect(e.Stroke);
                return;
            }

            // Analyze stroke geometry.
            bool    closed;
            Point[] vertices;
            double  tolerance = 500.0;            //Heuristic: 500 seems about right.
            StrokeAnalyzer.AnalyzeClosedness(e.Stroke, tolerance, out closed, out vertices);

            // Interpret stroke in document-context: first, consider closed strokes.
            if (closed)
            {
                // Check for a small elliptical-gesture over two or more bodies.
                // If so, it's a pin joint!
                Rectangle bbox = e.Stroke.GetBoundingBox();
                Point     midp = Geometry.Midpoint(bbox);

                RigidBodyBase[] hits = doc.HitTestBodies(midp);

                if (hits.Length >= 2 && bbox.Width < 1000 && bbox.Height < 1000)
                {
                    RigidBodyBase top    = hits[0];
                    RigidBodyBase bottom = hits[1];

                    // Snap to CG if close to either top's or bottom's.
                    if (Geometry.DistanceBetween(midp, top.CG) < 500.0)
                    {
                        midp = top.CG;
                    }
                    else if (Geometry.DistanceBetween(midp, bottom.CG) < 500.0)
                    {
                        midp = bottom.CG;
                    }

                    dbg.WriteLine("new JointMech");

                    JointMech newmech = new JointMech();
                    newmech.EndpointA = BodyRef.For(bottom, midp);
                    newmech.EndpointB = BodyRef.For(top, midp);

                    newmech.strokeid = e.Stroke.Id;
                    doc.Mechanisms.Add(newmech);

                    // Repaint area around the newmech (unions with stroke bbox, below).
                    Rectangle dirtybbox = newmech.BoundingBox;
                    InvalidateInkSpaceRectangle(dirtybbox);
                    return;
                }
                else
                {
                    // Larger stroke, and/or no centerpoint hits -- form a new solid body.
                    RigidBodyBase newbody = MakeBodyFromClosedStroke(e.Stroke, vertices);

                    // Repaint area around the newbody (unions with stroke bbox, below).
                    Rectangle dirtybbox = newbody.BoundingBox;
                    InvalidateInkSpaceRectangle(dirtybbox);

                    // Select it, to show the smart tag.
                    inkoverlay.Selection = this.MakeSingleStrokeCollection(e.Stroke);
                    return;
                }
            }

            // An unclosed stroke --
            // Check if head and/or tail is hit on existing bodies.
            int   np = e.Stroke.PacketCount;
            Point head = e.Stroke.GetPoint(0), tail = e.Stroke.GetPoint(np - 1);

            RigidBodyBase[] headhits = doc.HitTestBodies(head);
            RigidBodyBase[] tailhits = doc.HitTestBodies(tail);

            if (headhits.Length == 0 && tailhits.Length == 0)
            {
                // Neither head or tail hit, so let's try harder to make a body
                // out of this stroke.
                Point[] dummy;
                tolerance = 2000.0;                 //Heuristic: vastly relax closure tolerance.
                StrokeAnalyzer.AnalyzeClosedness(e.Stroke, tolerance, out closed, out dummy);

                if (closed)
                {
                    RigidBodyBase newbody = MakeBodyFromClosedStroke(e.Stroke, vertices);

                    // Repaint area around the newbody (unions with stroke bbox, below).
                    Rectangle dirtybbox = newbody.BoundingBox;
                    InvalidateInkSpaceRectangle(dirtybbox);

                    // Select it, to show the smart tag.
                    inkoverlay.Selection = this.MakeSingleStrokeCollection(e.Stroke);
                    return;
                }
                else if (Geometry.DistanceBetween(head, tail) > 500.0)
                {
                    // Interpret this stroke as a gravity-vector.
                    GravitationalForceMech newgrav = new GravitationalForceMech();
                    newgrav.Body   = null;                   // Applies to all bodies!
                    newgrav.origin = head;
                    newgrav.vector = Vector.FromPoints(head, tail);

                    // Repaint area around the gravity vector (unions with stroke bbox, below).
                    Rectangle dirtybbox = newgrav.BoundingBox;
                    InvalidateInkSpaceRectangle(dirtybbox);

                    // Throw out the current gravity-vector stroke, if it exists.
                    if (doc.Gravity != null)
                    {
                        dirtybbox = doc.Gravity.BoundingBox;
                        InvalidateInkSpaceRectangle(dirtybbox);

                        Stroke old = GetStrokeById(doc.Gravity.strokeid);
                        doc.Ink.DeleteStroke(old);
                    }

                    newgrav.strokeid = e.Stroke.Id;
                    doc.Gravity      = newgrav;
                    return;
                }
                else
                {
                    // This stroke is probably an accidental tap -- discard it.
                    e.Cancel = true;
                    return;
                }
            }

            if (headhits.Length > 0 && tailhits.Length == 0)
            {
                // If only the head is hit, it must be an 'attractive force'.
                RigidBodyBase body = headhits[0];

                dbg.WriteLine("new ExternalForceMech");

                ExternalForceMech newmech = new ExternalForceMech();
                newmech.Body   = BodyRef.For(body, head);
                newmech.vector = Vector.FromPoints(head, tail);

                newmech.strokeid = e.Stroke.Id;
                doc.Mechanisms.Add(newmech);

                // Repaint area around the newmech (unions with stroke bbox, below).
                Rectangle dirtybbox = newmech.BoundingBox;
                InvalidateInkSpaceRectangle(dirtybbox);
                return;
            }

            if (headhits.Length == 0 && tailhits.Length > 0)
            {
                // If only the tail is hit, it must be a 'propulsive force'.
                RigidBodyBase body = tailhits[0];

                dbg.WriteLine("new PropulsiveForceMech");

                PropulsiveForceMech newmech = new PropulsiveForceMech();
                newmech.Body   = BodyRef.For(body, tail);
                newmech.vector = Vector.FromPoints(head, tail);

                newmech.strokeid = e.Stroke.Id;
                doc.Mechanisms.Add(newmech);

                // Repaint area around the newmech (unions with stroke bbox, below).
                Rectangle dirtybbox = newmech.BoundingBox;
                InvalidateInkSpaceRectangle(dirtybbox);
                return;
            }

            if (true)             // scope
            {
                // Create a binding mechanism between two bodies.
                RigidBodyBase headbody = headhits[0], tailbody = tailhits[0];

                // If both the head and the tail hit same object,
                // attach the head to the one behind.
                if (Object.ReferenceEquals(headbody, tailbody))
                {
                    if (headhits.Length > 1)
                    {
                        headbody = headhits[1];
                    }
                    else if (tailhits.Length > 1)
                    {
                        tailbody = tailhits[1];
                    }
                    else
                    {
                        // Don't self-connect. We will perhaps interpret the stroke as an
                        // anchor-gesture or a selection-gesture,
                        // but if we cannot, we will cancel.
                        int nc = e.Stroke.PolylineCusps.Length;
                        if (np <= 25)
                        {
                            inkoverlay.Selection = MakeSingleStrokeCollection(headbody.strokeid);
                            SetEditingMode(InkOverlayEditingMode.Select, false);
                        }
                        else if (np <= 150 && (nc >= 3 && nc <= 5))
                        {
                            headbody.anchored = !headbody.anchored;                             //toggle
                        }

                        e.Cancel = true;

                        // Repaint area around the headbody (unions with stroke bbox, below).
                        Rectangle dirtybbox = headbody.BoundingBox;
                        InvalidateInkSpaceRectangle(dirtybbox);
                        return;
                    }
                }

                // Create a rope, rod, or spring out of the stroke.
                MechanismBase newmech = MakeRodRopeOrSpring(e.Stroke, headbody, tailbody);

                if (newmech != null)
                {
                    // Repaint area around the newmech (unions with stroke bbox, below).
                    Rectangle dirtybbox = newmech.BoundingBox;
                    InvalidateInkSpaceRectangle(dirtybbox);
                    return;
                }
                else
                {
                    // Throw out the stroke, and return.
                    e.Cancel = true;
                    return;
                }
            }
        }
        catch (Exception ex)
        {
            // Cancel the stroke, and log the error.
            e.Cancel = true;
            Global.HandleThreadException(this, new System.Threading.ThreadExceptionEventArgs(ex));
        }
        finally
        {
            // Repaint the area around the stroke (unions with newbody/mech region, above).
            Rectangle dirtybbox = e.Stroke.GetBoundingBox();
            InvalidateInkSpaceRectangle(dirtybbox);
        }
    }
Пример #2
0
        static bool CompareDocuments(MagicDocument a, MagicDocument b)
        {
            if (a.version != b.version)
            {
                return(false);
            }
            if (a.Bodies.Count != b.Bodies.Count)
            {
                return(false);
            }
            if (a.Mechanisms.Count != b.Mechanisms.Count)
            {
                return(false);
            }
            if (a.xml_Ink != b.xml_Ink)
            {
                return(false);
            }

            for (int i = 0; i < a.Bodies.Count; ++i)
            {
                RigidBodyBase a1 = ((RigidBodyBase)a.Bodies[i]);
                RigidBodyBase b1 = ((RigidBodyBase)b.Bodies[i]);
                if (a1.GetType() != b1.GetType())
                {
                    return(false);
                }
                if (a1.strokeid != b1.strokeid)
                {
                    return(false);
                }
                if (a1.anchored != b1.anchored)
                {
                    return(false);
                }
                if (a1.cfriction != b1.cfriction)
                {
                    return(false);
                }
                if (a1.density != b1.density)
                {
                    return(false);
                }
            }

            for (int i = 0; i < a.Mechanisms.Count; ++i)
            {
                MechanismBase a1 = ((MechanismBase)a.Mechanisms[i]);
                MechanismBase b1 = ((MechanismBase)b.Mechanisms[i]);
                if (a1.GetType() != b1.GetType())
                {
                    return(false);
                }
                if (a1 is ForceMechanismBase)
                {
                    ForceMechanismBase a2 = (ForceMechanismBase)a1;
                    ForceMechanismBase b2 = (ForceMechanismBase)b1;
                    if (a2.Body.strokeref != b2.Body.strokeref)
                    {
                        return(false);
                    }
                    if (a2.Body.attachloc != b2.Body.attachloc)
                    {
                        return(false);
                    }
                    if (a2.vector != b2.vector)
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }