Exemple #1
0
        public override VerbResult OtherVerb(EditableView.ClickPosition position, SAW.Functions.Codes code)
        {
            switch (code)
            {
            case SAW.Functions.Codes.Increment:
            case SAW.Functions.Codes.Decrement:
                int   delta = code == SAW.Functions.Codes.Increment ? 1 : -1;
                float step  = position.ScalarSnapStep(0);
                if (step <= 0)
                {
                    step = delta * Globals.Root.CurrentConfig.ReadSingle(Config.Radius_Step, 1);
                }
                else
                {
                    step *= delta;
                }
                SizeF vector = Vertices[0].VectorTo(Vertices[1]);
                float length = vector.Length();
                if (length + step < 5 || length < Geometry.NEGLIGIBLESMALL)
                {
                    return(VerbResult.Rejected);
                }
                Vertices[1] = Vertices[0] + vector.ChangeLength(length + step);
                m_Bounds    = RectangleF.Empty;
                return(VerbResult.Continuing);

            default:
                return(VerbResult.Rejected);
            }
        }
Exemple #2
0
        /// <summary>
        /// Returns the type of manipulation that will happen when interacting with the element at the specified point.
        /// </summary>
        /// <param name="point">The point to start manipulating.</param>
        /// <param name="altDown">Whether any alt key is pressed.</param>
        /// <param name="preview">whether to set the preview manipulation, or the real one.</param>
        /// <param name="translateOnly">Whether to ignore any special manipulations and only use translate.</param>
        /// <returns>The manipulation type for the specified point. <c>null</c> if no manipulation would happen
        /// at this point.</returns>
        /// <remarks>Manipulation preview is used to show what would be modified on a selected element. We cannot
        /// keep updating the element manipulation as the mouse moves, but do want to provide a visual indicator.</remarks>
        public override bool StartManipulating(Point point, bool altDown, bool preview = false, bool translateOnly = false)
        {
            SizeF d = point - this.Location;

            if (d.Length() > this.Radius + 2)
            {
                this.PreviewManipulation = null;
                return(false);
            }

            // Scale if mouse over the outter edge.
            if (Math.Sqrt(Math.Abs(Math.Pow(d.Width, 2) + Math.Pow(d.Height, 2) - Math.Pow(this.Radius, 2))) < 16 && !translateOnly)
            {
                this.SetManipulation(
                    new ElementManipulation
                {
                    Type      = ElementManipulationType.Scale,
                    Index     = 0,
                    Direction = d.GetUnitVector()
                },
                    preview);
                return(true);
            }

            this.SetManipulation(
                new ElementManipulation
            {
                Type  = ElementManipulationType.Translate,
                Index = 0
            },
                preview);

            return(true);
        }
Exemple #3
0
        /// <summary>
        /// Returns the unit vector of the specified speed.
        /// </summary>
        /// <param name="speed">The speed to get the unit vector of.</param>
        /// <returns>The unit vector.</returns>
        public static SizeF GetUnitVector(this SizeF speed)
        {
            /* The unit vector is calculated as
             *
             * v * ||v||
             */

            return(speed.Multiply(1 / speed.Length()));
        }
