示例#1
0
        /// <summary>
        /// Calculates the bounds of the plotting area inside the control.
        /// </summary>
        /// <param name="g">A Graphics object.</param>
        /// <param name="bounds">The bounds of the entire control.</param>
        void Measure(Graphics g, Rectangle bounds)
        {
            SizeF size;

            PointF[] v = new PointF[3];
            Norms    norms;
            double   x0, x1, y0, y1, z0, z1;

            size = g.MeasureString("0.5", Model.ScaleFont);
            fd   = Math.Max(1, (int)(size.Height + 0.5F));
            size = g.MeasureString("0.5", Model.LegendFont);
            fld  = Math.Max(1, (int)(size.Height + 0.5F));

            Bounds = bounds;
            x0     = Model.x0; x1 = Model.x1; y0 = Model.y0; y1 = Model.y1; z0 = Model.z0; z1 = Model.z1;
            Model.x.scaleOutside  = Model.y.scaleOutside = Model.z.scaleOutside = true;
            Model.x.rasterOutside = Model.y.rasterOutside = Model.z.rasterOutside = false;
            Model.x.r             = Model.x.Raster(Bounds.Width < 200);
            Model.y.r             = Model.y.Raster(Bounds.Width < 200);
            Model.z.r             = Model.z.Raster(Bounds.Height < 200);
            if (Model.Border && Bounds.Width > 1 && Bounds.Height > 1)
            {
                norms = new Norms(new Matrix());

                Model.View.ResetBounds();
                GraphicsBase.Point[] p  = Model.View.BoundsCube();
                PointF[]             dp = new PointF[p.Length];
                Model.View.DeviceCoordinates(p, dp);

                norms.Add(dp);

                Crop(norms, bounds);
            }
        }
示例#2
0
        /// <summary>
        /// Crops the Ploting area according to the extensions of the plot described by norms
        /// </summary>
        /// <param name="norms">The extensions of the plot</param>
        /// <param name="bounds">The bounds of the painting area</param>
        /// <returns>Returns true, if the plotting area was cropped</returns>
        public override bool Crop(Norms norms, Rectangle bounds)
        {
            bool res = base.Crop(norms, bounds);

            Model.View.SetBounds(Model, Bounds);
            return(res);
        }
示例#3
0
        /// <summary>
        /// This routine moves the points in p so that the average/min/max Y component is y and the
        /// average/min/max X component is x.
        /// </summary>
        public static void MovePoints(ref PointF[] p, float x, Norm xmode, float y, Norm ymode)
        {
            float dx = 0, dy = 0;

            Norms norms = new Norms();

            norms.Add(p);

            switch (xmode)
            {
            case Norm.Average: dx = x - norms.avg.X; break;

            case Norm.Max: dx = x - norms.max.X; break;

            case Norm.Min: dx = x - norms.min.X; break;
            }
            switch (ymode)
            {
            case Norm.Average: dy = y - norms.avg.Y; break;

            case Norm.Max: dy = y - norms.max.Y; break;

            case Norm.Min: dy = y - norms.min.Y; break;
            }
            for (int i = 0; i < p.Length; i++)
            {
                p[i].X += dx; p[i].Y += dy;
            }
        }
示例#4
0
            /// <summary>
            /// Returns a rectangle with the device coordinate bounds of the <see cref="PlotModel"/>'s bounds.
            /// </summary>
            /// <param name="Model"></param>
            /// <returns></returns>
            public RectangleF Bounds(PlotModel Model)
            {
                Point[]  cube  = BoundsCube();
                PointF[] cubed = new PointF[8];
                Norms    norms = new Norms();

                DeviceCoordinates(cube, cubed);
                norms.Add(cubed);
                return(norms.Bounds);
            }
