mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 08:25:40 +02:00
Upgrade Wagyu to version 0.5.0
This commit is contained in:
@@ -61,9 +61,7 @@ std::string output_edges(active_bound_list<T> const& bnds) {
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
bool is_even_odd_fill_type(bound<T> const& bound,
|
||||
fill_type subject_fill_type,
|
||||
fill_type clip_fill_type) {
|
||||
bool is_even_odd_fill_type(bound<T> const& bound, fill_type subject_fill_type, fill_type clip_fill_type) {
|
||||
if (bound.poly_type == polygon_type_subject) {
|
||||
return subject_fill_type == fill_type_even_odd;
|
||||
} else {
|
||||
@@ -72,9 +70,7 @@ bool is_even_odd_fill_type(bound<T> const& bound,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is_even_odd_alt_fill_type(bound<T> const& bound,
|
||||
fill_type subject_fill_type,
|
||||
fill_type clip_fill_type) {
|
||||
bool is_even_odd_alt_fill_type(bound<T> const& bound, fill_type subject_fill_type, fill_type clip_fill_type) {
|
||||
if (bound.poly_type == polygon_type_subject) {
|
||||
return clip_fill_type == fill_type_even_odd;
|
||||
} else {
|
||||
@@ -94,11 +90,11 @@ struct bound_insert_location {
|
||||
auto const& bound1 = *b;
|
||||
if (values_are_equal(bound2.current_x, bound1.current_x)) {
|
||||
if (bound2.current_edge->top.y > bound1.current_edge->top.y) {
|
||||
return static_cast<double>(bound2.current_edge->top.x) <
|
||||
get_current_x(*(bound1.current_edge), bound2.current_edge->top.y);
|
||||
return less_than(static_cast<double>(bound2.current_edge->top.x),
|
||||
get_current_x(*(bound1.current_edge), bound2.current_edge->top.y));
|
||||
} else {
|
||||
return static_cast<double>(bound1.current_edge->top.x) >
|
||||
get_current_x(*(bound2.current_edge), bound1.current_edge->top.y);
|
||||
return greater_than(static_cast<double>(bound1.current_edge->top.x),
|
||||
get_current_x(*(bound2.current_edge), bound1.current_edge->top.y));
|
||||
}
|
||||
} else {
|
||||
return bound2.current_x < bound1.current_x;
|
||||
@@ -107,42 +103,44 @@ struct bound_insert_location {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
active_bound_list_itr<T>
|
||||
insert_bound_into_ABL(bound<T>& left, bound<T>& right, active_bound_list<T>& active_bounds) {
|
||||
active_bound_list_itr<T> insert_bound_into_ABL(bound<T>& left, bound<T>& right, active_bound_list<T>& active_bounds) {
|
||||
|
||||
auto itr =
|
||||
std::find_if(active_bounds.begin(), active_bounds.end(), bound_insert_location<T>(left));
|
||||
auto itr = std::find_if(active_bounds.begin(), active_bounds.end(), bound_insert_location<T>(left));
|
||||
#ifdef GCC_MISSING_VECTOR_RANGE_INSERT
|
||||
itr = active_bounds.insert(itr, &right);
|
||||
return active_bounds.insert(itr, &left);
|
||||
#else
|
||||
return active_bounds.insert(itr, { &left, &right });
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool is_maxima(bound<T>& bnd, T y) {
|
||||
inline bool is_maxima(bound<T> const& bnd, T y) {
|
||||
return bnd.next_edge == bnd.edges.end() && bnd.current_edge->top.y == y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool is_maxima(active_bound_list_itr<T>& bnd, T y) {
|
||||
inline bool is_maxima(active_bound_list_itr<T> const& bnd, T y) {
|
||||
return is_maxima(*(*bnd), y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool is_intermediate(bound<T>& bnd, T y) {
|
||||
inline bool is_intermediate(bound<T> const& bnd, T y) {
|
||||
return bnd.next_edge != bnd.edges.end() && bnd.current_edge->top.y == y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool is_intermediate(active_bound_list_itr<T>& bnd, T y) {
|
||||
inline bool is_intermediate(active_bound_list_itr<T> const& bnd, T y) {
|
||||
return is_intermediate(*(*bnd), y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool current_edge_is_horizontal(active_bound_list_itr<T>& bnd) {
|
||||
inline bool current_edge_is_horizontal(active_bound_list_itr<T> const& bnd) {
|
||||
return is_horizontal(*((*bnd)->current_edge));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool next_edge_is_horizontal(active_bound_list_itr<T>& bnd) {
|
||||
inline bool next_edge_is_horizontal(active_bound_list_itr<T> const& bnd) {
|
||||
return is_horizontal(*((*bnd)->next_edge));
|
||||
}
|
||||
|
||||
@@ -154,20 +152,19 @@ void next_edge_in_bound(bound<T>& bnd, scanbeam_list<T>& scanbeam) {
|
||||
++(bnd.next_edge);
|
||||
bnd.current_x = static_cast<double>(current_edge->bot.x);
|
||||
if (!is_horizontal<T>(*current_edge)) {
|
||||
scanbeam.push_back(current_edge->top.y);
|
||||
insert_sorted_scanbeam(scanbeam, current_edge->top.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
active_bound_list_itr<T> get_maxima_pair(active_bound_list_itr<T> const& bnd,
|
||||
active_bound_list<T>& active_bounds) {
|
||||
active_bound_list_itr<T> get_maxima_pair(active_bound_list_itr<T> bnd, active_bound_list<T>& active_bounds) {
|
||||
bound_ptr<T> maximum = (*bnd)->maximum_bound;
|
||||
return std::find(active_bounds.begin(), active_bounds.end(), maximum);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_winding_count(active_bound_list_itr<T>& bnd_itr,
|
||||
void set_winding_count(active_bound_list_itr<T> bnd_itr,
|
||||
active_bound_list<T>& active_bounds,
|
||||
fill_type subject_fill_type,
|
||||
fill_type clip_fill_type) {
|
||||
@@ -181,8 +178,7 @@ void set_winding_count(active_bound_list_itr<T>& bnd_itr,
|
||||
|
||||
// find the edge of the same polytype that immediately preceeds 'edge' in
|
||||
// AEL
|
||||
while (rev_bnd_itr != active_bounds.rend() &&
|
||||
(*rev_bnd_itr)->poly_type != (*bnd_itr)->poly_type) {
|
||||
while (rev_bnd_itr != active_bounds.rend() && (*rev_bnd_itr)->poly_type != (*bnd_itr)->poly_type) {
|
||||
++rev_bnd_itr;
|
||||
}
|
||||
if (rev_bnd_itr == active_bounds.rend()) {
|
||||
@@ -204,8 +200,7 @@ void set_winding_count(active_bound_list_itr<T>& bnd_itr,
|
||||
(*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count;
|
||||
} else {
|
||||
// otherwise continue to 'decrease' WC ...
|
||||
(*bnd_itr)->winding_count =
|
||||
(*rev_bnd_itr)->winding_count + (*bnd_itr)->winding_delta;
|
||||
(*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count + (*bnd_itr)->winding_delta;
|
||||
}
|
||||
} else {
|
||||
// now outside all polys of same polytype so set own WC ...
|
||||
@@ -219,8 +214,7 @@ void set_winding_count(active_bound_list_itr<T>& bnd_itr,
|
||||
(*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count;
|
||||
} else {
|
||||
// otherwise add to WC ...
|
||||
(*bnd_itr)->winding_count =
|
||||
(*rev_bnd_itr)->winding_count + (*bnd_itr)->winding_delta;
|
||||
(*bnd_itr)->winding_count = (*rev_bnd_itr)->winding_count + (*bnd_itr)->winding_delta;
|
||||
}
|
||||
}
|
||||
(*bnd_itr)->winding_count2 = (*rev_bnd_itr)->winding_count2;
|
||||
@@ -244,10 +238,7 @@ void set_winding_count(active_bound_list_itr<T>& bnd_itr,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is_contributing(bound<T> const& bnd,
|
||||
clip_type cliptype,
|
||||
fill_type subject_fill_type,
|
||||
fill_type clip_fill_type) {
|
||||
bool is_contributing(bound<T> const& bnd, clip_type cliptype, fill_type subject_fill_type, fill_type clip_fill_type) {
|
||||
fill_type pft = subject_fill_type;
|
||||
fill_type pft2 = clip_fill_type;
|
||||
if (bnd.poly_type != polygon_type_subject) {
|
||||
@@ -350,15 +341,14 @@ void insert_lm_left_and_right_bound(bound<T>& left_bound,
|
||||
(*rb_abl_itr)->winding_count = (*lb_abl_itr)->winding_count;
|
||||
(*rb_abl_itr)->winding_count2 = (*lb_abl_itr)->winding_count2;
|
||||
if (is_contributing(left_bound, cliptype, subject_fill_type, clip_fill_type)) {
|
||||
add_local_minimum_point(*(*lb_abl_itr), *(*rb_abl_itr), active_bounds,
|
||||
(*lb_abl_itr)->current_edge->bot, rings);
|
||||
add_local_minimum_point(*(*lb_abl_itr), *(*rb_abl_itr), active_bounds, (*lb_abl_itr)->current_edge->bot, rings);
|
||||
}
|
||||
|
||||
// Add top of edges to scanbeam
|
||||
scanbeam.push_back((*lb_abl_itr)->current_edge->top.y);
|
||||
insert_sorted_scanbeam(scanbeam, (*lb_abl_itr)->current_edge->top.y);
|
||||
|
||||
if (!current_edge_is_horizontal<T>(rb_abl_itr)) {
|
||||
scanbeam.push_back((*rb_abl_itr)->current_edge->top.y);
|
||||
insert_sorted_scanbeam(scanbeam, (*rb_abl_itr)->current_edge->top.y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,8 +366,8 @@ void insert_local_minima_into_ABL(T const bot_y,
|
||||
initialize_lm<T>(current_lm);
|
||||
auto& left_bound = (*current_lm)->left_bound;
|
||||
auto& right_bound = (*current_lm)->right_bound;
|
||||
insert_lm_left_and_right_bound(left_bound, right_bound, active_bounds, rings, scanbeam,
|
||||
cliptype, subject_fill_type, clip_fill_type);
|
||||
insert_lm_left_and_right_bound(left_bound, right_bound, active_bounds, rings, scanbeam, cliptype,
|
||||
subject_fill_type, clip_fill_type);
|
||||
++current_lm;
|
||||
}
|
||||
}
|
||||
@@ -392,16 +382,15 @@ void insert_horizontal_local_minima_into_ABL(T const top_y,
|
||||
clip_type cliptype,
|
||||
fill_type subject_fill_type,
|
||||
fill_type clip_fill_type) {
|
||||
while (current_lm != minima_sorted.end() && top_y == (*current_lm)->y &&
|
||||
(*current_lm)->minimum_has_horizontal) {
|
||||
while (current_lm != minima_sorted.end() && top_y == (*current_lm)->y && (*current_lm)->minimum_has_horizontal) {
|
||||
initialize_lm<T>(current_lm);
|
||||
auto& left_bound = (*current_lm)->left_bound;
|
||||
auto& right_bound = (*current_lm)->right_bound;
|
||||
insert_lm_left_and_right_bound(left_bound, right_bound, active_bounds, rings, scanbeam,
|
||||
cliptype, subject_fill_type, clip_fill_type);
|
||||
insert_lm_left_and_right_bound(left_bound, right_bound, active_bounds, rings, scanbeam, cliptype,
|
||||
subject_fill_type, clip_fill_type);
|
||||
++current_lm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
// Copyright 2005, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
|
||||
//
|
||||
// The Google C++ Testing Framework (Google Test)
|
||||
|
||||
// This template class serves as a compile-time function from size to
|
||||
// type. It maps a size in bytes to a primitive type with that
|
||||
// size. e.g.
|
||||
//
|
||||
// TypeWithSize<4>::UInt
|
||||
//
|
||||
// is typedef-ed to be unsigned int (unsigned integer made up of 4
|
||||
// bytes).
|
||||
//
|
||||
// Such functionality should belong to STL, but I cannot find it
|
||||
// there.
|
||||
//
|
||||
// Google Test uses this class in the implementation of floating-point
|
||||
// comparison.
|
||||
//
|
||||
// For now it only handles UInt (unsigned int) as that's all Google Test
|
||||
// needs. Other types can be easily added in the future if need
|
||||
// arises.
|
||||
namespace mapbox {
|
||||
namespace geometry {
|
||||
namespace wagyu {
|
||||
namespace util {
|
||||
|
||||
template <size_t size>
|
||||
class TypeWithSize {
|
||||
public:
|
||||
// This prevents the user from using TypeWithSize<N> with incorrect
|
||||
// values of N.
|
||||
typedef void UInt;
|
||||
};
|
||||
|
||||
// The specialization for size 4.
|
||||
template <>
|
||||
class TypeWithSize<4> {
|
||||
public:
|
||||
// unsigned int has size 4 in both gcc and MSVC.
|
||||
//
|
||||
// As base/basictypes.h doesn't compile on Windows, we cannot use
|
||||
// uint32, uint64, and etc here.
|
||||
typedef int Int;
|
||||
typedef unsigned int UInt;
|
||||
};
|
||||
|
||||
// The specialization for size 8.
|
||||
template <>
|
||||
class TypeWithSize<8> {
|
||||
public:
|
||||
#if GTEST_OS_WINDOWS
|
||||
typedef __int64 Int;
|
||||
typedef unsigned __int64 UInt;
|
||||
#else
|
||||
typedef long long Int; // NOLINT
|
||||
typedef unsigned long long UInt; // NOLINT
|
||||
#endif // GTEST_OS_WINDOWS
|
||||
};
|
||||
|
||||
// This template class represents an IEEE floating-point number
|
||||
// (either single-precision or double-precision, depending on the
|
||||
// template parameters).
|
||||
//
|
||||
// The purpose of this class is to do more sophisticated number
|
||||
// comparison. (Due to round-off error, etc, it's very unlikely that
|
||||
// two floating-points will be equal exactly. Hence a naive
|
||||
// comparison by the == operation often doesn't work.)
|
||||
//
|
||||
// Format of IEEE floating-point:
|
||||
//
|
||||
// The most-significant bit being the leftmost, an IEEE
|
||||
// floating-point looks like
|
||||
//
|
||||
// sign_bit exponent_bits fraction_bits
|
||||
//
|
||||
// Here, sign_bit is a single bit that designates the sign of the
|
||||
// number.
|
||||
//
|
||||
// For float, there are 8 exponent bits and 23 fraction bits.
|
||||
//
|
||||
// For double, there are 11 exponent bits and 52 fraction bits.
|
||||
//
|
||||
// More details can be found at
|
||||
// http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
|
||||
//
|
||||
// Template parameter:
|
||||
//
|
||||
// RawType: the raw floating-point type (either float or double)
|
||||
template <typename RawType>
|
||||
class FloatingPoint {
|
||||
public:
|
||||
// Defines the unsigned integer type that has the same size as the
|
||||
// floating point number.
|
||||
typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;
|
||||
|
||||
// Constants.
|
||||
|
||||
// # of bits in a number.
|
||||
static const size_t kBitCount = 8 * sizeof(RawType);
|
||||
|
||||
// # of fraction bits in a number.
|
||||
static const size_t kFractionBitCount = std::numeric_limits<RawType>::digits - 1;
|
||||
|
||||
// # of exponent bits in a number.
|
||||
static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
|
||||
|
||||
// The mask for the sign bit.
|
||||
static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
|
||||
|
||||
// The mask for the fraction bits.
|
||||
static const Bits kFractionBitMask = ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
|
||||
|
||||
// The mask for the exponent bits.
|
||||
static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
|
||||
|
||||
// How many ULP's (Units in the Last Place) we want to tolerate when
|
||||
// comparing two numbers. The larger the value, the more error we
|
||||
// allow. A 0 value means that two numbers must be exactly the same
|
||||
// to be considered equal.
|
||||
//
|
||||
// The maximum error of a single floating-point operation is 0.5
|
||||
// units in the last place. On Intel CPU's, all floating-point
|
||||
// calculations are done with 80-bit precision, while double has 64
|
||||
// bits. Therefore, 4 should be enough for ordinary use.
|
||||
//
|
||||
// See the following article for more details on ULP:
|
||||
// http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm.
|
||||
static const size_t kMaxUlps = 4;
|
||||
|
||||
// Constructs a FloatingPoint from a raw floating-point number.
|
||||
//
|
||||
// On an Intel CPU, passing a non-normalized NAN (Not a Number)
|
||||
// around may change its bits, although the new value is guaranteed
|
||||
// to be also a NAN. Therefore, don't expect this constructor to
|
||||
// preserve the bits in x when x is a NAN.
|
||||
explicit FloatingPoint(const RawType& x) : u_(x) {
|
||||
}
|
||||
|
||||
// Static methods
|
||||
|
||||
// Reinterprets a bit pattern as a floating-point number.
|
||||
//
|
||||
// This function is needed to test the AlmostEquals() method.
|
||||
static RawType ReinterpretBits(const Bits bits) {
|
||||
FloatingPoint fp(0);
|
||||
fp.u_.bits_ = bits;
|
||||
return fp.u_.value_;
|
||||
}
|
||||
|
||||
// Returns the floating-point number that represent positive infinity.
|
||||
static RawType Infinity() {
|
||||
return ReinterpretBits(kExponentBitMask);
|
||||
}
|
||||
|
||||
// Non-static methods
|
||||
|
||||
// Returns the bits that represents this number.
|
||||
const Bits& bits() const {
|
||||
return u_.bits_;
|
||||
}
|
||||
|
||||
// Returns the exponent bits of this number.
|
||||
Bits exponent_bits() const {
|
||||
return kExponentBitMask & u_.bits_;
|
||||
}
|
||||
|
||||
// Returns the fraction bits of this number.
|
||||
Bits fraction_bits() const {
|
||||
return kFractionBitMask & u_.bits_;
|
||||
}
|
||||
|
||||
// Returns the sign bit of this number.
|
||||
Bits sign_bit() const {
|
||||
return kSignBitMask & u_.bits_;
|
||||
}
|
||||
|
||||
// Returns true iff this is NAN (not a number).
|
||||
bool is_nan() const {
|
||||
// It's a NAN if the exponent bits are all ones and the fraction
|
||||
// bits are not entirely zeros.
|
||||
return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
|
||||
}
|
||||
|
||||
// Returns true iff this number is at most kMaxUlps ULP's away from
|
||||
// rhs. In particular, this function:
|
||||
//
|
||||
// - returns false if either number is (or both are) NAN.
|
||||
// - treats really large numbers as almost equal to infinity.
|
||||
// - thinks +0.0 and -0.0 are 0 DLP's apart.
|
||||
bool AlmostEquals(const FloatingPoint& rhs) const {
|
||||
// The IEEE standard says that any comparison operation involving
|
||||
// a NAN must return false.
|
||||
if (is_nan() || rhs.is_nan())
|
||||
return false;
|
||||
|
||||
return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) <= kMaxUlps;
|
||||
}
|
||||
|
||||
private:
|
||||
// The data type used to store the actual floating-point number.
|
||||
union FloatingPointUnion {
|
||||
explicit FloatingPointUnion(RawType val) : value_(val) {
|
||||
}
|
||||
RawType value_; // The raw floating-point number.
|
||||
Bits bits_; // The bits that represent the number.
|
||||
};
|
||||
|
||||
// Converts an integer from the sign-and-magnitude representation to
|
||||
// the biased representation. More precisely, let N be 2 to the
|
||||
// power of (kBitCount - 1), an integer x is represented by the
|
||||
// unsigned number x + N.
|
||||
//
|
||||
// For instance,
|
||||
//
|
||||
// -N + 1 (the most negative number representable using
|
||||
// sign-and-magnitude) is represented by 1;
|
||||
// 0 is represented by N; and
|
||||
// N - 1 (the biggest number representable using
|
||||
// sign-and-magnitude) is represented by 2N - 1.
|
||||
//
|
||||
// Read http://en.wikipedia.org/wiki/Signed_number_representations
|
||||
// for more details on signed number representations.
|
||||
static Bits SignAndMagnitudeToBiased(const Bits& sam) {
|
||||
if (kSignBitMask & sam) {
|
||||
// sam represents a negative number.
|
||||
return ~sam + 1;
|
||||
} else {
|
||||
// sam represents a positive number.
|
||||
return kSignBitMask | sam;
|
||||
}
|
||||
}
|
||||
|
||||
// Given two numbers in the sign-and-magnitude representation,
|
||||
// returns the distance between them as an unsigned number.
|
||||
static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits& sam1, const Bits& sam2) {
|
||||
const Bits biased1 = SignAndMagnitudeToBiased(sam1);
|
||||
const Bits biased2 = SignAndMagnitudeToBiased(sam2);
|
||||
return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
|
||||
}
|
||||
|
||||
FloatingPointUnion u_;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
@@ -65,16 +65,14 @@ struct bound {
|
||||
side(std::move(b.side)) {
|
||||
}
|
||||
|
||||
bound(bound<T>const& b) = delete;
|
||||
bound(bound<T> const& b) = delete;
|
||||
bound<T>& operator=(bound<T> const&) = delete;
|
||||
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
template <class charT, class traits, typename T>
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
|
||||
const bound<T>& bnd) {
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out, const bound<T>& bnd) {
|
||||
out << " Bound: " << &bnd << std::endl;
|
||||
out << " current_x: " << bnd.current_x << std::endl;
|
||||
out << " last_point: " << bnd.last_point.x << ", " << bnd.last_point.y << std::endl;
|
||||
@@ -96,6 +94,6 @@ inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, t
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -23,6 +23,6 @@ void bubble_sort(It begin, It end, Compare c, MethodOnSwap m) {
|
||||
}
|
||||
} while (modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -85,14 +85,11 @@ bool build_edge_list(mapbox::geometry::linear_ring<T2> const& path_geometry, edg
|
||||
}
|
||||
if (!edges.empty()) {
|
||||
auto const& back_top = edges.back().top;
|
||||
if (static_cast<T1>(back_pt.x) == back_top.x &&
|
||||
static_cast<T1>(back_pt.y) == back_top.y) {
|
||||
if (static_cast<T1>(back_pt.x) == back_top.x && static_cast<T1>(back_pt.y) == back_top.y) {
|
||||
auto const& back_bot = edges.back().bot;
|
||||
pt1 = mapbox::geometry::point<T2>(static_cast<T2>(back_bot.x),
|
||||
static_cast<T2>(back_bot.y));
|
||||
pt1 = mapbox::geometry::point<T2>(static_cast<T2>(back_bot.x), static_cast<T2>(back_bot.y));
|
||||
} else {
|
||||
pt1 = mapbox::geometry::point<T2>(static_cast<T2>(back_top.x),
|
||||
static_cast<T2>(back_top.y));
|
||||
pt1 = mapbox::geometry::point<T2>(static_cast<T2>(back_top.x), static_cast<T2>(back_top.y));
|
||||
}
|
||||
back_pt = pt1;
|
||||
} else {
|
||||
@@ -181,6 +178,6 @@ bool build_edge_list(mapbox::geometry::linear_ring<T2> const& path_geometry, edg
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -21,6 +21,6 @@ bool add_linear_ring(mapbox::geometry::linear_ring<T2> const& path_geometry,
|
||||
add_ring_to_local_minima_list(new_edges, minima_list, p_type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -10,9 +10,7 @@ namespace geometry {
|
||||
namespace wagyu {
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void push_ring_to_polygon(mapbox::geometry::polygon<T2>& poly,
|
||||
ring_ptr<T1> r,
|
||||
bool reverse_output) {
|
||||
void push_ring_to_polygon(mapbox::geometry::polygon<T2>& poly, ring_ptr<T1> r, bool reverse_output) {
|
||||
mapbox::geometry::linear_ring<T2> lr;
|
||||
lr.reserve(r->size() + 1);
|
||||
auto firstPt = r->points;
|
||||
@@ -34,7 +32,7 @@ void push_ring_to_polygon(mapbox::geometry::polygon<T2>& poly,
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void build_result_polygons(mapbox::geometry::multi_polygon<T2>& solution,
|
||||
ring_vector<T1>const& rings,
|
||||
ring_vector<T1> const& rings,
|
||||
bool reverse_output) {
|
||||
for (auto r : rings) {
|
||||
if (r == nullptr) {
|
||||
@@ -62,11 +60,9 @@ void build_result_polygons(mapbox::geometry::multi_polygon<T2>& solution,
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
void build_result(mapbox::geometry::multi_polygon<T2>& solution,
|
||||
ring_manager<T1>const& rings,
|
||||
bool reverse_output) {
|
||||
void build_result(mapbox::geometry::multi_polygon<T2>& solution, ring_manager<T1> const& rings, bool reverse_output) {
|
||||
build_result_polygons(solution, rings.children, reverse_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -5,25 +5,22 @@
|
||||
#include <list>
|
||||
#include <stdexcept>
|
||||
|
||||
// GCC 4.8 missing range std::vector::insert (c++11)
|
||||
#ifdef __GNUC__
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
#define GCC_MISSING_VECTOR_RANGE_INSERT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mapbox {
|
||||
namespace geometry {
|
||||
namespace wagyu {
|
||||
|
||||
enum clip_type : std::uint8_t {
|
||||
clip_type_intersection = 0,
|
||||
clip_type_union,
|
||||
clip_type_difference,
|
||||
clip_type_x_or
|
||||
};
|
||||
enum clip_type : std::uint8_t { clip_type_intersection = 0, clip_type_union, clip_type_difference, clip_type_x_or };
|
||||
|
||||
enum polygon_type : std::uint8_t { polygon_type_subject = 0, polygon_type_clip };
|
||||
|
||||
enum fill_type : std::uint8_t {
|
||||
fill_type_even_odd = 0,
|
||||
fill_type_non_zero,
|
||||
fill_type_positive,
|
||||
fill_type_negative
|
||||
};
|
||||
enum fill_type : std::uint8_t { fill_type_even_odd = 0, fill_type_non_zero, fill_type_positive, fill_type_negative };
|
||||
|
||||
static double const def_arc_tolerance = 0.25;
|
||||
|
||||
@@ -48,6 +45,6 @@ enum end_type {
|
||||
|
||||
template <typename T>
|
||||
using maxima_list = std::list<T>;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -39,8 +39,7 @@ struct edge {
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
edge(mapbox::geometry::point<T2> const& current,
|
||||
mapbox::geometry::point<T2> const& next_pt) noexcept
|
||||
edge(mapbox::geometry::point<T2> const& current, mapbox::geometry::point<T2> const& next_pt) noexcept
|
||||
: bot(static_cast<T>(current.x), static_cast<T>(current.y)),
|
||||
top(static_cast<T>(current.x), static_cast<T>(current.y)),
|
||||
dx(0.0) {
|
||||
@@ -69,8 +68,8 @@ using edge_list_itr = typename edge_list<T>::iterator;
|
||||
|
||||
template <typename T>
|
||||
bool slopes_equal(edge<T> const& e1, edge<T> const& e2) {
|
||||
return (e1.top.y - e1.bot.y) * (e2.top.x - e2.bot.x) ==
|
||||
(e1.top.x - e1.bot.x) * (e2.top.y - e2.bot.y);
|
||||
return static_cast<std::int64_t>(e1.top.y - e1.bot.y) * static_cast<std::int64_t>(e2.top.x - e2.bot.x) ==
|
||||
static_cast<std::int64_t>(e1.top.x - e1.bot.x) * static_cast<std::int64_t>(e2.top.y - e2.bot.y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -83,16 +82,14 @@ inline double get_current_x(edge<T> const& edge, const T current_y) {
|
||||
if (current_y == edge.top.y) {
|
||||
return static_cast<double>(edge.top.x);
|
||||
} else {
|
||||
return static_cast<double>(edge.bot.x) +
|
||||
edge.dx * static_cast<double>(current_y - edge.bot.y);
|
||||
return static_cast<double>(edge.bot.x) + edge.dx * static_cast<double>(current_y - edge.bot.y);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
template <class charT, class traits, typename T>
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
|
||||
const edge<T>& e) {
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out, const edge<T>& e) {
|
||||
out << " Edge: " << std::endl;
|
||||
out << " bot x: " << e.bot.x << " y: " << e.bot.y << std::endl;
|
||||
out << " top x: " << e.top.x << " y: " << e.top.y << std::endl;
|
||||
@@ -118,6 +115,6 @@ inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, t
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace mapbox {
|
||||
namespace geometry {
|
||||
namespace wagyu {
|
||||
class clipper_exception : public std::exception {
|
||||
private:
|
||||
std::string m_descr;
|
||||
|
||||
public:
|
||||
clipper_exception(const char* description) : m_descr(description) {
|
||||
}
|
||||
virtual ~clipper_exception() noexcept {
|
||||
}
|
||||
virtual const char* what() const noexcept {
|
||||
return m_descr.c_str();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* To enable this by the program, define USE_WAGYU_INTERRUPT before including wagyu.hpp
|
||||
* To request an interruption, call `interrupt_request()`. As soon as Wagyu detects the request
|
||||
* it will raise an exception (`std::runtime_error`).
|
||||
*/
|
||||
|
||||
#ifdef USE_WAGYU_INTERRUPT
|
||||
|
||||
namespace {
|
||||
thread_local bool WAGYU_INTERRUPT_REQUESTED = false;
|
||||
}
|
||||
|
||||
namespace mapbox {
|
||||
namespace geometry {
|
||||
namespace wagyu {
|
||||
|
||||
static void interrupt_reset(void) {
|
||||
WAGYU_INTERRUPT_REQUESTED = false;
|
||||
}
|
||||
|
||||
static void interrupt_request(void) {
|
||||
WAGYU_INTERRUPT_REQUESTED = true;
|
||||
}
|
||||
|
||||
static void interrupt_check(void) {
|
||||
if (WAGYU_INTERRUPT_REQUESTED) {
|
||||
interrupt_reset();
|
||||
throw std::runtime_error("Wagyu interrupted");
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
#else /* ! USE_WAGYU_INTERRUPT */
|
||||
|
||||
namespace mapbox {
|
||||
namespace geometry {
|
||||
namespace wagyu {
|
||||
|
||||
static void interrupt_check(void) {
|
||||
}
|
||||
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
#endif /* USE_WAGYU_INTERRUPT */
|
||||
@@ -21,20 +21,18 @@ struct intersect_node {
|
||||
bound_ptr<T> bound2;
|
||||
mapbox::geometry::point<double> pt;
|
||||
|
||||
intersect_node(intersect_node<T>&& n)
|
||||
intersect_node(intersect_node<T>&& n) noexcept
|
||||
: bound1(std::move(n.bound1)), bound2(std::move(n.bound2)), pt(std::move(n.pt)) {
|
||||
}
|
||||
|
||||
intersect_node& operator=(intersect_node<T>&& n) {
|
||||
intersect_node& operator=(intersect_node<T>&& n) noexcept {
|
||||
bound1 = std::move(n.bound1);
|
||||
bound2 = std::move(n.bound2);
|
||||
pt = std::move(n.pt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
intersect_node(bound_ptr<T> const& bound1_,
|
||||
bound_ptr<T> const& bound2_,
|
||||
mapbox::geometry::point<double> const& pt_)
|
||||
intersect_node(bound_ptr<T> const& bound1_, bound_ptr<T> const& bound2_, mapbox::geometry::point<double> const& pt_)
|
||||
: bound1(bound1_), bound2(bound2_), pt(pt_) {
|
||||
}
|
||||
};
|
||||
@@ -67,6 +65,6 @@ inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, t
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -46,9 +46,7 @@ inline void swap_sides(bound<T>& b1, bound<T>& b2) {
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool get_edge_intersection(edge<T1> const& e1,
|
||||
edge<T1> const& e2,
|
||||
mapbox::geometry::point<T2>& pt) {
|
||||
bool get_edge_intersection(edge<T1> const& e1, edge<T1> const& e2, mapbox::geometry::point<T2>& pt) {
|
||||
T2 p0_x = static_cast<T2>(e1.bot.x);
|
||||
T2 p0_y = static_cast<T2>(e1.bot.y);
|
||||
T2 p1_x = static_cast<T2>(e1.top.x);
|
||||
@@ -79,8 +77,7 @@ bool get_edge_intersection(edge<T1> const& e1,
|
||||
template <typename T>
|
||||
struct intersection_compare {
|
||||
bool operator()(bound_ptr<T> const& b1, bound_ptr<T> const& b2) {
|
||||
return !(b1->current_x > b2->current_x &&
|
||||
!slopes_equal(*(b1->current_edge), *(b2->current_edge)));
|
||||
return !(b1->current_x > b2->current_x && !slopes_equal(*(b1->current_edge), *(b2->current_edge)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -301,14 +298,12 @@ void process_intersect_list(intersect_list<T>& intersects,
|
||||
ring_manager<T>& rings,
|
||||
active_bound_list<T>& active_bounds) {
|
||||
for (auto node_itr = intersects.begin(); node_itr != intersects.end(); ++node_itr) {
|
||||
auto b1 = std::find_if(active_bounds.begin(), active_bounds.end(),
|
||||
find_first_bound<T>(*node_itr));
|
||||
auto b1 = std::find_if(active_bounds.begin(), active_bounds.end(), find_first_bound<T>(*node_itr));
|
||||
auto b2 = std::next(b1);
|
||||
if (!bounds_adjacent(*node_itr, *b2)) {
|
||||
auto next_itr = std::next(node_itr);
|
||||
while (next_itr != intersects.end()) {
|
||||
auto n1 = std::find_if(active_bounds.begin(), active_bounds.end(),
|
||||
find_first_bound<T>(*next_itr));
|
||||
auto n1 = std::find_if(active_bounds.begin(), active_bounds.end(), find_first_bound<T>(*next_itr));
|
||||
auto n2 = std::next(n1);
|
||||
if (bounds_adjacent(*next_itr, *n2)) {
|
||||
b1 = n1;
|
||||
@@ -323,8 +318,8 @@ void process_intersect_list(intersect_list<T>& intersects,
|
||||
std::iter_swap(node_itr, next_itr);
|
||||
}
|
||||
mapbox::geometry::point<T> pt = round_point<T>(node_itr->pt);
|
||||
intersect_bounds(*(node_itr->bound1), *(node_itr->bound2), pt, cliptype, subject_fill_type,
|
||||
clip_fill_type, rings, active_bounds);
|
||||
intersect_bounds(*(node_itr->bound1), *(node_itr->bound2), pt, cliptype, subject_fill_type, clip_fill_type,
|
||||
rings, active_bounds);
|
||||
std::iter_swap(b1, b2);
|
||||
}
|
||||
}
|
||||
@@ -357,16 +352,14 @@ void process_intersections(T top_y,
|
||||
}
|
||||
|
||||
// Restore order of active bounds list
|
||||
std::stable_sort(
|
||||
active_bounds.begin(), active_bounds.end(),
|
||||
[](bound_ptr<T> const& b1, bound_ptr<T> const& b2) { return b1->pos < b2->pos; });
|
||||
std::stable_sort(active_bounds.begin(), active_bounds.end(),
|
||||
[](bound_ptr<T> const& b1, bound_ptr<T> const& b2) { return b1->pos < b2->pos; });
|
||||
|
||||
// Sort the intersection list
|
||||
std::stable_sort(intersects.begin(), intersects.end(), intersect_list_sorter<T>());
|
||||
|
||||
process_intersect_list(intersects, cliptype, subject_fill_type, clip_fill_type, rings,
|
||||
active_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
process_intersect_list(intersects, cliptype, subject_fill_type, clip_fill_type, rings, active_bounds);
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -45,8 +45,7 @@ using local_minimum_ptr_list_itr = typename local_minimum_ptr_list<T>::iterator;
|
||||
|
||||
template <typename T>
|
||||
struct local_minimum_sorter {
|
||||
inline bool operator()(local_minimum_ptr<T> const& locMin1,
|
||||
local_minimum_ptr<T> const& locMin2) {
|
||||
inline bool operator()(local_minimum_ptr<T> const& locMin1, local_minimum_ptr<T> const& locMin2) {
|
||||
if (locMin2->y == locMin1->y) {
|
||||
return locMin2->minimum_has_horizontal != locMin1->minimum_has_horizontal &&
|
||||
locMin1->minimum_has_horizontal;
|
||||
@@ -113,6 +112,6 @@ std::string output_all_edges(local_minimum_ptr_list<T> const& lms) {
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mapbox/geometry/wagyu/edge.hpp>
|
||||
#include <mapbox/geometry/wagyu/interrupt.hpp>
|
||||
#include <mapbox/geometry/wagyu/local_minimum.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -42,12 +43,10 @@ void start_list_on_local_maximum(edge_list<T>& edges) {
|
||||
break;
|
||||
}
|
||||
if (!edge_is_horizontal && prev_edge_is_horizontal) {
|
||||
if (y_decreasing_before_last_horizontal &&
|
||||
(edge->top == prev_edge->bot || edge->top == prev_edge->top)) {
|
||||
if (y_decreasing_before_last_horizontal && (edge->top == prev_edge->bot || edge->top == prev_edge->top)) {
|
||||
break;
|
||||
}
|
||||
} else if (!y_decreasing_before_last_horizontal && !prev_edge_is_horizontal &&
|
||||
edge_is_horizontal &&
|
||||
} else if (!y_decreasing_before_last_horizontal && !prev_edge_is_horizontal && edge_is_horizontal &&
|
||||
(prev_edge->top == edge->top || prev_edge->top == edge->bot)) {
|
||||
y_decreasing_before_last_horizontal = true;
|
||||
}
|
||||
@@ -84,12 +83,10 @@ bound<T> create_bound_towards_minimum(edge_list<T>& edges) {
|
||||
break;
|
||||
}
|
||||
if (!next_edge_is_horizontal && edge_is_horizontal) {
|
||||
if (y_increasing_before_last_horizontal &&
|
||||
(next_edge->bot == edge->bot || next_edge->bot == edge->top)) {
|
||||
if (y_increasing_before_last_horizontal && (next_edge->bot == edge->bot || next_edge->bot == edge->top)) {
|
||||
break;
|
||||
}
|
||||
} else if (!y_increasing_before_last_horizontal && !edge_is_horizontal &&
|
||||
next_edge_is_horizontal &&
|
||||
} else if (!y_increasing_before_last_horizontal && !edge_is_horizontal && next_edge_is_horizontal &&
|
||||
(edge->bot == next_edge->top || edge->bot == next_edge->bot)) {
|
||||
y_increasing_before_last_horizontal = true;
|
||||
}
|
||||
@@ -132,12 +129,10 @@ bound<T> create_bound_towards_maximum(edge_list<T>& edges) {
|
||||
break;
|
||||
}
|
||||
if (!next_edge_is_horizontal && edge_is_horizontal) {
|
||||
if (y_decreasing_before_last_horizontal &&
|
||||
(next_edge->top == edge->bot || next_edge->top == edge->top)) {
|
||||
if (y_decreasing_before_last_horizontal && (next_edge->top == edge->bot || next_edge->top == edge->top)) {
|
||||
break;
|
||||
}
|
||||
} else if (!y_decreasing_before_last_horizontal && !edge_is_horizontal &&
|
||||
next_edge_is_horizontal &&
|
||||
} else if (!y_decreasing_before_last_horizontal && !edge_is_horizontal && next_edge_is_horizontal &&
|
||||
(edge->top == next_edge->top || edge->top == next_edge->bot)) {
|
||||
y_decreasing_before_last_horizontal = true;
|
||||
}
|
||||
@@ -196,14 +191,11 @@ void move_horizontals_on_left_to_right(bound<T>& left_bound, bound<T>& right_bou
|
||||
auto dist = std::distance(left_bound.edges.begin(), edge_itr);
|
||||
std::move(left_bound.edges.begin(), edge_itr, std::back_inserter(right_bound.edges));
|
||||
left_bound.edges.erase(left_bound.edges.begin(), edge_itr);
|
||||
std::rotate(right_bound.edges.begin(), std::prev(right_bound.edges.end(), dist),
|
||||
right_bound.edges.end());
|
||||
std::rotate(right_bound.edges.begin(), std::prev(right_bound.edges.end(), dist), right_bound.edges.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void add_ring_to_local_minima_list(edge_list<T>& edges,
|
||||
local_minimum_list<T>& minima_list,
|
||||
polygon_type poly_type) {
|
||||
void add_ring_to_local_minima_list(edge_list<T>& edges, local_minimum_list<T>& minima_list, polygon_type poly_type) {
|
||||
|
||||
if (edges.empty()) {
|
||||
return;
|
||||
@@ -215,6 +207,7 @@ void add_ring_to_local_minima_list(edge_list<T>& edges,
|
||||
bound_ptr<T> first_minimum = nullptr;
|
||||
bound_ptr<T> last_maximum = nullptr;
|
||||
while (!edges.empty()) {
|
||||
interrupt_check(); // Check for interruptions
|
||||
bool lm_minimum_has_horizontal = false;
|
||||
auto to_minimum = create_bound_towards_minimum(edges);
|
||||
if (edges.empty()) {
|
||||
@@ -226,13 +219,11 @@ void add_ring_to_local_minima_list(edge_list<T>& edges,
|
||||
auto to_max_first_non_horizontal = to_maximum.edges.begin();
|
||||
auto to_min_first_non_horizontal = to_minimum.edges.begin();
|
||||
bool minimum_is_left = true;
|
||||
while (to_max_first_non_horizontal != to_maximum.edges.end() &&
|
||||
is_horizontal(*to_max_first_non_horizontal)) {
|
||||
while (to_max_first_non_horizontal != to_maximum.edges.end() && is_horizontal(*to_max_first_non_horizontal)) {
|
||||
lm_minimum_has_horizontal = true;
|
||||
++to_max_first_non_horizontal;
|
||||
}
|
||||
while (to_min_first_non_horizontal != to_minimum.edges.end() &&
|
||||
is_horizontal(*to_min_first_non_horizontal)) {
|
||||
while (to_min_first_non_horizontal != to_minimum.edges.end() && is_horizontal(*to_min_first_non_horizontal)) {
|
||||
lm_minimum_has_horizontal = true;
|
||||
++to_min_first_non_horizontal;
|
||||
}
|
||||
@@ -318,6 +309,6 @@ void initialize_lm(local_minimum_ptr_list_itr<T>& lm) {
|
||||
(*lm)->right_bound.ring = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -93,8 +93,7 @@ bool operator!=(point<T> const& lhs, mapbox::geometry::point<T> const& rhs) {
|
||||
#ifdef DEBUG
|
||||
|
||||
template <class charT, class traits, typename T>
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
|
||||
const point<T>& p) {
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out, const point<T>& p) {
|
||||
out << " point at: " << p.x << ", " << p.y;
|
||||
return out;
|
||||
}
|
||||
@@ -106,6 +105,6 @@ inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, t
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -32,8 +32,7 @@ active_bound_list_itr<T> process_horizontal_left_to_right(T scanline_y,
|
||||
|
||||
auto hp_itr = rings.current_hp_itr;
|
||||
while (hp_itr != rings.hot_pixels.end() &&
|
||||
(hp_itr->y > scanline_y ||
|
||||
(hp_itr->y == scanline_y && hp_itr->x < (*horz_bound)->current_edge->bot.x))) {
|
||||
(hp_itr->y > scanline_y || (hp_itr->y == scanline_y && hp_itr->x < (*horz_bound)->current_edge->bot.x))) {
|
||||
++hp_itr;
|
||||
}
|
||||
|
||||
@@ -48,15 +47,14 @@ active_bound_list_itr<T> process_horizontal_left_to_right(T scanline_y,
|
||||
// polygons) wherever hot pixels touch these horizontal edges. This helps
|
||||
//'simplifying' polygons (ie if the Simplify property is set).
|
||||
while (hp_itr != rings.hot_pixels.end() && hp_itr->y == scanline_y &&
|
||||
hp_itr->x < wround<T>((*bnd)->current_x) &&
|
||||
hp_itr->x < (*horz_bound)->current_edge->top.x) {
|
||||
hp_itr->x < wround<T>((*bnd)->current_x) && hp_itr->x < (*horz_bound)->current_edge->top.x) {
|
||||
if ((*horz_bound)->ring) {
|
||||
add_point_to_ring(*(*horz_bound), *hp_itr, rings);
|
||||
}
|
||||
++hp_itr;
|
||||
}
|
||||
|
||||
if ((*bnd)->current_x > static_cast<double>((*horz_bound)->current_edge->top.x)) {
|
||||
if (greater_than((*bnd)->current_x, static_cast<double>((*horz_bound)->current_edge->top.x))) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -70,17 +68,16 @@ active_bound_list_itr<T> process_horizontal_left_to_right(T scanline_y,
|
||||
|
||||
// note: may be done multiple times
|
||||
if ((*horz_bound)->ring) {
|
||||
add_point_to_ring(*(*horz_bound),
|
||||
mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
add_point_to_ring(*(*horz_bound), mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
rings);
|
||||
}
|
||||
|
||||
// OK, so far we're still in range of the horizontal Edge but make sure
|
||||
// we're at the last of consec. horizontals when matching with eMaxPair
|
||||
if (is_maxima_edge && bnd == bound_max_pair) {
|
||||
if ((*horz_bound)->ring) {
|
||||
add_local_maximum_point(*(*horz_bound), *(*bound_max_pair),
|
||||
(*horz_bound)->current_edge->top, rings, active_bounds);
|
||||
if ((*horz_bound)->ring && (*bound_max_pair)->ring) {
|
||||
add_local_maximum_point(*(*horz_bound), *(*bound_max_pair), (*horz_bound)->current_edge->top, rings,
|
||||
active_bounds);
|
||||
}
|
||||
*bound_max_pair = nullptr;
|
||||
*horz_bound = nullptr;
|
||||
@@ -90,8 +87,7 @@ active_bound_list_itr<T> process_horizontal_left_to_right(T scanline_y,
|
||||
return horizontal_itr_behind;
|
||||
}
|
||||
|
||||
intersect_bounds(*(*horz_bound), *(*bnd),
|
||||
mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
intersect_bounds(*(*horz_bound), *(*bnd), mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
cliptype, subject_fill_type, clip_fill_type, rings, active_bounds);
|
||||
std::iter_swap(horz_bound, bnd);
|
||||
horz_bound = bnd;
|
||||
@@ -135,15 +131,13 @@ active_bound_list_itr<T> process_horizontal_right_to_left(T scanline_y,
|
||||
bool is_maxima_edge = is_maxima(horz_bound_fwd, scanline_y);
|
||||
auto bound_max_pair = active_bounds.rend();
|
||||
if (is_maxima_edge) {
|
||||
bound_max_pair =
|
||||
active_bound_list_rev_itr<T>(get_maxima_pair<T>(horz_bound_fwd, active_bounds));
|
||||
bound_max_pair = active_bound_list_rev_itr<T>(get_maxima_pair<T>(horz_bound_fwd, active_bounds));
|
||||
--bound_max_pair;
|
||||
}
|
||||
auto hp_itr_fwd = rings.current_hp_itr;
|
||||
while (
|
||||
hp_itr_fwd != rings.hot_pixels.end() &&
|
||||
(hp_itr_fwd->y < scanline_y ||
|
||||
(hp_itr_fwd->y == scanline_y && hp_itr_fwd->x < (*horz_bound_fwd)->current_edge->top.x))) {
|
||||
while (hp_itr_fwd != rings.hot_pixels.end() &&
|
||||
(hp_itr_fwd->y < scanline_y ||
|
||||
(hp_itr_fwd->y == scanline_y && hp_itr_fwd->x < (*horz_bound_fwd)->current_edge->top.x))) {
|
||||
++hp_itr_fwd;
|
||||
}
|
||||
auto hp_itr = hot_pixel_rev_itr<T>(hp_itr_fwd);
|
||||
@@ -158,15 +152,14 @@ active_bound_list_itr<T> process_horizontal_right_to_left(T scanline_y,
|
||||
// this code block inserts extra coords into horizontal edges (in output
|
||||
// polygons) wherever hot pixels touch these horizontal edges.
|
||||
while (hp_itr != rings.hot_pixels.rend() && hp_itr->y == scanline_y &&
|
||||
hp_itr->x > wround<T>((*bnd)->current_x) &&
|
||||
hp_itr->x > (*horz_bound)->current_edge->top.x) {
|
||||
hp_itr->x > wround<T>((*bnd)->current_x) && hp_itr->x > (*horz_bound)->current_edge->top.x) {
|
||||
if ((*horz_bound)->ring) {
|
||||
add_point_to_ring(*(*horz_bound), *hp_itr, rings);
|
||||
}
|
||||
++hp_itr;
|
||||
}
|
||||
|
||||
if ((*bnd)->current_x < static_cast<double>((*horz_bound)->current_edge->top.x)) {
|
||||
if (less_than((*bnd)->current_x, static_cast<double>((*horz_bound)->current_edge->top.x))) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -180,25 +173,23 @@ active_bound_list_itr<T> process_horizontal_right_to_left(T scanline_y,
|
||||
|
||||
// note: may be done multiple times
|
||||
if ((*horz_bound)->ring) {
|
||||
add_point_to_ring(*(*horz_bound),
|
||||
mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
add_point_to_ring(*(*horz_bound), mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
rings);
|
||||
}
|
||||
|
||||
// OK, so far we're still in range of the horizontal Edge but make sure
|
||||
// we're at the last of consec. horizontals when matching with eMaxPair
|
||||
if (is_maxima_edge && bnd == bound_max_pair) {
|
||||
if ((*horz_bound)->ring) {
|
||||
add_local_maximum_point(*(*horz_bound), *(*bound_max_pair),
|
||||
(*horz_bound)->current_edge->top, rings, active_bounds);
|
||||
if ((*horz_bound)->ring && (*bound_max_pair)->ring) {
|
||||
add_local_maximum_point(*(*horz_bound), *(*bound_max_pair), (*horz_bound)->current_edge->top, rings,
|
||||
active_bounds);
|
||||
}
|
||||
*bound_max_pair = nullptr;
|
||||
*horz_bound = nullptr;
|
||||
return next_bnd_itr;
|
||||
}
|
||||
|
||||
intersect_bounds(*(*bnd), *(*horz_bound),
|
||||
mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
intersect_bounds(*(*bnd), *(*horz_bound), mapbox::geometry::point<T>(wround<T>((*bnd)->current_x), scanline_y),
|
||||
cliptype, subject_fill_type, clip_fill_type, rings, active_bounds);
|
||||
std::iter_swap(horz_bound, bnd);
|
||||
horz_bound = bnd;
|
||||
@@ -234,13 +225,11 @@ active_bound_list_itr<T> process_horizontal(T scanline_y,
|
||||
fill_type subject_fill_type,
|
||||
fill_type clip_fill_type) {
|
||||
if ((*horz_bound)->current_edge->bot.x < (*horz_bound)->current_edge->top.x) {
|
||||
return process_horizontal_left_to_right(scanline_y, horz_bound, active_bounds, rings,
|
||||
scanbeam, cliptype, subject_fill_type,
|
||||
clip_fill_type);
|
||||
return process_horizontal_left_to_right(scanline_y, horz_bound, active_bounds, rings, scanbeam, cliptype,
|
||||
subject_fill_type, clip_fill_type);
|
||||
} else {
|
||||
return process_horizontal_right_to_left(scanline_y, horz_bound, active_bounds, rings,
|
||||
scanbeam, cliptype, subject_fill_type,
|
||||
clip_fill_type);
|
||||
return process_horizontal_right_to_left(scanline_y, horz_bound, active_bounds, rings, scanbeam, cliptype,
|
||||
subject_fill_type, clip_fill_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,15 +243,14 @@ void process_horizontals(T scanline_y,
|
||||
fill_type clip_fill_type) {
|
||||
for (auto bnd_itr = active_bounds.begin(); bnd_itr != active_bounds.end();) {
|
||||
if (*bnd_itr != nullptr && current_edge_is_horizontal<T>(bnd_itr)) {
|
||||
bnd_itr = process_horizontal(scanline_y, bnd_itr, active_bounds, rings, scanbeam,
|
||||
cliptype, subject_fill_type, clip_fill_type);
|
||||
bnd_itr = process_horizontal(scanline_y, bnd_itr, active_bounds, rings, scanbeam, cliptype,
|
||||
subject_fill_type, clip_fill_type);
|
||||
} else {
|
||||
++bnd_itr;
|
||||
}
|
||||
}
|
||||
active_bounds.erase(std::remove(active_bounds.begin(), active_bounds.end(), nullptr),
|
||||
active_bounds.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
active_bounds.erase(std::remove(active_bounds.begin(), active_bounds.end(), nullptr), active_bounds.end());
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mapbox/geometry/wagyu/active_bound_list.hpp>
|
||||
#include <mapbox/geometry/wagyu/config.hpp>
|
||||
#include <mapbox/geometry/wagyu/edge.hpp>
|
||||
#include <mapbox/geometry/wagyu/interrupt.hpp>
|
||||
#include <mapbox/geometry/wagyu/intersect_util.hpp>
|
||||
#include <mapbox/geometry/wagyu/local_minimum.hpp>
|
||||
#include <mapbox/geometry/wagyu/local_minimum_util.hpp>
|
||||
@@ -33,16 +34,15 @@ active_bound_list_itr<T> do_maxima(active_bound_list_itr<T>& bnd,
|
||||
continue;
|
||||
}
|
||||
skipped = true;
|
||||
intersect_bounds(*(*bnd), *(*bnd_next), (*bnd)->current_edge->top, cliptype,
|
||||
subject_fill_type, clip_fill_type, manager, active_bounds);
|
||||
intersect_bounds(*(*bnd), *(*bnd_next), (*bnd)->current_edge->top, cliptype, subject_fill_type, clip_fill_type,
|
||||
manager, active_bounds);
|
||||
std::iter_swap(bnd, bnd_next);
|
||||
bnd = bnd_next;
|
||||
++bnd_next;
|
||||
}
|
||||
|
||||
if ((*bnd)->ring && (*bndMaxPair)->ring) {
|
||||
add_local_maximum_point(*(*bnd), *(*bndMaxPair), (*bnd)->current_edge->top, manager,
|
||||
active_bounds);
|
||||
add_local_maximum_point(*(*bnd), *(*bndMaxPair), (*bnd)->current_edge->top, manager, active_bounds);
|
||||
} else if ((*bnd)->ring || (*bndMaxPair)->ring) {
|
||||
throw std::runtime_error("DoMaxima error");
|
||||
}
|
||||
@@ -66,6 +66,7 @@ void process_edges_at_top_of_scanbeam(T top_y,
|
||||
fill_type clip_fill_type) {
|
||||
|
||||
for (auto bnd = active_bounds.begin(); bnd != active_bounds.end();) {
|
||||
interrupt_check(); // Check for interruptions
|
||||
if (*bnd == nullptr) {
|
||||
++bnd;
|
||||
continue;
|
||||
@@ -77,12 +78,10 @@ void process_edges_at_top_of_scanbeam(T top_y,
|
||||
|
||||
if (is_maxima_edge) {
|
||||
auto bnd_max_pair = get_maxima_pair(bnd, active_bounds);
|
||||
is_maxima_edge = ((bnd_max_pair == active_bounds.end() ||
|
||||
!current_edge_is_horizontal<T>(bnd_max_pair)) &&
|
||||
is_maxima_edge = ((bnd_max_pair == active_bounds.end() || !current_edge_is_horizontal<T>(bnd_max_pair)) &&
|
||||
is_maxima(bnd_max_pair, top_y));
|
||||
if (is_maxima_edge) {
|
||||
bnd = do_maxima(bnd, bnd_max_pair, cliptype, subject_fill_type, clip_fill_type,
|
||||
manager, active_bounds);
|
||||
bnd = do_maxima(bnd, bnd_max_pair, cliptype, subject_fill_type, clip_fill_type, manager, active_bounds);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -101,15 +100,12 @@ void process_edges_at_top_of_scanbeam(T top_y,
|
||||
}
|
||||
++bnd;
|
||||
}
|
||||
active_bounds.erase(std::remove(active_bounds.begin(), active_bounds.end(), nullptr),
|
||||
active_bounds.end());
|
||||
active_bounds.erase(std::remove(active_bounds.begin(), active_bounds.end(), nullptr), active_bounds.end());
|
||||
|
||||
insert_horizontal_local_minima_into_ABL(top_y, minima_sorted, current_lm, active_bounds,
|
||||
manager, scanbeam, cliptype, subject_fill_type,
|
||||
clip_fill_type);
|
||||
insert_horizontal_local_minima_into_ABL(top_y, minima_sorted, current_lm, active_bounds, manager, scanbeam,
|
||||
cliptype, subject_fill_type, clip_fill_type);
|
||||
|
||||
process_horizontals(top_y, active_bounds, manager, scanbeam, cliptype, subject_fill_type,
|
||||
clip_fill_type);
|
||||
process_horizontals(top_y, active_bounds, manager, scanbeam, cliptype, subject_fill_type, clip_fill_type);
|
||||
|
||||
// 4. Promote intermediate vertices
|
||||
|
||||
@@ -122,6 +118,6 @@ void process_edges_at_top_of_scanbeam(T top_y,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -18,23 +18,31 @@ mapbox::geometry::point<T> intersect(mapbox::geometry::point<T> a,
|
||||
switch (edge) {
|
||||
case 0:
|
||||
return mapbox::geometry::point<T>(
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.x) + static_cast<double>(b.x - a.x) * static_cast<double>(box.min.y - a.y) / static_cast<double>(b.y - a.y)),
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.x) + static_cast<double>(b.x - a.x) *
|
||||
static_cast<double>(box.min.y - a.y) /
|
||||
static_cast<double>(b.y - a.y)),
|
||||
box.min.y);
|
||||
|
||||
case 1:
|
||||
return mapbox::geometry::point<T>(
|
||||
box.max.x,
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.y) + static_cast<double>(b.y - a.y) * static_cast<double>(box.max.x - a.x) / static_cast<double>(b.x - a.x)));
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.y) + static_cast<double>(b.y - a.y) *
|
||||
static_cast<double>(box.max.x - a.x) /
|
||||
static_cast<double>(b.x - a.x)));
|
||||
|
||||
case 2:
|
||||
return mapbox::geometry::point<T>(
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.x) + static_cast<double>(b.x - a.x) * static_cast<double>(box.max.y - a.y) / static_cast<double>(b.y - a.y)),
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.x) + static_cast<double>(b.x - a.x) *
|
||||
static_cast<double>(box.max.y - a.y) /
|
||||
static_cast<double>(b.y - a.y)),
|
||||
box.max.y);
|
||||
|
||||
default: // case 3
|
||||
return mapbox::geometry::point<T>(
|
||||
box.min.x,
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.y) + static_cast<double>(b.y - a.y) * static_cast<double>(box.min.x - a.x) / static_cast<double>(b.x - a.x)));
|
||||
mapbox::geometry::wagyu::wround<T>(static_cast<double>(a.y) + static_cast<double>(b.y - a.y) *
|
||||
static_cast<double>(box.min.x - a.x) /
|
||||
static_cast<double>(b.x - a.x)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +101,11 @@ mapbox::geometry::linear_ring<T> quick_lr_clip(mapbox::geometry::linear_ring<T>
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
} // namespace quick_clip
|
||||
|
||||
template <typename T>
|
||||
mapbox::geometry::multi_polygon<T> clip(mapbox::geometry::polygon<T> const& poly,
|
||||
mapbox::geometry::box<T> const& b,
|
||||
fill_type subject_fill_type) {
|
||||
mapbox::geometry::multi_polygon<T>
|
||||
clip(mapbox::geometry::polygon<T> const& poly, mapbox::geometry::box<T> const& b, fill_type subject_fill_type) {
|
||||
mapbox::geometry::multi_polygon<T> result;
|
||||
wagyu<T> clipper;
|
||||
for (auto const& lr : poly) {
|
||||
@@ -112,9 +119,8 @@ mapbox::geometry::multi_polygon<T> clip(mapbox::geometry::polygon<T> const& poly
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
mapbox::geometry::multi_polygon<T> clip(mapbox::geometry::multi_polygon<T> const& mp,
|
||||
mapbox::geometry::box<T> const& b,
|
||||
fill_type subject_fill_type) {
|
||||
mapbox::geometry::multi_polygon<T>
|
||||
clip(mapbox::geometry::multi_polygon<T> const& mp, mapbox::geometry::box<T> const& b, fill_type subject_fill_type) {
|
||||
mapbox::geometry::multi_polygon<T> result;
|
||||
wagyu<T> clipper;
|
||||
for (auto const& poly : mp) {
|
||||
@@ -128,6 +134,6 @@ mapbox::geometry::multi_polygon<T> clip(mapbox::geometry::multi_polygon<T> const
|
||||
clipper.execute(clip_type_union, result, subject_fill_type, fill_type_even_odd);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -195,8 +195,7 @@ ring_ptr<T> create_new_ring(ring_manager<T>& manager) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
point_ptr<T>
|
||||
create_new_point(ring_ptr<T> r, mapbox::geometry::point<T> const& pt, ring_manager<T>& rings) {
|
||||
point_ptr<T> create_new_point(ring_ptr<T> r, mapbox::geometry::point<T> const& pt, ring_manager<T>& rings) {
|
||||
point_ptr<T> point;
|
||||
if (rings.storage.size() < rings.storage.capacity()) {
|
||||
rings.storage.emplace_back(r, pt);
|
||||
@@ -252,10 +251,8 @@ void assign_as_child(ring_ptr<T> new_ring, ring_ptr<T> parent, ring_manager<T>&
|
||||
// Assigning as a child assumes that this is
|
||||
// a brand new ring. Therefore it does
|
||||
// not have any existing relationships
|
||||
if ((parent == nullptr && new_ring->is_hole()) ||
|
||||
(parent != nullptr && new_ring->is_hole() == parent->is_hole())) {
|
||||
throw std::runtime_error(
|
||||
"Trying to assign a child that is the same orientation as the parent");
|
||||
if ((parent == nullptr && new_ring->is_hole()) || (parent != nullptr && new_ring->is_hole() == parent->is_hole())) {
|
||||
throw std::runtime_error("Trying to assign a child that is the same orientation as the parent");
|
||||
}
|
||||
auto& children = parent == nullptr ? manager.children : parent->children;
|
||||
set_to_children(new_ring, children);
|
||||
@@ -266,10 +263,8 @@ template <typename T>
|
||||
void reassign_as_child(ring_ptr<T> ring, ring_ptr<T> parent, ring_manager<T>& manager) {
|
||||
// Reassigning a ring assumes it already
|
||||
// has an existing parent
|
||||
if ((parent == nullptr && ring->is_hole()) ||
|
||||
(parent != nullptr && ring->is_hole() == parent->is_hole())) {
|
||||
throw std::runtime_error(
|
||||
"Trying to re-assign a child that is the same orientation as the parent");
|
||||
if ((parent == nullptr && ring->is_hole()) || (parent != nullptr && ring->is_hole() == parent->is_hole())) {
|
||||
throw std::runtime_error("Trying to re-assign a child that is the same orientation as the parent");
|
||||
}
|
||||
|
||||
// Remove the old child relationship
|
||||
@@ -288,8 +283,7 @@ void assign_as_sibling(ring_ptr<T> new_ring, ring_ptr<T> sibling, ring_manager<T
|
||||
// a brand new ring. Therefore it does
|
||||
// not have any existing relationships
|
||||
if (new_ring->is_hole() != sibling->is_hole()) {
|
||||
throw std::runtime_error(
|
||||
"Trying to assign to be a sibling that is not the same orientation as the sibling");
|
||||
throw std::runtime_error("Trying to assign to be a sibling that is not the same orientation as the sibling");
|
||||
}
|
||||
auto& children = sibling->parent == nullptr ? manager.children : sibling->parent->children;
|
||||
set_to_children(new_ring, children);
|
||||
@@ -305,8 +299,7 @@ void reassign_as_sibling(ring_ptr<T> ring, ring_ptr<T> sibling, ring_manager<T>&
|
||||
// a brand new ring. Therefore it does
|
||||
// not have any existing relationships
|
||||
if (ring->is_hole() != sibling->is_hole()) {
|
||||
throw std::runtime_error(
|
||||
"Trying to assign to be a sibling that is not the same orientation as the sibling");
|
||||
throw std::runtime_error("Trying to assign to be a sibling that is not the same orientation as the sibling");
|
||||
}
|
||||
// Remove the old child relationship
|
||||
auto& old_children = ring->parent == nullptr ? manager.children : ring->parent->children;
|
||||
@@ -387,10 +380,7 @@ void remove_ring_and_points(ring_ptr<T> r,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void remove_ring(ring_ptr<T> r,
|
||||
ring_manager<T>& manager,
|
||||
bool remove_children = true,
|
||||
bool remove_from_parent = true) {
|
||||
void remove_ring(ring_ptr<T> r, ring_manager<T>& manager, bool remove_children = true, bool remove_from_parent = true) {
|
||||
// Removes a ring and any children that might be
|
||||
// under that ring.
|
||||
for (auto& c : r->children) {
|
||||
@@ -513,8 +503,7 @@ void reverse_ring(point_ptr<T> pp) {
|
||||
#ifdef DEBUG
|
||||
|
||||
template <class charT, class traits, typename T>
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
|
||||
ring<T>& r) {
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out, ring<T>& r) {
|
||||
out << " ring_index: " << r.ring_index << std::endl;
|
||||
if (!r.parent) {
|
||||
// out << " parent_ring ptr: nullptr" << std::endl;
|
||||
@@ -601,8 +590,7 @@ std::string output_as_polygon(ring_ptr<T> r) {
|
||||
}
|
||||
|
||||
template <class charT, class traits, typename T>
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out,
|
||||
ring_vector<T>& rings) {
|
||||
inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& out, ring_vector<T>& rings) {
|
||||
out << "START RING VECTOR" << std::endl;
|
||||
for (auto& r : rings) {
|
||||
if (r == nullptr || !r->points) {
|
||||
@@ -640,6 +628,6 @@ inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, t
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -28,9 +28,7 @@ namespace geometry {
|
||||
namespace wagyu {
|
||||
|
||||
template <typename T>
|
||||
void set_hole_state(bound<T>& bnd,
|
||||
active_bound_list<T> const& active_bounds,
|
||||
ring_manager<T>& rings) {
|
||||
void set_hole_state(bound<T>& bnd, active_bound_list<T> const& active_bounds, ring_manager<T>& rings) {
|
||||
auto bnd_itr = std::find(active_bounds.rbegin(), active_bounds.rend(), &bnd);
|
||||
++bnd_itr;
|
||||
bound_ptr<T> bndTmp = nullptr;
|
||||
@@ -67,8 +65,7 @@ void update_current_hp_itr(T scanline_y, ring_manager<T>& rings) {
|
||||
|
||||
template <typename T>
|
||||
struct hot_pixel_sorter {
|
||||
inline bool operator()(mapbox::geometry::point<T> const& pt1,
|
||||
mapbox::geometry::point<T> const& pt2) {
|
||||
inline bool operator()(mapbox::geometry::point<T> const& pt1, mapbox::geometry::point<T> const& pt2) {
|
||||
if (pt1.y == pt2.y) {
|
||||
return pt1.x < pt2.x;
|
||||
} else {
|
||||
@@ -77,18 +74,17 @@ struct hot_pixel_sorter {
|
||||
}
|
||||
};
|
||||
|
||||
// Due to the nature of floating point calculations
|
||||
// and the high likely hood of values around X.5, we
|
||||
// need to fudge what is X.5 some for our rounding.
|
||||
const double rounding_offset = 1e-12;
|
||||
const double rounding_offset_y = 5e-13;
|
||||
|
||||
template <typename T>
|
||||
T round_towards_min(double val) {
|
||||
// 0.5 rounds to 0
|
||||
// 0.0 rounds to 0
|
||||
// -0.5 rounds to -1
|
||||
return static_cast<T>(std::ceil(val - 0.5 + rounding_offset));
|
||||
double half = std::floor(val) + 0.5;
|
||||
if (values_are_equal(val, half)) {
|
||||
return static_cast<T>(std::floor(val));
|
||||
} else {
|
||||
return static_cast<T>(std::llround(val));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -96,7 +92,12 @@ T round_towards_max(double val) {
|
||||
// 0.5 rounds to 1
|
||||
// 0.0 rounds to 0
|
||||
// -0.5 rounds to 0
|
||||
return static_cast<T>(std::floor(val + 0.5 + rounding_offset));
|
||||
double half = std::floor(val) + 0.5;
|
||||
if (values_are_equal(val, half)) {
|
||||
return static_cast<T>(std::ceil(val));
|
||||
} else {
|
||||
return static_cast<T>(std::llround(val));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -121,8 +122,7 @@ inline T get_edge_min_x(edge<T> const& edge, const T current_y) {
|
||||
return edge.bot.x;
|
||||
} else {
|
||||
double return_val =
|
||||
static_cast<double>(edge.bot.x) +
|
||||
edge.dx * (static_cast<double>(current_y - edge.bot.y) + 0.5 - rounding_offset_y);
|
||||
static_cast<double>(edge.bot.x) + edge.dx * (static_cast<double>(current_y - edge.bot.y) + 0.5);
|
||||
T value = round_towards_min<T>(return_val);
|
||||
return value;
|
||||
}
|
||||
@@ -151,8 +151,7 @@ inline T get_edge_max_x(edge<T> const& edge, const T current_y) {
|
||||
return edge.bot.x;
|
||||
} else {
|
||||
double return_val =
|
||||
static_cast<double>(edge.bot.x) +
|
||||
edge.dx * (static_cast<double>(current_y - edge.bot.y) + 0.5 - rounding_offset_y);
|
||||
static_cast<double>(edge.bot.x) + edge.dx * (static_cast<double>(current_y - edge.bot.y) + 0.5);
|
||||
T value = round_towards_max<T>(return_val);
|
||||
return value;
|
||||
}
|
||||
@@ -274,8 +273,7 @@ void insert_hot_pixels_in_path(bound<T>& bnd,
|
||||
}
|
||||
auto first_itr = hot_pixel_rev_itr<T>(itr);
|
||||
bool add_end_point_itr = (y != end_pt.y || add_end_point);
|
||||
hot_pixel_set_right_to_left(y, start_x, end_x, bnd, rings, first_itr, last_itr,
|
||||
add_end_point_itr);
|
||||
hot_pixel_set_right_to_left(y, start_x, end_x, bnd, rings, first_itr, last_itr, add_end_point_itr);
|
||||
}
|
||||
} else {
|
||||
for (; itr != rings.hot_pixels.end();) {
|
||||
@@ -293,8 +291,7 @@ void insert_hot_pixels_in_path(bound<T>& bnd,
|
||||
}
|
||||
auto last_itr = itr;
|
||||
bool add_end_point_itr = (y != end_pt.y || add_end_point);
|
||||
hot_pixel_set_left_to_right(y, start_x, end_x, bnd, rings, first_itr, last_itr,
|
||||
add_end_point_itr);
|
||||
hot_pixel_set_left_to_right(y, start_x, end_x, bnd, rings, first_itr, last_itr, add_end_point_itr);
|
||||
}
|
||||
}
|
||||
bnd.last_point = end_pt;
|
||||
@@ -319,9 +316,7 @@ void add_first_point(bound<T>& bnd,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void add_point_to_ring(bound<T>& bnd,
|
||||
mapbox::geometry::point<T> const& pt,
|
||||
ring_manager<T>& rings) {
|
||||
void add_point_to_ring(bound<T>& bnd, mapbox::geometry::point<T> const& pt, ring_manager<T>& rings) {
|
||||
assert(bnd.ring);
|
||||
// Handle hot pixels
|
||||
insert_hot_pixels_in_path(bnd, pt, rings, false);
|
||||
@@ -456,32 +451,32 @@ point_ptr<T> get_bottom_point(point_ptr<T> pp) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ring_ptr<T> get_lower_most_ring(ring_ptr<T> outRec1, ring_ptr<T> outRec2) {
|
||||
ring_ptr<T> get_lower_most_ring(ring_ptr<T> ring1, ring_ptr<T> ring2) {
|
||||
// work out which polygon fragment has the correct hole state ...
|
||||
if (!outRec1->bottom_point) {
|
||||
outRec1->bottom_point = get_bottom_point(outRec1->points);
|
||||
if (!ring1->bottom_point) {
|
||||
ring1->bottom_point = get_bottom_point(ring1->points);
|
||||
}
|
||||
if (!outRec2->bottom_point) {
|
||||
outRec2->bottom_point = get_bottom_point(outRec2->points);
|
||||
if (!ring2->bottom_point) {
|
||||
ring2->bottom_point = get_bottom_point(ring2->points);
|
||||
}
|
||||
point_ptr<T> OutPt1 = outRec1->bottom_point;
|
||||
point_ptr<T> OutPt2 = outRec2->bottom_point;
|
||||
if (OutPt1->y > OutPt2->y) {
|
||||
return outRec1;
|
||||
} else if (OutPt1->y < OutPt2->y) {
|
||||
return outRec2;
|
||||
} else if (OutPt1->x < OutPt2->x) {
|
||||
return outRec1;
|
||||
} else if (OutPt1->x > OutPt2->x) {
|
||||
return outRec2;
|
||||
} else if (OutPt1->next == OutPt1) {
|
||||
return outRec2;
|
||||
} else if (OutPt2->next == OutPt2) {
|
||||
return outRec1;
|
||||
} else if (first_is_bottom_point(OutPt1, OutPt2)) {
|
||||
return outRec1;
|
||||
point_ptr<T> pt1 = ring1->bottom_point;
|
||||
point_ptr<T> pt2 = ring2->bottom_point;
|
||||
if (pt1->y > pt2->y) {
|
||||
return ring1;
|
||||
} else if (pt1->y < pt2->y) {
|
||||
return ring2;
|
||||
} else if (pt1->x < pt2->x) {
|
||||
return ring1;
|
||||
} else if (pt1->x > pt2->x) {
|
||||
return ring2;
|
||||
} else if (pt1->next == pt1) {
|
||||
return ring2;
|
||||
} else if (pt2->next == pt2) {
|
||||
return ring1;
|
||||
} else if (first_is_bottom_point(pt1, pt2)) {
|
||||
return ring1;
|
||||
} else {
|
||||
return outRec2;
|
||||
return ring2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,10 +501,7 @@ void update_points_ring(ring_ptr<T> ring) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void append_ring(bound<T>& b1,
|
||||
bound<T>& b2,
|
||||
active_bound_list<T>& active_bounds,
|
||||
ring_manager<T>& manager) {
|
||||
void append_ring(bound<T>& b1, bound<T>& b2, active_bound_list<T>& active_bounds, ring_manager<T>& manager) {
|
||||
// get the start and ends of both output polygons ...
|
||||
ring_ptr<T> outRec1 = b1.ring;
|
||||
ring_ptr<T> outRec2 = b2.ring;
|
||||
@@ -645,8 +637,7 @@ point_in_polygon_result point_in_polygon(point<T> const& pt, point_ptr<T> op) {
|
||||
point_ptr<T> startOp = op;
|
||||
do {
|
||||
if (op->next->y == pt.y) {
|
||||
if ((op->next->x == pt.x) ||
|
||||
(op->y == pt.y && ((op->next->x > pt.x) == (op->x < pt.x)))) {
|
||||
if ((op->next->x == pt.x) || (op->y == pt.y && ((op->next->x > pt.x) == (op->x < pt.x)))) {
|
||||
return point_on_polygon;
|
||||
}
|
||||
}
|
||||
@@ -661,10 +652,8 @@ point_in_polygon_result point_in_polygon(point<T> const& pt, point_ptr<T> op) {
|
||||
result = point_outside_polygon;
|
||||
}
|
||||
} else {
|
||||
double d =
|
||||
static_cast<double>(op->x - pt.x) *
|
||||
static_cast<double>(op->next->y - pt.y) -
|
||||
static_cast<double>(op->next->x - pt.x) * static_cast<double>(op->y - pt.y);
|
||||
double d = static_cast<double>(op->x - pt.x) * static_cast<double>(op->next->y - pt.y) -
|
||||
static_cast<double>(op->next->x - pt.x) * static_cast<double>(op->y - pt.y);
|
||||
if (value_is_zero(d)) {
|
||||
return point_on_polygon;
|
||||
}
|
||||
@@ -680,10 +669,8 @@ point_in_polygon_result point_in_polygon(point<T> const& pt, point_ptr<T> op) {
|
||||
}
|
||||
} else {
|
||||
if (op->next->x > pt.x) {
|
||||
double d =
|
||||
static_cast<double>(op->x - pt.x) *
|
||||
static_cast<double>(op->next->y - pt.y) -
|
||||
static_cast<double>(op->next->x - pt.x) * static_cast<double>(op->y - pt.y);
|
||||
double d = static_cast<double>(op->x - pt.x) * static_cast<double>(op->next->y - pt.y) -
|
||||
static_cast<double>(op->next->x - pt.x) * static_cast<double>(op->y - pt.y);
|
||||
if (value_is_zero(d)) {
|
||||
return point_on_polygon;
|
||||
}
|
||||
@@ -705,8 +692,7 @@ point_in_polygon_result point_in_polygon(point<T> const& pt, point_ptr<T> op) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
point_in_polygon_result point_in_polygon(mapbox::geometry::point<double> const& pt,
|
||||
point_ptr<T> op) {
|
||||
point_in_polygon_result point_in_polygon(mapbox::geometry::point<double> const& pt, point_ptr<T> op) {
|
||||
// returns 0 if false, +1 if true, -1 if pt ON polygon boundary
|
||||
point_in_polygon_result result = point_outside_polygon;
|
||||
point_ptr<T> startOp = op;
|
||||
@@ -732,8 +718,7 @@ point_in_polygon_result point_in_polygon(mapbox::geometry::point<double> const&
|
||||
result = point_outside_polygon;
|
||||
}
|
||||
} else {
|
||||
double d =
|
||||
(op_x - pt.x) * (op_next_y - pt.y) - (op_next_x - pt.x) * (op_y - pt.y);
|
||||
double d = (op_x - pt.x) * (op_next_y - pt.y) - (op_next_x - pt.x) * (op_y - pt.y);
|
||||
if (value_is_zero(d)) {
|
||||
return point_on_polygon;
|
||||
}
|
||||
@@ -749,8 +734,7 @@ point_in_polygon_result point_in_polygon(mapbox::geometry::point<double> const&
|
||||
}
|
||||
} else {
|
||||
if (op_next_x > pt.x) {
|
||||
double d =
|
||||
(op_x - pt.x) * (op_next_y - pt.y) - (op_next_x - pt.x) * (op_y - pt.y);
|
||||
double d = (op_x - pt.x) * (op_next_y - pt.y) - (op_next_x - pt.x) * (op_y - pt.y);
|
||||
if (value_is_zero(d)) {
|
||||
return point_on_polygon;
|
||||
}
|
||||
@@ -793,7 +777,8 @@ template <typename T>
|
||||
mapbox::geometry::point<double> centroid_of_points(point_ptr<T> edge) {
|
||||
point_ptr<T> prev = edge->prev;
|
||||
point_ptr<T> next = edge->next;
|
||||
return { static_cast<double>(prev->x + edge->x + next->x) / 3.0, static_cast<double>(prev->y + edge->y + next->y) / 3.0 };
|
||||
return { static_cast<double>(prev->x + edge->x + next->x) / 3.0,
|
||||
static_cast<double>(prev->y + edge->y + next->y) / 3.0 };
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -819,8 +804,7 @@ point_in_polygon_result inside_or_outside_special(point_ptr<T> first_pt, point_p
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool box2_contains_box1(mapbox::geometry::box<T> const& box1,
|
||||
mapbox::geometry::box<T> const& box2) {
|
||||
bool box2_contains_box1(mapbox::geometry::box<T> const& box1, mapbox::geometry::box<T> const& box2) {
|
||||
return (box2.max.x >= box1.max.x && box2.max.y >= box1.max.y && box2.min.x <= box1.min.x &&
|
||||
box2.min.y <= box1.min.y);
|
||||
}
|
||||
@@ -847,6 +831,6 @@ bool poly2_contains_poly1(ring_ptr<T> ring1, ring_ptr<T> ring2) {
|
||||
point_in_polygon_result res = inside_or_outside_special(outpt1, outpt2);
|
||||
return res == point_inside_polygon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -12,13 +12,20 @@ namespace wagyu {
|
||||
template <typename T>
|
||||
using scanbeam_list = std::vector<T>;
|
||||
|
||||
template <typename T>
|
||||
void insert_sorted_scanbeam(scanbeam_list<T>& scanbeam, T& t) {
|
||||
typename scanbeam_list<T>::iterator i = std::lower_bound(scanbeam.begin(), scanbeam.end(), t);
|
||||
if (i == scanbeam.end() || t < *i) {
|
||||
scanbeam.insert(i, t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool pop_from_scanbeam(T& Y, scanbeam_list<T>& scanbeam) {
|
||||
if (scanbeam.empty()) {
|
||||
return false;
|
||||
}
|
||||
std::sort(scanbeam.begin(), scanbeam.end());
|
||||
scanbeam.erase(std::unique(scanbeam.begin(), scanbeam.end()), scanbeam.end());
|
||||
|
||||
Y = scanbeam.back();
|
||||
scanbeam.pop_back();
|
||||
return true;
|
||||
@@ -27,10 +34,12 @@ bool pop_from_scanbeam(T& Y, scanbeam_list<T>& scanbeam) {
|
||||
template <typename T>
|
||||
void setup_scanbeam(local_minimum_list<T>& minima_list, scanbeam_list<T>& scanbeam) {
|
||||
|
||||
scanbeam.reserve(minima_list.size());
|
||||
for (auto lm = minima_list.begin(); lm != minima_list.end(); ++lm) {
|
||||
scanbeam.push_back(lm->y);
|
||||
}
|
||||
std::sort(scanbeam.begin(), scanbeam.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -35,9 +35,7 @@ struct hp_intersection_swap {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void process_hot_pixel_intersections(T top_y,
|
||||
active_bound_list<T>& active_bounds,
|
||||
ring_manager<T>& manager) {
|
||||
void process_hot_pixel_intersections(T top_y, active_bound_list<T>& active_bounds, ring_manager<T>& manager) {
|
||||
if (active_bounds.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -121,8 +119,7 @@ void process_hot_pixel_edges_at_top_of_scanbeam(T top_y,
|
||||
++bnd;
|
||||
}
|
||||
}
|
||||
active_bounds.erase(std::remove(active_bounds.begin(), active_bounds.end(), nullptr),
|
||||
active_bounds.end());
|
||||
active_bounds.erase(std::remove(active_bounds.begin(), active_bounds.end(), nullptr), active_bounds.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -144,11 +141,11 @@ void insert_local_minima_into_ABL_hot_pixel(T top_y,
|
||||
right_bound.current_x = static_cast<double>(right_bound.current_edge->bot.x);
|
||||
auto lb_abl_itr = insert_bound_into_ABL(left_bound, right_bound, active_bounds);
|
||||
if (!current_edge_is_horizontal<T>(lb_abl_itr)) {
|
||||
scanbeam.push_back((*lb_abl_itr)->current_edge->top.y);
|
||||
insert_sorted_scanbeam(scanbeam, (*lb_abl_itr)->current_edge->top.y);
|
||||
}
|
||||
auto rb_abl_itr = std::next(lb_abl_itr);
|
||||
if (!current_edge_is_horizontal<T>(rb_abl_itr)) {
|
||||
scanbeam.push_back((*rb_abl_itr)->current_edge->top.y);
|
||||
insert_sorted_scanbeam(scanbeam, (*rb_abl_itr)->current_edge->top.y);
|
||||
}
|
||||
++lm;
|
||||
}
|
||||
@@ -182,14 +179,13 @@ void build_hot_pixels(local_minimum_list<T>& minima_list, ring_manager<T>& manag
|
||||
|
||||
process_hot_pixel_intersections(scanline_y, active_bounds, manager);
|
||||
|
||||
insert_local_minima_into_ABL_hot_pixel(scanline_y, minima_sorted, current_lm, active_bounds,
|
||||
manager, scanbeam);
|
||||
insert_local_minima_into_ABL_hot_pixel(scanline_y, minima_sorted, current_lm, active_bounds, manager, scanbeam);
|
||||
|
||||
process_hot_pixel_edges_at_top_of_scanbeam(scanline_y, scanbeam, active_bounds, manager);
|
||||
}
|
||||
preallocate_point_memory(manager, manager.hot_pixels.size());
|
||||
sort_hot_pixels(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -113,13 +113,11 @@ bool find_intersect_loop(std::unordered_multimap<ring_ptr<T>, point_ptr_pair<T>>
|
||||
for (auto& it = range.first; it != range.second;) {
|
||||
ring_ptr<T> it_ring1 = it->second.op1->ring;
|
||||
ring_ptr<T> it_ring2 = it->second.op2->ring;
|
||||
if (!it_ring1 || !it_ring2 || it_ring1 != ring_search ||
|
||||
(!it_ring1->is_hole() && !it_ring2->is_hole())) {
|
||||
if (!it_ring1 || !it_ring2 || it_ring1 != ring_search || (!it_ring1->is_hole() && !it_ring2->is_hole())) {
|
||||
it = dupe_ring.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (it_ring2 == ring_origin &&
|
||||
(ring_parent == it_ring2 || ring_parent == it_ring2->parent) &&
|
||||
if (it_ring2 == ring_origin && (ring_parent == it_ring2 || ring_parent == it_ring2->parent) &&
|
||||
*prev_pt != *it->second.op2 && *orig_pt != *it->second.op2) {
|
||||
iList.emplace_front(ring_search, it->second);
|
||||
return true;
|
||||
@@ -130,16 +128,15 @@ bool find_intersect_loop(std::unordered_multimap<ring_ptr<T>, point_ptr_pair<T>>
|
||||
auto range = dupe_ring.equal_range(ring_search);
|
||||
visited.insert(ring_search);
|
||||
// Check for connection through chain of other intersections
|
||||
for (auto& it = range.first;
|
||||
it != range.second && it != dupe_ring.end() && it->first == ring_search; ++it) {
|
||||
for (auto& it = range.first; it != range.second && it != dupe_ring.end() && it->first == ring_search; ++it) {
|
||||
ring_ptr<T> it_ring = it->second.op2->ring;
|
||||
if (visited.count(it_ring) > 0 || it_ring == nullptr ||
|
||||
(ring_parent != it_ring && ring_parent != it_ring->parent) ||
|
||||
value_is_zero(it_ring->area()) || *prev_pt == *it->second.op2) {
|
||||
(ring_parent != it_ring && ring_parent != it_ring->parent) || value_is_zero(it_ring->area()) ||
|
||||
*prev_pt == *it->second.op2) {
|
||||
continue;
|
||||
}
|
||||
if (find_intersect_loop(dupe_ring, iList, ring_parent, ring_origin, it_ring, visited,
|
||||
orig_pt, it->second.op2, rings)) {
|
||||
if (find_intersect_loop(dupe_ring, iList, ring_parent, ring_origin, it_ring, visited, orig_pt, it->second.op2,
|
||||
rings)) {
|
||||
iList.emplace_front(ring_search, it->second);
|
||||
return true;
|
||||
}
|
||||
@@ -190,20 +187,17 @@ point_vector<T> sort_ring_points(ring_ptr<T> r) {
|
||||
point_itr = point_itr->next;
|
||||
}
|
||||
sorted_points.push_back(last_point);
|
||||
std::stable_sort(sorted_points.begin(), sorted_points.end(),
|
||||
[](point_ptr<T> const& pt1, point_ptr<T> const& pt2) {
|
||||
if (pt1->y != pt2->y) {
|
||||
return (pt1->y > pt2->y);
|
||||
}
|
||||
return (pt1->x < pt2->x);
|
||||
});
|
||||
std::stable_sort(sorted_points.begin(), sorted_points.end(), [](point_ptr<T> const& pt1, point_ptr<T> const& pt2) {
|
||||
if (pt1->y != pt2->y) {
|
||||
return (pt1->y > pt2->y);
|
||||
}
|
||||
return (pt1->x < pt2->x);
|
||||
});
|
||||
return sorted_points;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ring_ptr<T> correct_self_intersection(point_ptr<T> pt1,
|
||||
point_ptr<T> pt2,
|
||||
ring_manager<T>& manager) {
|
||||
ring_ptr<T> correct_self_intersection(point_ptr<T> pt1, point_ptr<T> pt2, ring_manager<T>& manager) {
|
||||
if (pt1->ring != pt2->ring) {
|
||||
return static_cast<ring_ptr<T>>(nullptr);
|
||||
}
|
||||
@@ -263,9 +257,7 @@ void correct_repeated_points(ring_manager<T>& manager,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void find_and_correct_repeated_points(ring_ptr<T> r,
|
||||
ring_manager<T>& manager,
|
||||
ring_vector<T>& new_rings) {
|
||||
void find_and_correct_repeated_points(ring_ptr<T> r, ring_manager<T>& manager, ring_vector<T>& new_rings) {
|
||||
auto sorted_points = sort_ring_points(r);
|
||||
// Find sets of repeated points
|
||||
std::size_t count = 0;
|
||||
@@ -342,9 +334,7 @@ bool find_parent_in_tree(ring_ptr<T> r, ring_ptr<T> possible_parent, ring_manage
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assign_new_ring_parents(ring_manager<T>& manager,
|
||||
ring_ptr<T> original_ring,
|
||||
ring_vector<T>& new_rings) {
|
||||
void assign_new_ring_parents(ring_manager<T>& manager, ring_ptr<T> original_ring, ring_vector<T>& new_rings) {
|
||||
|
||||
// First lets remove any rings that have zero area
|
||||
// or have no points
|
||||
@@ -383,18 +373,16 @@ void assign_new_ring_parents(ring_manager<T>& manager,
|
||||
// The new ring is a child of original ring
|
||||
// Check the
|
||||
assign_as_child(new_rings.front(), original_ring, manager);
|
||||
reassign_children_if_necessary(new_rings.front(), original_ring->parent, manager,
|
||||
new_rings);
|
||||
reassign_children_if_necessary(new_rings.front(), original_ring->parent, manager, new_rings);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we want to sort rings from the largest in absolute area to the smallest
|
||||
// as we will assign the rings with the largest areas first
|
||||
std::stable_sort(new_rings.begin(), new_rings.end(),
|
||||
[](ring_ptr<T> const& r1, ring_ptr<T> const& r2) {
|
||||
return std::fabs(r1->area()) > std::fabs(r2->area());
|
||||
});
|
||||
std::stable_sort(new_rings.begin(), new_rings.end(), [](ring_ptr<T> const& r1, ring_ptr<T> const& r2) {
|
||||
return std::fabs(r1->area()) > std::fabs(r2->area());
|
||||
});
|
||||
|
||||
for (auto r_itr = new_rings.begin(); r_itr != new_rings.end(); ++r_itr) {
|
||||
double new_ring_area = (*r_itr)->area();
|
||||
@@ -420,8 +408,7 @@ void assign_new_ring_parents(ring_manager<T>& manager,
|
||||
}
|
||||
} else {
|
||||
if (find_parent_in_tree(*r_itr, *s_itr, manager)) {
|
||||
reassign_children_if_necessary(*r_itr, original_ring->parent, manager,
|
||||
new_rings);
|
||||
reassign_children_if_necessary(*r_itr, original_ring->parent, manager, new_rings);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@@ -482,11 +469,10 @@ bool correct_ring_self_intersections(ring_manager<T>& manager, ring_ptr<T> r, bo
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void process_single_intersection(
|
||||
std::unordered_multimap<ring_ptr<T>, point_ptr_pair<T>>& connection_map,
|
||||
point_ptr<T> op_j,
|
||||
point_ptr<T> op_k,
|
||||
ring_manager<T>& manager) {
|
||||
void process_single_intersection(std::unordered_multimap<ring_ptr<T>, point_ptr_pair<T>>& connection_map,
|
||||
point_ptr<T> op_j,
|
||||
point_ptr<T> op_k,
|
||||
ring_manager<T>& manager) {
|
||||
ring_ptr<T> ring_j = op_j->ring;
|
||||
ring_ptr<T> ring_k = op_k->ring;
|
||||
if (ring_j == ring_k) {
|
||||
@@ -560,14 +546,13 @@ void process_single_intersection(
|
||||
std::set<ring_ptr<T>> visited;
|
||||
visited.insert(ring_search);
|
||||
// Check for connection through chain of other intersections
|
||||
for (auto& it = range.first;
|
||||
it != range.second && it != connection_map.end() && it->first == ring_search; ++it) {
|
||||
for (auto& it = range.first; it != range.second && it != connection_map.end() && it->first == ring_search;
|
||||
++it) {
|
||||
ring_ptr<T> it_ring = it->second.op2->ring;
|
||||
if (it_ring != ring_search && *op_origin_2 != *it->second.op2 && it_ring != nullptr &&
|
||||
(ring_parent == it_ring || ring_parent == it_ring->parent) &&
|
||||
!value_is_zero(it_ring->area()) &&
|
||||
find_intersect_loop(connection_map, iList, ring_parent, ring_origin, it_ring,
|
||||
visited, op_origin_2, it->second.op2, manager)) {
|
||||
(ring_parent == it_ring || ring_parent == it_ring->parent) && !value_is_zero(it_ring->area()) &&
|
||||
find_intersect_loop(connection_map, iList, ring_parent, ring_origin, it_ring, visited, op_origin_2,
|
||||
it->second.op2, manager)) {
|
||||
found = true;
|
||||
iList.emplace_front(ring_search, it->second);
|
||||
break;
|
||||
@@ -749,11 +734,10 @@ void process_single_intersection(
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void correct_chained_repeats(
|
||||
ring_manager<T>& manager,
|
||||
std::unordered_multimap<ring_ptr<T>, point_ptr_pair<T>>& connection_map,
|
||||
point_vector_itr<T> const& begin,
|
||||
point_vector_itr<T> const& end) {
|
||||
void correct_chained_repeats(ring_manager<T>& manager,
|
||||
std::unordered_multimap<ring_ptr<T>, point_ptr_pair<T>>& connection_map,
|
||||
point_vector_itr<T> const& begin,
|
||||
point_vector_itr<T> const& end) {
|
||||
for (auto itr1 = begin; itr1 != end; ++itr1) {
|
||||
if ((*itr1)->ring == nullptr) {
|
||||
continue;
|
||||
@@ -818,13 +802,12 @@ ring_vector<T> sort_rings_largest_to_smallest(ring_manager<T>& manager) {
|
||||
for (auto& r : manager.rings) {
|
||||
sorted_rings.push_back(&r);
|
||||
}
|
||||
std::stable_sort(sorted_rings.begin(), sorted_rings.end(),
|
||||
[](ring_ptr<T> const& r1, ring_ptr<T> const& r2) {
|
||||
if (!r1->points || !r2->points) {
|
||||
return r1->points != nullptr;
|
||||
}
|
||||
return std::fabs(r1->area()) > std::fabs(r2->area());
|
||||
});
|
||||
std::stable_sort(sorted_rings.begin(), sorted_rings.end(), [](ring_ptr<T> const& r1, ring_ptr<T> const& r2) {
|
||||
if (!r1->points || !r2->points) {
|
||||
return r1->points != nullptr;
|
||||
}
|
||||
return std::fabs(r1->area()) > std::fabs(r2->area());
|
||||
});
|
||||
return sorted_rings;
|
||||
}
|
||||
|
||||
@@ -835,13 +818,12 @@ ring_vector<T> sort_rings_smallest_to_largest(ring_manager<T>& manager) {
|
||||
for (auto& r : manager.rings) {
|
||||
sorted_rings.push_back(&r);
|
||||
}
|
||||
std::stable_sort(sorted_rings.begin(), sorted_rings.end(),
|
||||
[](ring_ptr<T> const& r1, ring_ptr<T> const& r2) {
|
||||
if (!r1->points || !r2->points) {
|
||||
return r1->points != nullptr;
|
||||
}
|
||||
return std::fabs(r1->area()) < std::fabs(r2->area());
|
||||
});
|
||||
std::stable_sort(sorted_rings.begin(), sorted_rings.end(), [](ring_ptr<T> const& r1, ring_ptr<T> const& r2) {
|
||||
if (!r1->points || !r2->points) {
|
||||
return r1->points != nullptr;
|
||||
}
|
||||
return std::fabs(r1->area()) < std::fabs(r2->area());
|
||||
});
|
||||
return sorted_rings;
|
||||
}
|
||||
|
||||
@@ -1049,9 +1031,7 @@ bool has_collinear_edge(point_ptr<T> pt_a, point_ptr<T> pt_b) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void process_collinear_edges_same_ring(point_ptr<T> pt_a,
|
||||
point_ptr<T> pt_b,
|
||||
ring_manager<T>& manager) {
|
||||
void process_collinear_edges_same_ring(point_ptr<T> pt_a, point_ptr<T> pt_b, ring_manager<T>& manager) {
|
||||
ring_ptr<T> original_ring = pt_a->ring;
|
||||
// As they are the same ring that are forming a collinear edge
|
||||
// we should expect the creation of two different rings.
|
||||
@@ -1081,9 +1061,7 @@ void process_collinear_edges_same_ring(point_ptr<T> pt_a,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void process_collinear_edges_different_rings(point_ptr<T> pt_a,
|
||||
point_ptr<T> pt_b,
|
||||
ring_manager<T>& manager) {
|
||||
void process_collinear_edges_different_rings(point_ptr<T> pt_a, point_ptr<T> pt_b, ring_manager<T>& manager) {
|
||||
ring_ptr<T> ring_a = pt_a->ring;
|
||||
ring_ptr<T> ring_b = pt_b->ring;
|
||||
bool ring_a_larger = std::fabs(ring_a->area()) > std::fabs(ring_b->area());
|
||||
@@ -1364,6 +1342,6 @@ void correct_topology(ring_manager<T>& manager) {
|
||||
fixed_intersections = correct_self_intersections(manager, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <mapbox/geometry/point.hpp>
|
||||
#include <mapbox/geometry/polygon.hpp>
|
||||
#include <mapbox/geometry/wagyu/almost_equal.hpp>
|
||||
#include <mapbox/geometry/wagyu/point.hpp>
|
||||
|
||||
namespace mapbox {
|
||||
@@ -30,37 +31,48 @@ double area(mapbox::geometry::linear_ring<T> const& poly) {
|
||||
return -a * 0.5;
|
||||
}
|
||||
|
||||
inline bool value_is_zero(double val) {
|
||||
return std::fabs(val) < (5.0 * std::numeric_limits<double>::epsilon());
|
||||
inline bool values_are_equal(double x, double y) {
|
||||
return util::FloatingPoint<double>(x).AlmostEquals(util::FloatingPoint<double>(y));
|
||||
}
|
||||
|
||||
inline bool values_are_equal(double x, double y) {
|
||||
return value_is_zero(x - y);
|
||||
inline bool value_is_zero(double val) {
|
||||
return values_are_equal(val, static_cast<double>(0.0));
|
||||
}
|
||||
|
||||
inline bool greater_than_or_equal(double x, double y) {
|
||||
return x > y || values_are_equal(x, y);
|
||||
}
|
||||
|
||||
inline bool greater_than(double x, double y) {
|
||||
return (!values_are_equal(x, y) && x > y);
|
||||
}
|
||||
|
||||
inline bool less_than(double x, double y) {
|
||||
return (!values_are_equal(x, y) && x < y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool slopes_equal(mapbox::geometry::point<T> const& pt1,
|
||||
mapbox::geometry::point<T> const& pt2,
|
||||
mapbox::geometry::point<T> const& pt3) {
|
||||
return (pt1.y - pt2.y) * (pt2.x - pt3.x) == (pt1.x - pt2.x) * (pt2.y - pt3.y);
|
||||
return static_cast<std::int64_t>(pt1.y - pt2.y) * static_cast<std::int64_t>(pt2.x - pt3.x) ==
|
||||
static_cast<std::int64_t>(pt1.x - pt2.x) * static_cast<std::int64_t>(pt2.y - pt3.y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool slopes_equal(mapbox::geometry::wagyu::point<T> const& pt1,
|
||||
mapbox::geometry::wagyu::point<T> const& pt2,
|
||||
mapbox::geometry::point<T> const& pt3) {
|
||||
return (pt1.y - pt2.y) * (pt2.x - pt3.x) == (pt1.x - pt2.x) * (pt2.y - pt3.y);
|
||||
return static_cast<std::int64_t>(pt1.y - pt2.y) * static_cast<std::int64_t>(pt2.x - pt3.x) ==
|
||||
static_cast<std::int64_t>(pt1.x - pt2.x) * static_cast<std::int64_t>(pt2.y - pt3.y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool slopes_equal(mapbox::geometry::wagyu::point<T> const& pt1,
|
||||
mapbox::geometry::wagyu::point<T> const& pt2,
|
||||
mapbox::geometry::wagyu::point<T> const& pt3) {
|
||||
return (pt1.y - pt2.y) * (pt2.x - pt3.x) == (pt1.x - pt2.x) * (pt2.y - pt3.y);
|
||||
return static_cast<std::int64_t>(pt1.y - pt2.y) * static_cast<std::int64_t>(pt2.x - pt3.x) ==
|
||||
static_cast<std::int64_t>(pt1.x - pt2.x) * static_cast<std::int64_t>(pt2.y - pt3.y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -68,7 +80,8 @@ bool slopes_equal(mapbox::geometry::point<T> const& pt1,
|
||||
mapbox::geometry::point<T> const& pt2,
|
||||
mapbox::geometry::point<T> const& pt3,
|
||||
mapbox::geometry::point<T> const& pt4) {
|
||||
return (pt1.y - pt2.y) * (pt3.x - pt4.x) == (pt1.x - pt2.x) * (pt3.y - pt4.y);
|
||||
return static_cast<std::int64_t>(pt1.y - pt2.y) * static_cast<std::int64_t>(pt3.x - pt4.x) ==
|
||||
static_cast<std::int64_t>(pt1.x - pt2.x) * static_cast<std::int64_t>(pt3.y - pt4.y);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -80,6 +93,6 @@ template <>
|
||||
inline std::int64_t wround<std::int64_t>(double value) {
|
||||
return ::llround(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -42,25 +42,23 @@ void execute_vatti(local_minimum_list<T>& minima_list,
|
||||
|
||||
while (pop_from_scanbeam(scanline_y, scanbeam) || current_lm != minima_sorted.end()) {
|
||||
|
||||
process_intersections(scanline_y, active_bounds, cliptype, subject_fill_type,
|
||||
clip_fill_type, manager);
|
||||
process_intersections(scanline_y, active_bounds, cliptype, subject_fill_type, clip_fill_type, manager);
|
||||
|
||||
update_current_hp_itr(scanline_y, manager);
|
||||
|
||||
// First we process bounds that has already been added to the active bound list --
|
||||
// if the active bound list is empty local minima that are at this scanline_y and
|
||||
// have a horizontal edge at the local minima will be processed
|
||||
process_edges_at_top_of_scanbeam(scanline_y, active_bounds, scanbeam, minima_sorted,
|
||||
current_lm, manager, cliptype, subject_fill_type,
|
||||
clip_fill_type);
|
||||
process_edges_at_top_of_scanbeam(scanline_y, active_bounds, scanbeam, minima_sorted, current_lm, manager,
|
||||
cliptype, subject_fill_type, clip_fill_type);
|
||||
|
||||
// Next we will add local minima bounds to the active bounds list that are on the local
|
||||
// minima queue at
|
||||
// this current scanline_y
|
||||
insert_local_minima_into_ABL(scanline_y, minima_sorted, current_lm, active_bounds, manager,
|
||||
scanbeam, cliptype, subject_fill_type, clip_fill_type);
|
||||
insert_local_minima_into_ABL(scanline_y, minima_sorted, current_lm, active_bounds, manager, scanbeam, cliptype,
|
||||
subject_fill_type, clip_fill_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
#include <mapbox/geometry/wagyu/build_local_minima_list.hpp>
|
||||
#include <mapbox/geometry/wagyu/build_result.hpp>
|
||||
#include <mapbox/geometry/wagyu/config.hpp>
|
||||
#include <mapbox/geometry/wagyu/interrupt.hpp>
|
||||
#include <mapbox/geometry/wagyu/local_minimum.hpp>
|
||||
#include <mapbox/geometry/wagyu/snap_rounding.hpp>
|
||||
#include <mapbox/geometry/wagyu/topology_correction.hpp>
|
||||
#include <mapbox/geometry/wagyu/vatti.hpp>
|
||||
|
||||
#define WAGYU_MAJOR_VERSION 0
|
||||
#define WAGYU_MINOR_VERSION 4
|
||||
#define WAGYU_PATCH_VERSION 3
|
||||
#define WAGYU_MINOR_VERSION 5
|
||||
#define WAGYU_PATCH_VERSION 0
|
||||
|
||||
#define WAGYU_VERSION \
|
||||
(WAGYU_MAJOR_VERSION * 100000) + (WAGYU_MINOR_VERSION * 100) + (WAGYU_PATCH_VERSION)
|
||||
#define WAGYU_VERSION (WAGYU_MAJOR_VERSION * 100000) + (WAGYU_MINOR_VERSION * 100) + (WAGYU_PATCH_VERSION)
|
||||
|
||||
namespace mapbox {
|
||||
namespace geometry {
|
||||
@@ -44,14 +44,12 @@ public:
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
bool add_ring(mapbox::geometry::linear_ring<T2> const& pg,
|
||||
polygon_type p_type = polygon_type_subject) {
|
||||
bool add_ring(mapbox::geometry::linear_ring<T2> const& pg, polygon_type p_type = polygon_type_subject) {
|
||||
return add_linear_ring(pg, minima_list, p_type);
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
bool add_polygon(mapbox::geometry::polygon<T2> const& ppg,
|
||||
polygon_type p_type = polygon_type_subject) {
|
||||
bool add_polygon(mapbox::geometry::polygon<T2> const& ppg, polygon_type p_type = polygon_type_subject) {
|
||||
bool result = false;
|
||||
for (auto const& r : ppg) {
|
||||
if (add_ring(r, p_type)) {
|
||||
@@ -125,10 +123,16 @@ public:
|
||||
|
||||
ring_manager<T> manager;
|
||||
|
||||
interrupt_check(); // Check for interruptions
|
||||
|
||||
build_hot_pixels(minima_list, manager);
|
||||
|
||||
interrupt_check(); // Check for interruptions
|
||||
|
||||
execute_vatti(minima_list, manager, cliptype, subject_fill_type, clip_fill_type);
|
||||
|
||||
interrupt_check(); // Check for interruptions
|
||||
|
||||
correct_topology(manager);
|
||||
|
||||
build_result(solution, manager, reverse_output);
|
||||
@@ -136,6 +140,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace wagyu
|
||||
} // namespace geometry
|
||||
} // namespace mapbox
|
||||
|
||||
Reference in New Issue
Block a user