1 // Copyright (c) 2012, 2020 Tel-Aviv University (Israel).
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org).
5 //
6 // $URL$
7 // $Id$
8 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 // Author(s): Alex Tsui <alextsui05@gmail.com>
11 //            Ahmed Essam <theartful.ae@gmail.com>
12 
13 #include "GraphicsViewCurveInput.h"
14 #include "AlgebraicCurveParser.h"
15 #include "ArrangementTypes.h"
16 #include "ArrangementTypesUtils.h"
17 #include "CurveInputMethods.h"
18 #include "GraphicsViewCurveInputTyped.h"
19 #include "QtMetaTypes.h"
20 #include "Utils/Utils.h"
21 
22 #include <CGAL/polynomial_utils.h>
23 
24 #include <QEvent>
25 #include <QKeyEvent>
26 
27 template <std::size_t I = 0, typename FuncT, typename... Tp>
28 inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &,FuncT)29 for_each(std::tuple<Tp...>&, FuncT)
30 {
31 }
32 
33 template <std::size_t I = 0, typename FuncT, typename... Tp>
34   inline typename std::enable_if <
35   I<sizeof...(Tp), void>::type for_each(std::tuple<Tp...>& t, FuncT f)
36 {
37   f(std::get<I>(t));
38   for_each<I + 1, FuncT, Tp...>(t, f);
39 }
40 
41 namespace CGAL
42 {
43 namespace Qt
44 {
45 
GraphicsViewCurveInputBase(QObject * parent,QGraphicsScene * scene)46 GraphicsViewCurveInputBase::GraphicsViewCurveInputBase(
47   QObject* parent, QGraphicsScene* scene) :
48     Callback(parent, scene),
49     inputMethod(nullptr)
50 {
51 }
52 
setInputMethod(CurveInputMethod * inputMethod_)53 void GraphicsViewCurveInputBase::setInputMethod(CurveInputMethod* inputMethod_)
54 {
55   this->inputMethod = inputMethod_;
56 }
57 
reset()58 void GraphicsViewCurveInputBase::reset()
59 {
60   if (this->inputMethod)
61   {
62     this->inputMethod->reset();
63     this->inputMethod = nullptr;
64   }
65 }
66 
eventFilter(QObject * obj,QEvent * event)67 bool GraphicsViewCurveInputBase::eventFilter(QObject* obj, QEvent* event)
68 {
69   return this->inputMethod->eventFilter(obj, event);
70 }
71 
setColor(QColor c)72 void GraphicsViewCurveInputBase::setColor(QColor c)
73 {
74   this->inputMethod->setColor(c);
75 }
76 
77 template <typename Arr_>
GraphicsViewCurveInput(Arrangement * arrangement_,QObject * parent,QGraphicsScene * scene)78 GraphicsViewCurveInput<Arr_>::GraphicsViewCurveInput(
79   Arrangement* arrangement_, QObject* parent, QGraphicsScene* scene) :
80     GraphicsViewCurveInputBase(parent, scene),
81     arrangement(arrangement_)
82 {
83   this->setDefaultInputMethod(
84     std::integral_constant<
85       bool, std::tuple_size<InputMethodTuple>::value != 0>{});
86   for_each(inputMethods, [&](auto&& it) {
87     it.setScene(scene);
88     it.setCallback(this);
89   });
90   curveGenerator.setTraits(this->arrangement->traits());
91 }
92 
93 template <typename Arr_>
setCurveType(CurveType type)94 void GraphicsViewCurveInput<Arr_>::setCurveType(CurveType type)
95 {
96   this->reset();
97   for_each(inputMethods, [&](auto&& it) {
98     if (it.curveType() == type)
99       this->setInputMethod(static_cast<CurveInputMethod*>(&it));
100   });
101 }
102 
103 template <typename Arr_>
setPointSnapper(PointSnapperBase * snapper_)104 void GraphicsViewCurveInput<Arr_>::setPointSnapper(PointSnapperBase* snapper_)
105 {
106   for_each(inputMethods, [&](auto&& it) { it.setPointSnapper(snapper_); });
107 }
108 
109 template <typename Arr_>
110 template <typename>
setDefaultInputMethod(std::true_type)111 void GraphicsViewCurveInput<Arr_>::setDefaultInputMethod(std::true_type)
112 {
113   this->setInputMethod(&std::get<0>(inputMethods));
114 }
115 
116 template <typename Arr_>
setDefaultInputMethod(std::false_type)117 void GraphicsViewCurveInput<Arr_>::setDefaultInputMethod(std::false_type)
118 {
119 }
120 
121 template <typename Arr_>
generate(CGAL::Object o)122 void GraphicsViewCurveInput<Arr_>::generate(CGAL::Object o)
123 {
124   insertCurve(
125     demo_types::enumFromArrType<Arrangement>(),
126     CGAL::make_object(this->arrangement), o);
127   Q_EMIT CGAL::Qt::GraphicsViewCurveInputBase::modelChanged();
128 }
129 
130 template <typename Arr_>
curveInputDoneEvent(const std::vector<Point_2> & clickedPoints,CurveType type)131 void GraphicsViewCurveInput<Arr_>::curveInputDoneEvent(
132   const std::vector<Point_2>& clickedPoints, CurveType type)
133 {
134   boost::optional<Curve_2> cv =
135     this->curveGenerator.generate(clickedPoints, type);
136   if (cv)
137   {
138     Insert_curve<Arrangement>{}(this->arrangement, *cv);
139     Q_EMIT this->modelChanged();
140   }
141 }
142 
143 // CurveGeneratorBase
144 template <typename ArrTraits_>
generate(const std::vector<Point_2> & clickedPoints,CurveType type)145 auto CurveGeneratorBase<ArrTraits_>::generate(
146   const std::vector<Point_2>& clickedPoints, CurveType type)
147   -> boost::optional<Curve_2>
148 {
149   boost::optional<Curve_2> res;
150   switch (type)
151   {
152   case CurveType::Segment:
153     res = generateSegment(clickedPoints);
154     break;
155   case CurveType::Ray:
156     res = generateRay(clickedPoints);
157     break;
158   case CurveType::Line:
159     res = generateLine(clickedPoints);
160     break;
161   case CurveType::Polyline:
162     res = generatePolyline(clickedPoints);
163     break;
164   case CurveType::Circle:
165     res = generateCircle(clickedPoints);
166     break;
167   case CurveType::Ellipse:
168     res = generateEllipse(clickedPoints);
169     break;
170   case CurveType::ThreePointCircularArc:
171     res = generateThreePointCircularArc(clickedPoints);
172     break;
173   case CurveType::FivePointConicArc:
174     res = generateFivePointConicArc(clickedPoints);
175     break;
176   case CurveType::Bezier:
177     res = generateBezier(clickedPoints);
178   default:
179     break;
180   }
181   return res;
182 }
183 
184 template <typename ArrTraits_>
setTraits(const ArrTraits * traits_)185 void CurveGeneratorBase<ArrTraits_>::setTraits(const ArrTraits* traits_)
186 {
187   this->traits = traits_;
188 }
189 
190 // Curve Generator Segment Traits
191 template <typename Kernel_>
generateSegment(const std::vector<Point_2> & clickedPoints)192 auto CurveGenerator<CGAL::Arr_segment_traits_2<Kernel_>>::generateSegment(
193   const std::vector<Point_2>& clickedPoints) -> boost::optional<Curve_2>
194 {
195   Curve_2 res{clickedPoints[0], clickedPoints[1]};
196   return res;
197 }
198 
199 // Curve Generator Polyline Traits
200 template <typename SegmentTraits>
201 auto CurveGenerator<CGAL::Arr_polyline_traits_2<SegmentTraits>>::
generatePolyline(const std::vector<Point_2> & clickedPoints)202   generatePolyline(const std::vector<Point_2>& clickedPoints)
203     -> boost::optional<Curve_2>
204 {
205   if (clickedPoints.size() < 2) return {};
206 
207   auto construct_poly = this->traits->construct_curve_2_object();
208   Curve_2 res = construct_poly(clickedPoints.begin(), clickedPoints.end());
209   return res;
210 }
211 
212 // Curve Generator Linear Traits
213 template <typename Kernel_>
generateSegment(const std::vector<Point_2> & points)214 auto CurveGenerator<CGAL::Arr_linear_traits_2<Kernel_>>::generateSegment(
215   const std::vector<Point_2>& points) -> boost::optional<Curve_2>
216 {
217   Curve_2 res = Curve_2(Segment_2(points[0], points[1]));
218   return res;
219 }
220 
221 template <typename Kernel_>
generateRay(const std::vector<Point_2> & points)222 auto CurveGenerator<CGAL::Arr_linear_traits_2<Kernel_>>::generateRay(
223   const std::vector<Point_2>& points) -> boost::optional<Curve_2>
224 {
225   Curve_2 res = Curve_2(Ray_2(points[0], points[1]));
226   return res;
227 }
228 
229 template <typename Kernel_>
generateLine(const std::vector<Point_2> & points)230 auto CurveGenerator<CGAL::Arr_linear_traits_2<Kernel_>>::generateLine(
231   const std::vector<Point_2>& points) -> boost::optional<Curve_2>
232 {
233   Curve_2 res = Curve_2(Line_2(points[0], points[1]));
234   return res;
235 }
236 
237 // CurveGenerator Conic Traits
238 template <typename RatKernel, typename AlgKernel, typename NtTraits>
239 auto CurveGenerator<Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits>>::
generateSegment(const std::vector<Point_2> & points)240   generateSegment(const std::vector<Point_2>& points)
241     -> boost::optional<Curve_2>
242 {
243   Curve_2 res = Curve_2(Rat_segment_2(points[0], points[1]));
244   return res;
245 }
246 
247 template <typename RatKernel, typename AlgKernel, typename NtTraits>
248 auto CurveGenerator<Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits>>::
generateCircle(const std::vector<Point_2> & points)249   generateCircle(const std::vector<Point_2>& points) -> boost::optional<Curve_2>
250 {
251   auto sq_rad =
252     (points[0].x() - points[1].x()) * (points[0].x() - points[1].x()) +
253     (points[0].y() - points[1].y()) * (points[0].y() - points[1].y());
254   Curve_2 res = Curve_2(Rat_circle_2(points[0], sq_rad));
255   return res;
256 }
257 
258 template <typename RatKernel, typename AlgKernel, typename NtTraits>
259 auto CurveGenerator<Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits>>::
generateEllipse(const std::vector<Point_2> & points)260   generateEllipse(const std::vector<Point_2>& points)
261     -> boost::optional<Curve_2>
262 {
263   auto x1 = (CGAL::min)(points[0].x(), points[1].x());
264   auto y1 = (CGAL::min)(points[0].y(), points[1].y());
265   auto x2 = (CGAL::max)(points[0].x(), points[1].x());
266   auto y2 = (CGAL::max)(points[0].y(), points[1].y());
267 
268   Rat_FT a = CGAL::abs(Rat_FT(x1) - Rat_FT(x2)) / 2;
269   Rat_FT b = CGAL::abs(Rat_FT(y1) - Rat_FT(y2)) / 2;
270   Rat_FT a_sq = a * a;
271   Rat_FT b_sq = b * b;
272   Rat_FT x0 = (x2 + x1) / 2;
273   Rat_FT y0 = (y2 + y1) / 2;
274 
275   Rat_FT r = b_sq;
276   Rat_FT s = a_sq;
277   Rat_FT t = 0;
278   Rat_FT u = -2 * x0 * b_sq;
279   Rat_FT v = -2 * y0 * a_sq;
280   Rat_FT ww = x0 * x0 * b_sq + y0 * y0 * a_sq - a_sq * b_sq;
281 
282   Curve_2 res = Curve_2(r, s, t, u, v, ww);
283   return res;
284 }
285 
286 template <typename RatKernel, typename AlgKernel, typename NtTraits>
287 auto CurveGenerator<Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits>>::
generateThreePointCircularArc(const std::vector<Point_2> & points)288   generateThreePointCircularArc(const std::vector<Point_2>& points)
289     -> boost::optional<Curve_2>
290 {
291   auto& qp1 = points[0];
292   auto& qp2 = points[1];
293   auto& qp3 = points[2];
294   Rat_point_2 p1 = Rat_point_2(qp1.x(), qp1.y());
295   Rat_point_2 p2 = Rat_point_2(qp2.x(), qp2.y());
296   Rat_point_2 p3 = Rat_point_2(qp3.x(), qp3.y());
297   RatKernel ker;
298   if (!ker.collinear_2_object()(p1, p2, p3))
299   {
300     Curve_2 res(p1, p2, p3);
301     return res;
302   }
303   else
304   {
305     std::cout << "Points don't specify a valid conic." << std::endl;
306     return {};
307   }
308 }
309 
310 template <typename RatKernel, typename AlgKernel, typename NtTraits>
311 auto CurveGenerator<Arr_conic_traits_2<RatKernel, AlgKernel, NtTraits>>::
generateFivePointConicArc(const std::vector<Point_2> & points)312   generateFivePointConicArc(const std::vector<Point_2>& points)
313     -> boost::optional<Curve_2>
314 {
315   auto& qp0 = points[0];
316   auto& qp1 = points[1];
317   auto& qp2 = points[2];
318   auto& qp3 = points[3];
319   auto& qp4 = points[4];
320   Rat_point_2 p0 = Rat_point_2(qp0.x(), qp0.y());
321   Rat_point_2 p1 = Rat_point_2(qp1.x(), qp1.y());
322   Rat_point_2 p2 = Rat_point_2(qp2.x(), qp2.y());
323   Rat_point_2 p3 = Rat_point_2(qp3.x(), qp3.y());
324   Rat_point_2 p4 = Rat_point_2(qp4.x(), qp4.y());
325   try
326   {
327     Curve_2 res(p0, p1, p2, p3, p4);
328     if (res.is_valid())
329       return res;
330     else
331       std::cout << "Points don't specify a valid conic. Try again!"
332                 << std::endl;
333   }
334   catch (...)
335   {
336     std::cout << "Points don't specify a valid conic. Try again!" << std::endl;
337   }
338   return {};
339 }
340 
341 // CurveGenerator Algebraic Traits
342 template <typename Coefficient_>
343 auto CurveGenerator<CGAL::Arr_algebraic_segment_traits_2<Coefficient_>>::
generateLine(const std::vector<Point_2> & points)344   generateLine(const std::vector<Point_2>& points) -> boost::optional<Curve_2>
345 {
346   RationalTraits ratTraits;
347 
348   Rational dx = points[1].x() - points[0].x();
349   Rational dy = points[1].y() - points[0].y();
350   Polynomial_2 x = CGAL::shift(Polynomial_2(1), 1, 0);
351   Polynomial_2 y = CGAL::shift(Polynomial_2(1), 1, 1);
352 
353   Polynomial_2 poly;
354   if (dx != 0)
355   {
356     Rational mRat = dy / dx;
357     Rational cRat = points[0].y() - mRat * points[0].x();
358     // y = (a/b) x + (e/f)
359     auto a = ratTraits.numerator(mRat);
360     auto b = ratTraits.denominator(mRat);
361     auto e = ratTraits.numerator(cRat);
362     auto f = ratTraits.denominator(cRat);
363 
364     poly = b * f * y - f * a * x - b * e;
365   }
366   // vertical line
367   else
368   {
369     Rational xP = points[0].x();
370     auto a = ratTraits.numerator(xP);
371     auto b = ratTraits.denominator(xP);
372     poly = b * x - a;
373   }
374 
375   auto construct_curve = this->traits->construct_curve_2_object();
376   auto res = construct_curve(poly);
377   return res;
378 }
379 
380 template <typename Coefficient_>
381 auto CurveGenerator<CGAL::Arr_algebraic_segment_traits_2<Coefficient_>>::
generateCircle(const std::vector<Point_2> & points)382   generateCircle(const std::vector<Point_2>& points) -> boost::optional<Curve_2>
383 {
384   auto sq_rad =
385     (points[0].x() - points[1].x()) * (points[0].x() - points[1].x()) +
386     (points[0].y() - points[1].y()) * (points[0].y() - points[1].y());
387   return this->generateEllipse_(points[0], sq_rad, sq_rad);
388 }
389 
390 template <typename Coefficient_>
391 auto CurveGenerator<CGAL::Arr_algebraic_segment_traits_2<Coefficient_>>::
generateEllipse(const std::vector<Point_2> & points)392   generateEllipse(const std::vector<Point_2>& points)
393     -> boost::optional<Curve_2>
394 {
395   auto rx =
396     (points[0].x() - points[1].x()) * (points[0].x() - points[1].x()) / 4.;
397   auto ry =
398     (points[0].y() - points[1].y()) * (points[0].y() - points[1].y()) / 4.;
399   Point_2 center = {
400     (points[1].x() + points[0].x()) / 2, (points[1].y() + points[0].y()) / 2};
401   return this->generateEllipse_(center, rx, ry);
402 }
403 
404 template <typename Coefficient_>
405 auto CurveGenerator<CGAL::Arr_algebraic_segment_traits_2<Coefficient_>>::
generateEllipse_(const Point_2 & center,Rational rxRat,Rational ryRat)406   generateEllipse_(const Point_2& center, Rational rxRat, Rational ryRat)
407     -> boost::optional<Curve_2>
408 {
409   RationalTraits ratTraits;
410 
411   Polynomial_2 x = CGAL::shift(Polynomial_2(1), 1, 0);
412   Polynomial_2 y = CGAL::shift(Polynomial_2(1), 1, 1);
413   Rational xCenter = center.x();
414   Rational yCenter = center.y();
415 
416   // (g/h) (x - a/b)^2 + (e/f) (y - c/d)^2 = (e/f) * (g/h)
417   auto a = ratTraits.numerator(xCenter);
418   auto b = ratTraits.denominator(xCenter);
419   auto c = ratTraits.numerator(yCenter);
420   auto d = ratTraits.denominator(yCenter);
421   auto e = ratTraits.numerator(rxRat);
422   auto f = ratTraits.denominator(rxRat);
423   auto g = ratTraits.numerator(ryRat);
424   auto h = ratTraits.denominator(ryRat);
425 
426   Polynomial_2 poly = g * f * CGAL::ipower(b * d * x - d * a, 2) +
427                       e * h * CGAL::ipower(b * d * y - b * c, 2) -
428                       e * g * b * b * d * d;
429 
430   auto construct_curve = this->traits->construct_curve_2_object();
431   auto res = construct_curve(poly);
432   return res;
433 }
434 
435 template <
436   typename RatKernel, typename AlgKernel, typename NtTraits,
437   typename BoundingTraits>
438 auto CurveGenerator<
439   Arr_Bezier_curve_traits_2<RatKernel, AlgKernel, NtTraits, BoundingTraits>>::
generateBezier(const std::vector<Point_2> & clickedPoints)440   generateBezier(const std::vector<Point_2>& clickedPoints)
441     -> boost::optional<Curve_2>
442 {
443   if (clickedPoints.size() < 2) return {};
444   return Curve_2{clickedPoints.begin(), clickedPoints.end()};
445 }
446 
447 // msvc2015 doesn't play well with polymorphic lambdas
448 namespace
449 {
450 struct ExplicitLambda
451 {
452   template <typename Arrangement>
operator ()CGAL::Qt::__anonb647fab00411::ExplicitLambda453   void operator()(demo_types::TypeHolder<Arrangement>)
454   {
455     Arrangement* arr = nullptr;
456     CGAL::assign(arr, arr_obj);
457     res = new GraphicsViewCurveInput<Arrangement>(arr, parent, scene);
458   }
459 
460   GraphicsViewCurveInputBase*& res;
461   CGAL::Object& arr_obj;
462   QObject* parent;
463   QGraphicsScene* scene;
464 };
465 } // anonymous namespace
466 
create(demo_types::TraitsType tt,CGAL::Object arr_obj,QObject * parent,QGraphicsScene * scene)467 GraphicsViewCurveInputBase* GraphicsViewCurveInputBase::create(
468   demo_types::TraitsType tt, CGAL::Object arr_obj, QObject* parent,
469   QGraphicsScene* scene)
470 {
471   GraphicsViewCurveInputBase* res;
472   ExplicitLambda explicit_lambda{res, arr_obj, parent, scene};
473   demo_types::visitArrangementType(tt, explicit_lambda);
474   return res;
475 }
476 
477 } // namespace Qt
478 } // namespace CGAL
479 
480 #ifdef CGAL_USE_CORE
algebraicCurveFromExpression(const CGAL::Object & arr_obj,const std::string & exp,bool & is_first_curve)481 CGAL::Object algebraicCurveFromExpression(
482   const CGAL::Object& arr_obj, const std::string& exp, bool& is_first_curve)
483 {
484   using Polynomial_2 = demo_types::DemoTypes::Alg_seg_traits::Polynomial_2;
485   using Alg_seg_arr = demo_types::DemoTypes::Alg_seg_arr;
486 
487   Alg_seg_arr* arr;
488   if (!CGAL::assign(arr, arr_obj)) CGAL_error();
489 
490   is_first_curve = (arr->number_of_edges() == 0);
491 
492   auto poly = AlgebraicCurveParser<Polynomial_2>{}(exp);
493   if (!poly) return {};
494 
495   auto construct_curve = arr->traits()->construct_curve_2_object();
496   auto cv = construct_curve(*poly);
497 
498   return CGAL::make_object(cv);
499 }
500 
rationalCurveFromExpression(const CGAL::Object & arr_obj,const std::string & numerator,const std::string & denominator,bool & is_first_curve)501 CGAL::Object rationalCurveFromExpression(
502   const CGAL::Object& arr_obj, const std::string& numerator,
503   const std::string& denominator, bool& is_first_curve)
504 {
505   using Polynomial_1 = demo_types::DemoTypes::Rational_traits::Polynomial_1;
506   using Rational_arr = demo_types::DemoTypes::Rational_arr;
507 
508   Rational_arr* arr;
509   if (!CGAL::assign(arr, arr_obj)) CGAL_error();
510 
511   is_first_curve = (arr->number_of_edges() == 0);
512 
513   auto curve_parser = AlgebraicCurveParser<Polynomial_1>{};
514   auto poly_num = curve_parser(numerator);
515   if (!poly_num) return {};
516   auto poly_den = curve_parser(denominator);
517   if (!poly_den) return {};
518 
519   auto construct_curve = arr->traits()->construct_curve_2_object();
520   auto cv = construct_curve(*poly_num, *poly_den);
521 
522   return CGAL::make_object(cv);
523 }
524 #endif
525