Exemple #1
0
        public static Gtk.Image GetCircle(Color circleColor, byte alpha, int radius)
        {
            Pixmap color = new Pixmap(MainWindow.main.GdkWindow, radius * 2, radius * 2);
            Pixmap mask  = new Pixmap(MainWindow.main.GdkWindow, radius * 2, radius * 2);

            Gdk.GC background = new Gdk.GC(color)
            {
                RgbFgColor = circleColor
            };
            Gdk.GC visible = new Gdk.GC(mask)
            {
                RgbFgColor = new Color(alpha, alpha, alpha)
            };
            Gdk.GC invisible = new Gdk.GC(mask)
            {
                RgbFgColor = new Color(0, 0, 0)
            };

            color.DrawRectangle(background, true, new Rectangle(0, 0, radius * 2, radius * 2));
            mask.DrawRectangle(invisible, true, new Rectangle(0, 0, radius * 2, radius * 2));

            mask.DrawArc(visible, true, 0, 0, radius * 2, radius * 2, 0, FULL_CIRCLE);

            if (PlatformDetection.os == OS.Linux)
            {
                mask = Bitmapize(mask, 128);
            }

            return(new Gtk.Image(color, mask));
        }
Exemple #2
0
        private void Redraw()
        {
            TooltipText = GetToolTip();
            using (Gdk.GC gc = new Gdk.GC(map))
            {
                gc.Colormap = Gdk.Colormap.System;

                gc.RgbFgColor = new Color(255, 255, 255);
                map.DrawRectangle(gc, true, 0, 0, Diameter, Diameter);

                // Paint a black ring
                gc.RgbFgColor = new Color(0, 0, 0);
                map.DrawArc(gc, true, Radius / 2, Radius / 2, Radius, Radius, 0, 360 * 64);

                // Paint the inner colour
                gc.RgbFgColor = GetColour();
                map.DrawArc(gc, true, Radius / 2 + 1, Radius / 2 + 1, Radius - 2, Radius - 2, 0, 360 * 64);
            }
            image.QueueDraw();
        }
Exemple #3
0
        void Draw()
        {
            if (pixmap == null)
            {
                return;
            }

            Style     style = Style;
            StateType state = Sensitive ? StateType.Normal : StateType.Insensitive;

            if (width <= 0 || height <= 0)
            {
                return;
            }

            //clear the pixmap
            GtkBeans.Style.PaintFlatBox(style, pixmap, StateType.Normal, ShadowType.None, null, this, "curve_bg", 0, 0, Allocation.Width, Allocation.Height);

            //draw the grid lines
            for (int i = 0; i < 5; i++)
            {
                pixmap.DrawLine(style.DarkGC(state),
                                x_offset,
                                i * (int)(height / 4.0) + y_offset,
                                width + x_offset,
                                i * (int)(height / 4.0) + y_offset);
                pixmap.DrawLine(style.DarkGC(state),
                                i * (int)(width / 4.0) + x_offset,
                                y_offset,
                                i * (int)(width / 4.0) + x_offset,
                                height + y_offset);
            }

            //draw the curve
            pixmap.DrawPoints(style.ForegroundGC(state), Interpolate(width, height));

            //draw the bullets
            if (CurveType != CurveType.Free)
            {
                foreach (var keyval in points)
                {
                    if (keyval.Key < MinX)
                    {
                        continue;
                    }
                    int x = Project(keyval.Key, MinX, MaxX, width);
                    int y = height - Project(keyval.Value, MinY, MaxY, height);
                    pixmap.DrawArc(style.ForegroundGC(state), true, x, y, radius * 2, radius * 2, 0, 360 * 64);
                }
            }
            GdkWindow.DrawDrawable(style.ForegroundGC(state), pixmap, 0, 0, 0, 0, Allocation.Width, Allocation.Height);
        }
