示例#1
0
        public static void generateTopAndBottomLayers(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, int downSkinCount, int upSkinCount)
        {
            SliceLayer layer = storage.layers[layerIndex];

            for (int partNr = 0; partNr < layer.parts.Count; partNr++)
            {
                SliceLayerPart part = layer.parts[partNr];

                Polygons upskin   = part.insets[part.insets.Count - 1].Offset(-extrusionWidth / 2);
                Polygons downskin = upskin;

                if (part.insets.Count > 1)
                {
                    // Add thin wall filling by taking the area between the insets.
                    Polygons thinWalls = part.insets[0].Offset(-extrusionWidth / 2).CreateDifference(part.insets[1].Offset(extrusionWidth / 2));
                    upskin.AddAll(thinWalls);
                    downskin.AddAll(thinWalls);
                }

                if (layerIndex - downSkinCount >= 0)
                {
                    SliceLayer layer2 = storage.layers[layerIndex - downSkinCount];
                    for (int partIndex = 0; partIndex < layer2.parts.Count; partIndex++)
                    {
                        if (part.boundaryBox.hit(layer2.parts[partIndex].boundaryBox))
                        {
                            downskin = downskin.CreateDifference(layer2.parts[partIndex].insets[layer2.parts[partIndex].insets.Count - 1]);
                        }
                    }
                }

                if (layerIndex + upSkinCount < storage.layers.Count)
                {
                    SliceLayer layer2 = storage.layers[layerIndex + upSkinCount];
                    for (int partIndex = 0; partIndex < layer2.parts.Count; partIndex++)
                    {
                        if (part.boundaryBox.hit(layer2.parts[partIndex].boundaryBox))
                        {
                            upskin = upskin.CreateDifference(layer2.parts[partIndex].insets[layer2.parts[partIndex].insets.Count - 1]);
                        }
                    }
                }

                part.skinOutline = upskin.CreateUnion(downskin);

                double minAreaSize = (2 * Math.PI * (extrusionWidth / 1000.0) * (extrusionWidth / 1000.0)) * 0.3;
                for (int outlineIndex = 0; outlineIndex < part.skinOutline.Count; outlineIndex++)
                {
                    double area = Math.Abs(part.skinOutline[outlineIndex].Area()) / 1000.0 / 1000.0;
                    if (area < minAreaSize) // Only create an up/down skin if the area is large enough. So you do not create tiny blobs of "trying to fill"
                    {
                        part.skinOutline.RemoveAt(outlineIndex);
                        outlineIndex -= 1;
                    }
                }
            }
        }