Exemple #4
0
        /// <summary>
        /// Renders the key in the specified surface.
        /// </summary>
        /// <param name="g">The GDI+ surface to render on.</param>
        /// <param name="speed">The speed of the mouse.</param>
        public void Render(Graphics g, SizeF speed)
        {
            var subStyle = GlobalSettings.CurrentStyle.TryGetElementStyle <MouseSpeedIndicatorStyle>(this.Id)
                           ?? GlobalSettings.CurrentStyle.DefaultMouseSpeedIndicatorStyle;

            // Small circles have a fifth of the radius of the full control.
            var smallRadius = (float)this.Radius / 5;

            // The sensitivity is a factor over the mouse speed.
            var sensitivity = GlobalSettings.Settings.MouseSensitivity / (float)100;

            // The total length is determined by the sensitivity, speed and radius. But never more than the radius.
            var pointerLength = (int)Math.Min(this.Radius, sensitivity * speed.Length() * this.Radius);

            var colorMultiplier = Math.Max(0, Math.Min(1, (float)pointerLength / this.Radius));

            Color color1     = subStyle.InnerColor;
            Color outerColor = subStyle.OuterColor;
            // The second color should be averaged over the two specified colours, based upon how far out the thingymabob is.
            var color2 = Color.FromArgb(
                (int)(color1.R * (1 - colorMultiplier) + outerColor.R * colorMultiplier),
                (int)(color1.G * (1 - colorMultiplier) + outerColor.G * colorMultiplier),
                (int)(color1.B * (1 - colorMultiplier) + outerColor.B * colorMultiplier));

            // Draw the edge.
            g.DrawEllipse(
                new Pen(color1, subStyle.OutlineWidth),
                Geom.CircleToRectangle(this.Location, this.Radius));

            // Only calculate the pointer data if it has some length.
            if (pointerLength > 0)
            {
                // Determine the angle of the pointer.
                var angle = speed.GetAngle();

                // Determine the location of the pointer end.
                var pointerEnd = this.Location.CircularTranslate(pointerLength, angle);

                // If the pointer doesn't end where it starts, draw it.
                if (pointerEnd.X != this.Location.X || pointerEnd.Y != this.Location.Y)
                {
                    // Draw the pointer, as a pie.
                    g.FillPie(
                        new LinearGradientBrush(this.Location, pointerEnd, color1, color2),
                        Geom.CircleToRectangle(this.Location, pointerLength),
                        Geom.RadToDeg(angle) - 10,
                        20);

                    // Draw a circle on the outter edge in the direction of the pointer.
                    var pointerEdge = this.Location.CircularTranslate(this.Radius, angle);
                    g.FillEllipse(new SolidBrush(color2), Geom.CircleToRectangle(pointerEdge, (int)smallRadius));
                }
            }

            // Draw the circle in the center.
            g.FillEllipse(new SolidBrush(color1), Geom.CircleToRectangle(this.Location, (int)smallRadius));
        }
Exemple #5
0
        protected internal override void DoGrabMove(GrabMovement move)
        {
            switch (move.GrabType)
            {
            case GrabTypes.SingleVertex:
            {
                int index    = move.ShapeIndex;
                int previous = (index + 3) % 4;
                int next     = (index + 1) % 4;
                int opposite = (index + 2) % 4;
                // keep orientation unchanged.  Opposite point is only invariant.  Calc vectors from this to the 2 semi-moving points
                SizeF previousVector = Vertices[opposite].VectorTo(Vertices[previous]);
                SizeF nextVector     = Vertices[opposite].VectorTo(Vertices[next]);
                SizeF movingVector   = Vertices[opposite].VectorTo(move.Current.Snapped);
                previousVector = Geometry.ProjectionVector(movingVector, previousVector);
                nextVector     = Geometry.ProjectionVector(movingVector, nextVector);
                if (previousVector.Length() < Geometry.THINLINE || nextVector.Length() < Geometry.THINLINE)
                {
                    return;                                     // degenerate
                }
                Vertices[next]     = Vertices[opposite] + nextVector;
                Vertices[previous] = Vertices[opposite] + previousVector;
                Vertices[index]    = Vertices[previous] + nextVector;
                m_Bounds           = CalculateBounds();
            }
            break;

            case GrabTypes.Radius:
            {
                int    vertex    = move.ShapeIndex;                           // vertex at start of line
                int    end       = (move.ShapeIndex + 1) % 4;                 // and end of line
                int    previous  = (move.ShapeIndex + 3) % 4;                 // vertex at start of previous line leading to intVertex
                int    opposite  = (move.ShapeIndex + 2) % 4;                 // and at opposite corner; end of line leading from intEnd
                PointF newPoint  = Geometry.ClosestPointOnLine(Vertices[previous], Vertices[vertex], move.Current.Snapped);
                SizeF  newVector = Vertices[previous].VectorTo(newPoint);     // new length of the sides which are being stretched
                if (newVector.Length() < Geometry.THINLINE)
                {
                    return;
                }
                Vertices[vertex] = newPoint;
                Vertices[end]    = Vertices[opposite] + newVector;
                m_Bounds         = CalculateBounds();
            }
            break;

            default:
                base.DoGrabMove(move);
                break;
            }
            DiscardPath();
        }