示例#5
0
        private void Measure(Graphics g, Rectangle bounds)
        {
            SizeF size;
            Size  oldSize;

            PointF[] v = new PointF[3];
            Norms    norms;
            double   x0, x1, y0, y1;

            size = g.MeasureString("0.5", Model.ScaleFont);
            fd   = Math.Max(1, (int)(size.Height + 0.5F));
            size = g.MeasureString("0.5", Model.LegendFont);
            fld  = Math.Max(1, (int)(size.Height + 0.5F));

            oldSize = Bounds.Size;
            Bounds  = bounds;
            x0      = Model.x0; x1 = Model.x1; y0 = Model.y0; y1 = Model.y1;
            Model.x.scaleOutside  = Model.y.scaleOutside = Model.Border;
            Model.x.rasterOutside = Model.y.rasterOutside = false;
            Model.z.scaleOutside  = Model.z.rasterOutside = true;
            Model.y.r             = Model.y.Raster(Bounds.Height < 100); Model.z.r = Model.z.Raster(Bounds.Height < 100);
            if (Model.Border && Bounds.Width > 1 && Bounds.Height > 1)
            {
                do
                {
                    // Set the x0 and x1 bounds. Do not use Model.SetRange, because it would set Model.Modified to true.
                    Model.x.lower = x0; Model.x.upper = x1; Model.x.r = Model.x.Raster(Bounds.Width < 100);
                    norms         = new Norms(new Matrix());
                    if (Model.x.scale || Model.x.unit != null)
                    {
                        v[0] = new PointF(Bounds.X, Bounds.Y + Bounds.Height);
                        v[1] = new PointF(Bounds.X + Bounds.Width, Bounds.Y + Bounds.Height);
                        v[2] = new PointF(Bounds.X, Bounds.Y);
                        Model.x.Draw(g, v, false, ref norms);
                    }

                    if (Model.y.scale || Model.y.unit != null)
                    {
                        v[0] = new PointF(Bounds.X, Bounds.Y);
                        v[1] = new PointF(Bounds.X, Bounds.Y + Bounds.Height);
                        v[2] = new PointF(Bounds.X + Bounds.Width, Bounds.Y);
                        Model.y.Draw(g, v, false, ref norms);
                    }

                    Graphics2D.DrawZScale(g, Model, ref v, false, new Rectangle(Bounds.X + Bounds.Width + fd, Bounds.Y, fd, Bounds.Height), ref norms);
                } while (Crop(norms, bounds, out x0, out x1));
            }
            Bounds.Width = Math.Max(Bounds.Width, 1); Bounds.Height = Math.Max(Bounds.Height, 1);

            if (oldSize != Bounds.Size)
            {
                Model.SetRange(x0, x1, Model.y0, Model.y1, Model.z0, Model.z1, Bounds, false, false);
            }
        }
示例#6
0
        private bool Crop(Norms norms, Rectangle bounds, out double x0, out double x1)
        {
            bool res = base.Crop(norms, bounds);

            if (Model.FixXtoY && Bounds.Width > 1 && Bounds.Height > 1)
            {
                Model.CalcRange(Model.x0, Model.x1, Model.y0, Model.y1, Bounds, out x0, out x1);
            }
            else
            {
                x0 = Model.x0; x1 = Model.x1;
            }
            return(res);
        }
示例#7
0
        /// <summary>
        /// Adds two norms together. The norms must have the same T transformation matrix.
        /// </summary>
        public static Norms operator+(Norms a, Norms b)
        {
            if (a.T != b.T)
            {
                throw new ArgumentException("Transformations must be the same in a and b.");
            }
            Norms res = new Norms(a.T);

            res.sum.X  = a.sum.X + b.sum.X; res.sum.Y = a.sum.Y + b.sum.Y;
            res.min.X  = Math.Min(a.min.X, b.min.X);
            res.min.Y  = Math.Min(a.min.Y, b.min.Y);
            res.max.X  = Math.Max(a.max.X, b.max.X);
            res.max.Y  = Math.Max(a.max.Y, b.max.Y);
            res.sumT.X = a.sumT.X + b.sumT.X; res.sumT.Y = a.sumT.Y + b.sumT.Y;
            res.minT.X = Math.Min(a.minT.X, b.minT.X);
            res.minT.Y = Math.Min(a.minT.Y, b.minT.Y);
            res.maxT.X = Math.Max(a.maxT.X, b.maxT.X);
            res.maxT.Y = Math.Max(a.maxT.Y, b.maxT.Y);
            res.N      = a.N + b.N;
            return(res);
        }
