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