void Comb::calcMinMax() { int64_t minX_global = INT64_MAX; int64_t maxX_global = INT64_MIN; for(unsigned int boundary_poly_idx = 0; boundary_poly_idx < boundary.size(); boundary_poly_idx++) { minX[boundary_poly_idx] = INT64_MAX; maxX[boundary_poly_idx] = INT64_MIN; PolygonRef poly = boundary[boundary_poly_idx]; Point p0 = transformation_matrix.apply(poly.back()); for(unsigned int boundary_point_idx = 0; boundary_point_idx < poly.size(); boundary_point_idx++) { Point p1 = transformation_matrix.apply(poly[boundary_point_idx]); if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y)) { int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); if (x >= transformed_startPoint.X && x <= transformed_endPoint.X) { if (x < minX[boundary_poly_idx]) { minX[boundary_poly_idx] = x; minIdx[boundary_poly_idx] = boundary_point_idx; } if (x > maxX[boundary_poly_idx]) { maxX[boundary_poly_idx] = x; maxIdx[boundary_poly_idx] = boundary_point_idx; } if (x < minX_global) { minX_global = x; minIdx_global = boundary_poly_idx; } if (x > maxX_global) { maxX_global = x; maxIdx_global = boundary_poly_idx; } } } p0 = p1; } } }
bool polygonCollidesWithlineSegment(PolygonRef poly, Point& transformed_startPoint, Point& transformed_endPoint, PointMatrix transformation_matrix) { Point p0 = transformation_matrix.apply(poly.back()); for(Point p1_ : poly) { Point p1 = transformation_matrix.apply(p1_); if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y)) { int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); if (x > transformed_startPoint.X && x < transformed_endPoint.X) return true; } p0 = p1; } return false; }
void ListPolyIt::convertPolygonToList(PolygonRef poly, ListPolygon& result) { #ifdef DEBUG Point last = poly.back(); #endif // DEBUG for (Point& p : poly) { result.push_back(p); #ifdef DEBUG // usually polygons shouldn't have such degenerate verts // in PolygonProximityLinker (where this function is (also) used) it is // required to not have degenerate verts, because verts are mapped // to links, but if two different verts are at the same place the mapping fails. assert(p != last); last = p; #endif // DEBUG } }
int PathOrderOptimizer::getClosestPointInPolygon(Point prev_point, int poly_idx) { PolygonRef poly = polygons[poly_idx]; int best_point_idx = -1; float best_point_score = std::numeric_limits<float>::infinity(); Point p0 = poly.back(); for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point& p1 = poly[point_idx]; Point& p2 = poly[(point_idx + 1) % poly.size()]; int64_t dist = vSize2(p1 - prev_point); float is_on_inside_corner_score = -LinearAlg2D::getAngleLeft(p0, p1, p2) / M_PI * 5000 * 5000; // prefer inside corners // this score is in the order of 5 mm if (dist + is_on_inside_corner_score < best_point_score) { best_point_idx = point_idx; best_point_score = dist + is_on_inside_corner_score; } p0 = p1; } return best_point_idx; }
void LinePolygonsCrossings::calcScanlineCrossings() { min_crossing_idx = NO_INDEX; max_crossing_idx = NO_INDEX; for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++) { PolyCrossings minMax(poly_idx); PolygonRef poly = boundary[poly_idx]; Point p0 = transformation_matrix.apply(poly.back()); for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point p1 = transformation_matrix.apply(poly[point_idx]); if ((p0.Y > transformed_startPoint.Y && p1.Y < transformed_startPoint.Y) || (p1.Y > transformed_startPoint.Y && p0.Y < transformed_startPoint.Y)) { int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); if (x >= transformed_startPoint.X && x <= transformed_endPoint.X) { if (x < minMax.min.x) { minMax.min.x = x; minMax.min.point_idx = point_idx; } if (x > minMax.max.x) { minMax.max.x = x; minMax.max.point_idx = point_idx; } } } p0 = p1; } if (minMax.min.point_idx != NO_INDEX) { // then also max.point_idx != -1 if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); } if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); } crossings.push_back(minMax); } } }
/* * 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 * * rough explanation of the 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]) * (see infill/ZigzagConnectorProcessor.h for actual implementation details) * * * we call the areas between two consecutive scanlines a 'scansegment'. * Scansegment x is the area between scanline x and scanline x+1 * Edit: the term scansegment is wrong, since I call a boundary segment leaving from an even scanline to the left as belonging to an even scansegment, * while I also call a boundary segment leaving from an even scanline toward the right as belonging to an even scansegment. */ void Infill::generateLinearBasedInfill(const int outline_offset, Polygons& result, const int line_distance, const PointMatrix& rotation_matrix, ZigzagConnectorProcessor& zigzag_connector_processor, const bool connected_zigzags, int64_t extra_shift) { if (line_distance == 0) { return; } if (in_outline.size() == 0) { return; } int shift = extra_shift + this->shift; Polygons outline; if (outline_offset != 0) { outline = in_outline.offset(outline_offset); if (perimeter_gaps) { perimeter_gaps->add(in_outline.difference(outline.offset(infill_line_width / 2 + perimeter_gaps_extra_offset))); } } else { outline = in_outline; } outline = outline.offset(infill_overlap); if (outline.size() == 0) { return; } outline.applyMatrix(rotation_matrix); if (shift < 0) { shift = line_distance - (-shift) % line_distance; } else { shift = shift % line_distance; } AABB boundary(outline); int scanline_min_idx = computeScanSegmentIdx(boundary.min.X - shift, line_distance); int line_count = computeScanSegmentIdx(boundary.max.X - shift, line_distance) + 1 - scanline_min_idx; std::vector<std::vector<int64_t> > cut_list; // mapping from scanline to all intersections with polygon segments for(int scanline_idx = 0; scanline_idx < line_count; scanline_idx++) { cut_list.push_back(std::vector<int64_t>()); } for(unsigned int poly_idx = 0; poly_idx < outline.size(); poly_idx++) { PolygonRef poly = outline[poly_idx]; Point p0 = poly.back(); zigzag_connector_processor.registerVertex(p0); // always adds the first point to ZigzagConnectorProcessorEndPieces::first_zigzag_connector when using a zigzag infill type for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) { Point p1 = poly[point_idx]; if (p1.X == p0.X) { zigzag_connector_processor.registerVertex(p1); // TODO: how to make sure it always adds the shortest line? (in order to prevent overlap with the zigzag connectors) // note: this is already a problem for normal infill, but hasn't really cothered anyone so far. p0 = p1; continue; } int scanline_idx0; int scanline_idx1; // this way of handling the indices takes care of the case where a boundary line segment ends exactly on a scanline: // in case the next segment moves back from that scanline either 2 or 0 scanline-boundary intersections are created // otherwise only 1 will be created, counting as an actual intersection int direction = 1; if (p0.X < p1.X) { scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance); // -1 cause the vertex point is handled in the next segment (or not in the case which looks like >) } else { direction = -1; scanline_idx0 = computeScanSegmentIdx(p0.X - shift, line_distance); // -1 cause the vertex point is handled in the previous segment (or not in the case which looks like >) scanline_idx1 = computeScanSegmentIdx(p1.X - shift, line_distance) + 1; // + 1 cause we don't cross the scanline of the first scan segment } for(int scanline_idx = scanline_idx0; scanline_idx != scanline_idx1 + direction; scanline_idx += direction) { int x = scanline_idx * line_distance + shift; int y = p1.Y + (p0.Y - p1.Y) * (x - p1.X) / (p0.X - p1.X); assert(scanline_idx - scanline_min_idx >= 0 && scanline_idx - scanline_min_idx < int(cut_list.size()) && "reading infill cutlist index out of bounds!"); cut_list[scanline_idx - scanline_min_idx].push_back(y); Point scanline_linesegment_intersection(x, y); zigzag_connector_processor.registerScanlineSegmentIntersection(scanline_linesegment_intersection, scanline_idx % 2 == 0); } zigzag_connector_processor.registerVertex(p1); p0 = p1; } zigzag_connector_processor.registerPolyFinished(); } if (cut_list.size() == 0) { return; } if (connected_zigzags && cut_list.size() == 1 && cut_list[0].size() <= 2) { return; // don't add connection if boundary already contains whole outline! } addLineInfill(result, rotation_matrix, scanline_min_idx, line_distance, boundary, cut_list, shift); }
unsigned int Polygons::findInside(Point p, bool border_result) { Polygons& thiss = *this; if (size() < 1) { return false; } int64_t min_x[size()]; std::fill_n(min_x, size(), std::numeric_limits<int64_t>::max()); // initialize with int.max int crossings[size()]; std::fill_n(crossings, size(), 0); // initialize with zeros for (unsigned int poly_idx = 0; poly_idx < size(); poly_idx++) { PolygonRef poly = thiss[poly_idx]; Point p0 = poly.back(); for(Point& p1 : poly) { short comp = pointLiesOnTheRightOfLine(p, p0, p1); if (comp == 1) { crossings[poly_idx]++; int64_t x; if (p1.Y == p0.Y) { x = p0.X; } else { x = p0.X + (p1.X-p0.X) * (p.Y-p0.Y) / (p1.Y-p0.Y); } if (x < min_x[poly_idx]) { min_x[poly_idx] = x; } } else if (border_result && comp == 0) { return poly_idx; } p0 = p1; } } int64_t min_x_uneven = std::numeric_limits<int64_t>::max(); unsigned int ret = NO_INDEX; unsigned int n_unevens = 0; for (unsigned int array_idx = 0; array_idx < size(); array_idx++) { if (crossings[array_idx] % 2 == 1) { n_unevens++; if (min_x[array_idx] < min_x_uneven) { min_x_uneven = min_x[array_idx]; ret = array_idx; } } } if (n_unevens % 2 == 0) { ret = NO_INDEX; } return ret; }
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; }