示例#8
0
        /// <summary>
        /// Draws text. The supplied X/Y coordinates denote the average/min/max value of the points of the rectangle around the text,
        /// according to xnorm/ynorm. You can specify a rotation matrix, to draw the text at a certain angle. Also you can specify
        /// a transformation matrix T and its inverse Tinv to influence the drawing position of the text. Note that the matrix T only
        /// influences the position where the text will be drawn and not it's angle or shape.
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> to draw to.</param>
        /// <param name="text">The text to draw</param>
        /// <param name="draw">If false, the text is not drawn but it's outline rectangle points are assigned to p and added to
        /// norms.</param>
        /// <param name="font">The font to use</param>
        /// <param name="brush">The brush used to draw the text</param>
        /// <param name="background">If background != null, the text background is filled with background</param>
        /// <param name="X">The avg/max/min x-coordinate of the text</param>
        /// <param name="xnorm">The norm to use for the x-coordinate (avg/min/max)</param>
        /// <param name="Y">The avg/max/min y-coordinate of the text</param>
        /// <param name="ynorm">The norm to use for the y-coordinate (avg/min/max)</param>
        /// <param name="Rotation">An additional rotation of the text</param>
        /// <param name="T">The default transformation used for drawing</param>
        /// <param name="Tinv">The inverse of T</param>
        /// <param name="p">Returns the edges of the text rectangle in T-space. You must pass a <see cref="PointF"/> array of size 4</param>
        /// <param name="norms">The <see cref="Norms"/>, the edges of the text will be added to.</param>
        public static void DrawText(Graphics g, string text, bool draw, Font font, Brush brush, Brush background, float X, Norm xnorm, float Y, Norm ynorm,
                                    //TODO Subscript & Superscript
                                    Matrix Rotation, Matrix T, Matrix Tinv, ref PointF[] p, ref Norms norms)
        {
            SizeF size   = g.MeasureString(text, font);
            Norms norms1 = new Norms(T);

            // create a rectangle with size size in p.
            p[0] = new PointF(0, 0); p[1] = new PointF(size.Width, 0);
            p[2] = new PointF(size.Width, size.Height); p[3] = new PointF(0, size.Height);
            // rotate the rectangle with rotation
            Rotation.TransformPoints(p);
            // tranform the rectangle into T space
            Tinv.TransformPoints(p);
            // place the rectangle in the correct position
            MovePoints(ref p, X, xnorm, Y, ynorm);
            // calculate the extrema
            norms1.Add(p);
            if (draw)               // draw the text at Avg(p)
            {
                Matrix T0 = g.Transform.Clone();
                g.TranslateTransform(norms1.avgT.X, norms1.avgT.Y);
                g.MultiplyTransform(Rotation);
                if (background != null)
                {
                    p[0].X = -size.Width / 2; p[0].Y = -size.Height / 2;
                    p[2].X = size.Width - size.Width / 2;     p[2].Y = size.Height - size.Height / 2;
                    p[1].X = p[2].X; p[1].Y = p[0].Y;
                    p[3].X = p[0].X; p[3].Y = p[2].Y;
                    g.FillPolygon(background, p);
                }
                g.DrawString(text, font, brush, -size.Width / 2, -size.Height / 2);
                g.Transform = T0;
            }
            norms += norms1;
        }
示例#9
0
 /// <summary>
 /// Draws a scale. The point-array v is an array of 3 points that denote the orientation of the scale. The primary scale
 /// is drawn from v[0] -> v[1], the opposite scale is drawn from v[0] + v[2] -> v[1] + v[2].
 /// </summary>
 /// <param name="g">The Graphics object to Draw to</param>
 /// <param name="v">The orientation of the scale</param>
 /// <param name="draw">If false the Scale is not drawn but its extensions are calculated in norms</param>
 /// <param name="norms">The extensions of the scale</param>
 public void Draw(Graphics g, PointF[] v, bool draw, ref Norms norms)
 {
     Graphics2D.DrawScale(g, v, model, this, draw, ref norms);
 }
示例#10
0
 /// <summary>
 /// Increases the size of the plot area until its bounds fit into bounds.
 /// </summary>
 public void Grow(Norms norms, Rectangle bounds)
 {
 }
示例#11
0
        /// <summary>
        /// Paints the control.
        /// </summary>
        /// <param name="g">The Graphics object to paint to.</param>
        /// <param name="bounds">The bounds of the plotting area.</param>
        public override void Draw(Graphics g, Rectangle bounds)
        {
            PointF[] v     = new PointF[3];
            Norms    norms = new Norms();
            Pen      Pen   = new Pen(new SolidBrush(Color.White));

            g.Clear(Model.BackgroundColor);

            Measure(g, bounds);

            if (Bounds.Width >= 1 && Bounds.Height >= 1)
            {
                //calculate functions
                PaintThread.DrawStart(this);

                //draw items
                PaintThread.DrawItems(g);

                // draw selection
                if (sw != 0 && sh != 0)
                {
                    Pen.Color = Color.White;
                    g.DrawRectangle(Pen, Math.Min(sx, sx + sw), Math.Min(sy, sy + sh),
                                    Math.Abs(sw), Math.Abs(sh));
                    Pen.DashPattern = new float[2] {
                        3, 3
                    };
                    Pen.DashStyle = DashStyle.Custom;
                    Pen.Color     = Color.Black;
                    g.DrawRectangle(Pen, Math.Min(sx, sx + sw), Math.Min(sy, sy + sh),
                                    Math.Abs(sw), Math.Abs(sh));
                    Pen.DashStyle = DashStyle.Solid;
                }

                Pen.Color = Model.ScaleColor;
                Pen.Width = Model.ScaleLineWidth;

                GraphicsBase.Point[] p  = Model.View.BoundsCube();
                PointF[]             dp = new PointF[p.Length];
                Model.View.DeviceCoordinates(p, dp);
                g.DrawLines(Pen, dp);

                //draw legend
                GraphicsBase.DrawLegend(g, this);

                //draw status message
                if (!PaintThread.DrawDone)
                {
                    if (Parent != null)
                    {
                        Parent.Cursor = Cursors.WaitCursor;
                    }
                }
                else
                {
                    if (Parent != null)
                    {
                        Parent.Cursor = Cursors.Cross;
                    }
                    if (ProgressBar != null)
                    {
                        lock (ProgressBar) {
                            ProgressBar.Value   = 0;
                            ProgressBar.Visible = false;
                        }
                    }
                }
            }
        }
