Arquivos
CuraEngine/src/wallOverlap.cpp
T
Ghostkeeper 635ef25030 Optimize if-statement structure
Also more readable, in my opinion. It's just a bunch of cases and a base case, with lazy evaluation in between.

Contributes to issue CURA-1911.
2016-08-17 16:43:09 +02:00

228 linhas
8.7 KiB
C++

/** Copyright (C) 2016 Ultimaker - Released under terms of the AGPLv3 License */
#include "wallOverlap.h"
#include <cmath> // isfinite
#include <sstream>
#include "utils/AABB.h" // for debug output svg html
#include "utils/SVG.h"
namespace cura
{
WallOverlapComputation::WallOverlapComputation(Polygons& polygons, int line_width)
: overlap_linker(polygons, line_width)
, line_width(line_width)
{
}
float WallOverlapComputation::getFlow(Point& from, Point& to)
{
using Point2LinkIt = PolygonProximityLinker::Point2Link::iterator;
if (!overlap_linker.isLinked(from))
{ // [from] is not linked
return 1;
}
const std::pair<Point2LinkIt, Point2LinkIt> to_links = overlap_linker.getLinks(to);
if (to_links.first == to_links.second)
{ // [to] is not linked
return 1;
}
int64_t overlap_area = 0;
// note that we don't need to loop over all from_links, because they are handled in the previous getFlow(.) call (or in the very last)
for (Point2LinkIt to_link_it = to_links.first; to_link_it != to_links.second; ++to_link_it)
{
const ProximityPointLink& to_link = to_link_it->second;
ListPolyIt to_it = to_link.a;
ListPolyIt to_other_it = to_link.b;
if (to_link.a.p() != to)
{
assert(to_link.b.p() == to && "Either part of the link should be the point in the link!");
std::swap(to_it, to_other_it);
}
ListPolyIt from_it = to_it.prev();
if (from_it.p() != from)
{
logWarning("Polygon has multiple verts at the same place: (%lli, %lli); PolygonProximityLinker fails in such a case!\n", from.X, from.Y);
}
ListPolyIt to_other_next_it = to_other_it.next(); // move towards [from]; the lines on the other side move in the other direction
// to from
// o<--o<--T<--F
// | : :
// v : :
// o-->o-->o-->o
// , ,
// ; to_other_next
// to other
bool are_in_same_general_direction = dot(from - to, to_other_it.p() - to_other_next_it.p()) > 0;
// handle multiple points linked to [to]
// o<<<T<<<F
// / |
// / |
// o>>>o>>>o
// , ,
// ; to other next
// to other
if (!are_in_same_general_direction)
{
overlap_area += handlePotentialOverlap(to_it, to_it, to_link, to_other_next_it, to_other_it);
}
// handle multiple points linked to [to_other]
// o<<<T<<<F
// | /
// | /
// o>>>o>>>o
bool all_are_in_same_general_direction = are_in_same_general_direction && dot(from - to, to_other_it.prev().p() - to_other_it.p()) > 0;
if (!all_are_in_same_general_direction)
{
overlap_area += handlePotentialOverlap(from_it, to_it, to_link, to_other_it, to_other_it);
}
// handle normal case where the segment from-to overlaps with another segment
// o<<<T<<<F
// | |
// | |
// o>>>o>>>o
// , ,
// ; to other next
// to other
if (!are_in_same_general_direction)
{
overlap_area += handlePotentialOverlap(from_it, to_it, to_link, to_other_next_it, to_other_it);
}
}
int64_t normal_area = vSize(from - to) * line_width;
float ratio = float(normal_area - overlap_area) / normal_area;
// clamp the ratio because overlap compensation might be faulty because
// WallOverlapComputation::getApproxOverlapArea only gives roughly accurate results
return std::min(1.0f, std::max(0.0f, ratio));
}
int64_t WallOverlapComputation::handlePotentialOverlap(const ListPolyIt from_it, const ListPolyIt to_it, const ProximityPointLink& to_link, const ListPolyIt from_other_it, const ListPolyIt to_other_it)
{
if (from_it == to_other_it && from_it == from_other_it)
{ // don't compute overlap with a line and itself
return 0;
}
const ProximityPointLink* from_link = overlap_linker.getLink(from_it, from_other_it);
if (!from_link)
{
return 0;
}
if (!getIsPassed(to_link, *from_link))
{ // check whether the segment is already passed
setIsPassed(to_link, *from_link);
return 0;
}
return getApproxOverlapArea(from_it.p(), to_it.p(), to_link.dist, to_other_it.p(), from_other_it.p(), from_link->dist);
}
int64_t WallOverlapComputation::getApproxOverlapArea(const Point from, const Point to, const int64_t to_dist, const Point other_from, const Point other_to, const int64_t from_dist)
{
const int64_t overlap_width_2 = line_width * 2 - from_dist - to_dist; //Twice the width of the overlap area, perpendicular to the lines.
// check whether the line segment overlaps with the point if one of the line segments is just a point
if (from == to)
{
if (LinearAlg2D::pointIsProjectedBeyondLine(from, other_from, other_to) != 0)
{
return 0;
}
const int64_t overlap_length_2 = vSize(other_to - other_from); //Twice the length of the overlap area, alongside the lines.
return overlap_length_2 * overlap_width_2 / 4; //Area = width * height.
}
if (other_from == other_to)
{
if (LinearAlg2D::pointIsProjectedBeyondLine(other_from, from, to) != 0)
{
return 0;
}
const int64_t overlap_length_2 = vSize(from - to); //Twice the length of the overlap area, alongside the lines.
return overlap_length_2 * overlap_width_2 / 4; //Area = width * height.
}
short from_rel = LinearAlg2D::pointIsProjectedBeyondLine(from, other_from, other_to);
short to_rel = LinearAlg2D::pointIsProjectedBeyondLine(to, other_from, other_to);
short other_from_rel = LinearAlg2D::pointIsProjectedBeyondLine(other_from, from, to);
short other_to_rel = LinearAlg2D::pointIsProjectedBeyondLine(other_to, from, to);
if (from_rel != 0 && to_rel == from_rel && other_from_rel != 0 && other_to_rel == other_from_rel)
{
// both segments project fully beyond or before each other
// for example: or:
// O<------O . O------>O
// : : \_
// ' O------->O O------>O
return 0;
}
if (from_rel != 0 && from_rel == other_from_rel && to_rel == 0 && other_to_rel == 0)
{
// only ends of line segments overlap
//
// to_proj
// ^^^^^
// O<--+----O
// : :
// O-----+-->O
// ,,,,,
// other_to_proj
const Point other_vec = other_from - other_to;
const int64_t to_proj = dot(to - other_to, other_vec) / vSize(other_vec);
const Point vec = from - to;
const int64_t other_to_proj = dot(other_to - to, vec) / vSize(vec);
const int64_t overlap_length_2 = to_proj + other_to_proj; //Twice the length of the overlap area, alongside the lines.
return overlap_length_2 * overlap_width_2 / 4; //Area = width * height.
}
if (to_rel != 0 && to_rel == other_to_rel && from_rel == 0 && other_from_rel == 0)
{
// only beginnings of line segments overlap
//
// from_proj
// ^^^^^
// O<---+---O
// : :
// O---+---->O
// ,,,,,
// other_from_proj
const Point other_vec = other_to - other_from;
const int64_t from_proj = dot(from - other_from, other_vec) / vSize(other_vec);
const Point vec = to - from;
const int64_t other_from_proj = dot(other_from - from, vec) / vSize(vec);
const int64_t overlap_length_2 = from_proj + other_from_proj; //Twice the length of the overlap area, alongside the lines.
return overlap_length_2 * overlap_width_2 / 4; //Area = width * height.
}
//More complex case.
const Point from_middle = other_to + from; // don't divide by two just yet
const Point to_middle = other_from + to; // don't divide by two just yet
const int64_t overlap_length_2 = vSize(from_middle - to_middle); //(An approximation of) twice the length of the overlap area, alongside the lines.
return overlap_length_2 * overlap_width_2 / 4; //Area = width * height.
}
bool WallOverlapComputation::getIsPassed(const ProximityPointLink& link_a, const ProximityPointLink& link_b)
{
return passed_links.find(SymmetricPair<ProximityPointLink>(link_a, link_b)) != passed_links.end();
}
void WallOverlapComputation::setIsPassed(const ProximityPointLink& link_a, const ProximityPointLink& link_b)
{
passed_links.emplace(link_a, link_b);
}
}//namespace cura