示例#2
0
        public void GenerateTopAndBottoms(int layerIndex, int extrusionWidth_um, int outerPerimeterWidth_um, int downLayerCount, int upLayerCount)
        {
            ExtruderLayers extruder = this;
            SliceLayer     layer    = extruder.Layers[layerIndex];

            for (int islandIndex = 0; islandIndex < layer.Islands.Count; islandIndex++)
            {
                LayerIsland island = layer.Islands[islandIndex];
                // this is the entire extrusion width to make sure we are outside of the extrusion line
                Polygons lastInset       = island.InsetToolPaths[island.InsetToolPaths.Count - 1];
                Polygons insetWithOffset = lastInset.Offset(-extrusionWidth_um);
                Polygons infillOutlines  = new Polygons(insetWithOffset);

                // calculate the bottom outlines
                if (downLayerCount > 0)
                {
                    Polygons bottomOutlines = new Polygons(insetWithOffset);

                    if (layerIndex - 1 >= 0)
                    {
                        bottomOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex - 1].Islands, island.BoundingBox, bottomOutlines);
                        bottomOutlines.RemoveSmallAreas(extrusionWidth_um);
                    }

                    infillOutlines = infillOutlines.CreateDifference(bottomOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    island.SolidBottomToolPaths = bottomOutlines;
                }

                // calculate the top outlines
                if (upLayerCount > 0)
                {
                    Polygons topOutlines = new Polygons(insetWithOffset);
                    topOutlines = topOutlines.CreateDifference(island.SolidBottomToolPaths);
                    topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

                    for (int insetIndex = 0; insetIndex < island.InsetToolPaths.Count - 1; insetIndex++)
                    {
                        // Add thin wall filling by taking the area between the insets.
                        Polygons largerInset  = island.InsetToolPaths[insetIndex].Offset(-extrusionWidth_um / 2);
                        Polygons smallerInset = island.InsetToolPaths[insetIndex + 1].Offset(extrusionWidth_um / 2);

                        Polygons thinWalls = largerInset.CreateDifference(smallerInset).Offset(-extrusionWidth_um / 4);
                        if (thinWalls.Count > 0)
                        {
                            topOutlines.AddAll(thinWalls);
                        }
                    }

                    if (layerIndex + 1 < extruder.Layers.Count)
                    {
                        // Remove the top layer that is above this one to get only the data that is a top layer on this layer.
                        topOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex + 1].Islands, island.BoundingBox, topOutlines);
                    }

                    topOutlines.RemoveSmallAreas(extrusionWidth_um);

                    infillOutlines = infillOutlines.CreateDifference(topOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    island.SolidTopToolPaths = topOutlines;
                }

                // calculate the solid infill outlines
                if (upLayerCount > 1 || downLayerCount > 1)
                {
                    Polygons solidInfillOutlines = new Polygons(insetWithOffset);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(island.SolidBottomToolPaths);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(island.SolidTopToolPaths);

                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);

                    int upEnd = layerIndex + upLayerCount + 1;
                    if (upEnd <= extruder.Layers.Count && layerIndex - downLayerCount >= 0)
                    {
                        Polygons totalPartsToRemove = new Polygons(insetWithOffset);

                        int upStart = layerIndex + 2;

                        for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++)
                        {
                            totalPartsToRemove = AddIslandsToPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, totalPartsToRemove);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                        }

                        int downStart = layerIndex - 1;
                        int downEnd   = layerIndex - downLayerCount;

                        for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
                        {
                            totalPartsToRemove = AddIslandsToPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, totalPartsToRemove);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                        }

                        solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove);
                        solidInfillOutlines.RemoveSmallAreas(extrusionWidth_um);

                        solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    }

                    island.SolidInfillToolPaths = solidInfillOutlines;
                    infillOutlines = infillOutlines.CreateDifference(solidInfillOutlines);
                }

                infillOutlines.RemoveSmallAreas(extrusionWidth_um);
                infillOutlines         = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
                island.InfillToolPaths = infillOutlines;
            }
        }