Exemple #6
0
        internal static SizeF AngleSnapVector(SizeF sz, float stepAngle = -1)
        {
            if (stepAngle <= 0)
            {
                stepAngle = AngleStep();
            }
            if (sz.IsEmpty)
            {
                Debug.Fail("Cannot AngleSnapVector empty vector");
                return(sz);
            }
            float angle = sz.VectorAngle();

            angle = (float)(Math.Round(angle / stepAngle) * stepAngle);
            return(ScalarToVector(sz.Length(), angle));
        }
Exemple #7
0
        private bool AdjustPoint(int index, EditableView.ClickPosition position)
        {
            // Used by both Float and DoGrabMove.  Returns true if the point is valid
            PointF pt = position.Snapped;
            // the main task is to restrict the line to the correct directions
            float angularStep = GetAngularStep(position.Page);
            SizeF vector      = Vertices[1 - index].VectorTo(pt);

            if (vector.IsEmpty)
            {
                return(false);
            }
            // the following calculations will fail if the two points are on top of each other
            // but there is no need to do any of this - the position of point(0) is a valid position (ish)
            // calculate the angle in radians of this line counting clockwise from vertically up (must count from vertically in order to match the isometric paper)
            float angle  = vector.VectorAngle();
            float length = vector.Length();

            angle = (float)(Math.Round(angle / angularStep) * angularStep);
            // angle changed to the closest of the allowed angles
            // now snapped the length so that it will hit a grid point
            // the complication is if we are going diagonally on squared paper, in which case the incremental length is sqrt(2) * spacing.
            // the following function should take care of this
            float step = position.ScalarSnapStep(angle);

            if (step > 0)
            {
                length = (float)Math.Round(length / step) * step;                 // make it a multiple of a grid unit
            }
            vector = Geometry.ScalarToVector(length, angle);
            pt     = PointF.Add(Vertices[1 - index], vector);
            // and finally we snap the resulting point back to the page grid - it should already be on this, but just in case
            // of any rounding errors
            if (position.RequestedSnap == SnapModes.Grid)
            {
                position.Snapped = position.Page.Paper.SnapPoint2(pt);
            }
            else
            {
                position.Snapped = pt;
            }
            // in order to avoid flickering it is worth checking if the target has actually moved - because this line is very constrained often it does not move at all, or and repainting a completely unchanged line is the most flickery situation
            return(true);
        }
Exemple #8
0
        internal override SizeF SocketExitVector(int index)
        {
            EnsureSocketList();
            if (index < 0 || index >= m_Sockets.Count)
            {
                Debug.Fail("Invalid socket index");
                return(new SizeF(1, 0));
            }
            SizeF exit = m_Sockets[index].ExitVector;

            // we cannot return SizeF.Empty; if the user has not specified then exit vector we will deduce it based on the group
            if (exit.IsEmpty)
            {
                exit = Middle().VectorTo(m_Sockets[index].Centre);
                // and just in case the user has managed to place the socket right in the middle
                if (exit.Length() < Geometry.NEGLIGIBLE)
                {
                    exit = new SizeF(1, 0);
                }
            }
            return(exit);
        }
