// 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; } }
// Define the model. private void DefineModel(Model3DGroup group) { // Axes. //MeshExtensions.AddAxes(group); // Make a scatter plot. Point3D[] values = LoadData(); string[] xlabels = { "10 mpg", "20 mpg", "30 mpg", "40 mpg" }; string[] ylabels = { "$10 k", "$20 k", "$30 k", "$40 k", "$50 k", "$60 k" }; string[] zlabels = { "100 hp", "200 hp", "300 hp", "400 hp" }; const double labelGap = 0.25; const double labelWid = 2.25; const double labelHgt = 0.75; const double labelFontSize = 0.6; const double titleHgt = 1.5; const double titleFontSize = 0.7; MakeScatterPlot(group, new Point3D(-3, -4, -3), 7, 9, 7, values, Brushes.Red, 0.05, 0.04, Brushes.Blue, Brushes.LightBlue, Brushes.Red, labelWid, labelHgt, labelGap, labelFontSize, Brushes.Blue, titleHgt, titleFontSize, Brushes.DarkBlue, 10, 40, 5, 10, "Mileage", xlabels, 10000, 60000, 5000, 10000, "Base Price", ylabels, 100, 400, 50, 100, "Horsepower", zlabels); // Title. FontFamily ff = new FontFamily("Franklin Gothic Demi"); Point3D ll = new Point3D(-4, 7, 2); double fontSize = 0.9; MakeLabel("Car Cost, Mileage,", ll, new Vector3D(6, 0, -6), D3.YVector(1.25), Brushes.Transparent, Brushes.Black, fontSize, ff, HorizontalAlignment.Center, VerticalAlignment.Center, group); ll.Y -= 1; MakeLabel("and Horsepower", ll, new Vector3D(6, 0, -6), D3.YVector(1.25), Brushes.Transparent, Brushes.Black, fontSize, ff, HorizontalAlignment.Center, VerticalAlignment.Center, group); }
// 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) { 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. 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); 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); }
// Process the models. private void ProcessModels(bool invertTextures, bool zIsUp) { // Make the dictionary of materials. foreach (ObjMaterial material in AllMaterials) { // Make the material's MaterialGroup. material.MatGroup = new MaterialGroup(); // Transparency. (Not used.) byte alpha = (byte)(material.Alpha * 255); // Diffuse. byte diffR = (byte)(material.Kd.X * 255); byte diffG = (byte)(material.Kd.Y * 255); byte diffB = (byte)(material.Kd.Z * 255); Color diffColor = Color.FromArgb(255, diffR, diffG, diffB); SolidColorBrush diffBrush = new SolidColorBrush(diffColor); DiffuseMaterial diffMat = new DiffuseMaterial(diffBrush); material.MatGroup.Children.Add(diffMat); // If it has a file, use it. if (material.Filename != null) { // Use the file. string filename = material.Filename; ImageBrush imgBrush = new ImageBrush(); imgBrush.ViewportUnits = BrushMappingMode.Absolute; imgBrush.TileMode = TileMode.Tile; // Invert the texture if necessary. if (invertTextures) { TransformGroup trans = new TransformGroup(); trans.Children.Add(new ScaleTransform(1, -1)); trans.Children.Add(new TranslateTransform(0, 1)); imgBrush.Transform = trans; } imgBrush.ImageSource = new BitmapImage(new Uri(filename, UriKind.Relative)); DiffuseMaterial imgMat = new DiffuseMaterial(imgBrush); material.MatGroup.Children.Add(imgMat); } // Specular. byte specR = (byte)(material.Ks.X * 255); byte specG = (byte)(material.Ks.Y * 255); byte specB = (byte)(material.Ks.Z * 255); Color specColor = Color.FromArgb(255, specR, specG, specB); SolidColorBrush specBrush = new SolidColorBrush(specColor); SpecularMaterial specMat = new SpecularMaterial(specBrush, material.Ns); material.MatGroup.Children.Add(specMat); // We ignore Ka and Tr. // Add it to the materials dictionary. MtlMaterials.Add(material.Name, material); } // Convert the object models into meshes. foreach (ObjModel model in AllObjectModels) { // Make the mesh. MeshGeometry3D mesh = new MeshGeometry3D(); Meshes.Add(mesh); MeshNames.Add(model.Name); MaterialNames.Add(model.MaterialName); // Make a new list of smoothing groups. Dictionary <int, Dictionary <Point3D, int> > smoothingGroups = new Dictionary <int, Dictionary <Point3D, int> >(); // Entry 0 is null (no smoothing). smoothingGroups.Add(0, null); // Make the faces. foreach (ObjFace face in model.Faces) { // Make the face's vertices. int numPoints = face.Vertices.Count; Point3D[] points = new Point3D[numPoints]; for (int i = 0; i < numPoints; i++) { points[i] = AllVertices[face.Vertices[i] - 1]; } // Get texture coordinates if present. Point[] textureCoords = null; if (face.TextureCoords.Count > 0) { textureCoords = new Point[numPoints]; for (int i = 0; i < numPoints; i++) { textureCoords[i] = AllTextureCoordinates[face.TextureCoords[i] - 1]; } } // Get normals if present. Vector3D[] normals = null; if (face.Normals.Count > 0) { normals = new Vector3D[numPoints]; for (int i = 0; i < numPoints; i++) { normals[i] = AllNormals[face.Normals[i] - 1]; } } // Get the point dictionary for this smoothing group. // Add new groups if needed. if (!smoothingGroups.ContainsKey(face.SmoothingGroup)) { smoothingGroups.Add(face.SmoothingGroup, new Dictionary <Point3D, int>()); } Dictionary <Point3D, int> pointDict = smoothingGroups[face.SmoothingGroup]; // Make the polygon. mesh.AddPolygon(pointDict: pointDict, textureCoords: textureCoords, normals: normals, points: points); } // If Z is up, rotate the model. if (zIsUp) { mesh.ApplyTransformation(D3.Rotate(D3.XVector(), D3.Origin, -90)); } } }