Ejemplo n.º 1
0
        public GCodeFile GenerateGCode(IList <Tuple <DMesh3, TPrintSettings> > parts,
                                       TPrintSettings globalSettings,
                                       out IEnumerable <string> generationReport,
                                       Action <GCodeLine> gcodeLineReadyF = null,
                                       Action <string> progressMessageF   = null)
        {
            if (AcceptsParts == false && parts != null && parts.Count > 0)
            {
                throw new Exception("Must pass null or empty list of parts to generator that does not accept parts.");
            }

            // Create print mesh set
            PrintMeshAssembly meshes = new PrintMeshAssembly();

            foreach (var part in parts)
            {
                if (part.Item2 != null)
                {
                    throw new ArgumentException($"Entries for the `parts` arguments must have a null second item since this generator does not handle per-part settings.");
                }
                meshes.AddMesh(part.Item1, PrintMeshOptions.Default());
            }

            progressMessageF?.Invoke("Slicing...");

            // Do slicing
            MeshPlanarSlicer slicer = new MeshPlanarSlicer()
            {
                LayerHeightMM = globalSettings.LayerHeightMM
            };

            slicer.Add(meshes);
            PlanarSliceStack slices = slicer.Compute();

            // Run the print generator
            progressMessageF?.Invoke("Running print generator...");
            var printGenerator = new TPrintGenerator();
            AssemblerFactoryF overrideAssemblerF = globalSettings.AssemblerType();

            printGenerator.Initialize(meshes, slices, globalSettings, overrideAssemblerF);

            if (printGenerator.Generate())
            {
                generationReport = printGenerator.GenerationReport;
                return(printGenerator.Result);
            }
            else
            {
                throw new Exception("PrintGenerator failed to generate gcode!");
            }
        }
Ejemplo n.º 2
0
 public int AddMesh(DMesh3 mesh)
 {
     return(AddMesh(mesh, PrintMeshOptions.Default()));
 }
Ejemplo n.º 3
0
 public void AddMeshes(IEnumerable <DMesh3> meshes)
 {
     AddMeshes(meshes, PrintMeshOptions.Default());
 }