示例#12
0
        /// <summary>
        /// Crops the display area of the Plot according to norms and bounds. If the max/min value of the norms lies outside the
        /// bounds, the bounds are adjusted accordingly.
        /// </summary>
        /// <returns>Returns true if the Plot was cropped, i.e. the max/min value of the norms was outside the
        /// Plot.Bounds.</returns>
        public virtual bool Crop(Norms norms, Rectangle bounds)
        {
            const int B = GraphicsBase.Border;             // The width of the white-space border around the plotting area

            /*
             * int dx, dy, dw, dh;
             *
             * dx = (int)(Math.Max(0, Graphics2D.D - norms.minT.X) + 0.5F);
             * dy = (int)(Math.Max(0, Graphics2D.D - norms.minT.Y) + 0.5F);
             * dw = (int)(Math.Min(0, bounds.Width - Graphics2D.D - norms.maxT.X - dx) + 0.5F);
             * dh = (int)(Math.Min(0, bounds.Height - Graphics2D.D - norms.maxT.Y - dy) + 0.5F);
             *
             * X += dx; Y += dy; Width += dw; Height += dh;
             * Width = Math.Max(Width, 1); Height = Math.Max(Height, 1);
             *
             * return (dx > 0 || dy > 0 || dw > 0 || dh > 0) && Width > 1 && Height > 1;
             */
            Rectangle innerBounds, d = new Rectangle();
            int       dw, dh;

            // set innerBounds to the rectangle that is by Graphics2D.Border smaller than bounds.
            if (Model.Border)
            {
                innerBounds = new Rectangle(bounds.X + B, bounds.Y + B, bounds.Width - 2 * B, bounds.Height - 2 * B);
            }
            else
            {
                innerBounds = bounds;
            }

            d.X      = (int)(innerBounds.X - norms.minT.X + 0.5F);
            d.Y      = (int)(innerBounds.Y - norms.minT.Y + 0.5F);
            d.X      = Math.Max(0, d.X); d.Y = Math.Max(0, d.Y);
            d.Width  = (int)(innerBounds.X + innerBounds.Width - norms.maxT.X + 0.5F) - d.X;
            d.Height = (int)(innerBounds.Y + innerBounds.Height - norms.maxT.Y + 0.5F) - d.Y;
            d.Width  = Math.Min(0, d.Width); d.Height = Math.Min(0, d.Height);

            Bounds.X += d.X; Bounds.Y += d.Y; Bounds.Width += d.Width; Bounds.Height += d.Height;

            if (Model.Border)               // check if Bounds lie outside innerBounds
            // crop Bounds to innerBounds
            {
                if (Bounds.X < innerBounds.X)
                {
                    Bounds.X = innerBounds.X;
                }
                if (Bounds.Y < innerBounds.Y)
                {
                    Bounds.Y = innerBounds.Y;
                }
                dw = Bounds.X + Bounds.Width - innerBounds.X - innerBounds.Width;
                dh = Bounds.Y + Bounds.Height - innerBounds.Y - innerBounds.Height;
                if (dw > 0)
                {
                    Bounds.Width -= dw;
                }
                if (dh > 0)
                {
                    Bounds.Height += dh;
                }
            }
            Bounds.Width = Math.Max(1, Bounds.Width); Bounds.Height = Math.Max(1, Bounds.Height);

            return((d.X != 0 || d.Y != 0 || d.Width != 0 || d.Height != 0) && Bounds.Width > 1 && Bounds.Height > 1);
        }