示例#3
0
        private static Polygons GetSkirtBounds(ConfigSettings config, LayerDataStorage storage, bool externalOnly, int distance, int extrusionWidth_um, int brimCount)
        {
            bool hasWipeTower = storage.wipeTower.PolygonLength() > 0;

            Polygons skirtPolygons = new Polygons();

            if (config.EnableRaft)
            {
                skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline);
            }
            else
            {
                Polygons allOutlines = hasWipeTower ? new Polygons(storage.wipeTower.Offset(-extrusionWidth_um / 2)) : new Polygons();

                if (storage.wipeShield.Count > 0 &&
                    storage.wipeShield[0].Count > 0)
                {
                    allOutlines = allOutlines.CreateUnion(storage.wipeShield[0].Offset(-extrusionWidth_um / 2));
                }

                // Loop over every extruder
                for (int extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++)
                {
                    // Only process the first extruder on spiral vase or
                    // skip extruders that have empty layers
                    if (config.ContinuousSpiralOuterPerimeter)
                    {
                        SliceLayer layer0 = storage.Extruders[extrudeIndex].Layers[0];
                        allOutlines.AddAll(layer0.Islands[0]?.IslandOutline);
                    }
                    else
                    {
                        // Add the layers outline to allOutlines
                        SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0];
                        foreach (var island in layer.Islands)
                        {
                            if (island.IslandOutline?.Count > 0)
                            {
                                allOutlines.Add(island.IslandOutline[0]);
                            }
                        }
                    }
                }

                if (brimCount > 0)
                {
                    Polygons brimIslandOutlines = new Polygons();

                    // Grow each island by the current brim distance
                    // Union the island brims
                    brimIslandOutlines = brimIslandOutlines.CreateUnion(allOutlines);

                    if (storage.support != null)
                    {
                        brimIslandOutlines = brimIslandOutlines.CreateUnion(storage.support.GetBedOutlines());
                    }

                    brimIslandOutlines = brimIslandOutlines.Offset(extrusionWidth_um * (brimCount - 1));

                    // Loop over the requested brimCount creating and unioning a new perimeter for each island
                    List <Polygons> brimIslands = brimIslandOutlines.ProcessIntoSeparateIslands();

                    foreach (var brimIsland in brimIslands)
                    {
                        Polygons brimLoops = new Polygons();
                        for (int brimIndex = brimCount - 1; brimIndex >= 0; brimIndex--)
                        {
                            int offsetDistance = extrusionWidth_um * brimIndex;

                            // Extend the polygons to account for the brim (ensures convex hull takes this data into account)
                            brimLoops.AddAll(brimIsland.Offset(-offsetDistance + extrusionWidth_um / 2));
                        }

                        storage.Brims.Add(brimLoops);
                    }

                    // and extend the bonuds of the skirt polygons
                    skirtPolygons = skirtPolygons.CreateUnion(brimIslandOutlines);
                }

                skirtPolygons = skirtPolygons.CreateUnion(allOutlines);

                if (storage.support != null)
                {
                    skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines());
                }
            }

            return(skirtPolygons);
        }
        private static Polygons GetSkirtBounds(ConfigSettings config, LayerDataStorage storage, bool externalOnly, int distance, int extrusionWidth_um, int brimCount)
        {
            bool hasWipeTower = storage.wipeTower.PolygonLength() > 0;

            Polygons skirtPolygons = new Polygons();

            if (config.EnableRaft)
            {
                skirtPolygons = skirtPolygons.CreateUnion(storage.raftOutline);
            }
            else
            {
                Polygons allOutlines = hasWipeTower ? new Polygons(storage.wipeTower) : new Polygons();

                // Loop over every extruder
                for (int extrudeIndex = 0; extrudeIndex < storage.Extruders.Count; extrudeIndex++)
                {
                    // Only process the first extruder on spiral vase or
                    // skip extruders that have empty layers
                    if (config.ContinuousSpiralOuterPerimeter)
                    {
                        SliceLayer layer0 = storage.Extruders[extrudeIndex].Layers[0];
                        allOutlines.AddAll(layer0.Islands[0]?.IslandOutline);
                    }
                    else
                    {
                        // Add the layers outline to allOutlines
                        SliceLayer layer = storage.Extruders[extrudeIndex].Layers[0];
                        foreach (var island in layer.Islands)
                        {
                            if (island.IslandOutline?.Count > 0)
                            {
                                allOutlines.Add(island.IslandOutline[0]);
                            }
                        }
                    }
                }

                if (brimCount > 0)
                {
                    Polygons unionedIslandOutlines = new Polygons();

                    // Grow each island by the current brim distance
                    // Union the island brims
                    unionedIslandOutlines = unionedIslandOutlines.CreateUnion(allOutlines);

                    if (storage.support != null)
                    {
                        unionedIslandOutlines = unionedIslandOutlines.CreateUnion(storage.support.GetBedOutlines());
                    }

                    Polygons brimLoops = new Polygons();

                    // Loop over the requested brimCount creating and unioning a new perimeter for each island
                    for (int brimIndex = 0; brimIndex < brimCount; brimIndex++)
                    {
                        int offsetDistance = extrusionWidth_um * brimIndex + extrusionWidth_um / 2;

                        // Extend the polygons to account for the brim (ensures convex hull takes this data into account)
                        brimLoops.AddAll(unionedIslandOutlines.Offset(offsetDistance));
                    }

                    // TODO: This is a quick hack, reuse the skirt data to stuff in the brim. Good enough from proof of concept
                    storage.skirt.AddAll(brimLoops);

                    skirtPolygons = skirtPolygons.CreateUnion(brimLoops);
                }
                else
                {
                    skirtPolygons = skirtPolygons.CreateUnion(allOutlines);
                }

                if (storage.support != null)
                {
                    skirtPolygons = skirtPolygons.CreateUnion(storage.support.GetBedOutlines());
                }
            }

            return(skirtPolygons);
        }
