inline void polygons_append(Polygons &dst, const ExPolygon &src) { dst.reserve(dst.size() + src.holes.size() + 1); dst.push_back(src.contour); dst.insert(dst.end(), src.holes.begin(), src.holes.end()); }
list loadCOCO( const std::string & name, int fold ) { using namespace rapidjson; // Load the annotations char buf[1024]; sprintf( buf, COCO_ANNOT.c_str(), name.c_str() ); // Read the json file Document doc; std::ifstream t(buf); std::string json_str = std::string(std::istreambuf_iterator<char>(t),std::istreambuf_iterator<char>()); doc.Parse( (char*)json_str.c_str() ); // Go through all instance labels std::unordered_map< uint64_t, std::vector<int> > categories; std::unordered_map< uint64_t, std::vector<float> > areas; std::unordered_map< uint64_t, std::vector<Polygons> > segments; const Value & instances = doc["instances"]; for ( Value::ConstValueIterator i = instances.Begin(); i != instances.End(); i++ ) { // Get the image id Value::ConstMemberIterator cmi_image_id = i->FindMember("image_id"); eassert( cmi_image_id != i->MemberEnd() ); const int image_id = cmi_image_id->value.GetInt(); // Get the category id Value::ConstMemberIterator cmi_category_id = i->FindMember("category_id"); eassert( cmi_category_id != i->MemberEnd() ); const int category_id = cmi_category_id->value.GetInt(); // Get the category id Value::ConstMemberIterator cmi_area = i->FindMember("area"); eassert( cmi_area != i->MemberEnd() ); const float area = cmi_area->value.GetDouble(); // Read the polygon Value::ConstMemberIterator cmi_segmentation = i->FindMember("segmentation"); eassert( cmi_segmentation != i->MemberEnd() ); const Value & segmentations = cmi_segmentation->value; // For now just use the first segmentation for each object Polygons polygons; for( Value::ConstValueIterator segmentation = segmentations.Begin(); segmentation!=segmentations.End(); segmentation++ ){ Polygon polygon = RMatrixXf( segmentation->Size() / 2, 2 ); float * ppolygon = polygon.data(); for ( Value::ConstValueIterator j = segmentation->Begin(); j != segmentation->End(); j++ ) *(ppolygon++) = j->GetDouble(); polygons.push_back( polygon ); } if( !ONLY_CONNECTED || polygons.size() == 1 ) { categories[ image_id ].push_back( category_id ); segments[ image_id ].push_back( polygons ); areas[ image_id ].push_back( area ); } } // Load all images Value::ConstValueIterator B = doc["images"].Begin(), E = doc["images"].End(); const int N = E-B; Value::ConstValueIterator i0 = B+(fold*N/N_FOLDS), i1 = B+((fold+1)*N/N_FOLDS); std::vector< std::shared_ptr<Image8u> > images( i1 - i0 ); #pragma omp parallel for for ( int k=0; k<i1-i0; k++ ) { Value::ConstValueIterator i = i0+k; // Get the file name and path Value::ConstMemberIterator cmi_file_name = i->FindMember("file_name"); eassert( cmi_file_name != i->MemberEnd() ); const std::string file_name = cmi_file_name->value.GetString(); Value::ConstMemberIterator cmi_file_path = i->FindMember("file_path"); eassert( cmi_file_path != i->MemberEnd() ); const std::string file_path = cmi_file_path->value.GetString(); // Add the image entry images[i-i0] = imreadShared( coco_dir+"/"+file_path+"/"+file_name ); } // Create the python struct with the result list r; for ( Value::ConstValueIterator i = i0; i != i1; i++ ) { // Get the image id Value::ConstMemberIterator cmi_image_id = i->FindMember("id"); eassert( cmi_image_id != i->MemberEnd() ); const int image_id = cmi_image_id->value.GetInt(); // Add the image entry const int N = categories[ image_id ].size(); if( N > 0 ){ dict entry; entry["id"] = image_id; entry["image"] = images[i - i0]; entry["categories"] = categories[ image_id ]; entry["areas"] = areas[ image_id ]; entry["segmentation"] = segments[ image_id ]; r.append( entry ); } // else // printf("Image '%d' doesn't have any annotations!\n", image_id ); } return r; }
void DrawPolygon(Polygons &pgs, poly_color_type pct) { switch (pct) { case pctSubject: glColor4f(0.0f, 0.0f, 1.0f, 0.062f); break; case pctClip: glColor4f(1.0f, 1.0f, 0.0f, 0.062f); break; default: glColor4f(0.0f, 1.0f, 0.0f, 0.25f); } GLUtesselator* tess = gluNewTess(); gluTessCallback(tess, GLU_TESS_BEGIN, (void (CALLBACK*)())&BeginCallback); gluTessCallback(tess, GLU_TESS_VERTEX, (void (CALLBACK*)())&VertexCallback); gluTessCallback(tess, GLU_TESS_END, (void (CALLBACK*)())&EndCallback); gluTessCallback(tess, GLU_TESS_COMBINE, (void (CALLBACK*)())&CombineCallback); gluTessCallback(tess, GLU_TESS_ERROR, (void (CALLBACK*)())&ErrorCallback); gluTessNormal(tess, 0.0, 0.0, 1.0); switch (pft) { case pftEvenOdd: gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); break; case pftNonZero: gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); break; case pftPositive: gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE); break; default: //case pftNegative if (pct == pctSolution) gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); else gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE); } gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE); //GL_FALSE gluTessBeginPolygon(tess, NULL); for (Polygons::size_type i = 0; i < pgs.size(); ++i) { gluTessBeginContour(tess); for (ClipperLib::Polygon::size_type j = 0; j < pgs[i].size(); ++j) { GLdouble *vert = NewVector((GLdouble)pgs[i][j].X/scale, (GLdouble)pgs[i][j].Y/scale); gluTessVertex(tess, vert, vert); } gluTessEndContour(tess); } gluTessEndPolygon(tess); ClearVectors(); switch (pct) { case pctSubject: glColor4f(0.0f, 0.6f, 1.0f, 0.5f); break; case pctClip: glColor4f(1.0f, 0.6f, 0.0f, 0.5f); break; default: glColor4f(0.0f, 0.4f, 0.0f, 1.0f); } if (pct == pctSolution) glLineWidth(1.0f); else glLineWidth(0.8f); gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE); for (Polygons::size_type i = 0; i < pgs.size(); ++i) { gluTessBeginPolygon(tess, NULL); gluTessBeginContour(tess); for (ClipperLib::Polygon::size_type j = 0; j < pgs[i].size(); ++j) { GLdouble *vert = NewVector((GLdouble)pgs[i][j].X/scale, (GLdouble)pgs[i][j].Y/scale); gluTessVertex(tess, vert, vert); } switch (pct) { case pctSubject: glColor4f(0.0f, 0.0f, 0.8f, 0.5f); break; case pctClip: glColor4f(0.6f, 0.0f, 0.0f, 0.5f); } gluTessEndContour(tess); gluTessEndPolygon(tess); } //final cleanup ... gluDeleteTess(tess); ClearVectors(); }
void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t primary_line_count, const bool is_skirt, Polygons& first_layer_outline) { const ExtruderTrain& train = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr"); const ExtruderTrain& support_infill_extruder = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("support_infill_extruder_nr"); const bool external_only = is_skirt || train.settings.get<bool>("brim_outside_only"); //Whether to include holes or not. Skirt doesn't have any holes. const LayerIndex layer_nr = 0; if (is_skirt) { constexpr bool include_support = true; constexpr bool include_prime_tower = true; first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_only); first_layer_outline = first_layer_outline.approxConvexHull(); } else { // add brim underneath support by removing support where there's brim around the model constexpr bool include_support = false; //Include manually below. constexpr bool include_prime_tower = false; //Include manually below. constexpr bool external_outlines_only = false; //Remove manually below. first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_outlines_only); first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule. Polygons first_layer_empty_holes; if (external_only) { first_layer_empty_holes = first_layer_outline.getEmptyHoles(); first_layer_outline = first_layer_outline.removeEmptyHoles(); } if (storage.support.generated && primary_line_count > 0 && !storage.support.supportLayers.empty()) { // remove model-brim from support SupportLayer& support_layer = storage.support.supportLayers[0]; if (support_infill_extruder.settings.get<bool>("brim_replaces_support")) { // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const coord_t primary_extruder_skirt_brim_line_width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor"); Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides if (external_only) { // don't remove support within empty holes where no brim is generated. model_brim_covered_area.add(first_layer_empty_holes); } AABB model_brim_covered_area_boundary_box(model_brim_covered_area); support_layer.excludeAreasFromSupportInfillAreas(model_brim_covered_area, model_brim_covered_area_boundary_box); } for (const SupportInfillPart& support_infill_part : support_layer.support_infill_parts) { first_layer_outline.add(support_infill_part.outline); } first_layer_outline.add(support_layer.support_bottom); first_layer_outline.add(support_layer.support_roof); } if (storage.primeTower.enabled) { first_layer_outline.add(storage.primeTower.outer_poly_first_layer); // don't remove parts of the prime tower, but make a brim for it } } constexpr coord_t join_distance = 20; first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon constexpr coord_t smallest_line_length = 200; constexpr coord_t largest_error_of_removed_point = 50; first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines if (first_layer_outline.size() == 0) { logError("Couldn't generate skirt / brim! No polygons on first layer.\n"); } }
unsigned int moveInside(Polygons& polygons, Point& from, int distance, int64_t maxDist2) { Point ret = from; int64_t bestDist2 = maxDist2; unsigned int bestPoly = NO_INDEX; for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) { PolygonRef poly = polygons[poly_idx]; if (poly.size() < 2) continue; Point p0 = poly[poly.size()-2]; Point p1 = poly.back(); bool projected_p_beyond_prev_segment = dot(p1 - p0, from - p0) > vSize2(p1 - p0); for(Point& p2 : poly) { // X = A + Normal( B - A ) * ((( B - A ) dot ( P - A )) / VSize( A - B )); // X = P projected on AB Point& a = p1; Point& b = p2; Point& p = from; Point ab = b - a; Point ap = p - a; int64_t ab_length = vSize(ab); int64_t ax_length = dot(ab, ap) / ab_length; if (ax_length < 0) // x is projected to before ab { if (projected_p_beyond_prev_segment) { // case which looks like: > . projected_p_beyond_prev_segment = false; Point& x = p1; int64_t dist2 = vSize2(x - p); if (dist2 < bestDist2) { bestDist2 = dist2; if (distance == 0) { ret = x; } else { ret = x + normal(crossZ(normal(a, distance*4) + normal(p1 - p0, distance*4)), distance); } // *4 to retain more precision for the eventual normalization bestPoly = poly_idx; } } else { projected_p_beyond_prev_segment = false; p0 = p1; p1 = p2; continue; } } else if (ax_length > ab_length) // x is projected to beyond ab { projected_p_beyond_prev_segment = true; p0 = p1; p1 = p2; continue; } else { projected_p_beyond_prev_segment = false; Point x = a + ab * ax_length / ab_length; int64_t dist2 = vSize2(x - from); if (dist2 < bestDist2) { bestDist2 = dist2; if (distance == 0) { ret = x; } else { ret = x + crossZ(normal(ab, distance)); } bestPoly = poly_idx; } } p0 = p1; p1 = p2; } } if (bestDist2 < maxDist2) { from = ret; return bestPoly; } return NO_INDEX; }
void SVG::draw(const Polygons &polygons, std::string fill) { for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) this->draw(*it, fill); }
void Weaver::weave(MeshGroup* meshgroup) { wireFrame.meshgroup = meshgroup; int maxz = meshgroup->max().z; int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1; std::vector<AdaptiveLayer> layer_thicknesses; std::cerr << "Layer count: " << layer_count << "\n"; std::vector<cura::Slicer*> slicerList; for(Mesh& mesh : meshgroup->meshes) { cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"), false, &layer_thicknesses); slicerList.push_back(slicer); } int starting_layer_idx; { // find first non-empty layer for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++) { Polygons parts; for (cura::Slicer* slicer : slicerList) parts.add(slicer->layers[starting_layer_idx].polygons); if (parts.size() > 0) break; } if (starting_layer_idx > 0) { logWarning("First %i layers are empty!\n", starting_layer_idx); } } std::cerr<< "chainifying layers..." << std::endl; { int starting_z = -1; for (cura::Slicer* slicer : slicerList) wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons); CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*0,*/ wireFrame.bottom_outline, 1, 1, 1); if (slicerList.empty()) //Wait, there is nothing to slice. { wireFrame.z_bottom = 0; } else { wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z; } Point starting_point_in_layer; if (wireFrame.bottom_outline.size() > 0) starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2; else starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2; Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr); for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++) { Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine Polygons parts1; for (cura::Slicer* slicer : slicerList) parts1.add(slicer->layers[layer_idx].polygons); Polygons chainified; chainify_polygons(parts1, starting_point_in_layer, chainified); CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*layer_idx - starting_layer_idx,*/ chainified, 1, 1, 1); if (chainified.size() > 0) { if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z; wireFrame.layers.emplace_back(); WeaveLayer& layer = wireFrame.layers.back(); layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z; layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z; layer.supported = chainified; starting_point_in_layer = layer.supported.back().back(); } } } std::cerr<< "finding horizontal parts..." << std::endl; { Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr); for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) { Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine WeaveLayer& layer = wireFrame.layers[layer_idx]; Polygons empty; Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty; createHorizontalFill(layer, layer_above); } } // at this point layer.supported still only contains the polygons to be connected // when connecting layers, we further add the supporting polygons created by the roofs std::cerr<< "connecting layers..." << std::endl; { Polygons* lower_top_parts = &wireFrame.bottom_outline; int last_z = wireFrame.z_bottom; for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last { WeaveLayer& layer = wireFrame.layers[layer_idx]; connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer); layer.supported.add(layer.roofs.roof_outlines); lower_top_parts = &layer.supported; last_z = layer.z1; } } { // roofs: if (!wireFrame.layers.empty()) //If there are no layers, create no roof. { WeaveLayer& top_layer = wireFrame.layers.back(); Polygons to_be_supported; // empty for the top layer fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs); } } { // bottom: if (!wireFrame.layers.empty()) //If there are no layers, create no bottom. { Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported) fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill); } } }
void Weaver::fillRoofs(Polygons& supporting, Polygons& to_be_supported, int direction, int z, WeaveRoof& horizontals) { std::vector<WeaveRoofPart>& insets = horizontals.roof_insets; if (supporting.size() == 0) return; // no parts to start the roof from! Polygons roofs = supporting.difference(to_be_supported); roofs = roofs.offset(-roof_inset).offset(roof_inset); if (roofs.size() == 0) return; Polygons roof_outlines; Polygons roof_holes; { // split roofs into outlines and holes std::vector<PolygonsPart> roof_parts = roofs.splitIntoParts(); for (PolygonsPart& roof_part : roof_parts) { roof_outlines.add(roof_part[0]); for (unsigned int hole_idx = 1; hole_idx < roof_part.size(); hole_idx++) { roof_holes.add(roof_part[hole_idx]); roof_holes.back().reverse(); } } } Polygons supporting_outlines; std::vector<PolygonsPart> supporting_parts = supporting.splitIntoParts(); for (PolygonsPart& supporting_part : supporting_parts) supporting_outlines.add(supporting_part[0]); // only add outlines, not the holes Polygons inset1; Polygons last_inset; Polygons last_supported = supporting; for (Polygons inset0 = supporting_outlines; inset0.size() > 0; inset0 = last_inset) { last_inset = inset0.offset(direction * roof_inset, ClipperLib::jtRound); inset1 = last_inset.intersection(roof_outlines); // stay within roof area inset1 = inset1.unionPolygons(roof_holes);// make insets go around holes if (inset1.size() == 0) break; insets.emplace_back(); connect(last_supported, z, inset1, z, insets.back(), true); inset1 = inset1.remove(roof_holes); // throw away holes which appear in every intersection inset1 = inset1.remove(roof_outlines);// throw away fully filled regions last_supported = insets.back().supported; // chainified } horizontals.roof_outlines.add(roofs); // TODO just add the new lines, not the lines of the roofs which are already supported ==> make outlines into a connection from which we only print the top, not the connection }
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength) { if (count == 0) return; bool externalOnly = (distance > 0); Polygons support; if (storage.support.generated) support = storage.support.supportLayers[0].supportAreas; { // get support polygons for(SliceMeshStorage& mesh : storage.meshes) { if (mesh.layers.size() < 1) continue; SliceLayer* layer = &mesh.layers[0]; for(unsigned int i=0; i<layer->parts.size(); i++) support = support.difference(layer->parts[i].outline); } // expand and contract to smooth the final polygon if (count == 1 && distance > 0) { int dist = extrusionWidth * 5; support = support.offset(dist).offset(-dist); } } int overshoot = 0; // distance by which to expand and contract the skirt to approximate the convex hull of the first layer if (count == 1 && distance > 0) { overshoot = 100000; // 10 cm } for(int skirtNr=0; skirtNr<count;skirtNr++) { int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2 + overshoot; Polygons skirtPolygons(storage.wipeTower.offset(offsetDistance)); for(SliceMeshStorage& mesh : storage.meshes) { if (mesh.layers.size() < 1) continue; for(SliceLayerPart& part : mesh.layers[0].parts) { if (externalOnly) { Polygons p; p.add(part.outline.outerPolygon()); skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance, ClipperLib::jtRound)); } else { skirtPolygons = skirtPolygons.unionPolygons(part.outline.offset(offsetDistance, ClipperLib::jtRound)); } } } skirtPolygons = skirtPolygons.unionPolygons(support.offset(offsetDistance, ClipperLib::jtRound)); //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area" for(unsigned int n=0; n<skirtPolygons.size(); n++) { double area = skirtPolygons[n].area(); if (area < 0 && area > -extrusionWidth * extrusionWidth * 100) skirtPolygons.remove(n--); } storage.skirt.add(skirtPolygons); int lenght = storage.skirt.polygonLength(); if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength) // make brim have more lines when total length is too small count++; } //Add a skirt under the wipetower to make it stick better. Polygons wipe_tower = storage.wipeTower.offset(-extrusionWidth / 2); while(wipe_tower.size() > 0) { storage.skirt.add(wipe_tower); wipe_tower = wipe_tower.offset(-extrusionWidth); } if (overshoot > 0) { storage.skirt = storage.skirt.offset(-overshoot, ClipperLib::jtRound); } }
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) { if (mesh.getSettingAsCount("wall_line_count") == 0) { return; } int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness"); int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist"); int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value int64_t range_random_point_dist = avg_dist_between_points / 2; for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++) { SliceLayer& layer = mesh.layers[layer_nr]; for (SliceLayerPart& part : layer.parts) { Polygons results; Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0]; for (PolygonRef poly : skin) { // generate points in between p0 and p1 PolygonRef result = results.newPoly(); int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point Point* p0 = &poly.back(); for (Point& p1 : poly) { // 'a' is the (next) new point between p0 and p1 Point p0p1 = p1 - *p0; int64_t p0p1_size = vSize(p0p1); int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist) { int r = rand() % (fuzziness * 2) - fuzziness; Point perp_to_p0p1 = crossZ(p0p1); Point fuzz = normal(perp_to_p0p1, r); Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz; result.add(pa); dist_last_point = p0pa_dist; } dist_left_over = p0p1_size - dist_last_point; p0 = &p1; } while (result.size() < 3 ) { unsigned int point_idx = poly.size() - 2; result.add(poly[point_idx]); if (point_idx == 0) { break; } point_idx--; } if (result.size() < 3) { result.clear(); for (Point& p : poly) result.add(p); } } skin = results; sendPolygons(Inset0Type, layer_nr, skin, mesh.getSettingInMicrons("wall_line_width_0")); } } }
void generateZigZagIninfill_noEndPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation) { if (in_outline.size() == 0) return; Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2); if (outline.size() == 0) return; PointMatrix matrix(rotation); outline.applyMatrix(matrix); auto addLine = [&](Point from, Point to) { PolygonRef p = result.newPoly(); p.add(matrix.unapply(from)); p.add(matrix.unapply(to)); }; AABB boundary(outline); int scanline_min_idx = boundary.min.X / lineSpacing; int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx; std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments for(int n=0; n<lineCount; n++) cutList.push_back(std::vector<int64_t>()); for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++) { std::vector<Point> firstBoundarySegment; std::vector<Point> boundarySegment; bool isFirstBoundarySegment = true; bool firstBoundarySegmentEndsInEven; bool isEvenScanSegment = false; Point p0 = outline[polyNr][outline[polyNr].size()-1]; for(unsigned int i=0; i < outline[polyNr].size(); i++) { Point p1 = outline[polyNr][i]; int64_t xMin = p1.X, xMax = p0.X; if (xMin == xMax) { p0 = p1; continue; } if (xMin > xMax) { xMin = p0.X; xMax = p1.X; } int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ... int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number) int direction = 1; if (p0.X > p1.X) { direction = -1; scanline_idx1 += 1; // only consider the scanlines in between the scansegments } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0); else boundarySegment.push_back(p0); for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction) { int x = scanline_idx * lineSpacing; int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); cutList[scanline_idx - scanline_min_idx].push_back(y); bool last_isEvenScanSegment = isEvenScanSegment; if (scanline_idx % 2 == 0) isEvenScanSegment = true; else isEvenScanSegment = false; if (!isFirstBoundarySegment) { if (last_isEvenScanSegment && !isEvenScanSegment) { // add whole boundarySegment (including the just obtained point) for (unsigned int p = 1; p < boundarySegment.size(); p++) { addLine(boundarySegment[p-1], boundarySegment[p]); } addLine(boundarySegment[boundarySegment.size()-1], Point(x,y)); boundarySegment.clear(); } else if (isEvenScanSegment) // we are either in an end piece or an uneven boundary segment { boundarySegment.clear(); boundarySegment.emplace_back(x,y); } else boundarySegment.clear(); } if (isFirstBoundarySegment) { firstBoundarySegment.emplace_back(x,y); firstBoundarySegmentEndsInEven = isEvenScanSegment; isFirstBoundarySegment = false; boundarySegment.emplace_back(x,y); } } if (!isFirstBoundarySegment && isEvenScanSegment) boundarySegment.push_back(p1); p0 = p1; } if (!isFirstBoundarySegment && isEvenScanSegment && !firstBoundarySegmentEndsInEven) { for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++) addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]); } } addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth); }
/*! * adapted from generateLineInfill(.) * * generate lines within the area of [in_outline], at regular intervals of [lineSpacing] * idea: * intersect a regular grid of 'scanlines' with the area inside [in_outline] * sigzag: * include pieces of boundary, connecting the lines, forming an accordion like zigzag instead of separate lines |_|^|_| * * we call the areas between two consecutive scanlines a 'scansegment' * * algorithm: * 1. for each line segment of each polygon: * store the intersections of that line segment with all scanlines in a mapping (vector of vectors) from scanline to intersections * (zigzag): add boundary segments to result * 2. for each scanline: * sort the associated intersections * and connect them using the even-odd rule * * zigzag algorithm: * while walking around (each) polygon (1.) * if polygon intersects with even scanline * start boundary segment (add each following segment to the [result]) * when polygon intersects with a scanline again * stop boundary segment (stop adding segments to the [result]) * if polygon intersects with even scanline again (instead of odd) * dont add the last line segment to the boundary (unless [connect_zigzags]) * * * <-- * ___ * | | | * | | | * | |___| * --> * * ^ = even scanline * * start boundary from even scanline! :D * * * _____ * | | | , * | | | | * |_____| |__/ * * ^ ^ ^ scanlines * ^ disconnected end piece */ void generateZigZagIninfill_endPieces(const Polygons& in_outline, Polygons& result, int extrusionWidth, int lineSpacing, double infillOverlap, double rotation, bool connect_zigzags) { // if (in_outline.size() == 0) return; // Polygons outline = in_outline.offset(extrusionWidth * infillOverlap / 100 - extrusionWidth / 2); Polygons empty; Polygons outline = in_outline.difference(empty); // copy if (outline.size() == 0) return; PointMatrix matrix(rotation); outline.applyMatrix(matrix); auto addLine = [&](Point from, Point to) { PolygonRef p = result.newPoly(); p.add(matrix.unapply(from)); p.add(matrix.unapply(to)); }; AABB boundary(outline); int scanline_min_idx = boundary.min.X / lineSpacing; int lineCount = (boundary.max.X + (lineSpacing - 1)) / lineSpacing - scanline_min_idx; std::vector<std::vector<int64_t> > cutList; // mapping from scanline to all intersections with polygon segments for(int n=0; n<lineCount; n++) cutList.push_back(std::vector<int64_t>()); for(unsigned int polyNr=0; polyNr < outline.size(); polyNr++) { std::vector<Point> firstBoundarySegment; std::vector<Point> unevenBoundarySegment; // stored cause for connected_zigzags a boundary segment which ends in an uneven scanline needs to be included bool isFirstBoundarySegment = true; bool firstBoundarySegmentEndsInEven; bool isEvenScanSegment = false; Point p0 = outline[polyNr][outline[polyNr].size()-1]; Point lastPoint = p0; for(unsigned int i=0; i < outline[polyNr].size(); i++) { Point p1 = outline[polyNr][i]; int64_t xMin = p1.X, xMax = p0.X; if (xMin == xMax) { lastPoint = p1; p0 = p1; continue; } if (xMin > xMax) { xMin = p0.X; xMax = p1.X; } int scanline_idx0 = (p0.X + ((p0.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -1 cause a linesegment on scanline x counts as belonging to scansegment x-1 ... int scanline_idx1 = (p1.X + ((p1.X > 0)? -1 : -lineSpacing)) / lineSpacing; // -linespacing because a line between scanline -n and -n-1 belongs to scansegment -n-1 (for n=positive natural number) int direction = 1; if (p0.X > p1.X) { direction = -1; scanline_idx1 += 1; // only consider the scanlines in between the scansegments } else scanline_idx0 += 1; // only consider the scanlines in between the scansegments if (isFirstBoundarySegment) firstBoundarySegment.push_back(p0); for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1+direction; scanline_idx+=direction) { int x = scanline_idx * lineSpacing; int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); cutList[scanline_idx - scanline_min_idx].push_back(y); bool last_isEvenScanSegment = isEvenScanSegment; if (scanline_idx % 2 == 0) isEvenScanSegment = true; else isEvenScanSegment = false; if (!isFirstBoundarySegment) { if (last_isEvenScanSegment && (connect_zigzags || !isEvenScanSegment)) addLine(lastPoint, Point(x,y)); else if (connect_zigzags && !last_isEvenScanSegment && !isEvenScanSegment) // if we end an uneven boundary in an uneven segment { // add whole unevenBoundarySegment (including the just obtained point) for (unsigned int p = 1; p < unevenBoundarySegment.size(); p++) { addLine(unevenBoundarySegment[p-1], unevenBoundarySegment[p]); } addLine(unevenBoundarySegment[unevenBoundarySegment.size()-1], Point(x,y)); unevenBoundarySegment.clear(); } if (connect_zigzags && last_isEvenScanSegment && !isEvenScanSegment) unevenBoundarySegment.push_back(Point(x,y)); else unevenBoundarySegment.clear(); } lastPoint = Point(x,y); if (isFirstBoundarySegment) { firstBoundarySegment.emplace_back(x,y); firstBoundarySegmentEndsInEven = isEvenScanSegment; isFirstBoundarySegment = false; } } if (!isFirstBoundarySegment) { if (isEvenScanSegment) addLine(lastPoint, p1); else if (connect_zigzags) unevenBoundarySegment.push_back(p1); } lastPoint = p1; p0 = p1; } if (isEvenScanSegment || isFirstBoundarySegment || connect_zigzags) { for (unsigned int i = 1; i < firstBoundarySegment.size() ; i++) { if (i < firstBoundarySegment.size() - 1 || !firstBoundarySegmentEndsInEven || connect_zigzags) // only add last element if connect_zigzags or boundary segment ends in uneven scanline addLine(firstBoundarySegment[i-1], firstBoundarySegment[i]); } } else if (!firstBoundarySegmentEndsInEven) addLine(firstBoundarySegment[firstBoundarySegment.size()-2], firstBoundarySegment[firstBoundarySegment.size()-1]); } if (cutList.size() == 0) return; if (connect_zigzags && cutList.size() == 1 && cutList[0].size() <= 2) return; // don't add connection if boundary already contains whole outline! addLineInfill(result, matrix, scanline_min_idx, lineSpacing, boundary, cutList, extrusionWidth); }
inline void polygons_append(Polygons &dst, ExPolygon &&src) { dst.reserve(dst.size() + src.holes.size() + 1); dst.push_back(std::move(src.contour)); std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(dst)); }
void generateSkinAreas(int layer_nr, SliceMeshStorage& storage, int innermost_wall_extrusion_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic) { SliceLayer& layer = storage.layers[layer_nr]; if (downSkinCount == 0 && upSkinCount == 0) { return; } for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++) { SliceLayerPart& part = layer.parts[partNr]; if (int(part.insets.size()) < wall_line_count) { continue; // the last wall is not present, the part should only get inter preimeter gaps, but no skin. } Polygons upskin = part.insets.back().offset(-innermost_wall_extrusion_width/2); Polygons downskin = (downSkinCount == 0)? Polygons() : upskin; if (upSkinCount == 0) upskin = Polygons(); auto getInsidePolygons = [&part](SliceLayer& layer2) { Polygons result; for(SliceLayerPart& part2 : layer2.parts) { if (part.boundaryBox.hit(part2.boundaryBox)) result.add(part2.insets.back()); } return result; }; if (no_small_gaps_heuristic) { if (static_cast<int>(layer_nr - downSkinCount) >= 0) { downskin = downskin.difference(getInsidePolygons(storage.layers[layer_nr - downSkinCount])); // skin overlaps with the walls } if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(storage.layers.size())) { upskin = upskin.difference(getInsidePolygons(storage.layers[layer_nr + upSkinCount])); // skin overlaps with the walls } } else { if (layer_nr > 0 && downSkinCount > 0) { Polygons not_air = getInsidePolygons(storage.layers[layer_nr - 1]); for (int downskin_layer_nr = std::max(0, layer_nr - downSkinCount); downskin_layer_nr < layer_nr - 1; downskin_layer_nr++) { not_air = not_air.intersection(getInsidePolygons(storage.layers[downskin_layer_nr])); } downskin = downskin.difference(not_air); // skin overlaps with the walls } if (layer_nr < static_cast<int>(storage.layers.size()) - 1 && upSkinCount > 0) { Polygons not_air = getInsidePolygons(storage.layers[layer_nr + 1]); for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < std::min(static_cast<int>(storage.layers.size()) - 1, layer_nr + upSkinCount); upskin_layer_nr++) { not_air = not_air.intersection(getInsidePolygons(storage.layers[upskin_layer_nr])); } upskin = upskin.difference(not_air); // skin overlaps with the walls } } Polygons skin = upskin.unionPolygons(downskin); skin.removeSmallAreas(MIN_AREA_SIZE); for (PolygonsPart& skin_area_part : skin.splitIntoParts()) { part.skin_parts.emplace_back(); part.skin_parts.back().outline = skin_area_part; } } }
static AS3_Val clipPolygon(void* self, AS3_Val args) { AS3_Val asSubjectVertices; AS3_Val asClipVertices; int subjectVerticeCount, clipVerticeCount, clipTypeArg, subjectFillTypeArg, clipFillTypeArg; // Get the function arguments (subjectVertices:Array, subjectVerticeCount:int, extractVertices:Array, // extractVerticeCount:int, clipType:int, subjectFillType:int, clipFillType:int) AS3_ArrayValue( args, "AS3ValType, IntType, AS3ValType, IntType, IntType, IntType, IntType", &asSubjectVertices, &subjectVerticeCount, &asClipVertices, &clipVerticeCount, &clipTypeArg, &subjectFillTypeArg, &clipFillTypeArg ); Polygon subjectPolygon(subjectVerticeCount / 2), clipPolygon(clipVerticeCount / 2); Polygons solution; // Populate the subject polygon for (int i = 0; i < subjectVerticeCount; i += 2) { subjectPolygon[i / 2] = IntPoint( AS3_IntValue(AS3_Get( asSubjectVertices, AS3_Int(i) )), AS3_IntValue(AS3_Get( asSubjectVertices, AS3_Int(i+1) )) ); } // Populate the clip polygon for (int i = 0; i < clipVerticeCount; i += 2) { clipPolygon[i / 2] = IntPoint( AS3_IntValue(AS3_Get( asClipVertices, AS3_Int(i) )), AS3_IntValue(AS3_Get( asClipVertices, AS3_Int(i+1) )) ); } // Create the AS3 return array AS3_Val returnArray = AS3_Array(""); ClipType clipType; switch (clipTypeArg) { default: case 0: clipType = ctIntersection; break; case 1: clipType = ctUnion; break; case 2: clipType = ctDifference; break; case 3: clipType = ctXor; break; } PolyFillType subjectFillType, clipFillType; switch (subjectFillTypeArg) { default: case 0: subjectFillType = pftEvenOdd; break; case 1: subjectFillType = pftNonZero; break; } switch (clipFillTypeArg) { default: case 0: clipFillType = pftEvenOdd; break; case 1: clipFillType = pftNonZero; break; } Clipper c; c.AddPolygon(subjectPolygon, ptSubject); c.AddPolygon(clipPolygon, ptClip); if (c.Execute(clipType, solution, subjectFillType, clipFillType)) { for (int i = 0; i < (int)solution.size(); i++) { // Create a new AS3 array AS3_Val verticeArray = AS3_Array(""); for (int j = 0; j < (int)solution[i].size(); j++) { // Push all the vertices into the array AS3_Set(verticeArray, AS3_Int(j * 2), AS3_Int(solution[i][j].X)); AS3_Set(verticeArray, AS3_Int(j * 2 + 1), AS3_Int(solution[i][j].Y)); } // Insert the array into the returnArray AS3_Set(returnArray, AS3_Int(i), verticeArray); } } // Cleanup AS3_Release(asSubjectVertices); AS3_Release(asClipVertices); return returnArray; }
void Weaver::weave(PrintObject* object, CommandSocket* commandSocket) { int maxz = object->max().z; int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1; DEBUG_SHOW(layer_count); std::vector<cura::Slicer*> slicerList; for(Mesh& mesh : object->meshes) { cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching")); slicerList.push_back(slicer); } int starting_layer_idx; { // find first non-empty layer for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++) { Polygons parts; for (cura::Slicer* slicer : slicerList) parts.add(slicer->layers[starting_layer_idx].polygonList); if (parts.size() > 0) break; } if (starting_layer_idx > 0) { logError("First %i layers are empty!\n", starting_layer_idx); } } std::cerr<< "chainifying layers..." << std::endl; { int starting_z = -1; for (cura::Slicer* slicer : slicerList) wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList); if (commandSocket) commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline); wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z; Point starting_point_in_layer; if (wireFrame.bottom_outline.size() > 0) starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2; else starting_point_in_layer = (Point(0,0) + object->max() + object->min()) / 2; for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++) { logProgress("inset", layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine Polygons parts1; for (cura::Slicer* slicer : slicerList) parts1.add(slicer->layers[layer_idx].polygonList); Polygons chainified; chainify_polygons(parts1, starting_point_in_layer, chainified, false); if (commandSocket) commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified); if (chainified.size() > 0) { if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z; wireFrame.layers.emplace_back(); WeaveLayer& layer = wireFrame.layers.back(); layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z; layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z; layer.supported = chainified; starting_point_in_layer = layer.supported.back().back(); } } } std::cerr<< "finding horizontal parts..." << std::endl; { Polygons* lower_top_parts = &wireFrame.bottom_outline; for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) { logProgress("skin", layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine WeaveLayer& layer = wireFrame.layers[layer_idx]; Polygons empty; Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty; createHorizontalFill(*lower_top_parts, layer, layer_above, layer.z1); lower_top_parts = &layer.supported; } } // at this point layer.supported still only contains the polygons to be connected // when connecting layers, we further add the supporting polygons created by the roofs std::cerr<< "connecting layers..." << std::endl; { Polygons* lower_top_parts = &wireFrame.bottom_outline; int last_z = wireFrame.z_bottom; for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last { WeaveLayer& layer = wireFrame.layers[layer_idx]; connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer); layer.supported.add(layer.roofs.roof_outlines); lower_top_parts = &layer.supported; last_z = layer.z1; } } { // roofs: WeaveLayer& top_layer = wireFrame.layers.back(); Polygons to_be_supported; // empty for the top layer fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs); } { // bottom: Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported) fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill); } }
void BridgeDetector::coverage(double angle, Polygons* coverage) const { // Clone our expolygon and rotate it so that we work with vertical lines. ExPolygon expolygon = this->expolygon; expolygon.rotate(PI/2.0 - angle, Point(0,0)); /* Outset the bridge expolygon by half the amount we used for detecting anchors; we'll use this one to generate our trapezoids and be sure that their vertices are inside the anchors and not on their contours leading to false negatives. */ ExPolygons grown; offset(expolygon, &grown, this->extrusion_width/2.0); // Compute trapezoids according to a vertical orientation Polygons trapezoids; for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) it->get_trapezoids2(&trapezoids, PI/2.0); // get anchors, convert them to Polygons and rotate them too Polygons anchors; for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) { Polygons pp = *anchor; for (Polygons::iterator p = pp.begin(); p != pp.end(); ++p) p->rotate(PI/2.0 - angle, Point(0,0)); anchors.insert(anchors.end(), pp.begin(), pp.end()); } Polygons covered; for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { Lines lines = trapezoid->lines(); Lines supported; intersection(lines, anchors, &supported); // not nice, we need a more robust non-numeric check for (size_t i = 0; i < supported.size(); ++i) { if (supported[i].length() < this->extrusion_width) { supported.erase(supported.begin() + i); i--; } } if (supported.size() >= 2) covered.push_back(*trapezoid); } // merge trapezoids and rotate them back Polygons _coverage; union_(covered, &_coverage); for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p) p->rotate(-(PI/2.0 - angle), Point(0,0)); // intersect trapezoids with actual bridge area to remove extra margins // and append it to result intersection(_coverage, this->expolygon, coverage); /* if (0) { my @lines = map @{$_->lines}, @$trapezoids; $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; require "Slic3r/SVG.pm"; Slic3r::SVG::output( "coverage_" . rad2deg($angle) . ".svg", expolygons => [$self->expolygon], green_expolygons => $self->_anchors, red_expolygons => $coverage, lines => \@lines, ); } */ }
void removeOverlapping(Polygons& poly, int extrusionWidth, Polygons& result) { result = poly.offset(extrusionWidth/2).offset(-extrusionWidth).offset(extrusionWidth/2); }
bool BridgeDetector::detect_angle() { if (this->_edges.empty() || this->_anchors.empty()) return false; /* Outset the bridge expolygon by half the amount we used for detecting anchors; we'll use this one to clip our test lines and be sure that their endpoints are inside the anchors and not on their contours leading to false negatives. */ Polygons clip_area; offset(this->expolygon, &clip_area, +this->extrusion_width/2); /* we'll now try several directions using a rudimentary visibility check: bridge in several directions and then sum the length of lines having both endpoints within anchors */ // we test angles according to configured resolution std::vector<double> angles; for (int i = 0; i <= PI/this->resolution; ++i) angles.push_back(i * this->resolution); // we also test angles of each bridge contour { Polygons pp = this->expolygon; for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { Lines lines = p->lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) angles.push_back(line->direction()); } } /* we also test angles of each open supporting edge (this finds the optimal angle for C-shaped supports) */ for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) { if (edge->first_point().coincides_with(edge->last_point())) continue; angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); } // remove duplicates double min_resolution = PI/180.0; // 1 degree std::sort(angles.begin(), angles.end()); for (size_t i = 1; i < angles.size(); ++i) { if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) { angles.erase(angles.begin() + i); --i; } } /* compare first value with last one and remove the greatest one (PI) in case they are parallel (PI, 0) */ if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) angles.pop_back(); BridgeDirectionComparator bdcomp(this->extrusion_width); double line_increment = this->extrusion_width; bool have_coverage = false; for (std::vector<double>::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) { Polygons my_clip_area = clip_area; ExPolygons my_anchors = this->_anchors; // rotate everything - the center point doesn't matter for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it) it->rotate(-*angle, Point(0,0)); for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) it->rotate(-*angle, Point(0,0)); // generate lines in this direction BoundingBox bb; for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it) bb.merge((Points)*it); Lines lines; for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment) lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y))); Lines clipped_lines; intersection(lines, my_clip_area, &clipped_lines); // remove any line not having both endpoints within anchors for (size_t i = 0; i < clipped_lines.size(); ++i) { Line &line = clipped_lines[i]; if (!Slic3r::Geometry::contains(my_anchors, line.a) || !Slic3r::Geometry::contains(my_anchors, line.b)) { clipped_lines.erase(clipped_lines.begin() + i); --i; } } std::vector<double> lengths; double total_length = 0; for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) { double len = line->length(); lengths.push_back(len); total_length += len; } if (total_length) have_coverage = true; // sum length of bridged lines bdcomp.dir_coverage[*angle] = total_length; /* The following produces more correct results in some cases and more broken in others. TODO: investigate, as it looks more reliable than line clipping. */ // $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0; // max length of bridged lines bdcomp.dir_avg_length[*angle] = !lengths.empty() ? *std::max_element(lengths.begin(), lengths.end()) : 0; } // if no direction produced coverage, then there's no bridge direction if (!have_coverage) return false; // sort directions by score std::sort(angles.begin(), angles.end(), bdcomp); this->angle = angles.front(); if (this->angle >= PI) this->angle -= PI; #ifdef SLIC3R_DEBUG printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle)); #endif return true; }
/* * Algorithm: * From top layer to bottom layer: * - find overhang by looking at the difference between two consucutive layers * - join with support areas from layer above * - subtract current layer * - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more stability) * - perform inset using X/Y-distance and bottom Z distance * * for support buildplate only: purge all support not connected to buildplate */ void generateSupportAreas(SliceDataStorage& storage, SliceMeshStorage* object, int layer_count) { // given settings ESupportType support_type = object->settings->getSettingAsSupportType("support_type"); storage.support.generated = false; if (!object->settings->getSettingBoolean("support_enable")) return; if (support_type == Support_None) return; double supportAngle = object->settings->getSettingInAngleRadians("support_angle"); bool supportOnBuildplateOnly = support_type == Support_PlatformOnly; int supportXYDistance = object->settings->getSettingInMicrons("support_xy_distance"); int supportZDistance = object->settings->getSettingInMicrons("support_z_distance"); int supportZDistanceBottom = object->settings->getSettingInMicrons("support_bottom_distance"); int supportZDistanceTop = object->settings->getSettingInMicrons("support_top_distance"); int supportJoinDistance = object->settings->getSettingInMicrons("support_join_distance"); int support_bottom_stair_step_height = object->settings->getSettingInMicrons("support_bottom_stair_step_height"); int smoothing_distance = object->settings->getSettingInMicrons("support_area_smoothing"); int supportTowerDiameter = object->settings->getSettingInMicrons("support_tower_diameter"); int supportMinAreaSqrt = object->settings->getSettingInMicrons("support_minimal_diameter"); double supportTowerRoofAngle = object->settings->getSettingInAngleRadians("support_tower_roof_angle"); //std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl; int min_smoothing_area = 100*100; // minimal area for which to perform smoothing int z_layer_distance_tower = 1; // start tower directly below overhang point int layerThickness = object->settings->getSettingInMicrons("layer_height"); int extrusionWidth = object->settings->getSettingInMicrons("wall_line_width_x"); // TODO check for layer0extrusionWidth! // derived settings: if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance; if (supportZDistanceTop < 0) supportZDistanceTop = supportZDistance; int supportLayerThickness = layerThickness; int layerZdistanceTop = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang int layerZdistanceBottom = supportZDistanceBottom / supportLayerThickness; double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged int support_layer_count = layer_count; double tanTowerRoofAngle = tan(supportTowerRoofAngle); int towerRoofExpansionDistance = layerThickness / tanTowerRoofAngle; // computation std::vector<Polygons> joinedLayers; // join model layers of all meshes into polygons and store small areas which need tower support std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs AreaSupport::joinMeshesAndDetectOverhangPoints(storage, joinedLayers, overhang_points, layer_count, supportMinAreaSqrt, extrusionWidth); // initialization of supportAreasPerLayer for (int layer_idx = 0; layer_idx < layer_count ; layer_idx++) storage.support.supportAreasPerLayer.emplace_back(); int overhang_points_pos = overhang_points.size() - 1; Polygons supportLayer_last; std::vector<Polygons> towerRoofs; for (int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx >= 0 ; layer_idx--) { // compute basic overhang and put in right layer ([layerZdistanceTOp] layers below) Polygons supportLayer_supportee = joinedLayers[layer_idx+layerZdistanceTop]; Polygons supportLayer_supported = joinedLayers[layer_idx-1+layerZdistanceTop].offset(maxDistFromLowerLayer); Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported); Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer); support_extension = support_extension.intersection(supportLayer_supported); support_extension = support_extension.intersection(supportLayer_supportee); Polygons overhang = basic_overhang.unionPolygons(support_extension); /* supported * ................. * ______________| * _______| ^^^^^ basic overhang * * ^^^^^^^^^ overhang extensions * ^^^^^^^^^^^^^^ overhang */ Polygons& supportLayer_this = overhang; supportLayer_this = supportLayer_this.simplify(50); // TODO: hardcoded value! if (supportMinAreaSqrt > 0) { // handle straight walls AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter); // handle towers AreaSupport::handleTowers(supportLayer_this, towerRoofs, overhang_points, overhang_points_pos, layer_idx, towerRoofExpansionDistance, supportTowerDiameter, supportMinAreaSqrt, layer_count, z_layer_distance_tower); } if (layer_idx+1 < support_layer_count) { // join with support from layer up Polygons& supportLayer_up = supportLayer_last; Polygons joined = supportLayer_this.unionPolygons(supportLayer_up); // join different parts if (supportJoinDistance > 0) { joined = joined.offset(supportJoinDistance); joined = joined.offset(-supportJoinDistance); } if (smoothing_distance > 0) joined = joined.smooth(smoothing_distance, min_smoothing_area); // remove layer Polygons insetted = joined.difference(joinedLayers[layer_idx]); supportLayer_this = insetted; } supportLayer_last = supportLayer_this; // inset using X/Y distance if (supportLayer_this.size() > 0) supportLayer_this = supportLayer_this.difference(joinedLayers[layer_idx].offset(supportXYDistance)); // move up from model if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom) { int stepHeight = support_bottom_stair_step_height / supportLayerThickness + 1; int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight; supportLayer_this = supportLayer_this.difference(joinedLayers[bottomLayer]); } storage.support.supportAreasPerLayer[layer_idx] = supportLayer_this; logProgress("support", support_layer_count - layer_idx, support_layer_count); } // do stuff for when support on buildplate only if (supportOnBuildplateOnly) { Polygons touching_buildplate = storage.support.supportAreasPerLayer[0]; for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportAreasPerLayer.size() ; layer_idx++) { Polygons& supportLayer = storage.support.supportAreasPerLayer[layer_idx]; touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease! storage.support.supportAreasPerLayer[layer_idx] = touching_buildplate; } } joinedLayers.clear(); storage.support.generated = true; }
void GyroidInfill::generateTotalGyroidInfill(Polygons& result_lines, bool zig_zaggify, coord_t outline_offset, coord_t infill_line_width, coord_t line_distance, const Polygons& in_outline, coord_t z) { // generate infill based on the gyroid equation: sin_x * cos_y + sin_y * cos_z + sin_z * cos_x = 0 // kudos to the author of the Slic3r implementation equation code, the equation code here is based on that if (zig_zaggify) { outline_offset -= infill_line_width / 2; // the infill line zig zag connections must lie next to the border, not on it } const Polygons outline = in_outline.offset(outline_offset); const AABB aabb(outline); int pitch = line_distance * 2.41; // this produces similar density to the "line" infill pattern int num_steps = 4; int step = pitch / num_steps; while (step > 500 && num_steps < 16) { num_steps *= 2; step = pitch / num_steps; } pitch = step * num_steps; // recalculate to avoid precision errors const double z_rads = 2 * M_PI * z / pitch; const double cos_z = std::cos(z_rads); const double sin_z = std::sin(z_rads); std::vector<coord_t> odd_line_coords; std::vector<coord_t> even_line_coords; Polygons result; std::vector<Point> chains[2]; // [start_points[], end_points[]] std::vector<unsigned> connected_to[2]; // [chain_indices[], chain_indices[]] std::vector<int> line_numbers; // which row/column line a chain is part of if (std::abs(sin_z) <= std::abs(cos_z)) { // "vertical" lines const double phase_offset = ((cos_z < 0) ? M_PI : 0) + M_PI; for (coord_t y = 0; y < pitch; y += step) { const double y_rads = 2 * M_PI * y / pitch; const double a = cos_z; const double b = std::sin(y_rads + phase_offset); const double odd_c = sin_z * std::cos(y_rads + phase_offset); const double even_c = sin_z * std::cos(y_rads + phase_offset + M_PI); const double h = std::sqrt(a * a + b * b); const double odd_x_rads = ((h != 0) ? std::asin(odd_c / h) + std::asin(b / h) : 0) - M_PI/2; const double even_x_rads = ((h != 0) ? std::asin(even_c / h) + std::asin(b / h) : 0) - M_PI/2; odd_line_coords.push_back(odd_x_rads / M_PI * pitch); even_line_coords.push_back(even_x_rads / M_PI * pitch); } const unsigned num_coords = odd_line_coords.size(); unsigned num_columns = 0; for (coord_t x = (std::floor(aabb.min.X / pitch) - 2.25) * pitch; x <= aabb.max.X + pitch/2; x += pitch/2) { bool is_first_point = true; Point last; bool last_inside = false; unsigned chain_end_index = 0; Point chain_end[2]; for (coord_t y = (std::floor(aabb.min.Y / pitch) - 1) * pitch; y <= aabb.max.Y + pitch; y += pitch) { for (unsigned i = 0; i < num_coords; ++i) { Point current(x + ((num_columns & 1) ? odd_line_coords[i] : even_line_coords[i])/2 + pitch, y + (coord_t)(i * step)); bool current_inside = outline.inside(current, true); if (!is_first_point) { if (last_inside && current_inside) { // line doesn't hit the boundary, add the whole line result.addLine(last, current); } else if (last_inside != current_inside) { // line hits the boundary, add the part that's inside the boundary Polygons line; line.addLine(last, current); line = outline.intersectionPolyLines(line); if (line.size() > 0) { // some of the line is inside the boundary result.addLine(line[0][0], line[0][1]); if (zig_zaggify) { chain_end[chain_end_index] = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1]; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_columns); } } } else { // none of the line is inside the boundary so the point that's actually on the boundary // is the chain end if (zig_zaggify) { chain_end[chain_end_index] = (last_inside) ? last : current; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_columns); } } } } } last = current; last_inside = current_inside; is_first_point = false; } } ++num_columns; } } else { // "horizontal" lines const double phase_offset = (sin_z < 0) ? M_PI : 0; for (coord_t x = 0; x < pitch; x += step) { const double x_rads = 2 * M_PI * x / pitch; const double a = sin_z; const double b = std::cos(x_rads + phase_offset); const double odd_c = cos_z * std::sin(x_rads + phase_offset + M_PI); const double even_c = cos_z * std::sin(x_rads + phase_offset); const double h = std::sqrt(a * a + b * b); const double odd_y_rads = ((h != 0) ? std::asin(odd_c / h) + std::asin(b / h) : 0) + M_PI/2; const double even_y_rads = ((h != 0) ? std::asin(even_c / h) + std::asin(b / h) : 0) + M_PI/2; odd_line_coords.push_back(odd_y_rads / M_PI * pitch); even_line_coords.push_back(even_y_rads / M_PI * pitch); } const unsigned num_coords = odd_line_coords.size(); unsigned num_rows = 0; for (coord_t y = (std::floor(aabb.min.Y / pitch) - 1) * pitch; y <= aabb.max.Y + pitch/2; y += pitch/2) { bool is_first_point = true; Point last; bool last_inside = false; unsigned chain_end_index = 0; Point chain_end[2]; for (coord_t x = (std::floor(aabb.min.X / pitch) - 1) * pitch; x <= aabb.max.X + pitch; x += pitch) { for (unsigned i = 0; i < num_coords; ++i) { Point current(x + (coord_t)(i * step), y + ((num_rows & 1) ? odd_line_coords[i] : even_line_coords[i])/2); bool current_inside = outline.inside(current, true); if (!is_first_point) { if (last_inside && current_inside) { // line doesn't hit the boundary, add the whole line result.addLine(last, current); } else if (last_inside != current_inside) { // line hits the boundary, add the part that's inside the boundary Polygons line; line.addLine(last, current); line = outline.intersectionPolyLines(line); if (line.size() > 0) { // some of the line is inside the boundary result.addLine(line[0][0], line[0][1]); if (zig_zaggify) { chain_end[chain_end_index] = line[0][(line[0][0] != last && line[0][0] != current) ? 0 : 1]; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_rows); } } } else { // none of the line is inside the boundary so the point that's actually on the boundary // is the chain end if (zig_zaggify) { chain_end[chain_end_index] = (last_inside) ? last : current; if (++chain_end_index == 2) { chains[0].push_back(chain_end[0]); chains[1].push_back(chain_end[1]); chain_end_index = 0; connected_to[0].push_back(std::numeric_limits<unsigned>::max()); connected_to[1].push_back(std::numeric_limits<unsigned>::max()); line_numbers.push_back(num_rows); } } } } } last = current; last_inside = current_inside; is_first_point = false; } } ++num_rows; } } if (zig_zaggify && chains[0].size() > 0) { // zig-zaggification consists of joining alternate chain ends to make a chain of chains // the basic algorithm is that we follow the infill area boundary and as we progress we are either drawing a connector or not // whenever we come across the end of a chain we toggle the connector drawing state // things are made more complicated by the fact that we want to avoid generating loops and so we need to keep track // of the indentity of the first chain in a connected sequence int chain_ends_remaining = chains[0].size() * 2; for (ConstPolygonRef outline_poly : outline) { std::vector<Point> connector_points; // the points that make up a connector line // we need to remember the first chain processed and the path to it from the first outline point // so that later we can possibly connect to it from the last chain processed unsigned first_chain_chain_index = std::numeric_limits<unsigned>::max(); std::vector<Point> path_to_first_chain; bool drawing = false; // true when a connector line is being (potentially) created // keep track of the chain+point that a connector line started at unsigned connector_start_chain_index = std::numeric_limits<unsigned>::max(); unsigned connector_start_point_index = std::numeric_limits<unsigned>::max(); Point cur_point; // current point of interest - either an outline point or a chain end // go round all of the region's outline and find the chain ends that meet it // quit the loop early if we have seen all the chain ends and are not currently drawing a connector for (unsigned outline_point_index = 0; (chain_ends_remaining > 0 || drawing) && outline_point_index < outline_poly.size(); ++outline_point_index) { Point op0 = outline_poly[outline_point_index]; Point op1 = outline_poly[(outline_point_index + 1) % outline_poly.size()]; std::vector<unsigned> points_on_outline_chain_index; std::vector<unsigned> points_on_outline_point_index; // collect the chain ends that meet this segment of the outline for (unsigned chain_index = 0; chain_index < chains[0].size(); ++chain_index) { for (unsigned point_index = 0; point_index < 2; ++point_index) { // don't include chain ends that are close to the segment but are beyond the segment ends short beyond = 0; if (LinearAlg2D::getDist2FromLineSegment(op0, chains[point_index][chain_index], op1, &beyond) < 10 && !beyond) { points_on_outline_point_index.push_back(point_index); points_on_outline_chain_index.push_back(chain_index); } } } if (outline_point_index == 0 || vSize2(op0 - cur_point) > 100) { // this is either the first outline point or it is another outline point that is not too close to cur_point if (first_chain_chain_index == std::numeric_limits<unsigned>::max()) { // include the outline point in the path to the first chain path_to_first_chain.push_back(op0); } cur_point = op0; if (drawing) { // include the start point of this outline segment in the connector connector_points.push_back(op0); } } // iterate through each of the chain ends that meet the current outline segment while (points_on_outline_chain_index.size() > 0) { // find the nearest chain end to the current point unsigned nearest_point_index = 0; float nearest_point_dist2 = std::numeric_limits<float>::infinity(); for (unsigned pi = 0; pi < points_on_outline_chain_index.size(); ++pi) { float dist2 = vSize2f(chains[points_on_outline_point_index[pi]][points_on_outline_chain_index[pi]] - cur_point); if (dist2 < nearest_point_dist2) { nearest_point_dist2 = dist2; nearest_point_index = pi; } } const unsigned point_index = points_on_outline_point_index[nearest_point_index]; const unsigned chain_index = points_on_outline_chain_index[nearest_point_index]; // make the chain end the current point and add it to the connector line cur_point = chains[point_index][chain_index]; if (drawing && connector_points.size() > 0 && vSize2(cur_point - connector_points.back()) < 100) { // this chain end will be too close to the last connector point so throw away the last connector point connector_points.pop_back(); } connector_points.push_back(cur_point); if (first_chain_chain_index == std::numeric_limits<unsigned>::max()) { // this is the first chain to be processed, remember it first_chain_chain_index = chain_index; path_to_first_chain.push_back(cur_point); } if (drawing) { // add the connector line segments but only if // 1 - the start/end points are not the opposite ends of the same chain // 2 - the other end of the current chain is not connected to the chain the connector line is coming from if (chain_index != connector_start_chain_index && connected_to[(point_index + 1) % 2][chain_index] != connector_start_chain_index) { for (unsigned pi = 1; pi < connector_points.size(); ++pi) { result.addLine(connector_points[pi - 1], connector_points[pi]); } drawing = false; connector_points.clear(); // remember the connection connected_to[point_index][chain_index] = connector_start_chain_index; connected_to[connector_start_point_index][connector_start_chain_index] = chain_index; } else { // start a new connector from the current location connector_points.clear(); connector_points.push_back(cur_point); // remember the chain+point that the connector started from connector_start_chain_index = chain_index; connector_start_point_index = point_index; } } else { // we have just jumped a gap so now we want to start drawing again drawing = true; // if this connector is the first to be created or we are not connecting chains from the same row/column, // remember the chain+point that this connector is starting from if (connector_start_chain_index == std::numeric_limits<unsigned>::max() || line_numbers[chain_index] != line_numbers[connector_start_chain_index]) { connector_start_chain_index = chain_index; connector_start_point_index = point_index; } } // done with this chain end points_on_outline_chain_index.erase(points_on_outline_chain_index.begin() + nearest_point_index); points_on_outline_point_index.erase(points_on_outline_point_index.begin() + nearest_point_index); // decrement total amount of work to do --chain_ends_remaining; } } // we have now visited all the points in the outline, if a connector was (potentially) being drawn // check whether the first chain is already connected to the last chain and, if not, draw the // connector between if (drawing && first_chain_chain_index != std::numeric_limits<unsigned>::max() && first_chain_chain_index != connector_start_chain_index && connected_to[0][first_chain_chain_index] != connector_start_chain_index && connected_to[1][first_chain_chain_index] != connector_start_chain_index) { // output the connector line segments from the last chain to the first point in the outline connector_points.push_back(outline_poly[0]); for (unsigned pi = 1; pi < connector_points.size(); ++pi) { result.addLine(connector_points[pi - 1], connector_points[pi]); } // output the connector line segments from the first point in the outline to the first chain for (unsigned pi = 1; pi < path_to_first_chain.size(); ++pi) { result.addLine(path_to_first_chain[pi - 1], path_to_first_chain[pi]); } } if (chain_ends_remaining < 1) { break; } } } result_lines = result; }
void generateSparse(int layerNr, SliceVolumeStorage& storage, int extrusionWidth, int downSkinCount, int upSkinCount) { SliceLayer* layer = &storage.layers[layerNr]; for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++) { SliceLayerPart* part = &layer->parts[partNr]; Polygons sparse = part->insets[part->insets.size() - 1].offset(-extrusionWidth/2); Polygons downskin = sparse; Polygons upskin = sparse; if (int(layerNr - downSkinCount) >= 0) { SliceLayer* layer2 = &storage.layers[layerNr - downSkinCount]; for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++) { if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox)) { if (layer2->parts[partNr2].insets.size() > 1) { downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]); }else{ downskin = downskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]); } } } } if (int(layerNr + upSkinCount) < (int)storage.layers.size()) { SliceLayer* layer2 = &storage.layers[layerNr + upSkinCount]; for(unsigned int partNr2=0; partNr2<layer2->parts.size(); partNr2++) { if (part->boundaryBox.hit(layer2->parts[partNr2].boundaryBox)) { if (layer2->parts[partNr2].insets.size() > 1) { upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 2]); }else{ upskin = upskin.difference(layer2->parts[partNr2].insets[layer2->parts[partNr2].insets.size() - 1]); } } } } Polygons result = upskin.unionPolygons(downskin); double minAreaSize = 3.0;//(2 * M_PI * (double(config.extrusionWidth) / 1000.0) * (double(config.extrusionWidth) / 1000.0)) * 3; for(unsigned int i=0; i<result.size(); i++) { double area = fabs(ClipperLib::Area(result[i])) / 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" */ { result.remove(i); i -= 1; } } part->sparseOutline = sparse.difference(result); } }
void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count) { const bool is_skirt = start_distance > 0; const unsigned int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr"); const ExtruderTrain* adhesion_extruder = storage.meshgroup->getExtruderTrain(adhesion_extruder_nr); const int primary_extruder_skirt_brim_line_width = adhesion_extruder->getSettingInMicrons("skirt_brim_line_width") * adhesion_extruder->getSettingAsRatio("initial_layer_line_width_factor"); const int64_t primary_extruder_minimal_length = adhesion_extruder->getSettingInMicrons("skirt_brim_minimal_length"); Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr]; Polygons first_layer_outline; getFirstLayerOutline(storage, primary_line_count, primary_extruder_skirt_brim_line_width, is_skirt, first_layer_outline); const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0; const bool has_draft_shield = storage.draft_protection_shield.size() > 0; if (is_skirt && (has_ooze_shield || has_draft_shield)) { // make sure we don't generate skirt through draft / ooze shield first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield); if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } first_layer_outline = first_layer_outline.approxConvexHull(); start_distance = primary_extruder_skirt_brim_line_width / 2; } int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_skirt_brim_line_width, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder); // generate brim for ooze shield and draft shield if (!is_skirt && (has_ooze_shield || has_draft_shield)) { // generate areas where to make extra brim for the shields // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides Polygons shield_brim; if (has_ooze_shield) { shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)); } if (has_draft_shield) { shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width))); } const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound); shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width)); // generate brim within shield_brim skirt_brim_primary_extruder.add(shield_brim); while (shield_brim.size() > 0) { shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width); skirt_brim_primary_extruder.add(shield_brim); } // update parameters to generate secondary skirt around first_layer_outline = outer_primary_brim; if (has_draft_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield); } if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } offset_distance = 0; } { // process other extruders' brim/skirt (as one brim line around the old brim) int last_width = primary_extruder_skirt_brim_line_width; std::vector<bool> extruder_is_used = storage.getExtrudersUsed(); for (unsigned int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++) { if (extruder == adhesion_extruder_nr || !extruder_is_used[extruder]) { continue; } const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder); const int width = train->getSettingInMicrons("skirt_brim_line_width") * train->getSettingAsRatio("initial_layer_line_width_factor"); const int64_t minimal_length = train->getSettingInMicrons("skirt_brim_minimal_length"); offset_distance += last_width / 2 + width/2; last_width = width; while (storage.skirt_brim[extruder].polygonLength() < minimal_length) { storage.skirt_brim[extruder].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound)); offset_distance += width; } } } }
void SkirtBrim::generate(SliceDataStorage& storage, Polygons first_layer_outline, int start_distance, unsigned int primary_line_count) { const bool is_skirt = start_distance > 0; Scene& scene = Application::getInstance().current_slice->scene; const size_t adhesion_extruder_nr = scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").extruder_nr; const Settings& adhesion_settings = scene.extruders[adhesion_extruder_nr].settings; const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor"); const coord_t primary_extruder_minimal_length = adhesion_settings.get<coord_t>("skirt_brim_minimal_length"); Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr]; const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0; const bool has_draft_shield = storage.draft_protection_shield.size() > 0; if (is_skirt && (has_ooze_shield || has_draft_shield)) { // make sure we don't generate skirt through draft / ooze shield first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield); if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } first_layer_outline = first_layer_outline.approxConvexHull(); start_distance = primary_extruder_skirt_brim_line_width / 2; } int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder); // handle support-brim const ExtruderTrain& support_infill_extruder = scene.current_mesh_group->settings.get<ExtruderTrain&>("support_infill_extruder_nr"); if (support_infill_extruder.settings.get<bool>("support_brim_enable")) { generateSupportBrim(storage); } // generate brim for ooze shield and draft shield if (!is_skirt && (has_ooze_shield || has_draft_shield)) { // generate areas where to make extra brim for the shields // avoid gap in the middle // V // +---+ +----+ // |+-+| |+--+| // || || ||[]|| > expand to fit an extra brim line // |+-+| |+--+| // +---+ +----+ const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides Polygons shield_brim; if (has_ooze_shield) { shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)); } if (has_draft_shield) { shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width))); } const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound); shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width)); // generate brim within shield_brim skirt_brim_primary_extruder.add(shield_brim); while (shield_brim.size() > 0) { shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width); skirt_brim_primary_extruder.add(shield_brim); } // update parameters to generate secondary skirt around first_layer_outline = outer_primary_brim; if (has_draft_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield); } if (has_ooze_shield) { first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]); } offset_distance = 0; } { // process other extruders' brim/skirt (as one brim line around the old brim) int last_width = primary_extruder_skirt_brim_line_width; std::vector<bool> extruder_is_used = storage.getExtrudersUsed(); for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++) { if (extruder_nr == adhesion_extruder_nr || !extruder_is_used[extruder_nr]) { continue; } const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr]; const coord_t width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor"); const coord_t minimal_length = train.settings.get<coord_t>("skirt_brim_minimal_length"); offset_distance += last_width / 2 + width/2; last_width = width; while (storage.skirt_brim[extruder_nr].polygonLength() < minimal_length) { storage.skirt_brim[extruder_nr].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound)); offset_distance += width; } } } }
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount) { if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast<size_t>(mesh.getSettingAsCount("top_layers")) || mesh.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated. { return; } if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid. { return; } /* We need to round down the layer index we start at to the nearest divisible index. Otherwise we get some parts that have infill at divisible layers and some at non-divisible layers. Those layers would then miss each other. */ size_t min_layer = mesh.getSettingAsCount("bottom_layers") + amount - 1; min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine. size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers"); max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine. for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more. { SliceLayer* layer = &mesh.layers[layer_idx]; for(unsigned int combine_count_here = 1; combine_count_here < amount; combine_count_here++) { if(layer_idx < combine_count_here) { break; } size_t lower_layer_idx = layer_idx - combine_count_here; if (lower_layer_idx < min_layer) { break; } SliceLayer* lower_layer = &mesh.layers[lower_layer_idx]; for (SliceLayerPart& part : layer->parts) { for (unsigned int density_idx = 0; density_idx < part.infill_area_per_combine_per_density.size(); density_idx++) { // go over each density of gradual infill (these density areas overlap!) std::vector<Polygons>& infill_area_per_combine = part.infill_area_per_combine_per_density[density_idx]; Polygons result; for (SliceLayerPart& lower_layer_part : lower_layer->parts) { if (part.boundaryBox.hit(lower_layer_part.boundaryBox)) { Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.infill_area).offset(-200).offset(200); result.add(intersection); // add area to be thickened infill_area_per_combine[combine_count_here - 1] = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here if (density_idx < lower_layer_part.infill_area_per_combine_per_density.size()) { // only remove from *same density* areas on layer below // If there are no same density areas, then it's ok to print them anyway // Don't remove other density areas unsigned int lower_density_idx = density_idx; std::vector<Polygons>& lower_infill_area_per_combine = lower_layer_part.infill_area_per_combine_per_density[lower_density_idx]; lower_infill_area_per_combine[0] = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (thickened) layer } } } infill_area_per_combine.push_back(result); } } } } }
void processFile(const char* input_filename, ConfigSettings& config, GCodeExport& gcode, bool firstFile) { for(unsigned int n=1; n<16;n++) gcode.setExtruderOffset(n, config.extruderOffset[n].p()); gcode.setFlavor(config.gcodeFlavor); double t = getTime(); log("Loading %s from disk...\n", input_filename); SimpleModel* m = loadModel(input_filename, config.matrix); if (!m) { log("Failed to load model: %s\n", input_filename); return; } log("Loaded from disk in %5.3fs\n", timeElapsed(t)); log("Analyzing and optimizing model...\n"); OptimizedModel* om = new OptimizedModel(m, Point3(config.objectPosition.X, config.objectPosition.Y, -config.objectSink)); for(unsigned int v = 0; v < m->volumes.size(); v++) { log(" Face counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size(), (int)om->volumes[v].faces.size(), float(om->volumes[v].faces.size()) / float(m->volumes[v].faces.size()) * 100); log(" Vertex counts: %i -> %i %0.1f%%\n", (int)m->volumes[v].faces.size() * 3, (int)om->volumes[v].points.size(), float(om->volumes[v].points.size()) / float(m->volumes[v].faces.size() * 3) * 100); } delete m; log("Optimize model %5.3fs \n", timeElapsed(t)); //om->saveDebugSTL("c:\\models\\output.stl"); log("Slicing model...\n"); vector<Slicer*> slicerList; for(unsigned int volumeIdx=0; volumeIdx < om->volumes.size(); volumeIdx++) { slicerList.push_back(new Slicer(&om->volumes[volumeIdx], config.initialLayerThickness / 2, config.layerThickness, config.fixHorrible & FIX_HORRIBLE_KEEP_NONE_CLOSED, config.fixHorrible & FIX_HORRIBLE_EXTENSIVE_STITCHING)); //slicerList[volumeIdx]->dumpSegments("C:\\models\\output.html"); } log("Sliced model in %5.3fs\n", timeElapsed(t)); SliceDataStorage storage; if (config.supportAngle > -1) { fprintf(stdout,"Generating support map...\n"); generateSupportGrid(storage.support, om); } storage.modelSize = om->modelSize; storage.modelMin = om->vMin; storage.modelMax = om->vMax; delete om; log("Generating layer parts...\n"); for(unsigned int volumeIdx=0; volumeIdx < slicerList.size(); volumeIdx++) { storage.volumes.push_back(SliceVolumeStorage()); createLayerParts(storage.volumes[volumeIdx], slicerList[volumeIdx], config.fixHorrible & (FIX_HORRIBLE_UNION_ALL_TYPE_A | FIX_HORRIBLE_UNION_ALL_TYPE_B)); delete slicerList[volumeIdx]; } //carveMultipleVolumes(storage.volumes); generateMultipleVolumesOverlap(storage.volumes, config.multiVolumeOverlap); log("Generated layer parts in %5.3fs\n", timeElapsed(t)); //dumpLayerparts(storage, "c:/models/output.html"); const unsigned int totalLayers = storage.volumes[0].layers.size(); for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++) { for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++) { generateInsets(&storage.volumes[volumeIdx].layers[layerNr], config.extrusionWidth, config.insetCount); } logProgress("inset",layerNr+1,totalLayers); } log("Generated inset in %5.3fs\n", timeElapsed(t)); //dumpLayerparts(storage, "c:/models/output.html"); for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++) { for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++) { generateSkins(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount, config.infillOverlap); generateSparse(layerNr, storage.volumes[volumeIdx], config.extrusionWidth, config.downSkinCount, config.upSkinCount); } logProgress("skin",layerNr+1,totalLayers); } log("Generated up/down skin in %5.3fs\n", timeElapsed(t)); generateSkirt(storage, config.skirtDistance, config.extrusionWidth, config.skirtLineCount, config.skirtMinLength); generateRaft(storage, config.raftMargin, config.supportAngle, config.supportEverywhere > 0, config.supportXYDistance); for(unsigned int volumeIdx=0; volumeIdx<storage.volumes.size(); volumeIdx++) { for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++) { for(unsigned int partNr=0; partNr<storage.volumes[volumeIdx].layers[layerNr].parts.size(); partNr++) { if (layerNr > 0) storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = bridgeAngle(&storage.volumes[volumeIdx].layers[layerNr].parts[partNr], &storage.volumes[volumeIdx].layers[layerNr-1]); else storage.volumes[volumeIdx].layers[layerNr].parts[partNr].bridgeAngle = -1; } } } gcode.setRetractionSettings(config.retractionAmount, config.retractionSpeed, config.retractionAmountExtruderSwitch, config.minimalExtrusionBeforeRetraction); if (firstFile) { if (gcode.getFlavor() == GCODE_FLAVOR_ULTIGCODE) { gcode.addCode(";FLAVOR:UltiGCode"); gcode.addCode(";TIME:<__TIME__>"); gcode.addCode(";MATERIAL:<FILAMENT>"); } gcode.addCode(config.startCode); }else{ gcode.addFanCommand(0); gcode.resetExtrusionValue(); gcode.addRetraction(); gcode.setZ(maxObjectHeight + 5000); gcode.addMove(Point(storage.modelMin.x, storage.modelMin.y), config.moveSpeed, 0); } gcode.addComment("total_layers=%d",totalLayers); GCodePathConfig skirtConfig(config.printSpeed, config.extrusionWidth, "SKIRT"); GCodePathConfig inset0Config(config.printSpeed, config.extrusionWidth, "WALL-OUTER"); GCodePathConfig inset1Config(config.printSpeed, config.extrusionWidth, "WALL-INNER"); GCodePathConfig fillConfig(config.infillSpeed, config.extrusionWidth, "FILL"); GCodePathConfig supportConfig(config.printSpeed, config.extrusionWidth, "SUPPORT"); if (config.raftBaseThickness > 0 && config.raftInterfaceThickness > 0) { GCodePathConfig raftBaseConfig(config.initialLayerSpeed, config.raftBaseLinewidth, "SUPPORT"); GCodePathConfig raftInterfaceConfig(config.initialLayerSpeed, config.raftInterfaceLinewidth, "SUPPORT"); { gcode.addComment("LAYER:-2"); gcode.addComment("RAFT"); GCodePlanner gcodeLayer(gcode, config.moveSpeed, config.retractionMinimalDistance); gcode.setZ(config.raftBaseThickness); gcode.setExtrusion(config.raftBaseThickness, config.filamentDiameter, config.filamentFlow); gcodeLayer.addPolygonsByOptimizer(storage.raftOutline, &raftBaseConfig); Polygons raftLines; generateLineInfill(storage.raftOutline, raftLines, config.raftBaseLinewidth, config.raftLineSpacing, config.infillOverlap, 0); gcodeLayer.addPolygonsByOptimizer(raftLines, &raftBaseConfig); gcodeLayer.writeGCode(false); } { gcode.addComment("LAYER:-1"); gcode.addComment("RAFT"); GCodePlanner gcodeLayer(gcode, config.moveSpeed, config.retractionMinimalDistance); gcode.setZ(config.raftBaseThickness + config.raftInterfaceThickness); gcode.setExtrusion(config.raftInterfaceThickness, config.filamentDiameter, config.filamentFlow); Polygons raftLines; generateLineInfill(storage.raftOutline, raftLines, config.raftInterfaceLinewidth, config.raftLineSpacing, config.infillOverlap, 90); gcodeLayer.addPolygonsByOptimizer(raftLines, &raftInterfaceConfig); gcodeLayer.writeGCode(false); } } int volumeIdx = 0; for(unsigned int layerNr=0; layerNr<totalLayers; layerNr++) { logProgress("export", layerNr+1, totalLayers); GCodePlanner gcodeLayer(gcode, config.moveSpeed, config.retractionMinimalDistance); gcode.addComment("LAYER:%d", layerNr); int32_t z = config.initialLayerThickness + layerNr * config.layerThickness; z += config.raftBaseThickness + config.raftInterfaceThickness; gcode.setZ(z); if (layerNr == 0) gcodeLayer.addPolygonsByOptimizer(storage.skirt, &skirtConfig); for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++) { if (volumeCnt > 0) volumeIdx = (volumeIdx + 1) % storage.volumes.size(); SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr]; gcodeLayer.setExtruder(volumeIdx); PathOptimizer partOrderOptimizer(gcode.getPositionXY()); for(unsigned int partNr=0; partNr<layer->parts.size(); partNr++) { partOrderOptimizer.addPolygon(layer->parts[partNr].insets[0][0]); } partOrderOptimizer.optimize(); for(unsigned int partCounter=0; partCounter<partOrderOptimizer.polyOrder.size(); partCounter++) { SliceLayerPart* part = &layer->parts[partOrderOptimizer.polyOrder[partCounter]]; if (config.enableCombing) gcodeLayer.setCombBoundary(&part->combBoundery); else gcodeLayer.setAlwaysRetract(true); gcodeLayer.forceRetract(); if (config.insetCount > 0) { for(int insetNr=part->insets.size()-1; insetNr>-1; insetNr--) { if (insetNr == 0) gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset0Config); else gcodeLayer.addPolygonsByOptimizer(part->insets[insetNr], &inset1Config); } } Polygons fillPolygons; int fillAngle = 45; if (layerNr & 1) fillAngle += 90; //int sparseSteps[1] = {config.extrusionWidth}; //generateConcentricInfill(part->skinOutline, fillPolygons, sparseSteps, 1); generateLineInfill(part->skinOutline, fillPolygons, config.extrusionWidth, config.extrusionWidth, config.infillOverlap, (part->bridgeAngle > -1) ? part->bridgeAngle : fillAngle); //int sparseSteps[2] = {config.extrusionWidth*5, config.extrusionWidth * 0.8}; //generateConcentricInfill(part->sparseOutline, fillPolygons, sparseSteps, 2); if (config.sparseInfillLineDistance > 0) { if (config.sparseInfillLineDistance > config.extrusionWidth * 4) { generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45); generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance * 2, config.infillOverlap, 45 + 90); } else { generateLineInfill(part->sparseOutline, fillPolygons, config.extrusionWidth, config.sparseInfillLineDistance, config.infillOverlap, fillAngle); } } gcodeLayer.addPolygonsByOptimizer(fillPolygons, &fillConfig); //After a layer part, make sure the nozzle is inside the comb boundary, so we do not retract on the perimeter. gcodeLayer.moveInsideCombBoundary(); } gcodeLayer.setCombBoundary(NULL); } if (config.supportAngle > -1) { if (config.supportExtruder > -1) gcodeLayer.setExtruder(config.supportExtruder); SupportPolyGenerator supportGenerator(storage.support, z, config.supportAngle, config.supportEverywhere > 0, config.supportXYDistance, config.supportZDistance); ClipperLib::Clipper supportClipper; supportClipper.AddPolygons(supportGenerator.polygons, ClipperLib::ptSubject); for(unsigned int volumeCnt = 0; volumeCnt < storage.volumes.size(); volumeCnt++) { SliceLayer* layer = &storage.volumes[volumeIdx].layers[layerNr]; Polygons polys; for(unsigned int n=0; n<layer->parts.size(); n++) for(unsigned int m=0; m<layer->parts[n].outline.size(); m++) polys.push_back(layer->parts[n].outline[m]); ClipperLib::OffsetPolygons(polys, polys, config.supportXYDistance, ClipperLib::jtSquare, 2, false); supportClipper.AddPolygons(polys, ClipperLib::ptClip); } supportClipper.Execute(ClipperLib::ctDifference, supportGenerator.polygons); Polygons supportLines; if (config.supportLineDistance > 0) { if (config.supportLineDistance > config.extrusionWidth * 4) { generateLineInfill(supportGenerator.polygons, supportLines, config.extrusionWidth, config.supportLineDistance*2, config.infillOverlap, 0); generateLineInfill(supportGenerator.polygons, supportLines, config.extrusionWidth, config.supportLineDistance*2, config.infillOverlap, 90); }else{ generateLineInfill(supportGenerator.polygons, supportLines, config.extrusionWidth, config.supportLineDistance, config.infillOverlap, (layerNr & 1) ? 0 : 90); } } gcodeLayer.addPolygonsByOptimizer(supportGenerator.polygons, &supportConfig); gcodeLayer.addPolygonsByOptimizer(supportLines, &supportConfig); } //Finish the layer by applying speed corrections for minimal layer times and slowdown for the initial layer. if (int(layerNr) < config.initialSpeedupLayers) { int n = config.initialSpeedupLayers; int layer0Factor = config.initialLayerSpeed * 100 / config.printSpeed; gcodeLayer.setExtrudeSpeedFactor((layer0Factor * (n - layerNr) + 100 * (layerNr)) / n); } gcodeLayer.forceMinimalLayerTime(config.minimalLayerTime, config.minimalFeedrate); if (layerNr == 0) gcode.setExtrusion(config.initialLayerThickness, config.filamentDiameter, config.filamentFlow); else gcode.setExtrusion(config.layerThickness, config.filamentDiameter, config.filamentFlow); if (int(layerNr) >= config.fanOnLayerNr) { int speed = config.fanSpeedMin; if (gcodeLayer.getExtrudeSpeedFactor() <= 50) { speed = config.fanSpeedMax; }else{ int n = gcodeLayer.getExtrudeSpeedFactor() - 50; speed = config.fanSpeedMin * n / 50 + config.fanSpeedMax * (50 - n) / 50; } gcode.addFanCommand(speed); }else{ gcode.addFanCommand(0); } gcodeLayer.writeGCode(config.coolHeadLift > 0); } /* support debug for(int32_t y=0; y<storage.support.gridHeight; y++) { for(int32_t x=0; x<storage.support.gridWidth; x++) { unsigned int n = x+y*storage.support.gridWidth; if (storage.support.grid[n].size() < 1) continue; int32_t z = storage.support.grid[n][0].z; gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0); gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, z), z); gcode.addMove(Point3(x * storage.support.gridScale + storage.support.gridOffset.X, y * storage.support.gridScale + storage.support.gridOffset.Y, 0), 0); } } //*/ log("Wrote layers in %5.2fs.\n", timeElapsed(t)); gcode.tellFileSize(); gcode.addFanCommand(0); logProgress("process", 1, 1); log("Total time elapsed %5.2fs.\n", timeElapsed(t,true)); //Store the object height for when we are printing multiple objects, as we need to clear every one of them when moving to the next position. maxObjectHeight = std::max(maxObjectHeight, storage.modelSize.z); }
void clear() { contour.points.clear(); holes.clear(); }