示例#13
0
        /// <summary>
        /// Paints the control.
        /// </summary>
        /// <param name="g">The Graphics object to paint to.</param>
        /// <param name="bounds">The bounds of the plotting area.</param>
        public override void Draw(Graphics g, Rectangle bounds)
        {
            PointF[] v     = new PointF[3];
            Norms    norms = new Norms();

            g.Clear(Model.BackgroundColor);
            SmoothingMode smoothingMode = g.SmoothingMode;

            g.SmoothingMode = SmoothingMode.AntiAlias;
            Measure(g, bounds);

            if (Bounds.Width >= 1 && Bounds.Height >= 1)
            {
                //calculate functions
                PaintThread.DrawStart(this);

                //draw items
                PaintThread.DrawItems(g);

                // draw selection
                if (sw != 0 && sh != 0)
                {
                    Pen Pen = new Pen(new SolidBrush(Color.White));
                    Pen.Color = Color.White;
                    g.DrawRectangle(Pen, Math.Min(sx, sx + sw), Math.Min(sy, sy + sh),
                                    Math.Abs(sw), Math.Abs(sh));
                    Pen.DashPattern = new float[2] {
                        3, 3
                    };
                    Pen.DashStyle = DashStyle.Custom;
                    Pen.Color     = Color.Black;
                    g.DrawRectangle(Pen, Math.Min(sx, sx + sw), Math.Min(sy, sy + sh),
                                    Math.Abs(sw), Math.Abs(sh));
                    Pen.DashStyle = DashStyle.Solid;
                }

                // draw x-scale
                v[0] = new PointF(Bounds.X, Bounds.Y + Bounds.Height);
                v[1] = new PointF(Bounds.X + Bounds.Width, Bounds.Y + Bounds.Height);
                v[2] = new PointF(Bounds.X, Bounds.Y);
                Model.x.Draw(g, v, true, ref norms);

                // draw y-scale
                v[0] = new PointF(Bounds.X, Bounds.Y);
                v[1] = new PointF(Bounds.X, Bounds.Y + Bounds.Height);
                v[2] = new PointF(Bounds.X + Bounds.Width, Bounds.Y);
                Model.y.Draw(g, v, true, ref norms);

                //draw z-scale
                Graphics2D.DrawZScale(g, Model, ref v, true, new Rectangle(Bounds.X + Bounds.Width + fd, Bounds.Y, fd, Bounds.Height), ref norms);

                //draw legend
                GraphicsBase.DrawLegend(g, this);

                //draw status message
                if (!PaintThread.DrawDone)
                {
                    if (Parent != null)
                    {
                        Parent.Cursor = Cursors.WaitCursor;
                    }
                }
                else
                {
                    if (Parent != null)
                    {
                        Parent.Cursor = Cursors.Cross;
                    }
                    if (ProgressBar != null)
                    {
                        lock (ProgressBar) {
                            ProgressBar.Value   = 0;
                            ProgressBar.Visible = false;
                        }
                    }
                }

                g.SmoothingMode = smoothingMode;
            }
        }
示例#14
0
        /// <summary>
        /// Draws a z-<see cref="Scale">Scale</see> for a <see cref="Plot2D">2D Plot</see>.
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> to paint to</param>
        /// <param name="Model">The <see cref="PlotModel"/> of the Plot</param>
        /// <param name="v">The orientation of the scale</param>
        /// <param name="draw">If false, the scale is not drawn only is extensions are measured</param>
        /// <param name="r">The rectangle where the z-gradient is drawn inside</param>
        /// <param name="norms">The extensions of the painting</param>
        public static void DrawZScale(Graphics g, PlotModel Model, ref PointF[] v, bool draw, Rectangle r, ref Norms norms)
        {
            Function2DItem f = Model.GetPaintableItem() as Function2DItem;

            if (Model.z.scale && f != null)
            {
                Model.z.oppositeRaster = Model.z.oppositeLine = false;
                Model.z.scaleOutside   = Model.z.rasterOutside = false;
                if (draw)
                {
                    GradientPainter.FillRectangle(g, f.Gradient, r, GradientPainter.Direction.Up);
                    Pen pen = new Pen(Model.ScaleColor, Model.ScaleLineWidth);
                    g.DrawRectangle(pen, r);
                }
                SizeF size = g.MeasureString("0.5", Model.ScaleFont);
                float s0   = Math.Max(1, size.Height);
                v[0] = new PointF(r.X + r.Width, r.Y + r.Height);
                v[1] = new PointF(r.X + r.Width, r.Y);
                v[2] = new PointF(r.X + r.Width + s0, r.Y + r.Height);

                DrawScale(g, v, Model, Model.z, draw, ref norms);
            }
        }