示例#5
0
        public static void GenerateTopAndBottom(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, int downLayerCount, int upLayerCount)
        {
            SliceLayer layer = storage.layers[layerIndex];

            for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
            {
                SliceLayerPart part            = layer.parts[partIndex];
                Polygons       insetWithOffset = part.Insets[part.Insets.Count - 1].Offset(-extrusionWidth / 2);
                Polygons       infillOutlines  = new Polygons(insetWithOffset);

                // calculate the bottom outlines
                if (downLayerCount > 0)
                {
                    Polygons bottomOutlines = new Polygons(insetWithOffset);

                    if (layerIndex - 1 >= 0)
                    {
                        bottomOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex - 1], part, bottomOutlines);
                        RemoveSmallAreas(extrusionWidth, bottomOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(bottomOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidBottomOutlines = bottomOutlines;
                }

                // calculate the top outlines
                if (upLayerCount > 0)
                {
                    Polygons topOutlines = new Polygons(insetWithOffset);
                    topOutlines = topOutlines.CreateDifference(part.SolidBottomOutlines);
                    topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

                    if (part.Insets.Count > 1)
                    {
                        // Add thin wall filling by taking the area between the insets.
                        Polygons thinWalls = part.Insets[0].Offset(-extrusionWidth / 2).CreateDifference(part.Insets[1].Offset(extrusionWidth / 2));
                        topOutlines.AddAll(thinWalls);
                    }

                    if (layerIndex + 1 < storage.layers.Count)
                    {
                        topOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex + 1], part, topOutlines);
                        RemoveSmallAreas(extrusionWidth, topOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(topOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidTopOutlines = topOutlines;
                }

                // calculate the solid infill outlines
                if (upLayerCount > 1 || downLayerCount > 1)
                {
                    Polygons solidInfillOutlines = new Polygons(insetWithOffset);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidBottomOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidTopOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);

                    int upEnd = layerIndex + upLayerCount + 1;
                    if (upEnd <= storage.layers.Count && layerIndex - downLayerCount >= 0)
                    {
                        Polygons totalPartsToRemove = new Polygons(insetWithOffset);

                        int upStart = layerIndex + 2;

                        for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                        }

                        int downStart = layerIndex - 1;
                        int downEnd   = layerIndex - downLayerCount;

                        for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                        }

                        solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove);
                        RemoveSmallAreas(extrusionWidth, solidInfillOutlines);

                        solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    }

                    part.SolidInfillOutlines = solidInfillOutlines;
                    infillOutlines           = infillOutlines.CreateDifference(solidInfillOutlines);
                }

                RemoveSmallAreas(extrusionWidth, infillOutlines);
                infillOutlines      = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
                part.InfillOutlines = infillOutlines;
            }
        }
