Example #1
0
        public virtual void Rotate(Xwt.Drawing.Context ctx, double x, double y)
        {
            // draws a line along the x-axis from (0,0) to (r,0) with a constant translation and an increasing
            // rotational component. This composite transform is then applied to a vertical line, with inverse
            // color, and an additional x-offset, to form a mirror image figure for easy visual comparison.
            // These transformed points must be drawn with the identity CTM, hence the Restore() each time.

            ctx.Save();                 // save caller's context (assumed to be the Identity CTM)
            ctx.SetLineWidth(3);        // should align exactly if drawn with half-pixel coordinates

            // Vector length (pixels) and rotation limit (degrees)
            double r   = 30;
            double end = 270;

            for (double n = 0; n <= end; n += 5)
            {
                ctx.Save();                     // save context and identity CTM for each line

                // Set up translation to centre point of first figure, ensuring pixel alignment
                ctx.Translate(x + 30.5, y + 30.5);
                ctx.Rotate(n);
                ctx.MoveTo(0, 0);
                ctx.RelLineTo(r, 0);
                double c = n / end;
                ctx.SetColor(new Color(c, c, c));
                ctx.Stroke();                   // stroke first figure with composite Translation and Rotation CTM

                // Generate mirror image figure as a visual test of TransformPoints
                Point   p0 = new Point(0, 0);
                Point   p1 = new Point(0, -r);
                Point[] p  = new Point[] { p0, p1 };
                ctx.TransformPoints(p);         // using composite transformation

                ctx.Restore();                  // restore identity CTM
                ctx.Save();                     // save again (to restore after additional Translation)

                ctx.Translate(2 * r + 1, 0);    // extra x-offset to clear first figure
                ctx.MoveTo(p[0]);
                ctx.LineTo(p[1]);
                c = 1 - c;
                ctx.SetColor(new Color(c, c, c));
                ctx.Stroke();           // stroke transformed points with offset in CTM

                ctx.Restore();          // restore identity CTM for next line
            }
            ctx.Restore();              // restore caller's context
        }
Example #2
0
        public virtual void Rotate(Xwt.Drawing.Context ctx, double x, double y)
        {
            ctx.Save();
            ctx.Translate(x + 30, y + 30);
            ctx.SetLineWidth(3);

            // Rotation

            double end = 270;
            double r   = 30;

            for (double n = 0; n <= end; n += 5)
            {
                ctx.Save();
                ctx.Rotate(n);
                ctx.MoveTo(0, 0);
                ctx.RelLineTo(r, 0);
                double c = n / end;
                ctx.SetColor(new Color(c, c, c));
                ctx.Stroke();

                // Visual test for TransformPoints
                Point   p0 = new Point(0, 0);
                Point   p1 = new Point(0, -r);
                Point[] p  = new Point[] { p0, p1 };
                ctx.TransformPoints(p);
                ctx.ResetTransform();
                ctx.Translate(2 * r + 1, 0);
                ctx.MoveTo(p[0]);
                ctx.LineTo(p[1]);
                c = 1 - c;
                ctx.SetColor(new Color(c, c, c));
                ctx.Stroke();
                ctx.Restore();
            }
            ctx.Restore();
        }