Exemple #9
0
        private const float TRANSVERSECLEARANCE = 5;               // clearance to leave around shape when stepping around it
        private void PositionLine()
        {
            // This builds up a list of points required at each of the beginning and end
            List <PointF> start = new List <PointF> {
                StartPoint
            };
            List <PointF> end = new List <PointF> {
                FinishPoint
            };
            SizeF vector = StartPoint.VectorTo(FinishPoint);

            if (vector.Length() < 1)
            {
                return;
            }
            float primary = PrimaryVector(vector).VectorAngle();

            ProcessExitVector(start, 0, primary);
            ProcessExitVector(end, 1, primary + Geometry.ANGLE180);

            // The remaining required vector after adding on the exit vectors...
            bool[] conflict = new bool[2];
            int    index    = 0;
            int    step     = 0;
            bool   done     = false;

            do
            {
                if (index == 0)
                {
                    vector = start[start.Count - 1].VectorTo(end[end.Count - 1]);
                }
                else                 // opposite direction reverses the vector...
                {
                    vector = end[end.Count - 1].VectorTo(start[start.Count - 1]);
                }
                SizeF primaryVector        = PrimaryVector(vector);
                SizeF transverseGuaranteed = TransverseVector(vector, true);
                SizeF transverseNull       = TransverseVector(vector, false);

                List <PointF> points = index == 0 ? start : end;
                List <PointF> other  = index == 1 ? start : end;
                conflict[index]     = PrimaryVectorConflicts(points, primaryVector, m_Links[index].Shape);
                conflict[1 - index] = PrimaryVectorConflicts(other, new SizeF(0, 0) - primaryVector, m_Links[1 - index].Shape);
                if (conflict[index])
                {
                    // If the other shape is not conflicted, the best strategy can be to move the entire transverse distance now (assuming that is far enough to clear this shape)
                    PointF pt = points[points.Count - 1] + transverseGuaranteed;
                    if (!conflict[1 - index] && !PrimaryVectorConflicts(pt, primaryVector, m_Links[index].Shape))
                    {
                        // prime vector won't conflict once entire distance away
                        points.Add(pt);
                        done = true;
                    }
                    else
                    {
                        // can't go correct distance away - probably not far enough to clear this shape
                        // therefore go far enough to clear this one
                        pt = points[points.Count - 1] + GetRequiredTransverseToClearShape(points[points.Count - 1], m_Links[index].Shape, primaryVector, transverseGuaranteed);
                        points.Add(pt);
                    }
                }
                else if (!conflict[1 - index])
                {
                    // neither now conflict - place the line as necessary
                    // first we check if extending one of the previous vectors, which is in the transverse direction
                    // will work.  This saves an extra wiggle (which does look quite silly) when the exits were, say, vertical and the current primary
                    // is horizontal, but there is nothing constraining us from just extending the exit vectors with a single horizontal line
                    SizeF last      = LastVector(points);
                    SizeF otherLast = LastVector(other);
                    if (VectorsMatch(last, transverseGuaranteed))
                    {
                        if (VectorsMatch(new SizeF(0, 0) - otherLast, transverseGuaranteed))
                        {
                            // both match, ideal option is to move half long each
                            points.Add(points[points.Count - 1] + transverseGuaranteed.MultiplyBy(0.5f));
                            other.Add(other[other.Count - 1] + transverseGuaranteed.MultiplyBy(-0.5f));
                        }
                        else
                        {
                            points.Add(points[points.Count - 1] + transverseGuaranteed);
                        }
                        done = true;
                    }
                    else if (VectorsMatch(new SizeF(0, 0) - otherLast, transverseGuaranteed))
                    {
                        other.Add(other[other.Count - 1] + transverseGuaranteed.MultiplyBy(-1));
                        done = true;
                    }
                    else
                    {
                        SizeF first = ChooseCrossStepPosition(index, points, other, primaryVector);
                        if (!first.IsEmpty)
                        {
                            PointF pt = points[points.Count - 1] + first;
                            points.Add(pt);
                            if (!transverseNull.IsEmpty)
                            {
                                pt = pt + transverseNull;
                                points.Add(pt);
                            }
                        }
                        done = true;
                    }
                }
                index = (index + 1) % 2;
                step += 1;
            } while (!(step >= 8 || done));

            // And now merge these lists to create the new list of vertices
            Vertices.Clear();
            Vertices.AddRange(start);
            end.Reverse();
            Vertices.AddRange(end);
            DecacheArrowheads();
            DiscardPath();
        }
Exemple #10
0
 internal static float ProjectionScalar(SizeF project, SizeF ontoDirection)
 {
     // calculates the projection of the first parameter in the direction given by the second parameter. (i.e. length of first parameter in the direction of the second)
     //The length of the second parameter does not affect the result
     return(project.DotProduct(ontoDirection) / ontoDirection.Length());
 }
Exemple #11
0
        internal static SizeF ChangeLength(this SizeF vector, float length)
        {
            float multiply = length / vector.Length();

            return(new SizeF(vector.Width * multiply, vector.Height * multiply));
        }