示例#6
0
		public void GenerateTopAndBottoms(int layerIndex, int extrusionWidth_um, int outerPerimeterWidth_um, int downLayerCount, int upLayerCount)
		{
			ExtruderLayers extruder = this;
			SliceLayer layer = extruder.Layers[layerIndex];

			for (int islandIndex = 0; islandIndex < layer.Islands.Count; islandIndex++)
			{
				LayerIsland island = layer.Islands[islandIndex];
				// this is the entire extrusion width to make sure we are outside of the extrusion line
				Polygons lastInset = island.InsetToolPaths[island.InsetToolPaths.Count - 1];
				Polygons insetWithOffset = lastInset.Offset(-extrusionWidth_um);
				Polygons infillOutlines = new Polygons(insetWithOffset);

				// calculate the bottom outlines
				if (downLayerCount > 0)
				{
					Polygons bottomOutlines = new Polygons(insetWithOffset);

					if (layerIndex - 1 >= 0)
					{
						bottomOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex - 1].Islands, island.BoundingBox, bottomOutlines);
						bottomOutlines.RemoveSmallAreas(extrusionWidth_um);
					}

					infillOutlines = infillOutlines.CreateDifference(bottomOutlines);
					infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

					island.SolidBottomToolPaths = bottomOutlines;
				}

				// calculate the top outlines
				if (upLayerCount > 0)
				{
					Polygons topOutlines = new Polygons(insetWithOffset);
					topOutlines = topOutlines.CreateDifference(island.SolidBottomToolPaths);
					topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

					for (int insetIndex = 0; insetIndex < island.InsetToolPaths.Count - 1; insetIndex++)
					{
						// Add thin wall filling by taking the area between the insets.
						Polygons largerInset = island.InsetToolPaths[insetIndex].Offset(-extrusionWidth_um / 2);
						Polygons smallerInset = island.InsetToolPaths[insetIndex + 1].Offset(extrusionWidth_um / 2);

						Polygons thinWalls = largerInset.CreateDifference(smallerInset).Offset(-extrusionWidth_um/4);
						if (thinWalls.Count > 0)
						{
							topOutlines.AddAll(thinWalls);
						}
					}

					if (layerIndex + 1 < extruder.Layers.Count)
					{
						// Remove the top layer that is above this one to get only the data that is a top layer on this layer.
						topOutlines = RemoveIslandsFromPolygons(extruder.Layers[layerIndex + 1].Islands, island.BoundingBox, topOutlines);
					}

					topOutlines.RemoveSmallAreas(extrusionWidth_um);

					infillOutlines = infillOutlines.CreateDifference(topOutlines);
					infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

					island.SolidTopToolPaths = topOutlines;
				}

				// calculate the solid infill outlines
				if (upLayerCount > 1 || downLayerCount > 1)
				{
					Polygons solidInfillOutlines = new Polygons(insetWithOffset);
					solidInfillOutlines = solidInfillOutlines.CreateDifference(island.SolidBottomToolPaths);
					solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
					solidInfillOutlines = solidInfillOutlines.CreateDifference(island.SolidTopToolPaths);

					solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);

					int upEnd = layerIndex + upLayerCount + 1;
					if (upEnd <= extruder.Layers.Count && layerIndex - downLayerCount >= 0)
					{
						Polygons totalPartsToRemove = new Polygons(insetWithOffset);

						int upStart = layerIndex + 2;

						for (int layerToTest = upStart; layerToTest < upEnd; layerToTest++)
						{
							totalPartsToRemove = AddIslandsToPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, totalPartsToRemove);
							totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
						}

						int downStart = layerIndex - 1;
						int downEnd = layerIndex - downLayerCount;

						for (int layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
						{
							totalPartsToRemove = AddIslandsToPolygons(extruder.Layers[layerToTest].Islands, island.BoundingBox, totalPartsToRemove);
							totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
						}

						solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove);
						solidInfillOutlines.RemoveSmallAreas(extrusionWidth_um);

						solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
					}

					island.SolidInfillToolPaths = solidInfillOutlines;
					infillOutlines = infillOutlines.CreateDifference(solidInfillOutlines);
				}

				infillOutlines.RemoveSmallAreas(extrusionWidth_um);
				infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
				island.InfillToolPaths = infillOutlines;
			}
		}