Example #3
0
        /// <summary>
        /// Draw a tick on the axis.
        /// </summary>
        /// <param name="ctx">The Drawing Context with on which to draw.</param>
        /// <param name="w">The tick position in world coordinates.</param>
        /// <param name="size">The size of the tick (in pixels)</param>
        /// <param name="text">The text associated with the tick</param>
        /// <param name="textOffset">The Offset to draw from the auto calculated position</param>
        /// <param name="axisPhysMin">The minimum physical extent of the axis</param>
        /// <param name="axisPhysMax">The maximum physical extent of the axis</param>
        /// <param name="boundingBox">out: The bounding rectangle for the tick and tickLabel drawn</param>
        /// <param name="labelOffset">out: offset from the axies required for axis label</param>
        public virtual void DrawTick( 
			Context ctx, 
			double w,
			double size,
			string text,
			Point textOffset,
			Point axisPhysMin,
			Point axisPhysMax,
			out Point labelOffset,
			out Rectangle boundingBox )
        {
            // determine physical location where tick touches axis.
            Point tickStart = WorldToPhysical (w, axisPhysMin, axisPhysMax, true);

            // determine offset from start point.
            Point  axisDir = Utils.UnitVector (axisPhysMin, axisPhysMax);

            // rotate axisDir anti-clockwise by TicksAngle radians to get tick direction. Note that because
            // the physical (pixel) origin is at the top left, a RotationTransform by a positive angle will
            // be clockwise.  Consequently, for anti-clockwise rotations, use cos(A-B), sin(A-B) formulae
            double x1 = Math.Cos (TicksAngle) * axisDir.X + Math.Sin (TicksAngle) * axisDir.Y;
            double y1 = Math.Cos (TicksAngle) * axisDir.Y - Math.Sin (TicksAngle) * axisDir.X;

            // now get the scaled tick vector.
            Point tickVector = new Point (TickScale * size * x1, TickScale * size * y1);

            if (TicksCrossAxis) {
                tickStart.X -= tickVector.X / 2;
                tickStart.Y -= tickVector.Y / 2;
            }

            // and the end point [point off axis] of tick mark.
            Point  tickEnd = new Point (tickStart.X + tickVector.X, tickStart.Y + tickVector.Y);

            // and draw it
            ctx.SetLineWidth (1);
            ctx.SetColor (LineColor);
            ctx.MoveTo (tickStart.X+0.5, tickStart.Y+0.5);
            ctx.LineTo (tickEnd.X+0.5, tickEnd.Y+0.5);
            ctx.Stroke ();

            // calculate bounds of tick.
            double minX = Math.Min (tickStart.X, tickEnd.X);
            double minY = Math.Min (tickStart.Y, tickEnd.Y);
            double maxX = Math.Max (tickStart.X, tickEnd.X);
            double maxY = Math.Max (tickStart.Y, tickEnd.Y);
            boundingBox = new Rectangle (minX, minY, maxX-minX, maxY-minY);

            // by default, label offset from axis is 0. TODO: revise this.
            labelOffset = new Point (-tickVector.X, -tickVector.Y);

            // ------------------------

            // now draw associated text.

            // **** TODO ****
            // The following code needs revising. A few things are hard coded when
            // they should not be. Also, angled tick text currently just works for
            // the bottom x-axis. Also, it's a bit hacky.

            if (text != "" && !HideTickText) {
                TextLayout layout = new TextLayout ();
                layout.Font = tickTextFontScaled;
                layout.Text = text;
                Size textSize = layout.GetSize ();

                // determine the center point of the tick text.
                double textCenterX;
                double textCenterY;

                // if text is at pointy end of tick.
                if (!TickTextNextToAxis) {
                    // offset due to tick.
                    textCenterX = tickStart.X + tickVector.X*1.2;
                    textCenterY = tickStart.Y + tickVector.Y*1.2;

                    // offset due to text box size.
                    textCenterX += 0.5 * x1 * textSize.Width;
                    textCenterY += 0.5 * y1 * textSize.Height;
                }
                    // else it's next to the axis.
                else {
                    // start location.
                    textCenterX = tickStart.X;
                    textCenterY = tickStart.Y;

                    // offset due to text box size.
                    textCenterX -= 0.5 * x1 * textSize.Width;
                    textCenterY -= 0.5 * y1 * textSize.Height;

                    // bring text away from the axis a little bit.
                    textCenterX -= x1*(2.0+FontScale);
                    textCenterY -= y1*(2.0+FontScale);
                }

                // If tick text is angled..
                if (TickTextAngle != 0) {

                    // determine the point we want to rotate text about.
                    Point textScaledTickVector = new Point (
                                                TickScale * x1 * (textSize.Height/2),
                                                TickScale * y1 * (textSize.Height/2) );
                    Point rotatePoint;
                    if (TickTextNextToAxis) {
                        rotatePoint = new Point (
                                            tickStart.X - textScaledTickVector.X,
                                            tickStart.Y - textScaledTickVector.Y);
                    }
                    else {
                        rotatePoint = new Point (
                                            tickEnd.X + textScaledTickVector.X,
                                            tickEnd.Y + textScaledTickVector.Y);
                    }

                    double actualAngle;
                    if (FlipTickText) {
                        double radAngle = TickTextAngle * Math.PI / 180;
                        rotatePoint.X += textSize.Width * Math.Cos (radAngle);
                        rotatePoint.Y += textSize.Width * Math.Sin (radAngle);
                        actualAngle = TickTextAngle + 180;
                    }
                    else {
                        actualAngle = TickTextAngle;
                    }

                    ctx.Save ();

                    ctx.Translate (rotatePoint.X, rotatePoint.Y);
                    ctx.Rotate (actualAngle);

                    Point [] recPoints = new Point [2];
                    recPoints[0] = new Point (0.0, -textSize.Height/2);
                    recPoints[1] = new Point (textSize.Width, textSize.Height);
                    ctx.TransformPoints (recPoints);

                    double t_x1 = Math.Min (recPoints[0].X, recPoints[1].X);
                    double t_x2 = Math.Max (recPoints[0].X, recPoints[1].X);
                    double t_y1 = Math.Min (recPoints[0].Y, recPoints[1].Y);
                    double t_y2 = Math.Max (recPoints[0].Y, recPoints[1].Y);

                    boundingBox = Rectangle.Union (boundingBox, new Rectangle (t_x1, t_y1, (t_x2-t_x1), (t_y2-t_y1)));

                    ctx.DrawTextLayout (layout, 0, -textSize.Height/2);

                    t_x2 -= tickStart.X;
                    t_y2 -= tickStart.Y;
                    t_x2 *= 1.25;
                    t_y2 *= 1.25;

                    labelOffset = new Point (t_x2, t_y2);

                    ctx.Restore ();

                    //ctx.Rectangle (boundingBox.X, boundingBox.Y, boundingBox.Width, boundingBox.Height);
                    //ctx.Stroke ();

                }
                else 				{
                    double bx1 = (textCenterX - textSize.Width/2);
                    double by1 = (textCenterY - textSize.Height/2);
                    double bx2 = textSize.Width;
                    double by2 = textSize.Height;

                    Rectangle drawRect = new Rectangle (bx1, by1, bx2, by2);
                    // ctx.Rectangle (drawRect);

                    boundingBox = Rectangle.Union (boundingBox, drawRect);

                    // ctx.Rectangle (boundingBox);

                    ctx.DrawTextLayout (layout, bx1, by1);

                    textCenterX -= tickStart.X;
                    textCenterY -= tickStart.Y;
                    textCenterX *= 2.3;
                    textCenterY *= 2.3;

                    labelOffset = new Point (textCenterX, textCenterY);
                }
            }
        }
