/* Copyright (c) 2018, John Lewin */ using System; using System.Diagnostics; using System.Linq; using MatterHackers.Agg; using MatterHackers.Agg.Image; using MatterHackers.PolygonMesh; using MatterHackers.VectorMath; namespace MatterHackers.MatterControl.Plugins.Lithophane { public static class Lithophane { class PixelInfo { public Vector3 Top { get; set; } public Vector3 Bottom { get; set; } } public static Mesh Generate(IImageData resizedImage, double maxZ, double nozzleWidth, double pixelsPerMM, bool invert, IProgress reporter) { throw new NotImplementedException(); //// TODO: Move this to a user supplied value //double baseThickness = nozzleWidth; // base thickness (in mm) //double zRange = maxZ - baseThickness; //// Dimensions of image //var width = resizedImage.Width; //var height = resizedImage.Height; //var zScale = zRange / 255; //var pixelData = resizedImage.Pixels; //Stopwatch stopwatch = Stopwatch.StartNew(); //var mesh = new Mesh(); ////var rescale = (double)onPlateWidth / imageData.Width; //var rescale = 1; //var progressStatus = new ProgressStatus(); //// Build an array of PixelInfo objects from each pixel //// Collapse from 4 bytes per pixel to one - makes subsequent processing more logical and has minimal cost //var pixels = pixelData.Where((x, i) => i % 4 == 0) // // Interpolate the pixel color to zheight // .Select(b => baseThickness + (invert ? 255 - b : b) * zScale) // // Create a Vector3 for each pixel at the computed x/y/z // .Select((z, i) => mesh.CreateVertex(new Vector3( // i % width * rescale, // (i - i % width) / width * rescale * -1, // z))) // // Create a mirrored vector for the pixel at z0 and return with top/bottom paired together // .Select(vec => new PixelInfo() // { // Top = vec, // Bottom = mesh.CreateVertex(new Vector3( // vec.Position.X, // vec.Position.Y, // 0)) // }).ToArray(); //Console.WriteLine("ElapsedTime - PixelInfo Linq Generation: {0}", stopwatch.ElapsedMilliseconds); //stopwatch.Restart(); //// Select pixels along image edges //var backRow = pixels.Take(width).Reverse().ToArray(); //var frontRow = pixels.Skip((height - 1) * width).Take(width).ToArray(); //var leftRow = pixels.Where((x, i) => i % width == 0).ToArray(); //var rightRow = pixels.Where((x, i) => (i + 1) % width == 0).Reverse().ToArray(); //int k, // nextJ, // nextK; //var notificationInterval = 100; //var workCount = (resizedImage.Width - 1) * (resizedImage.Height - 1) + // (height - 1) + // (width - 1); //double workIndex = 0; //// Vertical faces: process each row and column, creating the top and bottom faces as appropriate //for (int i = 0; i < resizedImage.Height - 1; ++i) //{ // var startAt = i * width; // // Process each column // for (int j = startAt; j < startAt + resizedImage.Width - 1; ++j) // { // k = j + 1; // nextJ = j + resizedImage.Width; // nextK = nextJ + 1; // // Create north, then south face // mesh.CreateFace(new IVertex[] { pixels[k].Top, pixels[j].Top, pixels[nextJ].Top, pixels[nextK].Top }); // mesh.CreateFace(new IVertex[] { pixels[j].Bottom, pixels[k].Bottom, pixels[nextK].Bottom, pixels[nextJ].Bottom }); // workIndex++; // if (workIndex % notificationInterval == 0) // { // progressStatus.Progress0To1 = workIndex / workCount; // reporter.Report(progressStatus); // } // } //} //// Side faces: East/West //for (int j = 0; j < height - 1; ++j) //{ // //Next row // k = j + 1; // // Create east, then west face // mesh.CreateFace(new IVertex[] { leftRow[k].Top, leftRow[j].Top, leftRow[j].Bottom, leftRow[k].Bottom }); // mesh.CreateFace(new IVertex[] { rightRow[k].Top, rightRow[j].Top, rightRow[j].Bottom, rightRow[k].Bottom }); // workIndex++; // if (workIndex % notificationInterval == 0) // { // progressStatus.Progress0To1 = workIndex / workCount; // reporter.Report(progressStatus); // } //} //// Side faces: North/South //for (int j = 0; j < width - 1; ++j) //{ // // Next row // k = j + 1; // // Create north, then south face // mesh.CreateFace(new IVertex[] { frontRow[k].Top, frontRow[j].Top, frontRow[j].Bottom, frontRow[k].Bottom }); // mesh.CreateFace(new IVertex[] { backRow[k].Top, backRow[j].Top, backRow[j].Bottom, backRow[k].Bottom }); // workIndex++; // if (workIndex % notificationInterval == 0) // { // progressStatus.Progress0To1 = workIndex / workCount; // reporter.Report(progressStatus); // } //} //Console.WriteLine("ElapsedTime - Face Generation: {0}", stopwatch.ElapsedMilliseconds); //return mesh; } public interface IImageData { byte[] Pixels { get; } int Width { get; } int Height { get; } } public class ImageBufferImageData : IImageData { ImageBuffer resizedImage; public ImageBufferImageData(ImageBuffer image, double pixelWidth) { resizedImage = this.ToResizedGrayscale(image, pixelWidth); resizedImage.FlipY(); } public int Width => resizedImage.Width; public int Height => resizedImage.Height; private ImageBuffer ToResizedGrayscale(ImageBuffer image, double onPlateWidth = 0) { var ratio = onPlateWidth / image.Width; var resizedImage = image.CreateScaledImage(ratio); var grayImage = resizedImage.ToGrayscale(); // Render grayscale pixels onto resized image with larger pixel format needed by caller resizedImage.NewGraphics2D().Render(grayImage, 0, 0); return resizedImage; } public byte[] Pixels => resizedImage.GetBuffer(); } } }