// Make rows of bars in the X and Z directions.
        private void MakeRowColumnBars(double xmin, double ymin,
                                       double zmin, double dx, double gap,
                                       double[,] values, string[] frontLabels, string[] sideLabels,
                                       Brush[,] barBrushes, Brush[] bgBrushes, Brush[] fgBrushes,
                                       FontFamily ff, Model3DGroup group)
        {
            int    numX = values.GetUpperBound(0) + 1;
            int    numZ = values.GetUpperBound(1) + 1;
            double x    = xmin;
            double z;
            double fontSize = 0.3;

            for (int ix = 0; ix < numX; ix++)
            {
                z = zmin;
                for (int iz = 0; iz < numZ; iz++)
                {
                    // Make the bar.
                    MeshGeometry3D barMesh = new MeshGeometry3D();
                    Point3D        corner  = new Point3D(x, ymin, z);
                    barMesh.AddBox(corner,
                                   D3.XVector(dx),
                                   D3.YVector(YScale * values[ix, iz]),
                                   D3.ZVector(dx));
                    group.Children.Add(barMesh.MakeModel(barBrushes[ix, iz]));

                    z += dx + gap;
                }

                // Display the front label.
                const double textWid = 2;
                MakeLabel(frontLabels[ix],
                          new Point3D(x + dx, ymin, z + textWid),
                          D3.ZVector(-textWid), D3.XVector(-dx),
                          bgBrushes[ix], fgBrushes[ix], fontSize, ff,
                          HorizontalAlignment.Left, VerticalAlignment.Center, group);

                x += dx + gap;
            }

            // Display the side labels.
            z = zmin + dx;
            double xmax = xmin + numX * dx + (numX - 1) * gap;

            for (int iz = 0; iz < numZ; iz++)
            {
                const double textWid = 2;
                MakeLabel(sideLabels[iz],
                          new Point3D(xmax + gap, ymin, z),
                          D3.XVector(textWid), D3.ZVector(-dx),
                          Brushes.Transparent, Brushes.Black, fontSize, ff,
                          HorizontalAlignment.Left, VerticalAlignment.Center, group);
                //MakeLabel(sideLabels[iz],
                //    new Point3D(xmin - textWid - gap, ymin, z),
                //    D3.XVector(textWid), D3.ZVector(-dx),
                //    Brushes.Transparent, Brushes.Black, fontSize, ff,
                //    HorizontalAlignment.Left, VerticalAlignment.Center, group);
                z += dx + gap;
            }
        }
        // Make a row of bars in the X direction.
        private void MakeBars(double xmin, double ymin,
                              double zmin, double dx, double gap,
                              double[] values, string[] frontLabels, string[] topLabels,
                              Brush[] barBrushes, Brush[] bgBrushes, Brush[] fgBrushes,
                              FontFamily ff, Model3DGroup group)
        {
            double x        = xmin;
            double fontSize = 0.4;

            for (int i = 0; i < values.Length; i++)
            {
                // Make the bar.
                MeshGeometry3D barMesh = new MeshGeometry3D();
                Point3D        corner  = new Point3D(x, ymin, zmin);
                barMesh.AddBox(corner,
                               D3.XVector(dx),
                               D3.YVector(YScale * values[i]),
                               D3.ZVector(dx));
                group.Children.Add(barMesh.MakeModel(barBrushes[i]));

                // Display the front label.
                const double textWid = 1.8;
                MakeLabel(frontLabels[i],
                          new Point3D(x + dx, ymin, textWid + zmin + dx + gap),
                          D3.ZVector(-textWid), D3.XVector(-dx),
                          bgBrushes[i], fgBrushes[i], fontSize, ff,
                          HorizontalAlignment.Left, VerticalAlignment.Center, group);

                // Display the top label.
                MakeLabel(topLabels[i],
                          new Point3D(x,
                                      ymin + YScale * values[i],
                                      zmin - 0.1),
                          D3.XVector(dx), D3.YVector(dx),
                          Brushes.Transparent, fgBrushes[i], fontSize, ff,
                          HorizontalAlignment.Center, VerticalAlignment.Bottom, group);

                x += dx + gap;
            }
        }
        // Make a row of stacked bars in the X direction.
        private void MakeStackedBars(double xmin, double ymin,
                                     double zmin, double dx, double gap,
                                     double[,] values, string[] frontLabels,
                                     Brush[] barBrushes, Brush bgBrush, Brush fgBrush,
                                     FontFamily ff, Model3DGroup group)
        {
            double x        = xmin;
            int    numX     = values.GetUpperBound(0) + 1;
            int    numZ     = values.GetUpperBound(1) + 1;
            double fontSize = 0.45;

            for (int ix = 0; ix < numX; ix++)
            {
                double y = ymin;
                for (int iz = 0; iz < numZ; iz++)
                {
                    // Make this piece of the bar.
                    MeshGeometry3D barMesh = new MeshGeometry3D();
                    Point3D        corner  = new Point3D(x, y, zmin);
                    barMesh.AddBox(corner,
                                   D3.XVector(dx),
                                   D3.YVector(YScale * values[ix, iz]),
                                   D3.ZVector(dx));
                    group.Children.Add(barMesh.MakeModel(barBrushes[iz]));
                    y += YScale * values[ix, iz];
                }

                // Display the front label.
                const double textWid = 1.5;
                MakeLabel(frontLabels[ix],
                          new Point3D(x + dx, ymin, textWid + zmin + dx + gap),
                          D3.ZVector(-textWid), D3.XVector(-dx),
                          bgBrush, fgBrush, fontSize, FontFamily,
                          HorizontalAlignment.Left, VerticalAlignment.Center, group);

                x += dx + gap;
            }
        }
        // Make a box with the back XY, YZ, and ZX planes labeled with lines,
        // tick marks, and labels every 1 unit.
        public void MakePlotBox(Model3DGroup group,
                                Point3D corner, double xLength, double yLength, double zLength,
                                double boxThickness, double lineThickness,
                                Brush boxBrush, Brush lineBrush, Brush tickBrush,
                                double labelWid, double labelHgt, double labelGap, double labelFontSize, Brush labelBrush,
                                double titleHgt, double titleFontSize, Brush titleBrush,
                                double xmin, double xmax, double tickDx, double labelDx, string xtitle, string[] xlabels,
                                double ymin, double ymax, double tickDy, double labelDy, string ytitle, string[] ylabels,
                                double zmin, double zmax, double tickDz, double labelDz, string ztitle, string[] zlabels,
                                bool shiftZ = false)
        {
            const double tickWid = 0.2;
            FontFamily   ff      = new FontFamily("Franklin Gothic Demi");

            // Draw the box outline.
            Vector3D       vx      = D3.XVector(xLength);
            Vector3D       vy      = D3.YVector(yLength);
            Vector3D       vz      = D3.ZVector(zLength);
            MeshGeometry3D boxMesh = new MeshGeometry3D();

            boxMesh.AddSegment(boxThickness, corner, corner + vx);
            boxMesh.AddSegment(boxThickness, corner, corner + vy);
            boxMesh.AddSegment(boxThickness, corner, corner + vz);
            boxMesh.AddSegment(boxThickness, corner + vy, corner + vy + vx);
            boxMesh.AddSegment(boxThickness, corner + vy, corner + vy + vz);
            boxMesh.AddSegment(boxThickness, corner + vx, corner + vx + vy);
            boxMesh.AddSegment(boxThickness, corner + vx, corner + vx + vz);
            boxMesh.AddSegment(boxThickness, corner + vz, corner + vz + vx);
            boxMesh.AddSegment(boxThickness, corner + vz, corner + vz + vy);
            group.Children.Add(boxMesh.MakeModel(boxBrush));

            // Minimum X, Y, and Z values, and scales.
            double px1    = corner.X;
            double px2    = corner.X + xLength;
            double py1    = corner.Y;
            double py2    = corner.Y + yLength;
            double pz1    = corner.Z;
            double pz2    = corner.Z + zLength;
            double xScale = xLength / (xmax - xmin);
            double yScale = yLength / (ymax - ymin);
            double zScale = zLength / (zmax - zmin);

            // Lines and labels.
            MeshGeometry3D lineMesh = new MeshGeometry3D();

            // Lines with different X values.
            Point3D ll;
            int     i = 0;

            for (double x = xmin; x < xmax + labelDx / 2; x += labelDx)
            {
                double px = px1 + (x - xmin) * xScale;

                // Label the line.
                ll = new Point3D(px + labelHgt / 2, py1, pz2 + labelWid + labelGap);
                MakeLabel(xlabels[i++], ll, D3.ZVector(-labelWid), D3.XVector(-labelHgt),
                          Brushes.Transparent, labelBrush, labelFontSize, ff,
                          HorizontalAlignment.Right, VerticalAlignment.Center, group);

                // Draw the line.
                if ((x > xmin) && (x < xmax - labelDx / 2))
                {
                    lineMesh.AddSegment(lineThickness,
                                        new Point3D(px, py1, pz1),
                                        new Point3D(px, py2, pz1));
                    lineMesh.AddSegment(lineThickness,
                                        new Point3D(px, py1, pz1),
                                        new Point3D(px, py1, pz2));
                }
            }

            // Lines with different Y values.
            i = 0;
            for (double y = ymin; y < ymax + labelDy / 2; y += labelDy)
            {
                double py = py1 + (y - ymin) * yScale;

                // Label the line.
                if (y > ymin)
                {
                    ll = new Point3D(px1, py - labelHgt / 2, pz2 + labelWid + labelGap);
                    MakeLabel(ylabels[i], ll, D3.ZVector(-labelWid), D3.YVector(labelHgt),
                              Brushes.Transparent, labelBrush, labelFontSize, ff,
                              HorizontalAlignment.Right, VerticalAlignment.Center, group);
                }
                i++;

                // Draw the line.
                if ((y > ymin) && (y < ymax - labelDy / 2))
                {
                    lineMesh.AddSegment(lineThickness,
                                        new Point3D(px1, py, pz1),
                                        new Point3D(px2, py, pz1));
                    lineMesh.AddSegment(lineThickness,
                                        new Point3D(px1, py, pz1),
                                        new Point3D(px1, py, pz2));
                }
            }

            // Lines with different Z values.
            double zshift = 0;

            if (shiftZ)
            {
                zshift -= (labelDz / 2) * zScale;
            }
            i = 0;
            for (double z = zmin; z < zmax + labelDz / 2; z += labelDz)
            {
                double pz = pz1 + (z - zmin) * zScale;

                // Label the line.
                ll = new Point3D(px2 + labelGap, py1, pz + labelHgt / 2 + zshift);
                MakeLabel(zlabels[i++], ll, D3.XVector(labelWid), D3.ZVector(-labelHgt),
                          Brushes.Transparent, labelBrush, labelFontSize, ff,
                          HorizontalAlignment.Left, VerticalAlignment.Center, group);

                // Draw the line.
                if ((z > zmin) && (z < zmax - labelDz / 2))
                {
                    lineMesh.AddSegment(lineThickness,
                                        new Point3D(px1, py1, pz),
                                        new Point3D(px2, py1, pz));
                    lineMesh.AddSegment(lineThickness,
                                        new Point3D(px1, py1, pz),
                                        new Point3D(px1, py2, pz));
                }
            }
            group.Children.Add(lineMesh.MakeModel(lineBrush));

            // Tick marks.
            MeshGeometry3D tickMesh = new MeshGeometry3D();

            // Lines with different X values.
            for (double x = xmin + tickDx; x < xmax + tickDx / 2; x += tickDx)
            {
                double px = px1 + (x - xmin) * xScale;
                tickMesh.AddSegment(lineThickness,
                                    new Point3D(px, py1, pz2 - tickWid),
                                    new Point3D(px, py1, pz2 + tickWid));
            }

            // Lines with different Y values.
            for (double y = ymin + tickDy; y < ymax + tickDy / 2; y += tickDy)
            {
                double py = py1 + (y - ymin) * yScale;
                tickMesh.AddSegment(lineThickness,
                                    new Point3D(px1, py, pz2 - tickWid),
                                    new Point3D(px1, py, pz2 + tickWid));
            }

            // Lines with different Z values.
            for (double z = zmin; z < zmax + tickDz / 2; z += tickDz)
            {
                double pz = pz1 + (z - zmin) * zScale;
                tickMesh.AddSegment(lineThickness,
                                    new Point3D(px2 - tickWid, py1, pz),
                                    new Point3D(px2 + tickWid, py1, pz));
            }
            group.Children.Add(tickMesh.MakeModel(lineBrush));

            // Draw titles.
            ll = new Point3D(
                px1,
                py1,
                pz2 + labelWid + labelGap + titleHgt);
            MakeLabel(xtitle, ll, D3.XVector(xLength), D3.ZVector(-titleHgt),
                      Brushes.Transparent, titleBrush, titleFontSize, ff,
                      HorizontalAlignment.Center, VerticalAlignment.Top, group);

            ll = new Point3D(
                px1,
                py2,
                pz2 + labelWid + labelGap + titleHgt);
            MakeLabel(ytitle, ll, D3.YVector(-yLength), D3.ZVector(-titleHgt),
                      Brushes.Transparent, titleBrush, titleFontSize, ff,
                      HorizontalAlignment.Center, VerticalAlignment.Top, group);

            ll = new Point3D(
                px2 + labelWid + labelGap + titleHgt,
                py1,
                pz2);
            MakeLabel(ztitle, ll, D3.ZVector(-zLength), D3.XVector(-titleHgt),
                      Brushes.Transparent, titleBrush, titleFontSize, ff,
                      HorizontalAlignment.Center, VerticalAlignment.Top, group);
        }