Ejemplo n.º 1
0
        // TODO: Make this obey blending rules properly
        public unsafe void DrawPieChart(uint* pxls, int width, int height,
            PieChartSection[] sections, uint color)
        {
            if (0 == sections.Length)
            {
                throw new ArgumentException("Pie chart sections array cannot be empty");
            }

            // We need at least a 5x5 image
            if (width < 5 || height < 5)
            {
                return;
            }

            // If the image is not square, we want non-zero starting points
            int ystart = 0;
            int yend = height - 1; // Inclusive upper bound
            if (height > width)
            {
                ystart = (height - width) / 2;
                yend = height - ystart;
            }

            float xcenter = (float)width / 2.0f;
            float ycenter = (float)height / 2.0f;
            double radius = Math.Min(xcenter, ycenter) - 2.0;

            // Make a circle object for the pie chart area
            Circle2D circle = new Circle2D(xcenter, ycenter, (float)radius);

            // Compute the sum of all values in the pie sections
            double sum = 0.0;
            foreach (PieChartSection pcs in sections)
            {
                sum += pcs.Value;
            }

            // Make an angle table. This table stores the ending angle of each category. Also
            // make a color table.
            double[] angles = new double[sections.Length];
            uint[] clrLookup = new uint[sections.Length];
            int i = 0;
            double angle = 0.0;
            foreach (PieChartSection pcs in sections)
            {
                clrLookup[i] = pcs.Color;
                angles[i] = pcs.Value / sum * (Math.PI * 2.0) + angle;
                angle = pcs.Value / sum * (Math.PI * 2.0) + angle;
                i++;
            }

            while (ystart <= yend)
            {
                // Compute the intersections of the scan line with the pie chart
                float minx, maxx;
                circle.HLineIntersection((float)ystart, out minx, out maxx);

                // Clear the row if there isn't an intersection
                if (float.IsNaN(minx) || float.IsNaN(maxx))
                {
                    DrawHorizontalLine(0, width - 1, ystart, 0, pxls, width, height, BlendMode.None);
                    ystart++;
                    continue;
                }

                int xstart = (int)Math.Floor(minx);
                int xend = (int)Math.Min(Math.Ceiling(maxx), (float)(width - 1));
                uint* row = &pxls[ystart * width + xstart];
                for (int x = xstart; x <= xend; x++)
                {
                    // For each pixel, we want to start by computing the distance from the center
                    Vector2D pos = new Vector2D((float)x - xcenter, ycenter - (float)ystart);
                    double dist = pos.Length;

                    // Set the pixel to 0 if we're outside of the pie chart area
                    if (dist >= radius)
                    {
                        *row++ = 0;
                        continue;
                    }

                    // Compute the angle so we know what category of the pie we're in
                    angle = pos.GetCCWAngle();
                    i = 0;
                    while (angle > angles[i])
                    {
                        i++;
                    }

                    // Advance to the next pixel
                    *row++ = clrLookup[i];
                }

                ystart++;
            }

            // Draw lines between sections
            if (sections.Length > 1)
            {
                List<Stadium> stadiums = new List<Stadium>();
                foreach (double tempAngle in angles)
                {
                    stadiums.Add(new Stadium(circle.Center,
                            new Vector2D(xcenter + (float)(radius * Math.Cos(tempAngle)),
                            ycenter - (float)(radius * Math.Sin(tempAngle))), 1.75f));
                }
                FillStadiums(new SimpleImage(width, height, pxls), color, stadiums.ToArray(), 2f);
            }

            // Draw a circular border around the pie chart
            FrameCircle_Composite(circle, 3.5f, color, pxls, width, height);
        }