Ejemplo n.º 4
0
 public void AddMesh(DMesh3 mesh)
 {
     AddMesh(mesh, PrintMeshOptions.Default());
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Slice the meshes and return the slice stack.
        /// </summary>
        public Result Compute()
        {
            Result result = new Result();

            if (Meshes.Count == 0)
            {
                return(result);
            }

            // find Z interval we want to slice in
            Interval1d zrange = Interval1d.Empty;

            foreach (var meshinfo in Meshes)
            {
                zrange.Contain(meshinfo.bounds.Min.z);
                zrange.Contain(meshinfo.bounds.Max.z);
            }
            if (SetMinZValue != double.MinValue)
            {
                zrange.a = SetMinZValue;
            }

            result.TopZ  = Math.Round(zrange.b, PrecisionDigits);
            result.BaseZ = Math.Round(zrange.a, PrecisionDigits);

            // [TODO] might be able to make better decisions if we took flat regions
            // into account when constructing initial Z-heights? if we have large flat
            // region just below Zstep, might make sense to do two smaller Z-steps so we
            // can exactly hit it??

            // construct list of clearing Z-heights
            List <double> clearingZLayers = new List <double>();
            double        cur_layer_z     = zrange.b;
            int           layer_i         = 0;

            while (cur_layer_z > zrange.a)
            {
                double layer_height = get_layer_height(layer_i);
                cur_layer_z -= layer_height;
                double z = Math.Round(cur_layer_z, PrecisionDigits);
                clearingZLayers.Add(z);
                layer_i++;
            }
            if (clearingZLayers.Last() < result.BaseZ)
            {
                clearingZLayers[clearingZLayers.Count - 1] = result.BaseZ;
            }
            if (clearingZLayers.Last() == clearingZLayers[clearingZLayers.Count - 2])
            {
                clearingZLayers.RemoveAt(clearingZLayers.Count - 1);
            }

            // construct layer slices from Z-heights
            List <PlanarSlice> clearing_slice_list = new List <PlanarSlice>();

            layer_i = 0;
            for (int i = 0; i < clearingZLayers.Count; ++i)
            {
                double layer_height = (i == clearingZLayers.Count - 1) ?
                                      (result.TopZ - clearingZLayers[i]) : (clearingZLayers[i + 1] - clearingZLayers[i]);
                double     z     = clearingZLayers[i];
                Interval1d zspan = new Interval1d(z, z + layer_height);

                if (SliceLocation == SliceLocations.EpsilonBase)
                {
                    z += 0.001;
                }

                PlanarSlice slice = SliceFactoryF(zspan, z, layer_i);
                clearing_slice_list.Add(slice);
                layer_i++;
            }


            int NH = clearing_slice_list.Count;

            if (NH > MaxLayerCount)
            {
                throw new Exception("MeshPlanarSlicer.Compute: exceeded layer limit. Increase .MaxLayerCount.");
            }

            PlanarSlice[] clearing_slices = clearing_slice_list.ToArray();

            // assume Resolve() takes 2x as long as meshes...
            TotalCompute = (Meshes.Count * NH) + (2 * NH);
            Progress     = 0;

            // compute slices separately for each mesh
            for (int mi = 0; mi < Meshes.Count; ++mi)
            {
                if (Cancelled())
                {
                    break;
                }

                DMesh3           mesh         = Meshes[mi].mesh;
                PrintMeshOptions mesh_options = Meshes[mi].options;

                // [TODO] should we hang on to this spatial? or should it be part of assembly?
                DMeshAABBTree3   spatial = new DMeshAABBTree3(mesh, true);
                AxisAlignedBox3d bounds  = Meshes[mi].bounds;

                bool is_cavity   = mesh_options.IsCavity;
                bool is_crop     = mesh_options.IsCropRegion;
                bool is_support  = mesh_options.IsSupport;
                bool is_closed   = (mesh_options.IsOpen) ? false : mesh.IsClosed();
                var  useOpenMode = (mesh_options.OpenPathMode == PrintMeshOptions.OpenPathsModes.Default) ?
                                   DefaultOpenPathMode : mesh_options.OpenPathMode;

                if (is_crop || is_support)
                {
                    throw new Exception("Not supported!");
                }

                // each layer is independent so we can do in parallel
                gParallel.ForEach(Interval1i.Range(NH), (i) => {
                    if (Cancelled())
                    {
                        return;
                    }

                    double z = clearing_slices[i].Z;
                    if (z < bounds.Min.z || z > bounds.Max.z)
                    {
                        return;
                    }

                    // compute cut
                    Polygon2d[] polys; PolyLine2d[] paths;
                    ComputeSlicePlaneCurves(mesh, spatial, z, is_closed, out polys, out paths);

                    if (is_closed)
                    {
                        // construct planar complex and "solids"
                        // (ie outer polys and nested holes)
                        PlanarComplex complex = new PlanarComplex();
                        foreach (Polygon2d poly in polys)
                        {
                            complex.Add(poly);
                        }

                        PlanarComplex.FindSolidsOptions options
                            = PlanarComplex.FindSolidsOptions.Default;
                        options.WantCurveSolids            = false;
                        options.SimplifyDeviationTolerance = 0.001;
                        options.TrustOrientations          = true;
                        options.AllowOverlappingHoles      = true;

                        PlanarComplex.SolidRegionInfo solids   = complex.FindSolidRegions(options);
                        List <GeneralPolygon2d> solid_polygons = ApplyValidRegions(solids.Polygons);

                        if (is_cavity)
                        {
                            add_cavity_polygons(clearing_slices[i], solid_polygons, mesh_options);
                        }
                        else
                        {
                            if (ExpandStockAmount > 0)
                            {
                                solid_polygons = ClipperUtil.MiterOffset(solid_polygons, ExpandStockAmount);
                            }
                            add_solid_polygons(clearing_slices[i], solid_polygons, mesh_options);
                        }
                    }

                    Interlocked.Increment(ref Progress);
                });       // end of parallel.foreach
            }             // end mesh iter

            // resolve planar intersections, etc
            gParallel.ForEach(Interval1i.Range(NH), (i) => {
                if (Cancelled())
                {
                    return;
                }
                clearing_slices[i].Resolve();
                Interlocked.Add(ref Progress, 2);
            });
            // add to clearing stack
            result.Clearing = SliceStackFactoryF();
            for (int k = 0; k < clearing_slices.Length; ++k)
            {
                result.Clearing.Add(clearing_slices[k]);
            }



            /*
             * Horizontal planar regions finishing pass.
             * First we find all planar horizontal Z-regions big enough to mill.
             * Then we add slices at the Z's we haven't touched yet.
             *
             * Cannot just 'fill' planar regions because we will miss edges that might
             * be millable. So we grow region and then intersect with full-slice millable area.
             */

            // find set of horizontal flat regions
            Dictionary <double, List <PlanarRegion> > flat_regions = FindPlanarZRegions(ToolDiameter);

            if (flat_regions.Count == 0)
            {
                goto done_slicing;
            }

            // if we have already milled this exact Z-height in clearing pass, then we can skip it
            List <double> doneZ = new List <double>();

            foreach (double z in flat_regions.Keys)
            {
                if (clearingZLayers.Contains(z))
                {
                    doneZ.Add(z);
                }
            }
            foreach (var z in doneZ)
            {
                flat_regions.Remove(z);
            }

            // create slice for each layer
            PlanarSlice[] horz_slices = new PlanarSlice[flat_regions.Count];
            List <double> flatZ       = new List <double>(flat_regions.Keys);

            flatZ.Sort();
            for (int k = 0; k < horz_slices.Length; ++k)
            {
                double     z     = flatZ[k];
                Interval1d zspan = new Interval1d(z, z + LayerHeightMM);
                horz_slices[k] = SliceFactoryF(zspan, z, k);

                // compute full millable region slightly above this slice.
                PlanarSlice clip_slice = ComputeSolidSliceAtZ(z + 0.0001, false);
                clip_slice.Resolve();

                // extract planar polys
                List <Polygon2d> polys   = GetPlanarPolys(flat_regions[z]);
                PlanarComplex    complex = new PlanarComplex();
                foreach (Polygon2d poly in polys)
                {
                    complex.Add(poly);
                }

                // convert to planar solids
                PlanarComplex.FindSolidsOptions options
                    = PlanarComplex.FindSolidsOptions.SortPolygons;
                options.SimplifyDeviationTolerance = 0.001;
                options.TrustOrientations          = true;
                options.AllowOverlappingHoles      = true;
                PlanarComplex.SolidRegionInfo solids         = complex.FindSolidRegions(options);
                List <GeneralPolygon2d>       solid_polygons = ApplyValidRegions(solids.Polygons);

                // If planar solid has holes, then when we do inset later, we might lose
                // too-thin parts. Shrink the holes to avoid this case.
                //FilterHoles(solid_polygons, 0.55 * ToolDiameter);

                // ok now we need to expand region and intersect with full region.
                solid_polygons = ClipperUtil.MiterOffset(solid_polygons, ToolDiameter * 0.5, 0.0001);
                solid_polygons = ClipperUtil.Intersection(solid_polygons, clip_slice.Solids, 0.0001);

                // Same idea as above, but if we do after, we keep more of the hole and
                // hence do less extra clearing.
                // Also this could then be done at the slicer level instead of here...
                // (possibly this entire thing should be done at slicer level, except we need clip_slice!)
                FilterHoles(solid_polygons, 1.1 * ToolDiameter);

                add_solid_polygons(horz_slices[k], solid_polygons, PrintMeshOptions.Default());
            }

            // resolve planar intersections, etc
            int NF = horz_slices.Length;

            gParallel.ForEach(Interval1i.Range(NF), (i) => {
                if (Cancelled())
                {
                    return;
                }
                horz_slices[i].Resolve();
                Interlocked.Add(ref Progress, 2);
            });
            // add to clearing stack
            result.HorizontalFinish = SliceStackFactoryF();
            for (int k = 0; k < horz_slices.Length; ++k)
            {
                result.HorizontalFinish.Add(horz_slices[k]);
            }

done_slicing:
            return(result);
        }