using System; using System.Collections; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; namespace PaintDotNet.Data.SurfacePlot { unsafe class JRenderer3D { SolidBrush brush = new SolidBrush(Color.Blue); SolidBrush brush1 = new SolidBrush(Color.White); Pen pen = new Pen(Color.Blue); Pen pen1 = new Pen(Color.Red); IntPtr ptr; BitmapData bmpData; int bytes; byte[] rgbValues; Rectangle rect; private Bitmap bufferedImage = null; private Graphics g2D = null; private int[] bufferPixels = null; // render buffer private double[] zbufferPixels = null; // Z-buffer private int bufferWidth = 512; // size of the buffers private int bufferHeight = 512; // Global rendering parameters private Color backgroundColor = Color.Gray; private Color legendTextColor = Color.White; // transform parameters private Transform transform = null; // the actual transformation private double tr_rotationX = 1.34; private double tr_rotationY = 0; private double tr_rotationZ = 1; private double tr_perspective = 0; private double tr_maxDistance = 256; private int zOrientation = -1; private double scale = 1; private double zAspectRatio = 1; private double xCenter = 0; // the x, y, and z - coordinates of the rotation center private double yCenter = 0; private double zCenter = 0; // objects to be drawn private ArrayList lines3D = null; private ArrayList cubeLines3D = null; private ArrayList text3D = null; // 3D points private PointsPlot pointsPlot = null; /** * Draws a point as a sphere (slowest). */ public static int POINT_SPHERE = Point3D.SPHERE; /** * Draws a point as a (2D) circle. */ public static int POINT_CIRCLE = Point3D.CIRCLE; /** * Draws a point as a dot. Size information has no effect. */ public static int POINT_DOT = Point3D.DOT; // surfacePlot constants & parameters private static int SURFACEGRID_DEFAULTWIDTH = 256; private static int SURFACEGRID_DEFAULTHEIGHT = 256; /** * Draws a surface plot using only dots, no illumination is used (fastest mode). */ public static int SURFACEPLOT_DOTSNOLIGHT = 10; /** * Draws a surface plot using only dots, illumination is active. */ public static int SURFACEPLOT_DOTS = 11; /** * Draws a surface plot using lines. Number of lines can be adjusted. */ public static int SURFACEPLOT_LINES = 12; /** * Draws a surface plot using a mesh. Mesh size can be adapted. */ public static int SURFACEPLOT_MESH = 13; /** * Draws a filled surface plot . */ public static int SURFACEPLOT_FILLED = 14; /** * Draws a filled surface plot (Slowest mode). */ public static int SURFACEPLOT_ISOLINES = 15; private SurfacePlot surfacePlot = null; private int surfacePlot_gridWidth = SURFACEGRID_DEFAULTWIDTH; private int surfacePlot_gridHeight = SURFACEGRID_DEFAULTHEIGHT; private Bitmap surfacePlot_imagePlusData = null; // image used for the surface plot private Bitmap surfacePlot_imagePlusTexture = null; // texture image private int surfacePlot_plotMode = SURFACEPLOT_LINES; private int surfacePlot_lutNr = LUT_ORIGINAL; private double surfacePlot_light = 0; /** * Draws a volume using only dots (fastest mode). */ public static int VOLUME_DOTS = 20; /** * Draws a volume using nearest neighbor interpolation. */ public static int VOLUME_SLICE_NEAREST_NEIGHBOR = 21; /** * Draws a volume using trilinear interpolation. */ public static int VOLUME_SLICE_TRILINEAR = 22; // public final static int VOLUME_SLICE_AND_BORDERS = 23; // public final static int VOLUME_SLICE_AND_VOLUME_DOTS = 24; /** * Draws a volume using trilinear interpolation projection from the front */ public static int VOLUME_PROJECTION_TRILINEAR_FRONT = 25; /** * Draws a volume using trilinear interpolation projection from the back */ // public static final int VOLUME_PROJECTION_TRILINEAR_BACK = 26; private Volume volume = null; private int volume_drawMode = VOLUME_DOTS; //private int volume_threshold = 0; //private int volume_cutDist = 0; // clips the view sceen private int volume_lutNr = LUT_ORIGINAL; // LUT type //private int volume_dotsDeltaX = 1; // subsampling factor in x direction (used by dots drawing) //private int volume_dotsDeltaY = 1; // subsampling factor in x direction (used by dots drawing) //private int volume_dotsDeltaZ = 1; // subsampling factor in x direction (used by dots drawing) private int surfacePlot_min = 0; // minimum value for the luminance transform private int surfacePlot_max = 255; // maximum value for the luminance transform private Bitmap image; private bool axes = true; private bool lines = true; private bool text = true; private bool legend = true; private double minZ; private double maxZ; // LUT constants /** * 3D represenations of objects are drawn with their original colors. */ public static int LUT_ORIGINAL = 50; /** * 3D representations of objects are drawn with grayscale colors. */ public static int LUT_GRAY = 51; /** * 3D representations of objects are drawn with spectrum colors. */ public static int LUT_SPECTRUM = 52; /** * 3D representations of objects are drawn with fire colors. */ public static int LUT_FIRE = 53; /** * 3D representations of objects are drawn with thermal colors. */ public static int LUT_THERMAL = 54; /** * 3D representations of objects are drawn in orange. */ public static int LUT_ORANGE = 55; /** * 3D representations of objects are drawn in blue. */ public static int LUT_BLUE = 56; /** * 3D representations of objects are drawn in black. */ public static int LUT_BLACK = 57; /** * 3D representations of objects are cored according to their gradient. */ public static int LUT_GRADIENT = 58; public static int LUT_GRADIENT2 = 59; /** * Creates a new JRenderer3D object. * * This has always to be the first step to generate a 3D scene. * * The center is assumed at (0,0,0) * */ public JRenderer3D() { //initBuffer(); } /** * Creates a new JRenderer3D object.
* * This has always to be the first step to generate a 3D scene. * * @param xCenter The x-coordinate of the rotation / plot center. * @param yCenter The y-coordinate of the rotation / plot center. * @param zCenter The z-coordinate of the rotation / plot center. */ public JRenderer3D(double xCenter, double yCenter, double zCenter) { this.xCenter = xCenter; this.yCenter = yCenter; this.zCenter = zCenter; //initBuffer(); } private void initBuffer() { image = new Bitmap(bufferWidth, bufferHeight); bytes = image.Width * image.Height; rgbValues = new byte[bytes * 3]; rect = new Rectangle(0, 0, image.Width, image.Height); bufferPixels = new int[bufferWidth * bufferHeight]; zbufferPixels = new double[bufferWidth * bufferHeight]; if (transform != null) { tr_rotationX = transform.getRotationX(); tr_rotationY = transform.getRotationY(); tr_rotationZ = transform.getRotationZ(); scale = transform.getScale(); zAspectRatio = transform.getZAspectRatio(); tr_perspective = transform.getPerspective(); tr_maxDistance = transform.getMaxDistance(); } transform = new Transform(bufferWidth, bufferHeight); // restore values from previous transformation transform.setZOrientation(zOrientation); transform.setRotationXYZ(tr_rotationX, tr_rotationY, tr_rotationZ); transform.setScale(scale); transform.setZAspectRatio(zAspectRatio); transform.setPerspective(tr_perspective); transform.setMaxDistance(tr_maxDistance); // if the surfacePlot exists, then update the references to the buffers if (surfacePlot != null) { surfacePlot.setBuffers(bufferPixels, zbufferPixels, bufferWidth, bufferHeight); surfacePlot.setTransform(transform); } /** if (volume != null) { volume.setBuffers(bufferPixels, zbufferPixels, bufferWidth, bufferHeight); volume.setTransform(transform); }**/ } private void lines1() { Point3D p0 = new Point3D(); Point3D p1 = new Point3D(); for (int i = 0; i < lines3D.Count; i++) { if (lines3D[i] != null && lines3D[i] is Line3D) { Line3D line = (Line3D)lines3D[i]; int color = line.color; setPoints(line, p0, p1); transform.transform(p0); double x0 = transform.X, y0 = transform.Y, z0 = transform.Z + 2; transform.transform(p1); double x1 = transform.X, y1 = transform.Y, z1 = transform.Z + 2; if (line.isPair) { i++; Line3D line2 = (Line3D)lines3D[i]; int color2 = line2.color; setPoints(line2, p0, p1); transform.transform(p0); double x0_2 = transform.X, y0_2 = transform.Y, z0_2 = transform.Z + 2; transform.transform(p1); double x1_2 = transform.X, y1_2 = transform.Y, z1_2 = transform.Z + 2; if (z0_2 + z1_2 > z0 + z1) { x0 = x0_2; y0 = y0_2; z0 = z0_2; x1 = x1_2; y1 = y1_2; z1 = z1_2; color = color2; } } double dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0; int numSteps = (int)Math.Max(Math.Abs(dx1), Math.Abs(dy1)); double step = (numSteps > 0) ? 1 / (double)numSteps : 1; for (int s = 0; s < numSteps; s++) { double f = s * step; int x = (int)(x0 + f * dx1); int y = (int)(y0 + f * dy1); if (x >= 0 && y >= 0 && x < bufferWidth && y < bufferHeight) { int pos = y * bufferWidth + x; double z = z0 + f * dz1; int v_ = (int)((z * 20) + 128); v_ = Math.Min(Math.Max(0, v_), 255); v_ = (int)(0xFF000000 | (v_ << 8)); if (z <= zbufferPixels[pos]) { zbufferPixels[pos] = z; bufferPixels[pos] = color; // v_; } } } } } } public void cubeLines() { Point3D p0 = new Point3D(); Point3D p1 = new Point3D(); for (int i = 0; i < cubeLines3D.Count; i++) { if (cubeLines3D[i] != null && cubeLines3D[i] is Line3D) { Line3D line = (Line3D)cubeLines3D[i]; int color = line.color; setPoints(line, p0, p1); transform.transform(p0); double x0 = transform.X, y0 = transform.Y, z0 = transform.Z; transform.transform(p1); double x1 = transform.X, y1 = transform.Y, z1 = transform.Z; double dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0; int numSteps = (int)Math.Max(Math.Abs(dx1), Math.Abs(dy1)); double step = (numSteps > 0) ? 1 / (double)numSteps : 1; for (int s = 0; s < numSteps; s++) { double f = s * step; int x = (int)(x0 + f * dx1); int y = (int)(y0 + f * dy1); if (x >= 0 && y >= 0 && x < bufferWidth && y < bufferHeight) { int pos = y * bufferWidth + x; double z = z0 + f * dz1; if (z <= zbufferPixels[pos]) { zbufferPixels[pos] = z; bufferPixels[pos] = color; } } } } } //第一个版本 /** if (image != null) { bmpData = image.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); ptr = bmpData.Scan0; Color color; for (int i = 0; i < bufferPixels.Length; i++) { color = Color.FromArgb(bufferPixels[i]); rgbValues[i * 3 + 2] = color.R; rgbValues[i * 3 + 1] = color.G; rgbValues[i * 3] = color.B; } System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes * 3); image.UnlockBits(bmpData); }**/ //第二个版本 if (image != null) { //bmpData = new BitmapData(); bmpData = image.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);//.Format24bppRgb ptr = bmpData.Scan0; for (int i = 0; i < bufferPixels.Length; i++) { int c0 = bufferPixels[i]; rgbValues[i * 3 + 2] = (byte)((c0 >> 16) & 0xff); rgbValues[i * 3 + 1] = (byte)((c0 >> 8) & 0xff); rgbValues[i * 3] = (byte)((c0) & 0xff); } System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes * 3); image.UnlockBits(bmpData); } //第三个版本 /** if (image != null) { bmpData = image.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);//.Format24bppRgb byte* ptr = (byte*)(bmpData.Scan0); for (int i = 0; i < bmpData.Width; i++) { for (int j = 0; j < bmpData.Height; j++) { int c0 = bufferPixels[i*j]; ptr[0] = (byte)((c0) & 0xff); ptr[1] = (byte)((c0 >> 8) & 0xff); ptr[2] = (byte)((c0 >> 16) & 0xff); } ptr += bmpData.Stride - bmpData.Width * 3; } //System.Runtime.InteropServices.Marshal.Copy(bufferPixels, 0, ptr, bytes); image.UnlockBits(bmpData); }**/ } private void setPoints(Line3D l0, Point3D p0, Point3D p1) { p0.x = l0.x1; p0.y = l0.y1; p0.z = l0.z1; p1.x = l0.x2; p1.y = l0.y2; p1.z = l0.z2; } private void finishAndDrawText() { if (bufferedImage == null || bufferedImage.Height != bufferHeight || bufferedImage.Width != bufferWidth) { bufferedImage = new Bitmap(bufferWidth, bufferHeight); g2D = Graphics.FromImage(bufferedImage); } g2D.FillRectangle(brush, 0, 0, bufferWidth, bufferHeight); g2D.DrawImage(image, 0, 0); Font font; if (text) { if (text3D != null) { double scale = transform.getScale(); for (int i = 0; i < text3D.Count; i++) { if (text3D[i] != null && text3D[i] is Text3D) { Text3D ti = (Text3D)text3D[i]; transform.transform(ti); double x = transform.X; double y = transform.Y; double z = transform.Z; double x2 = 0; double y2 = 0; double z2 = 0; if (ti.number == 2) { i++; Text3D ti2 = (Text3D)text3D[i]; transform.transform(ti2); x2 = transform.X; y2 = transform.Y; z2 = transform.Z; if (z2 < z) { x = x2; y = y2; z = z2; } } if (ti.number == 4) { i++; Text3D ti2 = (Text3D)text3D[i]; transform.transform(ti2); x2 = transform.X; y2 = transform.Y; z2 = transform.Z; i++; ti = (Text3D)text3D[i]; transform.transform(ti); double x3 = transform.X; double y3 = transform.Y; double z3 = transform.Z; i++; ti = (Text3D)text3D[i]; transform.transform(ti); double x4 = transform.X; double y4 = transform.Y; double z4 = transform.Z; if (x2 < x) { x = x2; y = y2; z = z3; } if (x3 < x) { x = x3; y = y3; z = z3; } if (x4 < x) { x = x4; y = y4; z = z4; } } if (z >= 0) { //g2D.setColor(ti.color); int strHeight = (int)(scale * ti.size); font = new Font("Sans", strHeight); //g2D.setFont(font); //FontMetrics metrics = g2D.getFontMetrics(); //int strWidth = metrics.stringWidth(ti.text); SizeF size = g2D.MeasureString(ti.text, font); g2D.DrawString(ti.text, font, brush1, (int)x - size.Width/ 2, (int)y + strHeight / 2); } } } } } if (text) { if (text3D != null) { double scale = transform.getScale(); for (int i = 0; i < text3D.Count; i++) { if (text3D[i] != null && text3D[i] is Text3D) { Text3D ti = (Text3D)text3D[i]; transform.transform(ti); double x = transform.X; double y = transform.Y; double z = transform.Z; double x2 = 0; double y2 = 0; double z2 = 0; if (ti.number == 2) { i++; Text3D ti2 = (Text3D)text3D[i]; transform.transform(ti2); x2 = transform.X; y2 = transform.Y; z2 = transform.Z; if (z2 < z) { x = x2; y = y2; z = z2; } } if (ti.number == 4) { i++; Text3D ti2 = (Text3D)text3D[i]; transform.transform(ti2); x2 = transform.X; y2 = transform.Y; z2 = transform.Z; i++; ti = (Text3D)text3D[i]; transform.transform(ti); double x3 = transform.X; double y3 = transform.Y; double z3 = transform.Z; i++; ti = (Text3D)text3D[i]; transform.transform(ti); double x4 = transform.X; double y4 = transform.Y; double z4 = transform.Z; if (x2 < x) { x = x2; y = y2; z = z3; } if (x3 < x) { x = x3; y = y3; z = z3; } if (x4 < x) { x = x4; y = y4; z = z4; } } //System.out.println("2 x:" + x + " y: " + y + " z: " + z + " " + ti.text); if (z < 0) { //g2D.setColor(ti.color); int strHeight = (int)(scale * ti.size); font = new Font("Sans", strHeight); //g2D.setFont(font); //FontMetrics metrics = g2D.getFontMetrics(); SizeF size = g2D.MeasureString(ti.text, font); g2D.DrawString(ti.text, font, brush1, (int)x - size.Width / 2, (int)y + strHeight / 2); } } } } } // show lut if (legend && surfacePlot != null) { int lutNr = surfacePlot.getSurfacePlotLut(); if ((lutNr > LUT_ORIGINAL && lutNr <= LUT_THERMAL) || (lutNr == LUT_ORIGINAL && surfacePlot.hasOtherLut)) { int hLut = 256; int wLut = 20; int xs = bufferWidth - 30; int xe = xs + wLut; int ys = (bufferHeight - hLut) / 2; bool isInverse = (surfacePlot.getInversefactor() == -1); //g2D.setColor(legendTextColor); g2D.DrawRectangle(pen1, new Rectangle(xs - 1, ys - 1, wLut + 2, hLut + 1)); for (int j = 0; j < 256; j++) { //g2D.setColor(new Color(surfacePlot.lut.colors[255 - j])); g2D.DrawLine(pen1, xs, ys + j, xe, ys + j); } double d = maxZ - minZ; double stepValue = calcStepSize(d, 11); double minStart = Math.Floor(minZ / stepValue) * stepValue; double delta = minStart - minZ; //g2D.setColor(legendTextColor); font = new Font("Sans", 12); //g2D.setFont(font); //FontMetrics metrics = g2D.getFontMetrics(); int stringHeight = 5; for (double value = 0; value + delta <= d; value += stepValue) { String s; if (Math.Floor(minStart + value) - (minStart + value) == 0) s = "" + (int)(minStart + value); else s = "" + (int)Math.Round((minStart + value) * 1000) / 1000; double pos = ((value + delta) * 256 / d); int y; if (pos >= 0) { y = (int)(-pos + 255 + ys); if (isInverse) y = (int)(pos + ys); //int strWidth = metrics.stringWidth(s); g2D.DrawString(s, font, brush1, xs - 5, y + stringHeight); g2D.DrawLine(pen1, xs - 3, y, xs - 1, y); } } } } } public void showRotation() { Font font = new Font("Sans", 13); //g2D.setFont(font); String str = "Rotation x = " + (int)((180 / Math.PI) * transform.getRotationX()) + "\u00b0" + ", Rotation z =" + (int)((180 / Math.PI) * transform.getRotationZ()) + "\u00b0"; g2D.DrawString(str, font, brush1, 10, 20); } public void setLegendTextColor(Color legendTextColor) { this.legendTextColor = legendTextColor; } double calcStepSize(double range, double targetSteps) { // Calculate an initial guess at step size double tempStep = range / targetSteps; // Get the magnitude of the step size double mag = Math.Floor(Math.Log(tempStep) / Math.Log(10)); double magPow = Math.Pow((double)10.0, mag); // Calculate most significant digit of the new step size double magMsd = ((int)(tempStep / magPow + .5)); // promote the MSD to either 1, 2, 4, or 5 if (magMsd > 6) // 5 magMsd = 10.0; else if (magMsd > 3) magMsd = 5.0; else if (magMsd > 2) magMsd = 4.0; else if (magMsd > 1) magMsd = 2.0; return magMsd * magPow; } private void clearBuffers() { for (int i = bufferPixels.Length - 1; i >= 0; i--) { bufferPixels[i] = 0; zbufferPixels[i] = 1000000; } } private Line3D setLinePoints(Line3D lineItem, double[] p1, double[] p2, int color) { lineItem.x1 = p1[0]; lineItem.y1 = p1[1]; lineItem.z1 = p1[2]; lineItem.x2 = p2[0]; lineItem.y2 = p2[1]; lineItem.z2 = p2[2]; lineItem.color = color; return lineItem; } private void addCubeLinesList(Line3D[] lines3D) { if (cubeLines3D == null) cubeLines3D = new ArrayList(); for (int i = 0; i < lines3D.Length; i++) { this.cubeLines3D.Add(lines3D[i]); } } private void clearCubeLines3D() { if (cubeLines3D != null) this.cubeLines3D.Clear(); } /** * This methods does the rendering and creates the 3D output. * It has to be redone after all setting changes (scale, angle, surface modes, lightning modes, color modes etc.) * Draws all given input data (lines, text, points, cubes, surfaces). */ public void doRendering() { clearBuffers(); if (volume != null) { transform.setOffsets(xCenter, yCenter, zCenter); volume.draw(); transform.setOffsets(0, 0, 0); } if (surfacePlot != null) { surfacePlot.draw(); } if (pointsPlot != null) { pointsPlot.draw(); } if (lines && lines3D != null) { //lines1(); } if (axes && cubeLines3D != null) { cubeLines(); } finishAndDrawText(); if (getSurfacePlotMode() == JRenderer3D.SURFACEPLOT_DOTSNOLIGHT) showRotation(); } /** * Creates the surface plot. * * @param imp The images to be drawn in 3D. */ public void setSurfacePlot(Bitmap imp) { //Cv2.ImShow("d111dd", OpenCvSharp.Extensions.BitmapConverter.ToMat(imp)); //在这里把图片resize下看看 //surfacePlot_imagePlusData = KiResizeImage(imp, surfacePlot_gridWidth, surfacePlot_gridHeight); surfacePlot_imagePlusData = imp; surfacePlot = new SurfacePlot(); surfacePlot.setSurfacePlotCenter(xCenter, yCenter, zCenter); surfacePlot.setSurfaceGridSize(surfacePlot_gridWidth, surfacePlot_gridHeight); surfacePlot.setSurfacePlotImage(surfacePlot_imagePlusData); if (surfacePlot_imagePlusTexture != null) { surfacePlot.setSurfacePlotTextureImage(surfacePlot_imagePlusTexture); } surfacePlot.resample(); surfacePlot.setSurfacePlotMode(surfacePlot_plotMode); surfacePlot.setSurfacePLotSetLight(surfacePlot_light); surfacePlot.setBuffers(bufferPixels, zbufferPixels, bufferWidth, bufferHeight); surfacePlot.setTransform(transform); } public void setSurfacePlotTexture(Bitmap impTexture) { surfacePlot_imagePlusTexture = impTexture; if (surfacePlot != null) { surfacePlot.setSurfacePlotTextureImage(impTexture); surfacePlot.resample(); } } /** * Sets the surface plot mode. *
* Available options: *
*
*