|
|
|
@ -1,6 +1,6 @@ |
|
|
|
/*
|
|
|
|
Copyright 2008 Intel Corporation |
|
|
|
|
|
|
|
|
|
|
|
Use, modification and distribution are subject to the Boost Software License, |
|
|
|
Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
|
|
|
http://www.boost.org/LICENSE_1_0.txt).
|
|
|
|
@ -22,7 +22,7 @@ namespace boost { namespace polygon { |
|
|
|
template <typename T> |
|
|
|
static inline T round_down(double val) { |
|
|
|
T rounded_val = (T)(val); |
|
|
|
if(val < (double)rounded_val) |
|
|
|
if(val < (double)rounded_val) |
|
|
|
--rounded_val; |
|
|
|
return rounded_val; |
|
|
|
} |
|
|
|
@ -57,11 +57,11 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
|
|
|
|
// copy constructor
|
|
|
|
inline polygon_set_data(const polygon_set_data& that) : |
|
|
|
inline polygon_set_data(const polygon_set_data& that) : |
|
|
|
data_(that.data_), dirty_(that.dirty_), unsorted_(that.unsorted_), is_45_(that.is_45_) {} |
|
|
|
|
|
|
|
// copy constructor
|
|
|
|
template <typename ltype, typename rtype, int op_type> |
|
|
|
template <typename ltype, typename rtype, int op_type> |
|
|
|
inline polygon_set_data(const polygon_set_view<ltype, rtype, op_type>& that); |
|
|
|
|
|
|
|
// destructor
|
|
|
|
@ -150,10 +150,10 @@ namespace boost { namespace polygon { |
|
|
|
insert(polygon_object, is_hole, polygon_concept()); } |
|
|
|
|
|
|
|
template <typename polygon_with_holes_type> |
|
|
|
inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, |
|
|
|
inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, |
|
|
|
polygon_with_holes_concept ) { |
|
|
|
insert(polygon_with_holes_object, is_hole, polygon_concept()); |
|
|
|
for(typename polygon_with_holes_traits<polygon_with_holes_type>::iterator_holes_type itr = |
|
|
|
for(typename polygon_with_holes_traits<polygon_with_holes_type>::iterator_holes_type itr = |
|
|
|
begin_holes(polygon_with_holes_object); |
|
|
|
itr != end_holes(polygon_with_holes_object); ++itr) { |
|
|
|
insert(*itr, !is_hole, polygon_concept()); |
|
|
|
@ -161,12 +161,12 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
|
|
|
|
template <typename polygon_with_holes_type> |
|
|
|
inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, |
|
|
|
inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, |
|
|
|
polygon_45_with_holes_concept ) { |
|
|
|
insert(polygon_with_holes_object, is_hole, polygon_with_holes_concept()); } |
|
|
|
|
|
|
|
template <typename polygon_with_holes_type> |
|
|
|
inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, |
|
|
|
inline void insert(const polygon_with_holes_type& polygon_with_holes_object, bool is_hole, |
|
|
|
polygon_90_with_holes_concept ) { |
|
|
|
insert(polygon_with_holes_object, is_hole, polygon_with_holes_concept()); } |
|
|
|
|
|
|
|
@ -212,7 +212,7 @@ namespace boost { namespace polygon { |
|
|
|
first_point = previous_point = current_point; |
|
|
|
} else { |
|
|
|
if(previous_point != current_point) { |
|
|
|
element_type elem(edge_type(previous_point, current_point), |
|
|
|
element_type elem(edge_type(previous_point, current_point), |
|
|
|
( previous_point.get(HORIZONTAL) == current_point.get(HORIZONTAL) ? -1 : 1) * multiplier); |
|
|
|
insert_clean(elem); |
|
|
|
} |
|
|
|
@ -222,7 +222,7 @@ namespace boost { namespace polygon { |
|
|
|
current_point = first_point; |
|
|
|
if(!first_iteration) { |
|
|
|
if(previous_point != current_point) { |
|
|
|
element_type elem(edge_type(previous_point, current_point), |
|
|
|
element_type elem(edge_type(previous_point, current_point), |
|
|
|
( previous_point.get(HORIZONTAL) == current_point.get(HORIZONTAL) ? -1 : 1) * multiplier); |
|
|
|
insert_clean(elem); |
|
|
|
} |
|
|
|
@ -270,14 +270,14 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// equivalence operator
|
|
|
|
// equivalence operator
|
|
|
|
inline bool operator==(const polygon_set_data& p) const { |
|
|
|
clean(); |
|
|
|
p.clean(); |
|
|
|
return data_ == p.data_; |
|
|
|
} |
|
|
|
|
|
|
|
// inequivalence operator
|
|
|
|
// inequivalence operator
|
|
|
|
inline bool operator!=(const polygon_set_data& p) const { |
|
|
|
return !((*this) == p); |
|
|
|
} |
|
|
|
@ -335,7 +335,7 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
|
|
|
|
void set(const value_type& value) { |
|
|
|
data_ = value; |
|
|
|
data_ = value; |
|
|
|
dirty_ = true; |
|
|
|
unsorted_ = true; |
|
|
|
} |
|
|
|
@ -359,27 +359,10 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
|
|
|
|
inline polygon_set_data& |
|
|
|
resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0) { |
|
|
|
if(!corner_fill_arc) { |
|
|
|
if(resizing < 0) |
|
|
|
return shrink(-resizing); |
|
|
|
if(resizing > 0) |
|
|
|
return bloat(resizing); |
|
|
|
return *this; |
|
|
|
} |
|
|
|
if(resizing == 0) return *this; |
|
|
|
std::list<polygon_with_holes_data<coordinate_type> > pl; |
|
|
|
get(pl); |
|
|
|
clear(); |
|
|
|
for(typename std::list<polygon_with_holes_data<coordinate_type> >::iterator itr = pl.begin(); itr != pl.end(); ++itr) { |
|
|
|
insert_with_resize(*itr, resizing, corner_fill_arc, num_circle_segments); |
|
|
|
} |
|
|
|
clean(); |
|
|
|
return *this; |
|
|
|
} |
|
|
|
resize(coordinate_type resizing, bool corner_fill_arc = false, unsigned int num_circle_segments=0); |
|
|
|
|
|
|
|
template <typename transform_type> |
|
|
|
inline polygon_set_data& |
|
|
|
inline polygon_set_data& |
|
|
|
transform(const transform_type& tr) { |
|
|
|
std::vector<polygon_with_holes_data<T> > polys; |
|
|
|
get(polys); |
|
|
|
@ -393,7 +376,7 @@ namespace boost { namespace polygon { |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
inline polygon_set_data& |
|
|
|
inline polygon_set_data& |
|
|
|
scale_up(typename coordinate_traits<coordinate_type>::unsigned_area_type factor) { |
|
|
|
for(typename value_type::iterator itr = data_.begin(); itr != data_.end(); ++itr) { |
|
|
|
::boost::polygon::scale_up((*itr).first.first, factor); |
|
|
|
@ -401,8 +384,8 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
inline polygon_set_data& |
|
|
|
|
|
|
|
inline polygon_set_data& |
|
|
|
scale_down(typename coordinate_traits<coordinate_type>::unsigned_area_type factor) { |
|
|
|
for(typename value_type::iterator itr = data_.begin(); itr != data_.end(); ++itr) { |
|
|
|
::boost::polygon::scale_down((*itr).first.first, factor); |
|
|
|
@ -412,9 +395,9 @@ namespace boost { namespace polygon { |
|
|
|
dirty_ = true; |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename scaling_type> |
|
|
|
inline polygon_set_data& scale(polygon_set_data& polygon_set, |
|
|
|
inline polygon_set_data& scale(polygon_set_data& polygon_set, |
|
|
|
const scaling_type& scaling) { |
|
|
|
for(typename value_type::iterator itr = begin(); itr != end(); ++itr) { |
|
|
|
::boost::polygon::scale((*itr).first.first, scaling); |
|
|
|
@ -425,37 +408,53 @@ namespace boost { namespace polygon { |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
static inline void compute_offset_edge(point_data<coordinate_type>& pt1, point_data<coordinate_type>& pt2, |
|
|
|
const point_data<coordinate_type>& prev_pt, |
|
|
|
const point_data<coordinate_type>& current_pt, |
|
|
|
coordinate_type distance, int multiplier) { |
|
|
|
coordinate_type dx = current_pt.x() - prev_pt.x(); |
|
|
|
coordinate_type dy = current_pt.y() - prev_pt.y(); |
|
|
|
double ddx = (double)dx; |
|
|
|
double ddy = (double)dy; |
|
|
|
double edge_length = std::sqrt(ddx*ddx + ddy*ddy); |
|
|
|
double dnx = dy; |
|
|
|
double dny = -dx; |
|
|
|
dnx = dnx * (double)distance / edge_length; |
|
|
|
dny = dny * (double)distance / edge_length; |
|
|
|
dnx = std::floor(dnx+0.5); |
|
|
|
dny = std::floor(dny+0.5); |
|
|
|
pt1.x(prev_pt.x() + (coordinate_type)dnx * (coordinate_type)multiplier); |
|
|
|
pt2.x(current_pt.x() + (coordinate_type)dnx * (coordinate_type)multiplier); |
|
|
|
pt1.y(prev_pt.y() + (coordinate_type)dny * (coordinate_type)multiplier); |
|
|
|
pt2.y(current_pt.y() + (coordinate_type)dny * (coordinate_type)multiplier); |
|
|
|
static inline void compute_offset_edge(point_data<long double>& pt1, point_data<long double>& pt2, |
|
|
|
const point_data<long double>& prev_pt, |
|
|
|
const point_data<long double>& current_pt, |
|
|
|
long double distance, int multiplier) { |
|
|
|
long double dx = current_pt.x() - prev_pt.x(); |
|
|
|
long double dy = current_pt.y() - prev_pt.y(); |
|
|
|
long double edge_length = std::sqrt(dx*dx + dy*dy); |
|
|
|
long double dnx = dy; |
|
|
|
long double dny = -dx; |
|
|
|
dnx = dnx * (long double)distance / edge_length; |
|
|
|
dny = dny * (long double)distance / edge_length; |
|
|
|
pt1.x(prev_pt.x() + (long double)dnx * (long double)multiplier); |
|
|
|
pt2.x(current_pt.x() + (long double)dnx * (long double)multiplier); |
|
|
|
pt1.y(prev_pt.y() + (long double)dny * (long double)multiplier); |
|
|
|
pt2.y(current_pt.y() + (long double)dny * (long double)multiplier); |
|
|
|
} |
|
|
|
|
|
|
|
static inline void modify_pt(point_data<coordinate_type>& pt, const point_data<coordinate_type>& prev_pt, |
|
|
|
const point_data<coordinate_type>& current_pt, const point_data<coordinate_type>& next_pt, |
|
|
|
coordinate_type distance, coordinate_type multiplier) { |
|
|
|
std::pair<point_data<coordinate_type>, point_data<coordinate_type> > he1(prev_pt, current_pt), he2(current_pt, next_pt); |
|
|
|
std::pair<point_data<long double>, point_data<long double> > he1, he2; |
|
|
|
he1.first.x((long double)(prev_pt.x())); |
|
|
|
he1.first.y((long double)(prev_pt.y())); |
|
|
|
he1.second.x((long double)(current_pt.x())); |
|
|
|
he1.second.y((long double)(current_pt.y())); |
|
|
|
he2.first.x((long double)(current_pt.x())); |
|
|
|
he2.first.y((long double)(current_pt.y())); |
|
|
|
he2.second.x((long double)(next_pt.x())); |
|
|
|
he2.second.y((long double)(next_pt.y())); |
|
|
|
compute_offset_edge(he1.first, he1.second, prev_pt, current_pt, distance, multiplier); |
|
|
|
compute_offset_edge(he2.first, he2.second, current_pt, next_pt, distance, multiplier); |
|
|
|
typename scanline_base<coordinate_type>::compute_intersection_pack pack; |
|
|
|
if(!pack.compute_lazy_intersection(pt, he1, he2, true, true)) { |
|
|
|
pt = he1.second; //colinear offset edges use shared point
|
|
|
|
typename scanline_base<long double>::compute_intersection_pack pack; |
|
|
|
point_data<long double> rpt; |
|
|
|
point_data<long double> bisectorpt((he1.second.x()+he2.first.x())/2, |
|
|
|
(he1.second.y()+he2.first.y())/2); |
|
|
|
point_data<long double> orig_pt((long double)pt.x(), (long double)pt.y()); |
|
|
|
if(euclidean_distance(bisectorpt, orig_pt) < distance/2) { |
|
|
|
if(!pack.compute_lazy_intersection(rpt, he1, he2, true, false)) { |
|
|
|
rpt = he1.second; //colinear offset edges use shared point
|
|
|
|
} |
|
|
|
} else { |
|
|
|
if(!pack.compute_lazy_intersection(rpt, he1, std::pair<point_data<long double>, point_data<long double> >(orig_pt, bisectorpt), true, false)) { |
|
|
|
rpt = he1.second; //colinear offset edges use shared point
|
|
|
|
} |
|
|
|
} |
|
|
|
pt.x((coordinate_type)(std::floor(rpt.x()+0.5))); |
|
|
|
pt.y((coordinate_type)(std::floor(rpt.y()+0.5))); |
|
|
|
} |
|
|
|
|
|
|
|
static void resize_poly_up(std::vector<point_data<coordinate_type> >& poly, coordinate_type distance, coordinate_type multiplier) { |
|
|
|
@ -566,8 +565,8 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
|
|
|
|
template <typename geometry_type> |
|
|
|
inline polygon_set_data& |
|
|
|
insert_with_resize_dispatch(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, bool hole, |
|
|
|
inline polygon_set_data& |
|
|
|
insert_with_resize_dispatch(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, bool hole, |
|
|
|
polygon_with_holes_concept tag) { |
|
|
|
insert_with_resize_dispatch(poly, resizing, corner_fill_arc, num_circle_segments, hole, polygon_concept()); |
|
|
|
for(typename polygon_with_holes_traits<geometry_type>::iterator_holes_type itr = |
|
|
|
@ -579,14 +578,14 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
|
|
|
|
template <typename geometry_type> |
|
|
|
inline polygon_set_data& |
|
|
|
insert_with_resize_dispatch(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, bool hole, |
|
|
|
inline polygon_set_data& |
|
|
|
insert_with_resize_dispatch(const geometry_type& poly, coordinate_type resizing, bool corner_fill_arc, unsigned int num_circle_segments, bool hole, |
|
|
|
polygon_concept tag) { |
|
|
|
|
|
|
|
if (resizing==0) |
|
|
|
return *this; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// one dimensional used to store CCW/CW flag
|
|
|
|
//direction_1d wdir = winding(poly);
|
|
|
|
// LOW==CLOCKWISE just faster to type
|
|
|
|
@ -615,7 +614,7 @@ namespace boost { namespace polygon { |
|
|
|
|
|
|
|
// for all corners
|
|
|
|
polygon_set_data<T> sizingSet; |
|
|
|
bool sizing_sign = resizing>0; |
|
|
|
bool sizing_sign = resizing<0; |
|
|
|
bool prev_concave = true; |
|
|
|
point_data<T> prev_point; |
|
|
|
//int iCtr=0;
|
|
|
|
@ -631,7 +630,7 @@ namespace boost { namespace polygon { |
|
|
|
point_data<coordinate_type> normal2( third->y()-second->y(), second->x()-third->x()); |
|
|
|
double direction = normal1.x()*normal2.y()- normal2.x()*normal1.y(); |
|
|
|
bool convex = direction>0; |
|
|
|
|
|
|
|
|
|
|
|
bool treat_as_concave = !convex; |
|
|
|
if(sizing_sign) |
|
|
|
treat_as_concave = convex; |
|
|
|
@ -644,12 +643,12 @@ namespace boost { namespace polygon { |
|
|
|
if (prev_concave) |
|
|
|
//TODO missing round_down()
|
|
|
|
curr_prev = point_data<T>(first->x()+v.x(),first->y()+v.y()); |
|
|
|
else |
|
|
|
else |
|
|
|
curr_prev = prev_point; |
|
|
|
|
|
|
|
// around concave corners - insert rectangle
|
|
|
|
// if previous corner is concave it's point info may be ignored
|
|
|
|
if ( treat_as_concave) { |
|
|
|
if ( treat_as_concave) { |
|
|
|
std::vector<point_data<T> > pts; |
|
|
|
|
|
|
|
pts.push_back(point_data<T>(second->x()+v.x(),second->y()+v.y())); |
|
|
|
@ -670,13 +669,13 @@ namespace boost { namespace polygon { |
|
|
|
direction_1d winding; |
|
|
|
winding = convex?COUNTERCLOCKWISE:CLOCKWISE; |
|
|
|
if (make_resizing_vertex_list(pts, curr_prev, prev_concave, *first, *second, *third, resizing |
|
|
|
, num_circle_segments, corner_fill_arc)) |
|
|
|
, num_circle_segments, corner_fill_arc)) |
|
|
|
{ |
|
|
|
if (first_pts.size()) { |
|
|
|
for (unsigned int i=0; i<pts.size(); i++) { |
|
|
|
sizingSet.insert_vertex_sequence(pts[i].begin(),pts[i].end(),winding,false); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
first_pts = pts[0]; |
|
|
|
first_wdir = resize_wdir; |
|
|
|
@ -685,7 +684,7 @@ namespace boost { namespace polygon { |
|
|
|
} |
|
|
|
} |
|
|
|
prev_point = curr_prev; |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
treat_as_concave = true; |
|
|
|
} |
|
|
|
@ -708,7 +707,7 @@ namespace boost { namespace polygon { |
|
|
|
first_pts[first_pts.size()-1]=prev_point; |
|
|
|
} |
|
|
|
sizingSet.insert_vertex_sequence(first_pts.begin(),first_pts.end(),first_wdir,false); |
|
|
|
|
|
|
|
|
|
|
|
polygon_set_data<coordinate_type> tmp; |
|
|
|
|
|
|
|
//insert original shape
|
|
|
|
@ -722,7 +721,7 @@ namespace boost { namespace polygon { |
|
|
|
|
|
|
|
|
|
|
|
inline polygon_set_data& |
|
|
|
interact(const polygon_set_data& that); |
|
|
|
interact(const polygon_set_data& that); |
|
|
|
|
|
|
|
inline bool downcast(polygon_45_set_data<coordinate_type>& result) const { |
|
|
|
if(!is_45_) return false; |
|
|
|
@ -811,7 +810,7 @@ namespace boost { namespace polygon { |
|
|
|
// }
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
inline int make_resizing_vertex_list(std::vector<std::vector<point_data< T> > >& return_points, |
|
|
|
inline int make_resizing_vertex_list(std::vector<std::vector<point_data< T> > >& return_points, |
|
|
|
point_data<T>& curr_prev, bool ignore_prev_point, |
|
|
|
point_data< T> start, point_data<T> middle, point_data< T> end, |
|
|
|
double sizing_distance, unsigned int num_circle_segments, bool corner_fill_arc) { |
|
|
|
@ -844,7 +843,7 @@ namespace boost { namespace polygon { |
|
|
|
int num = make_arc(return_points[return_points.size()-1],mid1_offset,mid2_offset,dmid,sizing_distance,num_circle_segments); |
|
|
|
curr_prev = round_down<T>(mid2_offset); |
|
|
|
return num; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::pair<point_data<double>,point_data<double> > he1(start_offset,mid1_offset); |
|
|
|
@ -882,21 +881,21 @@ namespace boost { namespace polygon { |
|
|
|
// returnPoints will start with the first point after start
|
|
|
|
// returnPoints vector may be empty
|
|
|
|
template <typename T> |
|
|
|
inline int make_arc(std::vector<point_data< T> >& return_points, |
|
|
|
inline int make_arc(std::vector<point_data< T> >& return_points, |
|
|
|
point_data< double> start, point_data< double> end, |
|
|
|
point_data< double> center, double r, unsigned int num_circle_segments) { |
|
|
|
const double our_pi=3.1415926535897932384626433832795028841971; |
|
|
|
|
|
|
|
// derive start and end angles
|
|
|
|
// derive start and end angles
|
|
|
|
double ps = atan2(start.y()-center.y(), start.x()-center.x()); |
|
|
|
double pe = atan2(end.y()-center.y(), end.x()-center.x()); |
|
|
|
if (ps < 0.0) |
|
|
|
if (ps < 0.0) |
|
|
|
ps += 2.0 * our_pi; |
|
|
|
if (pe <= 0.0) |
|
|
|
if (pe <= 0.0) |
|
|
|
pe += 2.0 * our_pi; |
|
|
|
if (ps >= 2.0 * our_pi) |
|
|
|
if (ps >= 2.0 * our_pi) |
|
|
|
ps -= 2.0 * our_pi; |
|
|
|
while (pe <= ps) |
|
|
|
while (pe <= ps) |
|
|
|
pe += 2.0 * our_pi; |
|
|
|
double delta_angle = (2.0 * our_pi) / (double)num_circle_segments; |
|
|
|
if ( start==end) // full circle?
|
|
|
|
@ -942,12 +941,12 @@ namespace boost { namespace polygon { |
|
|
|
inline connectivity_extraction() : ce_(), nodeCount_(0) {} |
|
|
|
inline connectivity_extraction(const connectivity_extraction& that) : ce_(that.ce_), |
|
|
|
nodeCount_(that.nodeCount_) {} |
|
|
|
inline connectivity_extraction& operator=(const connectivity_extraction& that) { |
|
|
|
ce_ = that.ce_; |
|
|
|
inline connectivity_extraction& operator=(const connectivity_extraction& that) { |
|
|
|
ce_ = that.ce_; |
|
|
|
nodeCount_ = that.nodeCount_; {} |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//insert a polygon set graph node, the value returned is the id of the graph node
|
|
|
|
inline unsigned int insert(const polygon_set_data<coordinate_type>& ps) { |
|
|
|
ps.clean(); |
|
|
|
@ -960,7 +959,7 @@ namespace boost { namespace polygon { |
|
|
|
ps.insert(geoObj); |
|
|
|
return insert(ps); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//extract connectivity and store the edges in the graph
|
|
|
|
//graph must be indexable by graph node id and the indexed value must be a std::set of
|
|
|
|
//graph node id
|
|
|
|
@ -996,5 +995,6 @@ namespace boost { namespace polygon { |
|
|
|
#include "detail/polygon_set_view.hpp"
|
|
|
|
|
|
|
|
#include "polygon_set_concept.hpp"
|
|
|
|
#include "detail/minkowski.hpp"
|
|
|
|
#endif
|
|
|
|
|