示例#15
0
        /*
         * /// <summary>
         * /// Represents a raster line
         * /// </summary>
         * public class Line {
         *      /// <summary>
         *      /// The x-coordinate of the line
         *      /// </summary>
         *      public double x;
         *      /// <summary>
         *      /// Indicates if the line is a big or small raster line
         *      /// </summary>
         *      public bool big = false;
         * }
         * /// <summary>
         * /// Represents all raster lines of a scale
         * /// </summary>
         * public class ScaleLines: List<Line> { }
         * /// <summary>
         * /// Draws the scale and raster lines specified in lines.
         * /// </summary>
         */ /*
         * public void DrawScaleLines(Graphics g, PlotModel Model, Scale scale, bool draw,  PointF[] v, ref Norms norms, ,
         *      ScaleLines lines) {
         *
         *      Norms normsText = new Norms();
         *      Norm yNorm;
         *
         *      // init matrices
         *      Matrix id, T0, T, Tprod, Trot, Tinv;
         *
         *      id = new Matrix();
         *      T0 = g.Transform.Clone();
         *      RectangleF Tframe = Bounds;
         *      T = new Matrix(Tframe, v);
         *      norms.T = normsText.T = T;
         *      Tinv = T.Clone();
         *      Tinv.Invert();
         *      Tprod = T0.Clone();
         *      Tprod.Multiply(T);
         *      Trot = new Matrix();
         *      if (scale.unitAngleRelative) Trot.Rotate(scale.unitAngle - Angle(Diff(v[1], v[0])));
         *      else Trot.Rotate(scale.unitAngle);
         *      g.Transform = Tprod;
         *
         *      PointF[] p = new PointF[4];
         *      double x0 = scale.lower, x1 = scale.upper;
         *      if (x0 > x1) {
         *              double t = x0; x0 = x1; x1 = t;
         *      }
         *      // double d = (x1 - x0)/W;
         *      // double x = Math.Floor(x0/scale.r)*scale.r + scale.r;
         *      double sr = scale.r/5;
         *      float Y, X0 = scale.DeviceCoordinate(0, Bounds);
         *      SizeF size = g.MeasureString("0.5", Model.ScaleFont);
         *      float s, s0 = Math.Max(1, (int)(size.Height + 0.5F));
         *      Pen pen = new Pen(Model.ScaleColor, Model.ScaleLineWidth); // the pen used for drawing
         *      SolidBrush brush = new SolidBrush(Model.ScaleColor);
         *      SolidBrush background = new SolidBrush(Model.BackgroundColor);
         *      foreach (Line l in lines) { // interate over raster
         *              if (x0 <= l.x && l.x <= x1) {
         *                      float X = scale.DeviceCoordinate(l.x, Bounds);
         *                      bool isX0 = Math.Round(X0) == Math.Round(X);
         *                      if (draw && (!isX0 || !scale.axis)) { // X is not axis
         *                              if (scale.grid && l.big) { // draw grid
         *                                      pen.Color = Color.FromArgb(255, Model.ScaleColor);
         *                                      g.DrawLine(pen, X, 0, X, Bounds.Height);
         *                                      pen.Color = Model.ScaleColor;
         *                              }
         *                              if (scale.raster) { // draw raster
         *                                      if (l.big) s = s0;
         *                                      else s = s0/2;
         *                                      if (scale.rasterOutside) { // draw raster on outside
         *                                              if (scale.raster) g.DrawLine(pen, X, -s, X, 0);
         *                                              if (scale.oppositeRaster) g.DrawLine(pen, X, Bounds.Height + s, X, H);
         *                                      } else { // draw raster on inside
         *                                              if (scale.raster) g.DrawLine(pen, X, 0, X, s);
         *                                              if (scale.oppositeRaster) g.DrawLine(pen, X, H - s, X, H);
         *                                      }
         *                              }
         *                      } else if (draw && scale.axis) { // draw 0-axis.
         *                              g.DrawLine(pen, X, 0, X, H);
         *                      }
         *
         *                      if (scale.scale && l.big) { // draw scale text
         *                              string label;
         *                              if (isX0) label = "0";
         *                              else label = scale.UnitToString(l.x);
         *
         *                              if (scale.scaleOutside != scale.rasterOutside) s = 0.5F*s0; // distance from line to scale
         *                              else s = 1.5F*s0;
         *                              if (scale.scaleOutside) { // draw scale outside the box
         *                                      Y = -s;
         *                                      yNorm = Norm.Max;
         *                              } else { // draw scale inside the box
         *                                      Y = s;
         *                                      yNorm = Norm.Min;
         *                              }
         *                              g.Transform = T0; // reset transformation
         *                              DrawText(g, label, draw, Model.ScaleFont, brush, background, X, Norm.Average, Y, yNorm, id, T, Tinv, ref p, ref normsText);
         *                              g.Transform = Tprod;
         *                      }
         *              }
         *      }
         *
         *      // draw units
         *      g.Transform = T0;
         *
         *      size = g.MeasureString(scale.unit, Model.UnitsFont);
         *      s0 = size.Height/2;
         *
         *      if (scale.scaleOutside) {
         *              Y = normsText.min.Y - s0; yNorm = Norm.Max; // draw the units below the scale
         *      } else {
         *              Y = normsText.max.Y + s0; yNorm = Norm.Min; // draw the units above the scale
         *      }
         *      DrawText(g, scale.unit, draw, Model.UnitsFont, brush, background, W/2, Norm.Average, Y, yNorm, Trot, T, Tinv, ref p, ref norms);
         *      norms += normsText;
         * }
         */

        /// <summary>
        /// Draws a <see cref="Scale">Scale</see>. The point-array v is an array of 3 points that denote the orientation
        /// of the scale. The primary scale is drawn from v[0] -> v[1], the opposite scale is drawn from
        /// v[0] + v[2] -> v[1] + v[2].
        /// </summary>
        /// <param name="g">The <see cref="Graphics"/> object to draw to.</param>
        /// <param name="v">The orientation of the scale</param>
        /// <param name="Model">The <see cref="PlotModel"/> of the Plot to draw</param>
        /// <param name="scale">The scale to draw</param>
        /// <param name="draw">If false, the scale is not drawn but it's dimensions are added to norms.</param>
        /// <param name="norms">The <see cref="Norms"/> the extension-points of the scale will be added to.</param>
        public static void DrawScale(Graphics g, PointF[] v, PlotModel Model, Scale scale, bool draw, ref Norms norms)
        {
            //TODO Inverse Scales (where scale.lower > scale.upper)
            //TODO logscales

            const int  GridAlpha = 64;            // the alpha-color value of the grid lines.
            float      X, X0, Y, W, H, s, s0;
            double     x, sr, t, x0, x1;
            Matrix     id, T0, T, Tprod, Trot, Tinv;
            int        n, Width;
            Pen        pen        = new Pen(Model.ScaleColor, Model.ScaleLineWidth); // the pen used for drawing
            SolidBrush brush      = new SolidBrush(Model.ScaleColor);
            SolidBrush background = new SolidBrush(Model.BackgroundColor);
            string     label;
            RectangleF Tframe;
            PointF     d1, d2, d3;
            bool       isX0;
            Norm       yNorm;

            PointF[] p = new PointF[4];
            SizeF    size;
            Norms    normsText = new Norms();

            x0 = scale.lower; x1 = scale.upper;
            if (x0 > x1)                // lower bound is greater than upper bound. Exchange bounds.
            {
                t = x0; x0 = x1; x1 = t;
            }
            d1 = Diff(v[1], v[0]);
            d2 = Diff(v[2], v[0]);
            d3 = Diff(v[1], v[2]);
            if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2])
            {
                return;
            }

            W = (float)Math.Sqrt(d1.X * d1.X + d1.Y * d1.Y);
            H = (float)Math.Sqrt(d2.X * d2.X + d2.Y * d2.Y);
            W = Math.Max(W, 1);
            H = Math.Max(H, 1);

            Width  = (int)(W + 1.5);
            Tframe = new RectangleF(0, 0, W, H);

            size = g.MeasureString("0.5", Model.ScaleFont);
            s0   = Math.Max(1, size.Height);

            // init matrices
            id      = new Matrix();
            T0      = g.Transform.Clone();
            T       = new Matrix(Tframe, v);
            norms.T = normsText.T = T;
            Tinv    = T.Clone();
            Tinv.Invert();
            Tprod = T0.Clone();
            Tprod.Multiply(T);
            Trot = new Matrix();
            if (scale.unitAngleRelative)
            {
                Trot.Rotate(scale.unitAngle - Angle(Diff(v[1], v[0])));
            }
            else
            {
                Trot.Rotate(scale.unitAngle);
            }
            g.Transform = Tprod;

            p[0] = new PointF(0, 0); p[1] = new PointF(W, 0); p[2] = new PointF(W, H); p[3] = new PointF(0, H);
            norms.Add(p);

            if (draw)                   // draw the solid lines of the scale and opposite scale
            {
                if (scale.line)
                {
                    g.DrawLine(pen, p[0], p[1]);
                }
                if (scale.oppositeLine)
                {
                    g.DrawLine(pen, p[3], p[2]);
                }
            }

            // add the minimum rectangle to the normsText. (So that the units will be in the right position, even if there is no scale.)
            if (scale.scaleOutside != scale.rasterOutside)
            {
                s = 0.5F * s0;
            }
            else
            {
                s = 1.5F * s0;
            }
            if (scale.scaleOutside)
            {
                s = -s;
            }
            Y    = s;
            p[0] = new PointF(0, Y); p[1] = new PointF(W, Y); p[2] = new PointF(W, Y); p[3] = new PointF(0, Y);
            normsText.Add(p);

            // draw scale
            if (!scale.logarithmic)
            {
                sr = scale.r;
                x  = Math.Floor(x0 / sr) * sr + sr;
                sr = sr / 5;
                X0 = scale.DeviceCoordinate(0, (int)(W + 0.5F));
                n  = -4;
                // interate over raster
                while ((sr > 0 && x + sr * n < x0) || (sr < 0 && x + sr * n > x0))
                {
                    n++;
                }
                while ((sr > 0 && x + sr * n < x1) || (sr < 0 && x + sr * n > x1))
                {
                    X = scale.DeviceCoordinate(x + sr * n, (int)(W + 0.5F));
                    // X = (float)((x + sr*n - x0)/d);
                    isX0 = Math.Round(X0) == Math.Round(X);
                    if (draw && (!isX0 || !scale.axis))                       // X is not axis
                    {
                        if (scale.grid && n % 5 == 0)                         // draw grid
                        {
                            pen.Color = Color.FromArgb(GridAlpha, Model.ScaleColor);
                            g.DrawLine(pen, X, 0, X, H);
                            pen.Color = Model.ScaleColor;
                        }
                        if (scale.raster)                           // draw raster
                        {
                            if (n % 5 == 0)
                            {
                                s = s0;
                            }
                            else
                            {
                                s = s0 / 2;
                            }
                            if (scale.rasterOutside)                               // draw raster on outside
                            {
                                if (scale.raster)
                                {
                                    g.DrawLine(pen, X, -s, X, 0);
                                }
                                if (scale.oppositeRaster)
                                {
                                    g.DrawLine(pen, X, H + s, X, H);
                                }
                            }
                            else                                 // draw raster on inside
                            {
                                if (scale.raster)
                                {
                                    g.DrawLine(pen, X, 0, X, s);
                                }
                                if (scale.oppositeRaster)
                                {
                                    g.DrawLine(pen, X, H - s, X, H);
                                }
                            }
                        }
                    }
                    else if (scale.axis && draw)                         // draw 0-axis.
                    {
                        g.DrawLine(pen, X, 0, X, H);
                    }

                    if (scale.scale && (n % 5 == 0))                     // draw scale
                    {
                        if (isX0)
                        {
                            label = "0";
                        }
                        else
                        {
                            label = scale.UnitToString(x + sr * n);
                        }

                        if (scale.scaleOutside != scale.rasterOutside)
                        {
                            s = 0.5F * s0;                                                                  // distance from line to scale
                        }
                        else
                        {
                            s = 1.5F * s0;
                        }
                        if (scale.scaleOutside)                           // draw scale outside the box
                        {
                            Y     = -s;
                            yNorm = Norm.Max;
                        }
                        else                             // draw scale inside the box
                        {
                            Y     = s;
                            yNorm = Norm.Min;
                        }
                        g.Transform = T0;                         // reset transformation
                        DrawText(g, label, draw, Model.ScaleFont, brush, null, X, Norm.Average, Y, yNorm, id, T, Tinv, ref p, ref normsText);
                        g.Transform = Tprod;
                    }
                    n++;
                }
            }
            else                 //scale is logarithmic
            {
                double l0 = Math.Log10(Math.Abs(x0)), l1 = Math.Log10(Math.Abs(x1));
                double ld = l1 - l0;
            }

            // draw units
            g.Transform = T0;

            size = g.MeasureString(scale.unit, Model.UnitsFont);
            s0   = size.Height / 2;

            if (scale.scaleOutside)
            {
                Y = normsText.min.Y - s0; yNorm = Norm.Max;                 // draw the units below the scale
            }
            else
            {
                Y = normsText.max.Y + s0; yNorm = Norm.Min;                 // draw the units above the scale
            }
            DrawText(g, scale.unit, draw, Model.UnitsFont, brush, null, W / 2, Norm.Average, Y, yNorm, Trot, T, Tinv, ref p, ref norms);
            norms += normsText;
        }