From cf0b75e26ac9114df9334d2b5a06a9da360e36c7 Mon Sep 17 00:00:00 2001 From: Daria Volvenkova Date: Wed, 2 Mar 2016 14:04:17 +0300 Subject: [PATCH] Area and line features clipped by tile rect. --- drape_frontend/apply_feature_functors.cpp | 123 ++++++------ drape_frontend/apply_feature_functors.hpp | 9 +- drape_frontend/rule_drawer.cpp | 12 +- geometry/clipping.cpp | 117 ++++++++++++ geometry/clipping.hpp | 20 ++ geometry/geometry.pro | 2 + geometry/geometry_tests/clipping_test.cpp | 210 +++++++++++++++++++++ geometry/geometry_tests/geometry_tests.pro | 1 + 8 files changed, 430 insertions(+), 64 deletions(-) create mode 100644 geometry/clipping.cpp create mode 100644 geometry/clipping.hpp create mode 100644 geometry/geometry_tests/clipping_test.cpp diff --git a/drape_frontend/apply_feature_functors.cpp b/drape_frontend/apply_feature_functors.cpp index 21d8c14709..9b5af5cb4f 100644 --- a/drape_frontend/apply_feature_functors.cpp +++ b/drape_frontend/apply_feature_functors.cpp @@ -13,6 +13,8 @@ #include "indexer/drawing_rules.hpp" #include "indexer/drules_include.hpp" +#include "geometry/clipping.hpp" + #include "drape/color.hpp" #include "drape/stipple_pen_resource.hpp" #include "drape/utils/projection.hpp" @@ -324,11 +326,12 @@ void ApplyPointFeature::Finish() } } -ApplyAreaFeature::ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, float minPosZ, float posZ, - int minVisibleScale, uint8_t rank, CaptionDescription const & captions) +ApplyAreaFeature::ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, m2::RectD tileRect, float minPosZ, + float posZ, int minVisibleScale, uint8_t rank, CaptionDescription const & captions) : TBase(insertShape, id, minVisibleScale, rank, captions, posZ) , m_minPosZ(minPosZ) , m_isBuilding(posZ > 0.0f) + , m_tileRect(tileRect) {} void ApplyAreaFeature::operator()(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) @@ -339,17 +342,17 @@ void ApplyAreaFeature::operator()(m2::PointD const & p1, m2::PointD const & p2, return; } - m_triangles.push_back(p1); + auto const clipFunctor = [this](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + m_triangles.push_back(p1); + m_triangles.push_back(p2); + m_triangles.push_back(p3); + }; + if (m2::CrossProduct(p2 - p1, p3 - p1) < 0) - { - m_triangles.push_back(p2); - m_triangles.push_back(p3); - } + m2::ClipTriangleByRect(m_tileRect, p1, p2, p3, clipFunctor); else - { - m_triangles.push_back(p3); - m_triangles.push_back(p2); - } + m2::ClipTriangleByRect(m_tileRect, p1, p3, p2, clipFunctor); } void ApplyAreaFeature::ProcessBuildingPolygon(m2::PointD const & p1, m2::PointD const & p2, @@ -367,20 +370,18 @@ void ApplyAreaFeature::ProcessBuildingPolygon(m2::PointD const & p1, m2::PointD if (fabs(crossProduct) < kEps) return; - m_triangles.push_back(p1); - - if (crossProduct < 0) + auto const clipFunctor = [this](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) { + m_triangles.push_back(p1); m_triangles.push_back(p2); m_triangles.push_back(p3); BuildEdges(GetIndex(p1), GetIndex(p2), GetIndex(p3)); - } + }; + + if (crossProduct < 0) + m2::ClipTriangleByRect(m_tileRect, p1, p2, p3, clipFunctor); else - { - m_triangles.push_back(p3); - m_triangles.push_back(p2); - BuildEdges(GetIndex(p1), GetIndex(p3), GetIndex(p2)); - } + m2::ClipTriangleByRect(m_tileRect, p1, p3, p2, clipFunctor); } int ApplyAreaFeature::GetIndex(m2::PointD const & pt) @@ -492,7 +493,7 @@ void ApplyAreaFeature::ProcessRule(Stylist::TRuleWrapper const & rule) TBase::ProcessRule(rule); } -ApplyLineFeature::ApplyLineFeature(TInsertShapeFn const & insertShape, FeatureID const & id, +ApplyLineFeature::ApplyLineFeature(TInsertShapeFn const & insertShape, FeatureID const & id, m2::RectD tileRect, int minVisibleScale, uint8_t rank, CaptionDescription const & captions, double currentScaleGtoP, bool simplify, size_t pointsCount) : TBase(insertShape, id, minVisibleScale, rank, captions) @@ -502,6 +503,7 @@ ApplyLineFeature::ApplyLineFeature(TInsertShapeFn const & insertShape, FeatureID , m_initialPointsCount(pointsCount) , m_shieldDepth(0.0) , m_shieldRule(nullptr) + , m_tileRect(tileRect) #ifdef CALC_FILTERED_POINTS , m_readedCount(0) #endif @@ -555,6 +557,11 @@ void ApplyLineFeature::ProcessRule(Stylist::TRuleWrapper const & rule) LineDefProto const * pLineRule = pRule->GetLine(); ShieldRuleProto const * pShieldRule = pRule->GetShield(); + m_clippedSplines = m2::ClipSplineByRect(m_tileRect, m_spline); + + if (m_clippedSplines.empty()) + return; + if (pCaptionRule != nullptr && pCaptionRule->height() > 2 && !m_captions.GetPathName().empty() && isWay) { @@ -570,7 +577,8 @@ void ApplyLineFeature::ProcessRule(Stylist::TRuleWrapper const & rule) params.m_textFont = fontDecl; params.m_baseGtoPScale = m_currentScaleGtoP; - m_insertShape(make_unique_dp(m_spline, params)); + for (auto const & spline : m_clippedSplines) + m_insertShape(make_unique_dp(spline, params)); } if (pLineRule != nullptr) @@ -588,7 +596,8 @@ void ApplyLineFeature::ProcessRule(Stylist::TRuleWrapper const & rule) params.m_step = symRule.step() * mainScale; params.m_baseGtoPScale = m_currentScaleGtoP; - m_insertShape(make_unique_dp(m_spline, params)); + for (auto const & spline : m_clippedSplines) + m_insertShape(make_unique_dp(spline, params)); } else { @@ -599,7 +608,8 @@ void ApplyLineFeature::ProcessRule(Stylist::TRuleWrapper const & rule) params.m_rank = m_rank; params.m_baseGtoPScale = m_currentScaleGtoP; - m_insertShape(make_unique_dp(m_spline, params)); + for (auto const & spline : m_clippedSplines) + m_insertShape(make_unique_dp(spline, params)); } } @@ -616,7 +626,7 @@ void ApplyLineFeature::Finish() LinesStat::Get().InsertLine(m_id, m_currentScaleGtoP, m_readedCount, m_spline->GetSize()); #endif - if (m_shieldRule == nullptr) + if (m_shieldRule == nullptr || m_clippedSplines.empty()) return; string const & roadNumber = m_captions.GetRoadNumber(); @@ -626,40 +636,43 @@ void ApplyLineFeature::Finish() dp::FontDecl font; ShieldRuleProtoToFontDecl(m_shieldRule, font); - double const pathPixelLength = m_spline->GetLength() * m_currentScaleGtoP; - int const textHeight = static_cast(font.m_size); + float const mainScale = df::VisualParams::Instance().GetVisualScale(); - // I don't know why we draw by this, but it's work before and will work now - if (pathPixelLength > (roadNumber.size() + 2) * textHeight) + TextViewParams viewParams; + viewParams.m_depth = m_shieldDepth; + viewParams.m_minVisibleScale = m_minVisibleScale; + viewParams.m_rank = m_rank; + viewParams.m_anchor = dp::Center; + viewParams.m_featureID = m_id; + viewParams.m_primaryText = roadNumber; + viewParams.m_primaryTextFont = font; + viewParams.m_primaryOffset = m2::PointF(0, 0); + viewParams.m_primaryOptional = true; + viewParams.m_secondaryOptional = true; + viewParams.m_extendingSize = m_shieldRule->has_min_distance() ? mainScale * m_shieldRule->min_distance() : 0; + + for (auto const & spline : m_clippedSplines) { - // TODO in future we need to choose emptySpace according GtoP scale. - double const emptySpace = 1000.0; - int const count = static_cast((pathPixelLength / emptySpace) + 2); - double const splineStep = pathPixelLength / count; + double const pathPixelLength = spline->GetLength() * m_currentScaleGtoP; + int const textHeight = static_cast(font.m_size); - float const mainScale = df::VisualParams::Instance().GetVisualScale(); - - TextViewParams viewParams; - viewParams.m_depth = m_shieldDepth; - viewParams.m_minVisibleScale = m_minVisibleScale; - viewParams.m_rank = m_rank; - viewParams.m_anchor = dp::Center; - viewParams.m_featureID = m_id; - viewParams.m_primaryText = roadNumber; - viewParams.m_primaryTextFont = font; - viewParams.m_primaryOffset = m2::PointF(0, 0); - viewParams.m_primaryOptional = true; - viewParams.m_secondaryOptional = true; - viewParams.m_extendingSize = m_shieldRule->has_min_distance() ? mainScale * m_shieldRule->min_distance() : 0; - - m2::Spline::iterator it = m_spline.CreateIterator(); - size_t textIndex = 0; - while (!it.BeginAgain()) + // I don't know why we draw by this, but it's work before and will work now + if (pathPixelLength > (roadNumber.size() + 2) * textHeight) { - m_insertShape(make_unique_dp(it.m_pos, viewParams, false /* hasPOI */, - textIndex, false /* affectedByZoomPriority */)); - it.Advance(splineStep); - textIndex++; + // TODO in future we need to choose emptySpace according GtoP scale. + double const emptySpace = 1000.0; + int const count = static_cast((pathPixelLength / emptySpace) + 2); + double const splineStep = pathPixelLength / count; + + m2::Spline::iterator it = spline.CreateIterator(); + size_t textIndex = 0; + while (!it.BeginAgain()) + { + m_insertShape(make_unique_dp(it.m_pos, viewParams, false /* hasPOI */, + textIndex, false /* affectedByZoomPriority */)); + it.Advance(splineStep); + textIndex++; + } } } } diff --git a/drape_frontend/apply_feature_functors.hpp b/drape_frontend/apply_feature_functors.hpp index 7e64afeb74..906338760b 100644 --- a/drape_frontend/apply_feature_functors.hpp +++ b/drape_frontend/apply_feature_functors.hpp @@ -78,8 +78,8 @@ class ApplyAreaFeature : public ApplyPointFeature using TBase = ApplyPointFeature; public: - ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, float minPosZ, float posZ, - int minVisibleScale, uint8_t rank, CaptionDescription const & captions); + ApplyAreaFeature(TInsertShapeFn const & insertShape, FeatureID const & id, m2::RectD tileRect, float minPosZ, + float posZ, int minVisibleScale, uint8_t rank, CaptionDescription const & captions); using TBase::operator (); @@ -103,6 +103,7 @@ private: vector> m_edges; float const m_minPosZ; bool const m_isBuilding; + m2::RectD m_tileRect; }; class ApplyLineFeature : public BaseApplyFeature @@ -110,7 +111,7 @@ class ApplyLineFeature : public BaseApplyFeature using TBase = BaseApplyFeature; public: - ApplyLineFeature(TInsertShapeFn const & insertShape, FeatureID const & id, + ApplyLineFeature(TInsertShapeFn const & insertShape, FeatureID const & id, m2::RectD tileRect, int minVisibleScale, uint8_t rank, CaptionDescription const & captions, double currentScaleGtoP, bool simplify, size_t pointsCount); @@ -121,6 +122,7 @@ public: private: m2::SharedSpline m_spline; + vector m_clippedSplines; double m_currentScaleGtoP; double m_sqrScale; m2::PointD m_lastAddedPoint; @@ -128,6 +130,7 @@ private: size_t m_initialPointsCount; double m_shieldDepth; ShieldRuleProto const * m_shieldRule; + m2::RectD m_tileRect; #ifdef CALC_FILTERED_POINTS int m_readedCount; diff --git a/drape_frontend/rule_drawer.cpp b/drape_frontend/rule_drawer.cpp index 5554951e47..f7a8756989 100644 --- a/drape_frontend/rule_drawer.cpp +++ b/drape_frontend/rule_drawer.cpp @@ -87,9 +87,8 @@ void RuleDrawer::operator()(FeatureType const & f) int const zoomLevel = m_context->GetTileKey().m_zoomLevel; m2::RectD const limitRect = f.GetLimitRect(zoomLevel); - m2::RectD const tileRect = m_context->GetTileKey().GetGlobalRect(); - if (!tileRect.IsIntersect(limitRect)) + if (!m_globalRect.IsIntersect(limitRect)) return; Stylist s; @@ -190,12 +189,13 @@ void RuleDrawer::operator()(FeatureType const & f) areaMinHeight = m2::PointD(rectMercator.SizeX(), rectMercator.SizeY()).Length(); } - ApplyAreaFeature apply(insertShape, f.GetID(), areaMinHeight, areaHeight, + ApplyAreaFeature apply(insertShape, f.GetID(), m_globalRect, areaMinHeight, areaHeight, minVisibleScale, f.GetRank(), s.GetCaptionDescription()); f.ForEachTriangle(apply, zoomLevel); - if (s.PointStyleExists()) - apply(feature::GetCenter(f, zoomLevel)); + m2::PointD const featureCenter = feature::GetCenter(f, zoomLevel); + if (s.PointStyleExists() && m_globalRect.IsPointInside(featureCenter)) + apply(featureCenter); if (CheckCancelled()) return; @@ -205,7 +205,7 @@ void RuleDrawer::operator()(FeatureType const & f) } else if (s.LineStyleExists()) { - ApplyLineFeature apply(insertShape, f.GetID(), minVisibleScale, f.GetRank(), + ApplyLineFeature apply(insertShape, f.GetID(), m_globalRect, minVisibleScale, f.GetRank(), s.GetCaptionDescription(), m_currentScaleGtoP, zoomLevel >= kLineSimplifyLevelStart && zoomLevel <= kLineSimplifyLevelEnd, f.GetPointsCount()); diff --git a/geometry/clipping.cpp b/geometry/clipping.cpp new file mode 100644 index 0000000000..0e708cc680 --- /dev/null +++ b/geometry/clipping.cpp @@ -0,0 +1,117 @@ +#include "clipping.hpp" + +#include "std/vector.hpp" + +#include +#include +#include +#include + +namespace m2 +{ + +using TPoint = boost::geometry::model::d2::point_xy; +using TPolygon = boost::geometry::model::polygon; +using TLine = boost::geometry::model::linestring; + +void ClipTriangleByRect(m2::RectD const & rect, m2::PointD const & p1, + m2::PointD const & p2, m2::PointD const & p3, + ClipTriangleByRectResultIt const & resultIterator) +{ + if (resultIterator == nullptr) + return; + + if (rect.IsPointInside(p1) && rect.IsPointInside(p2) && rect.IsPointInside(p3)) + { + resultIterator(p1, p2, p3); + return; + } + + m2::PointD const rt = rect.RightTop(); + m2::PointD const rb = rect.RightBottom(); + m2::PointD const lt = rect.LeftTop(); + m2::PointD const lb = rect.LeftBottom(); + TPolygon rectanglePoly; + boost::geometry::assign_points(rectanglePoly, + vector{ TPoint(lt.x, lt.y), TPoint(rt.x, rt.y), + TPoint(rb.x, rb.y), TPoint(lb.x, lb.y), + TPoint(lt.x, lt.y) }); + TPolygon trianglePoly; + boost::geometry::assign_points(trianglePoly, + vector{ TPoint(p1.x, p1.y), TPoint(p2.x, p2.y), + TPoint(p3.x, p3.y), TPoint(p1.x, p1.y) }); + vector output; + if (!boost::geometry::intersection(rectanglePoly, trianglePoly, output) || output.empty()) + return; + + ASSERT_EQUAL(output.size(), 1, ()); + m2::PointD firstPoint; + m2::PointD curPoint; + m2::PointD prevPoint; + size_t counter = 0; + size_t const pointsCount = boost::geometry::num_points(output.front()); + boost::geometry::for_each_point(output.front(), [&resultIterator, &firstPoint, + &curPoint, &prevPoint, &counter, &pointsCount](TPoint const & p) + { + if (counter == 0) + { + firstPoint = m2::PointD(p.x(), p.y()); + curPoint = firstPoint; + } + else + { + prevPoint = curPoint; + curPoint = m2::PointD(p.x(), p.y()); + } + counter++; + + if (counter > 2 && counter < pointsCount) + resultIterator(firstPoint, prevPoint, curPoint); + }); +} + +vector ClipSplineByRect(m2::RectD const & rect, m2::SharedSpline const & spline) +{ + vector result; + + m2::RectD splineRect; + for (m2::PointD const & p : spline->GetPath()) + splineRect.Add(p); + + if (rect.IsRectInside(splineRect)) + { + result.push_back(spline); + return result; + } + + m2::PointD const rt = rect.RightTop(); + m2::PointD const rb = rect.RightBottom(); + m2::PointD const lt = rect.LeftTop(); + m2::PointD const lb = rect.LeftBottom(); + TPolygon rectanglePoly; + boost::geometry::assign_points(rectanglePoly, + vector{ TPoint(lt.x, lt.y), TPoint(rt.x, rt.y), + TPoint(rb.x, rb.y), TPoint(lb.x, lb.y), + TPoint(lt.x, lt.y) }); + TLine line; + line.reserve(spline->GetSize()); + for (m2::PointD const & p : spline->GetPath()) + line.push_back(TPoint(p.x, p.y)); + + vector output; + if (!boost::geometry::intersection(rectanglePoly, line, output) || output.empty()) + return result; + + for (TLine const & outLine : output) + { + m2::SharedSpline s; + s.Reset(new m2::Spline(outLine.size())); + for (TPoint const & p : outLine) + s->AddPoint(m2::PointD(p.x(), p.y())); + result.push_back(move(s)); + } + + return result; +} + +} // namespace m2; diff --git a/geometry/clipping.hpp b/geometry/clipping.hpp new file mode 100644 index 0000000000..58b7691d44 --- /dev/null +++ b/geometry/clipping.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "rect2d.hpp" +#include "spline.hpp" +#include "triangle2d.hpp" + +#include "std/function.hpp" + +namespace m2 +{ + +using ClipTriangleByRectResultIt = function; + +void ClipTriangleByRect(m2::RectD const & rect, m2::PointD const & p1, + m2::PointD const & p2, m2::PointD const & p3, + ClipTriangleByRectResultIt const & resultIterator); + +vector ClipSplineByRect(m2::RectD const & rect, m2::SharedSpline const & spline); + +} // namespace m2 diff --git a/geometry/geometry.pro b/geometry/geometry.pro index ff07f193ff..bc560f64dd 100644 --- a/geometry/geometry.pro +++ b/geometry/geometry.pro @@ -11,6 +11,7 @@ include($$ROOT_DIR/common.pri) SOURCES += \ algorithm.cpp \ angles.cpp \ + clipping.cpp \ distance_on_sphere.cpp \ latlon.cpp \ mercator.cpp \ @@ -25,6 +26,7 @@ SOURCES += \ HEADERS += \ algorithm.hpp \ angles.hpp \ + clipping.hpp \ any_rect2d.hpp \ avg_vector.hpp \ cellid.hpp \ diff --git a/geometry/geometry_tests/clipping_test.cpp b/geometry/geometry_tests/clipping_test.cpp new file mode 100644 index 0000000000..bfe740d296 --- /dev/null +++ b/geometry/geometry_tests/clipping_test.cpp @@ -0,0 +1,210 @@ +#include "testing/testing.hpp" + +#include "geometry/clipping.hpp" + +namespace +{ + +bool CompareTriangleLists(vector const & list1, vector const & list2) +{ + if (list1.size() != list2.size()) + return false; + + double const kEps = 1e-5; + for (size_t i = 0; i < list1.size(); i++) + if (!list1[i].EqualDxDy(list2[i], kEps)) + return false; + + return true; +} + +bool CompareSplineLists(vector const & list1, vector const & list2) +{ + if (list1.size() != list2.size()) + return false; + + double const kEps = 1e-5; + for (size_t i = 0; i < list1.size(); i++) + { + auto & path1 = list1[i]->GetPath(); + auto & path2 = list2[i]->GetPath(); + if (path1.size() != path2.size()) + return false; + + for (size_t j = 0; j < path1.size(); j++) + if (!path1[j].EqualDxDy(path2[j], kEps)) + return false; + } + + return true; +} + +vector ConstructSplineList(vector> const & segments) +{ + vector result; + result.reserve(segments.size()); + for (size_t i = 0; i < segments.size(); i++) + { + m2::SharedSpline s; + s.Reset(new m2::Spline(segments[i].size())); + for (size_t j = 0; j < segments[i].size(); j++) + s->AddPoint(segments[i][j]); + result.push_back(move(s)); + } + return result; +} + +} // namespace + +UNIT_TEST(Clipping_ClipTriangleByRect) +{ + m2::RectD r(-1.0, -1.0, 1.0, 1.0); + + // Completely inside. + vector result1; + m2::ClipTriangleByRect(r, m2::PointD(0.5, 0.5), m2::PointD(0.5, -0.5), m2::PointD(0.0, 0.0), + [&result1](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result1.push_back(p1); + result1.push_back(p2); + result1.push_back(p3); + }); + vector expectedResult1 = { m2::PointD(0.5, 0.5), m2::PointD(0.5, -0.5), m2::PointD(0.0, 0.0) }; + TEST(CompareTriangleLists(result1, expectedResult1), (result1, expectedResult1)); + + // 1 point inside. + vector result2; + m2::ClipTriangleByRect(r, m2::PointD(0.0, 0.0), m2::PointD(2.0, 2.0), m2::PointD(2.0, -2.0), + [&result2](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result2.push_back(p1); + result2.push_back(p2); + result2.push_back(p3); + }); + vector expectedResult2 = { m2::PointD(1.0, 1.0), m2::PointD(1.0, -1.0), m2::PointD(0.0, 0.0) }; + TEST(CompareTriangleLists(result2, expectedResult2), (result2, expectedResult2)); + + // 2 points inside. + vector result3; + m2::ClipTriangleByRect(r, m2::PointD(0.0, 0.5), m2::PointD(2.0, 0.0), m2::PointD(0.0, -0.5), + [&result3](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result3.push_back(p1); + result3.push_back(p2); + result3.push_back(p3); + }); + vector expectedResult3 = { m2::PointD(1.0, 0.25), m2::PointD(1.0, -0.25), m2::PointD(0.0, -0.5), + m2::PointD(1.0, 0.25), m2::PointD(0.0, -0.5), m2::PointD(0.0, 0.5) }; + TEST(CompareTriangleLists(result3, expectedResult3), (result3, expectedResult3)); + + // 2 edges clipping. + vector result4; + m2::ClipTriangleByRect(r, m2::PointD(0.0, 0.0), m2::PointD(0.0, 1.5), m2::PointD(1.5, 0.0), + [&result4](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result4.push_back(p1); + result4.push_back(p2); + result4.push_back(p3); + }); + vector expectedResult4 = { m2::PointD(0.0, 1.0), m2::PointD(0.5, 1.0), m2::PointD(1.0, 0.5), + m2::PointD(0.0, 1.0), m2::PointD(1.0, 0.5), m2::PointD(1.0, 0.0), + m2::PointD(0.0, 1.0), m2::PointD(1.0, 0.0), m2::PointD(0.0, 0.0) }; + TEST(CompareTriangleLists(result4, expectedResult4), (result4, expectedResult4)); + + // 3 edges clipping. + vector result5; + m2::ClipTriangleByRect(r, m2::PointD(-1.5, 0.0), m2::PointD(0.0, 1.5), m2::PointD(1.5, 0.0), + [&result5](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result5.push_back(p1); + result5.push_back(p2); + result5.push_back(p3); + }); + vector expectedResult5 = { m2::PointD(-0.5, 1.0), m2::PointD(0.5, 1.0), m2::PointD(1.0, 0.5), + m2::PointD(-0.5, 1.0), m2::PointD(1.0, 0.5), m2::PointD(1.0, 0.0), + m2::PointD(-0.5, 1.0), m2::PointD(1.0, 0.0), m2::PointD(-1.0, 0.0), + m2::PointD(-0.5, 1.0), m2::PointD(-1.0, 0.0), m2::PointD(-1.0, 0.5) }; + TEST(CompareTriangleLists(result5, expectedResult5), (result5, expectedResult5)); + + // Completely outside. + vector result6; + m2::ClipTriangleByRect(r, m2::PointD(1.5, 1.5), m2::PointD(1.5, -1.5), m2::PointD(2.0, 0.0), + [&result6](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result6.push_back(p1); + result6.push_back(p2); + result6.push_back(p3); + }); + vector expectedResult6 = {}; + TEST(CompareTriangleLists(result6, expectedResult6), (result6, expectedResult6)); + + // Clip with an angle of rect. + vector result7; + m2::ClipTriangleByRect(r, m2::PointD(0.5, 0.5), m2::PointD(0.5, 2.0), m2::PointD(2.0, 0.5), + [&result7](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result7.push_back(p1); + result7.push_back(p2); + result7.push_back(p3); + }); + vector expectedResult7 = { m2::PointD(0.5, 1.0), m2::PointD(1.0, 1.0), m2::PointD(1.0, 0.5), + m2::PointD(0.5, 1.0), m2::PointD(1.0, 0.5), m2::PointD(0.5, 0.5) }; + TEST(CompareTriangleLists(result7, expectedResult7), (result7, expectedResult7)); + + // Triangle covers rect. + vector result8; + m2::ClipTriangleByRect(r, m2::PointD(0.0, 3.0), m2::PointD(5.0, -2.0), m2::PointD(-5.0, -2.0), + [&result8](m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3) + { + result8.push_back(p1); + result8.push_back(p2); + result8.push_back(p3); + }); + vector expectedResult8 = { m2::PointD(-1.0, 1.0), m2::PointD(1.0, 1.0), m2::PointD(1.0, -1.0), + m2::PointD(-1.0, 1.0), m2::PointD(1.0, -1.0), m2::PointD(-1.0, -1.0) }; + TEST(CompareTriangleLists(result8, expectedResult8), (result8, expectedResult8)); +} + +UNIT_TEST(Clipping_ClipSplineByRect) +{ + m2::RectD r(-1.0, -1.0, 1.0, 1.0); + + // Intersection. + m2::SharedSpline spline1; + spline1.Reset(new m2::Spline(2)); + spline1->AddPoint(m2::PointD(-2.0, 0.0)); + spline1->AddPoint(m2::PointD(2.0, 1.0)); + vector result1 = m2::ClipSplineByRect(r, spline1); + vector expectedResult1 = ConstructSplineList({ { m2::PointD(-1.0, 0.25), m2::PointD(1.0, 0.75) } }); + TEST(CompareSplineLists(result1, expectedResult1), ()); + + // Intersection. Several segments. + m2::SharedSpline spline2; + spline2.Reset(new m2::Spline(4)); + spline2->AddPoint(m2::PointD(-2.0, 0.0)); + spline2->AddPoint(m2::PointD(2.0, 1.0)); + spline2->AddPoint(m2::PointD(0.5, -2.0)); + spline2->AddPoint(m2::PointD(-0.5, -0.5)); + vector result2 = m2::ClipSplineByRect(r, spline2); + vector expectedResult2 = ConstructSplineList({ { m2::PointD(-1.0, 0.25), m2::PointD(1.0, 0.75) }, + { m2::PointD(-0.166666666, -1.0), m2::PointD(-0.5, -0.5) } }); + TEST(CompareSplineLists(result2, expectedResult2), ()); + + // Completely outside. + m2::SharedSpline spline3; + spline3.Reset(new m2::Spline(2)); + spline3->AddPoint(m2::PointD(-2.0, 2.0)); + spline3->AddPoint(m2::PointD(2.0, 3.0)); + vector result3 = m2::ClipSplineByRect(r, spline3); + vector expectedResult3 = {}; + TEST(CompareSplineLists(result3, expectedResult3), ()); + + // Completely inside. + m2::SharedSpline spline4; + spline4.Reset(new m2::Spline(2)); + spline4->AddPoint(m2::PointD(-0.5, 0.0)); + spline4->AddPoint(m2::PointD(0.5, 0.5)); + vector result4 = m2::ClipSplineByRect(r, spline4); + vector expectedResult4 = ConstructSplineList({ { m2::PointD(-0.5, 0.0), m2::PointD(0.5, 0.5) } }); + TEST(CompareSplineLists(result4, expectedResult4), ()); +} diff --git a/geometry/geometry_tests/geometry_tests.pro b/geometry/geometry_tests/geometry_tests.pro index 050968f793..8164b90f71 100644 --- a/geometry/geometry_tests/geometry_tests.pro +++ b/geometry/geometry_tests/geometry_tests.pro @@ -23,6 +23,7 @@ SOURCES += \ angle_test.cpp \ anyrect_test.cpp \ cellid_test.cpp \ + clipping_test.cpp \ common_test.cpp \ covering_test.cpp \ distance_on_sphere_test.cpp \