Example #4
0
        /// <summary>
        /// Draw the Axis Label
        /// </summary>
        /// <param name="ctx>The Drawing Context with which to draw.</param>
        /// <param name="offset">offset from axis. Should be calculated so as to make sure axis label misses tick labels.</param>
        /// <param name="axisPhysicalMin">The physical position corresponding to the world minimum of the axis.</param>
        /// <param name="axisPhysicalMax">The physical position corresponding to the world maximum of the axis.</param>
        /// <returns>boxed Rectangle indicating bounding box of label. null if no label printed.</returns>
        public object DrawLabel(Context ctx, Point offset, Point axisPhysicalMin, Point axisPhysicalMax)
        {
            if (Label != "") {

                // first calculate any extra offset for axis label spacing.
                double extraOffsetAmount = LabelOffset;
                extraOffsetAmount += 2; // empirically determed - text was too close to axis before this.
                if (AutoScaleText && LabelOffsetScaled) {
                    extraOffsetAmount *= FontScale;
                }
                // now extend offset.
                double offsetLength = Math.Sqrt (offset.X*offset.X + offset.Y*offset.Y);
                if (offsetLength > 0.01) {
                    double x_component = offset.X / offsetLength;
                    double y_component = offset.Y / offsetLength;

                    x_component *= extraOffsetAmount;
                    y_component *= extraOffsetAmount;

                    if (LabelOffsetAbsolute) {
                        offset.X = x_component;
                        offset.Y = y_component;
                    }
                    else {
                        offset.X += x_component;
                        offset.Y += y_component;
                    }
                }

                // determine angle of axis in degrees
                double theta = Math.Atan2 (
                    axisPhysicalMax.Y - axisPhysicalMin.Y,
                    axisPhysicalMax.X - axisPhysicalMin.X);
                theta = theta * 180.0 / Math.PI;

                Point average = new Point (
                    (axisPhysicalMax.X + axisPhysicalMin.X)/2,
                    (axisPhysicalMax.Y + axisPhysicalMin.Y)/2);

                ctx.Save ();

                ctx.Translate (average.X + offset.X , average.Y + offset.Y);	// this is done last.
                ctx.Rotate (theta);												// this is done first.

                TextLayout layout = new TextLayout ();
                layout.Font = labelFontScaled;
                layout.Text = Label;
                Size labelSize = layout.GetSize ();

                //Draw label centered around zero.
                ctx.DrawTextLayout (layout, -labelSize.Width/2, -labelSize.Height/2);

                // now work out physical bounds of Rotated and Translated label.
                Point [] recPoints = new Point [2];
                recPoints[0] = new Point (-labelSize.Width/2, -labelSize.Height/2);
                recPoints[1] = new Point ( labelSize.Width/2, labelSize.Height/2);
                ctx.TransformPoints (recPoints);

                double x1 = Math.Min (recPoints[0].X, recPoints[1].X);
                double x2 = Math.Max (recPoints[0].X, recPoints[1].X);
                double y1 = Math.Min (recPoints[0].Y, recPoints[1].Y);
                double y2 = Math.Max (recPoints[0].Y, recPoints[1].Y);

                ctx.Restore ();

                // and return label bounding box.
                return new Rectangle (x1, y1, (x2-x1), (y2-y1));
            }
            return null;
        }