Exemple #4
0
        public static Gtk.Image GetLocationPin(Color pinColor, int pinWidth, int pinHeight)
        {
            double pixelWidth  = pinWidth;
            double pixelHeight = pinHeight;

            //Independent quantities
            double w = pixelWidth * RESOLUTION_FACTOR;
            double r = w / 2;
            double h = pixelHeight * RESOLUTION_FACTOR;
            double m = BLACK_TRIM_WIDTH * RESOLUTION_FACTOR;

            //Dependent quantities
            double v = Math.Sqrt(h * h - h * w);
            double u = r / (h - r);

            Pixmap color = new Pixmap(MainWindow.main.GdkWindow, (int)w, (int)h);
            Pixmap mask  = new Pixmap(MainWindow.main.GdkWindow, (int)w, (int)h);

            Gdk.GC markerShape = new Gdk.GC(color)
            {
                RgbFgColor = pinColor
            };

            color.DrawRectangle(black, true, new Rectangle(0, 0, (int)w, (int)h));
            mask.DrawRectangle(invisible, true, new Rectangle(0, 0, (int)w, (int)h));

            color.DrawArc(markerShape, true, (int)m, (int)m, (int)(w - m * 2), (int)(w - m * 2), 0, FULL_CIRCLE);
            mask.DrawArc(visible, true, 0, 0, (int)w, (int)w, 0, FULL_CIRCLE);

            // The "triangle" here refers to the triangle formed by the bottom vertex, a tangent point and the bottom of the image.
            //
            //    -------     ---   Independent quantities:
            //  /    |    \    |    r = radius of circle
            // |     |_____|   |    h = height of pin
            // |     |  r  |   |    m = thickness of black trim
            //  \    |    /.   h
            //   \   |   / .   |    Dependent quantities:
            //    \  |θ /v .   |    θ = angle between axis of symmetry and the sides of the spear
            //     \ |^/   .   |    u = sin θ
            //      \|/.....  _|_   v = hypotenuse of the spear
            //           r
            //
            //      [center] O___
            //               |   ---___[innerRight]
            //               |         O--___
            //               |________/______--O [outerRight]
            //               |       /        /
            //               |      /        /
            //               |     /        /
            //               |    /        /
            //               |   /        /
            //               |  /        /
            //               | /        /
            //               |/        / v
            // [innerBottom] O--___   /
            //               |   m --/
            //               |      /
            //               |     /
            //               |    /
            //               |   /
            //               |θ /
            //               |^/
            //               |/
            // [outerBottom] O

            Vector2 center      = new Vector2(r, r);
            Vector2 outerBottom = new Vector2(w / 2, h);
            Vector2 innerBottom = new Vector2(r, h - m / u);
            Vector2 outerLeft   = center + new Vector2(-u * v, u * r);
            Vector2 outerRight  = center + new Vector2(u * v, u * r);
            Vector2 innerLeft   = outerLeft - new Vector2(-m * u * v / r, u * m);
            Vector2 innerRight  = outerRight - new Vector2(m * u * v / r, u * m);            //u*v/r = cos θ.

            color.DrawPolygon(markerShape, true, new Point[] { innerBottom.ToPoint(), innerLeft.ToPoint(), innerRight.ToPoint() });
            mask.DrawPolygon(visible, true, new Point[] { outerBottom.ToPoint(), outerLeft.ToPoint(), outerRight.ToPoint() });

            double coreRadius = w / 5;
            double coreCenter = w / 2;

            color.DrawArc(black, true,
                          (int)(coreCenter - coreRadius - m), (int)(coreCenter - coreRadius - m),
                          (int)(coreRadius * 2 + m * 2), (int)(coreRadius * 2 + m * 2),
                          0, FULL_CIRCLE);
            mask.DrawArc(film, true,
                         (int)(coreCenter - coreRadius), (int)(coreCenter - coreRadius),
                         (int)(coreRadius * 2), (int)(coreRadius * 2),
                         0, FULL_CIRCLE);

            Pixmap scaledColor = Scale(color, w, h, 0.1);
            Pixmap scaledMask  = Scale(mask, w, h, 0.1);

            if (PlatformDetection.os == OS.Linux)
            {
                scaledMask = Bitmapize(scaledMask, 128);
            }

            return(new Gtk.Image(scaledColor, scaledMask));
        }