示例#7
0
        public static void GenerateTopAndBottom(int layerIndex, SliceVolumeStorage storage, int extrusionWidth, ConfigSettings config)
        {
            var downLayerCount = config.numberOfBottomLayers;
            var upLayerCount   = config.numberOfTopLayers;

            SliceLayer layer = storage.layers[layerIndex];

            for (int partIndex = 0; partIndex < layer.parts.Count; partIndex++)
            {
                SliceLayerPart part            = layer.parts[partIndex];
                Polygons       insetWithOffset = part.Insets[part.Insets.Count - 1].Offset(-extrusionWidth / 2);
                Polygons       infillOutlines  = new Polygons(insetWithOffset);

                // calculate the bottom outlines
                if (downLayerCount > 0)
                {
                    Polygons bottomOutlines = new Polygons(insetWithOffset);

                    if (layerIndex - 1 >= 0)
                    {
                        bottomOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex - 1], part, bottomOutlines);
                        RemoveSmallAreas(extrusionWidth, bottomOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(bottomOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidBottomOutlines = bottomOutlines;
                }

                // calculate the top outlines
                if (upLayerCount > 0)
                {
                    Polygons topOutlines = new Polygons(insetWithOffset);
                    topOutlines = topOutlines.CreateDifference(part.SolidBottomOutlines);
                    topOutlines = Clipper.CleanPolygons(topOutlines, cleanDistance_um);

                    if (part.Insets.Count > 1)
                    {
                        // Add thin wall filling by taking the area between the insets.
                        Polygons thinWalls = part.Insets[0].Offset(-extrusionWidth / 2).CreateDifference(part.Insets[1].Offset(extrusionWidth / 2));
                        topOutlines.AddAll(thinWalls);
                    }

                    if (layerIndex + 1 < storage.layers.Count)
                    {
                        topOutlines = RemoveAdditionalOutlinesForPart(storage.layers[layerIndex + 1], part, topOutlines);
                        RemoveSmallAreas(extrusionWidth, topOutlines);
                    }

                    infillOutlines = infillOutlines.CreateDifference(topOutlines);
                    infillOutlines = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);

                    part.SolidTopOutlines = topOutlines;
                }

                // calculate the solid infill outlines
                if (upLayerCount > 1 || downLayerCount > 1)
                {
                    var solidInfillOutlines = new Polygons(insetWithOffset);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidBottomOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    solidInfillOutlines = solidInfillOutlines.CreateDifference(part.SolidTopOutlines);
                    solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);

                    var upStart   = layerIndex + 2;
                    var upEnd     = layerIndex + upLayerCount + 1;
                    var downStart = layerIndex - 1;
                    var downEnd   = layerIndex - downLayerCount;

                    if (upEnd <= storage.layers.Count && downEnd >= 0)
                    {
                        var makeInfillSolid    = false;
                        var totalPartsToRemove = new Polygons(insetWithOffset);

                        for (var layerToTest = upStart; layerToTest < upEnd; layerToTest++)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove, ref makeInfillSolid, config);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                            if (makeInfillSolid)
                            {
                                break;
                            }
                        }

                        for (var layerToTest = downStart; layerToTest >= downEnd; layerToTest--)
                        {
                            totalPartsToRemove = AddAllOutlines(storage.layers[layerToTest], part, totalPartsToRemove, ref makeInfillSolid, config);
                            totalPartsToRemove = Clipper.CleanPolygons(totalPartsToRemove, cleanDistance_um);
                            if (makeInfillSolid)
                            {
                                break;
                            }
                        }

                        if (!makeInfillSolid)
                        {
                            solidInfillOutlines = solidInfillOutlines.CreateDifference(totalPartsToRemove);
                            RemoveSmallAreas(extrusionWidth, solidInfillOutlines);
                        }

                        solidInfillOutlines = Clipper.CleanPolygons(solidInfillOutlines, cleanDistance_um);
                    }

                    part.SolidInfillOutlines = solidInfillOutlines;
                    infillOutlines           = infillOutlines.CreateDifference(solidInfillOutlines);

                    Polygons totalInfillOutlines = null;
                    double   totalInfillArea     = 0.0;

                    if (config.infillSolidProportion > 0)
                    {
                        totalInfillOutlines = infillOutlines.CreateUnion(solidInfillOutlines);
                        totalInfillArea     = totalInfillOutlines.TotalArea();
                    }

                    if (config.infillSolidProportion > 0)
                    {
                        var solidInfillArea = solidInfillOutlines.TotalArea();
                        if (solidInfillArea > totalInfillArea * config.infillSolidProportion)
                        {
                            solidInfillOutlines      = solidInfillOutlines.CreateUnion(infillOutlines);
                            infillOutlines           = new Polygons();
                            part.SolidInfillOutlines = solidInfillOutlines;
                        }
                        var solidTopOutlinesArea = part.SolidTopOutlines.TotalArea();
                        if (totalInfillArea < solidTopOutlinesArea * config.infillSolidProportion / 2)
                        {
                            var totalSolidTop = totalInfillOutlines.CreateUnion(part.SolidTopOutlines);
                            part.SolidTopOutlines    = totalSolidTop;
                            part.SolidInfillOutlines = new Polygons();
                            infillOutlines           = part.InfillOutlines = new Polygons();
                        }
                        var solidBottomOutlinesArea = part.SolidBottomOutlines.TotalArea();
                        if (totalInfillArea < solidBottomOutlinesArea * config.infillSolidProportion / 2)
                        {
                            var totalSolidBottom = totalInfillOutlines.CreateUnion(part.SolidBottomOutlines);
                            part.SolidBottomOutlines = totalSolidBottom;
                            part.SolidInfillOutlines = new Polygons();
                            infillOutlines           = part.InfillOutlines = new Polygons();
                        }
                    }
                    if (config.minInfillArea_mm2 > 0)
                    {
                        var infillArea = infillOutlines.TotalArea() / 1e6; // convert from um2 to mm2
                        if (infillArea < config.minInfillArea_mm2)
                        {
                            solidInfillOutlines      = solidInfillOutlines.CreateUnion(infillOutlines);
                            infillOutlines           = new Polygons();
                            part.SolidInfillOutlines = solidInfillOutlines;
                        }
                    }
                }

                RemoveSmallAreas(extrusionWidth, infillOutlines);
                infillOutlines      = Clipper.CleanPolygons(infillOutlines, cleanDistance_um);
                part.InfillOutlines = infillOutlines;
            }
        }