Exemple #5
0
        public static Gtk.Image GetIcon(object iconified, Color iconColor, int iconSize, bool decor = false)
        {
            IconRequest request = new IconRequest(iconified, iconColor, iconSize);

            if (iconCache.ContainsKey(request))
            {
                return(new Gtk.Image(iconCache[request].color, iconCache[request].mask));
            }

            double pixelSize = iconSize;
            double size      = pixelSize * RESOLUTION_FACTOR;        //Since 12 across is 0-11; we don't want to draw on 12 and lose pixels.

            Pixmap color = new Pixmap(MainWindow.main.GdkWindow, (int)size, (int)size);
            Pixmap mask  = new Pixmap(MainWindow.main.GdkWindow, (int)size, (int)size);

            Gdk.GC colorGC = new Gdk.GC(color)
            {
                RgbFgColor = iconColor
            };

            if (iconified is Threat)
            {
                color.DrawRectangle(colorGC, true, new Rectangle(0, 0, (int)size, (int)size));
                mask.DrawRectangle(invisible, true, new Rectangle(0, 0, (int)size, (int)size));

                switch ((Threat)iconified)
                {
                case Threat.C:                         // a circle
                    mask.DrawArc(visible, true, (int)(size * 0.225), (int)(size * 0.225),
                                 (int)(size * 0.55), (int)(size * 0.55), 0, FULL_CIRCLE);
                    break;

                case Threat.B:                         // A square
                    double squareSize = 0.55 * size;
                    double margin     = (size - squareSize) / 2;
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point((int)margin, (int)margin),
                        new Point((int)(margin + squareSize), (int)margin),
                        new Point((int)(margin + squareSize), (int)(margin + squareSize)),
                        new Point((int)margin, (int)(margin + squareSize))
                    });
                    break;

                case Threat.A:                         // A triangle with the point upwards.
                    double width  = size * 0.75;
                    double height = width * Math.Sqrt(3) / 2;
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point((int)(size * 0.1), (int)(size - height) / 2),
                        new Point((int)(size * 0.9), (int)(size - height) / 2),
                        new Point((int)(size / 2), (int)(size + height) / 2)
                    });
                    break;

                case Threat.S:                         // A four-pointed star.
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point(0, (int)(size / 2)),
                        new Point((int)(size / 3), (int)(size / 3)),
                        new Point((int)(size / 2), 0),
                        new Point((int)(size * 2 / 3), (int)(size / 3)),
                        new Point((int)size, (int)(size / 2)),
                        new Point((int)(size * 2 / 3), (int)(size * 2 / 3)),
                        new Point((int)(size / 2), (int)size),
                        new Point((int)(size / 3), (int)(size * 2 / 3))
                    });
                    break;

                case Threat.X:
                    mask.DrawArc(visible, true, (int)(size * 0.05), (int)(size * 0.05), (int)(size * 0.9), (int)(size * 0.9), 0, FULL_CIRCLE);
                    mask.DrawArc(invisible, true, (int)(size * 0.15), (int)(size * 0.15),
                                 (int)(size * 0.7), (int)(size * 0.7), 0, FULL_CIRCLE);
                    mask.DrawArc(visible, true, (int)(size * 0.4), (int)(size * 0.4),
                                 (int)(size * 0.2), (int)(size * 0.2), 0, FULL_CIRCLE);
                    mask.DrawPoint(visible, (int)(size / 2), (int)(size / 2));
                    break;
                }
            }
            else if (iconified is StructureType)
            {
                if (decor)
                {
                    color.DrawRectangle(black, true, new Rectangle(0, 0, (int)size, (int)size));
                    mask.DrawRectangle(invisible, true, new Rectangle(0, 0, (int)size, (int)size));
                    mask.DrawArc(translucent, true, 0, 0, (int)size, (int)size, 0, FULL_CIRCLE);
                }
                else
                {
                    color.DrawRectangle(colorGC, true, new Rectangle(0, 0, (int)size, (int)size));
                    mask.DrawRectangle(invisible, true, new Rectangle(0, 0, (int)size, (int)size));
                }
                switch ((StructureType)iconified)
                {
                case StructureType.Tactical:
                    double  width       = size * 0.7;
                    double  height      = size * 0.75;
                    double  xMargin     = (size - width) / 2;
                    double  yMargin     = (size - height) / 2;
                    double  dipHeight   = size * 0.1;
                    double  peakHeight  = size * 0.3;
                    Vector2 upperLeft   = new Vector2(xMargin, yMargin);
                    Vector2 upperRight  = upperLeft + new Vector2(width, 0);
                    Vector2 lowerLeft   = upperLeft + new Vector2(0, height - peakHeight);
                    Vector2 lowerRight  = upperRight + new Vector2(0, height - peakHeight);
                    Vector2 upperMiddle = new Vector2(size / 2, yMargin + dipHeight);
                    Vector2 lowerMiddle = new Vector2(size / 2, size - yMargin);
                    mask.DrawPolygon(visible, true, new Point[] {
                        upperLeft.ToPoint(),
                        upperMiddle.ToPoint(),
                        upperRight.ToPoint(),
                        lowerRight.ToPoint(),
                        lowerMiddle.ToPoint(),
                        lowerLeft.ToPoint()
                    });
                    if (decor)
                    {
                        color.DrawPolygon(colorGC, true, new Point[] {
                            upperLeft.ToPoint(),
                            upperMiddle.ToPoint(),
                            upperRight.ToPoint(),
                            lowerRight.ToPoint(),
                            lowerMiddle.ToPoint(),
                            lowerLeft.ToPoint()
                        });
                    }
                    break;

                case StructureType.Economic:
                    Vector2 center      = new Vector2(size / 2, size / 2);
                    double  radii       = 0.2 * size;
                    double  diameter    = radii * 2;
                    Vector2 topCenter   = center - new Vector2(0, 2 * radii / Math.Sqrt(3));
                    Vector2 leftCenter  = center + new Vector2(radii, Math.Sqrt(2) / 2 * radii);
                    Vector2 rightCenter = center + new Vector2(-radii, Math.Sqrt(2) / 2 * radii);
                    Vector2 topCorner   = topCenter - new Vector2(radii, radii);
                    Vector2 leftCorner  = leftCenter - new Vector2(radii, radii);
                    Vector2 rightCorner = rightCenter - new Vector2(radii, radii);
                    mask.DrawArc(visible, true, (int)topCorner.x, (int)topCorner.y, (int)diameter, (int)diameter, 0, FULL_CIRCLE);
                    if (decor)
                    {
                        color.DrawArc(colorGC, true, (int)topCorner.x, (int)topCorner.y, (int)diameter, (int)diameter, 0, FULL_CIRCLE);
                    }
                    mask.DrawArc(visible, true, (int)leftCorner.x, (int)leftCorner.y, (int)diameter, (int)diameter, 0, FULL_CIRCLE);
                    if (decor)
                    {
                        color.DrawArc(colorGC, true, (int)leftCorner.x, (int)leftCorner.y, (int)diameter, (int)diameter, 0, FULL_CIRCLE);
                    }
                    mask.DrawArc(visible, true, (int)rightCorner.x, (int)rightCorner.y, (int)diameter, (int)diameter, 0, FULL_CIRCLE);
                    if (decor)
                    {
                        color.DrawArc(colorGC, true, (int)rightCorner.x, (int)rightCorner.y, (int)diameter, (int)diameter, 0, FULL_CIRCLE);
                    }
                    break;

                case StructureType.Aesthetic:
                    double radius1 = size * 0.4;
                    double margin1 = size / 2 - radius1;
                    double radius2 = radius1 * 0.75;
                    double margin2 = size / 2 - radius2;
                    mask.DrawArc(visible, true, (int)margin1, (int)margin1, (int)(radius1 * 2), (int)(radius1 * 2), 0, FULL_CIRCLE);
                    if (decor)
                    {
                        color.DrawArc(colorGC, true, (int)margin1, (int)margin1, (int)(radius1 * 2), (int)(radius1 * 2), 0, FULL_CIRCLE);
                        color.DrawArc(black, true, (int)margin2, (int)margin1, (int)(radius2 * 2), (int)(radius2 * 2), 0, FULL_CIRCLE);
                        mask.DrawArc(translucent, true, (int)margin2, (int)margin1, (int)(radius2 * 2), (int)(radius2 * 2), 0, FULL_CIRCLE);
                    }
                    else
                    {
                        mask.DrawArc(invisible, true, (int)margin2, (int)margin1, (int)(radius2 * 2), (int)(radius2 * 2), 0, FULL_CIRCLE);
                    }
                    break;
                }
            }
            else if (iconified is IconTemplate)
            {
                color.DrawRectangle(colorGC, true, new Rectangle(0, 0, (int)size, (int)size));
                mask.DrawRectangle(invisible, true, new Rectangle(0, 0, (int)size, (int)size));
                switch ((IconTemplate)iconified)
                {
                case IconTemplate.LeftArrow:
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point((int)size, 0),
                        new Point(0, (int)(size / 2)),
                        new Point((int)size, (int)size)
                    });
                    break;

                case IconTemplate.RightArrow:
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point(0, 0),
                        new Point((int)size, (int)(size / 2)),
                        new Point(0, (int)size)
                    });
                    break;

                case IconTemplate.X:
                    int close = (int)(size / 6);
                    int far   = (int)(size * 5 / 6);
                    int end   = (int)size;
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point(close, 0),
                        new Point(end, far),
                        new Point(far, end),
                        new Point(0, close)
                    });
                    mask.DrawPolygon(visible, true, new Point[] {
                        new Point(far, 0),
                        new Point(0, far),
                        new Point(close, end),
                        new Point(end, close)
                    });
                    break;
                }
            }

            /*
             *                      double radius = 0.55 * size;
             *                      double d = radius * 2;
             *                      double eyeballRadius = radius * 0.45;
             *                      double pupilRadius = eyeballRadius * 0.45;
             *                      Vector2 upCenter = new Vector2(size / 2, size / 2 - radius / 2);
             *                      Vector2 downCenter = new Vector2(size / 2, size / 2 + radius / 2);
             *                      Vector2 upCorner = upCenter - new Vector2(radius, radius);
             *                      Vector2 downCorner = downCenter - new Vector2(radius, radius);
             *                      mask.DrawArc(visible, true, (int)upCorner.x, (int)upCorner.y, (int)d, (int)d, -30 * 64, -120 * 64);
             *                      mask.DrawArc(visible, true, (int)downCorner.x, (int)downCorner.y, (int)d, (int)d, 30 * 64, 120 * 64);
             *                      mask.DrawArc(invisible, true, (int)(size / 2 - eyeballRadius), (int)(size / 2 - eyeballRadius),
             *                                               (int)(eyeballRadius * 2), (int)(eyeballRadius * 2), 0, FULL_CIRCLE);
             *                      mask.DrawArc(visible, true, (int)(size / 2 - pupilRadius), (int)(size / 2 - pupilRadius),
             *                                               (int)(pupilRadius * 2), (int)(pupilRadius * 2), 0, FULL_CIRCLE);
             */

            Pixmap scaledColor = Scale(color, size, size, 0.1);
            Pixmap scaledMask  = Scale(mask, size, size, 0.1);

            if (PlatformDetection.os == OS.Linux)
            {
                scaledMask = Bitmapize(scaledMask, 75);
            }
            iconCache.Add(request, new Icon(scaledColor, scaledMask));
            return(new Gtk.Image(scaledColor, scaledMask));
        }
        public void DrawChart(object obj, SizeAllocatedArgs args)
        {
            int width  = args.Allocation.Width;
            int height = args.Allocation.Height;
            int size   = Math.Min(width, height);

            //If this is true, then we don't need to update.
            if (size == currentSize)
            {
                return;
            }
            currentSize = size;

            currentSize = size;
            int chartRadius = size;
            int labelRadius = 0;

            color = new Pixmap(MainWindow.main.GdkWindow, size, size);
            mask  = new Pixmap(MainWindow.main.GdkWindow, size, size);

            Gdk.GC visible = new Gdk.GC(mask)
            {
                RgbFgColor = new Color(255, 255, 255)
            };
            Gdk.GC invisible = new Gdk.GC(mask)
            {
                RgbFgColor = new Color(0, 0, 0)
            };

            Gdk.GC white = new Gdk.GC(color)
            {
                RgbFgColor = new Color(255, 255, 255)
            };                                                                                           // for marking the mask.
            Gdk.GC lightGrey = new Gdk.GC(color)
            {
                RgbFgColor = new Color(200, 200, 200)
            };                                                                                           // for the axes
            Gdk.GC grey = new Gdk.GC(color)
            {
                RgbFgColor = new Color(125, 125, 125)
            };                                                                                           // for the axis markings
            Gdk.GC darkGrey = new Gdk.GC(color)
            {
                RgbFgColor = new Color(100, 100, 100)
            };                                                                                           // for the axis labels
            Gdk.GC[] wrapperColors =
            {
                new Gdk.GC(color)
                {
                    RgbFgColor = new Gdk.Color(170, 140, 0)
                },                                                                             //Regular
                new Gdk.GC(color)
                {
                    RgbFgColor = new Color(0, 0, 200)
                },                                                                             //Tinker
                new Gdk.GC(color)
                {
                    RgbFgColor = new Color(0, 150, 0)
                },                                                                             //Master
                new Gdk.GC(color)
                {
                    RgbFgColor = new Color(0, 0, 0)
                },                                                                             //Breaker
            };

            color.DrawRectangle(white, true, new Rectangle(0, 0, size, size));
            mask.DrawRectangle(invisible, true, new Rectangle(0, 0, size, size));

            //The eight directions in which vertices lie
            Vector2[] directions =
            {
                new Vector2(0,                                -1),
                new Vector2(Math.Sqrt(2) / 2,  -Math.Sqrt(2) / 2),
                new Vector2(1,                                 0),
                new Vector2(Math.Sqrt(2) / 2,  Math.Sqrt(2) / 2),
                new Vector2(0,                                 1),
                new Vector2(-Math.Sqrt(2) / 2, Math.Sqrt(2) / 2),
                new Vector2(-1,                                0),
                new Vector2(-Math.Sqrt(2) / 2, -Math.Sqrt(2) / 2)
            };
            IntVector2 center = new IntVector2(size / 2, size / 2);


            //Compute magnitudes

            int[]   indexMap        = { 0, 1, 5, 3, 7, 0, 4, 6, 2 }; //old[0] -> new[1], old[1] -> new[5] etc.
            int[]   reverseIndexMap = { 5, 1, 8, 3, 6, 2, 7, 4 };    //old[0] -> new[1], old[1] -> new[5] etc.
            float[] magnitudes      = new float[8];                  //Magnitudes of the positions of the vertices
            float[,] fractions = new float[8, 3];                    //Fractional contribution of each wrapper of each rating classification

            for (int i = 1; i <= 8; i++)
            {
                int j = indexMap[i];
                magnitudes[j] = values[4, i] > 0 ? values[4, i] : 0;
                if (o_vals[4, i] != 0)
                {
                    fractions[j, 0] = (float)o_vals[0, i] / o_vals[4, i];
                    fractions[j, 1] = (float)(o_vals[0, i] + o_vals[1, i]) / o_vals[4, i];
                    fractions[j, 2] = (float)(o_vals[0, i] + o_vals[1, i] + o_vals[2, i]) / o_vals[4, i];
                }
            }

            float greatestMagnitude = 0;

            for (int i = 0; i < 8; i++)
            {
                if (magnitudes[i] > greatestMagnitude)
                {
                    greatestMagnitude = magnitudes[i];
                }
            }
            if (greatestMagnitude < 0.01)
            {
                return;
            }

            //Determing text radius and preload labels;
            Pango.Layout[] labels     = new Pango.Layout[8];
            IntVector2[]   labelSizes = new IntVector2[8];
            for (int i = 0; i < 8; i++)
            {
                //Label
                labels[i] = new Pango.Layout(PangoContext)
                {
                    Alignment = Pango.Alignment.Center
                };
                if (multipliers == null)
                {
                    labels[i].SetText(Ratings.symbols[reverseIndexMap[i]]);
                }
                else
                {
                    labels[i].SetMarkup(Ratings.symbols[reverseIndexMap[i]] + "\n<small>×" + multipliers[reverseIndexMap[i]].ToString("0.0") + "</small>");
                }
                labels[i].GetSize(out int labelWidth, out int labelHeight);
                labelSizes[i] = new IntVector2(labelWidth, labelHeight) / Pango.Scale.PangoScale;
                int thisLabelRadius = Math.Max(labelSizes[i].x, labelSizes[i].y);
                if (thisLabelRadius > labelRadius)
                {
                    labelRadius = thisLabelRadius;
                    chartRadius = size / 2 - labelRadius * 2;
                }
            }

            for (int i = 0; i < 8; i++)
            {
                IntVector2 textPoint = directions[i] * (chartRadius + labelRadius) + (Vector2)center - (Vector2)labelSizes[i] / 2;
                color.DrawLayout(darkGrey, textPoint.x, textPoint.y, labels[i]);
                mask.DrawLayout(visible, textPoint.x, textPoint.y, labels[i]);
                //Line
                IntVector2 endPoint = (directions[i] * chartRadius) + (Vector2)center;
                color.DrawLine(lightGrey, center.x, center.y, endPoint.x, endPoint.y);
                mask.DrawLine(visible, center.x, center.y, endPoint.x, endPoint.y);
            }


            //Draw circles and axis markings
            float theoreticalPtInterval     = (size / 6 + 20) / 2;
            float theoreticalRatingInterval = theoreticalPtInterval / chartRadius * greatestMagnitude; //(between 20pt markings)
            int   ratingInterval            = (int)Math.Round(theoreticalRatingInterval);              //Round it off for neatness
            float ptInterval = ratingInterval / greatestMagnitude * chartRadius;                       //Now what's the pt interval for that?

            for (int i = 1; ptInterval * i < chartRadius; i++)
            {
                //Circle
                int radius = (int)(ptInterval * i);
                color.DrawArc(lightGrey, false,
                              center.x - radius, center.y - radius,
                              radius * 2, radius * 2,
                              0, 23040);                              //Angles are in 1/64th degrees, 23040 = 360*64 = full circle.
                mask.DrawArc(visible, false,
                             center.x - radius, center.y - radius,
                             radius * 2, radius * 2,
                             0, 23040);
                //Axis marking
                Pango.Layout mark = new Pango.Layout(PangoContext);
                mark.SetText("" + ratingInterval * i);
                mark.GetSize(out int markWidth, out int markHeight);
                IntVector2 markSize   = new IntVector2(markWidth, markHeight) / Pango.Scale.PangoScale;
                IntVector2 markCenter = (Vector2)center + directions[2] * ptInterval * i;
                mask.DrawArc(invisible, true, markCenter.x - markSize.x / 2, markCenter.y - markSize.y / 2,
                             markSize.x, markSize.y, 0, 23040);                                               //Clears a circular space for the mark
                color.DrawLayout(grey, markCenter.x - markSize.x / 2, markCenter.y - markSize.y / 2, mark);   //Actually
                mask.DrawLayout(visible, markCenter.x - markSize.x / 2, markCenter.y - markSize.y / 2, mark); //draw the mark.
            }

            //Compute vertices
            IntVector2[,] vertices = new IntVector2[4, 8];
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    vertices[j, i] = directions[i] * (chartRadius / greatestMagnitude) * magnitudes[i] * fractions[i, j] + (Vector2)center;
                }
                vertices[3, i] = directions[i] * (chartRadius / greatestMagnitude) * magnitudes[i] + (Vector2)center;
            }

            //Bump vertices by a pixel if they overlap.
            bool changed = true;

            while (changed)
            {
                changed = false;
                for (int i = 3; i > 1; i--)
                {
                    for (int j = 0; j < 8; j++)
                    {
                        if (vertices[i, j] == vertices[i - 1, j])
                        {
                            vertices[i, j] += (IntVector2)directions[j];
                            changed         = true;
                        }
                    }
                }
            }

            //Draw polygons
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 7; j++)                   //j=7 is excluded as we need to connect it to vertex 0 manually.
                {
                    color.DrawLine(wrapperColors[i],
                                   vertices[i, j].x, vertices[i, j].y,                                               //This point
                                   vertices[i, j + 1].x, vertices[i, j + 1].y);                                      //Next point
                    mask.DrawLine(visible,
                                  vertices[i, j].x, vertices[i, j].y,                                                //This point
                                  vertices[i, j + 1].x, vertices[i, j + 1].y);                                       //Next point
                }
                color.DrawLine(wrapperColors[i],
                               vertices[i, 7].x, vertices[i, 7].y,                                   //Point 7
                               vertices[i, 0].x, vertices[i, 0].y);                                  //Point 0
                mask.DrawLine(visible,
                              vertices[i, 7].x, vertices[i, 7].y,                                    //Point 7
                              vertices[i, 0].x, vertices[i, 0].y);                                   //Point 0
            }

            if (metamultipliers != null)
            {
                int currentY = size - 5;
                for (int i = 3; i >= 0; i--)
                {
                    Pango.Layout label = new Pango.Layout(PangoContext)
                    {
                        Alignment = Pango.Alignment.Center
                    };
                    label.SetMarkup("<small>x" + metamultipliers[i].ToString("0.0") + "</small>");
                    label.GetSize(out int labelWidth, out int labelHeight);
                    labelWidth  = (int)(labelWidth / Pango.Scale.PangoScale);
                    labelHeight = (int)(labelHeight / Pango.Scale.PangoScale);
                    currentY   -= labelHeight;
                    color.DrawLayout(wrapperColors[i], size - labelWidth - 5, currentY, label);
                    mask.DrawLayout(visible, size - labelWidth - 5, currentY, label);
                }
            }

            SetFromPixmap(color, mask);
        }