1 // Copyright (c) 2005-2009  INRIA Sophia-Antipolis (France).
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org)
5 //
6 // $URL: https://github.com/CGAL/cgal/blob/v5.3/CGAL_ipelets/include/CGAL/CGAL_Ipelet_base_v7.h $
7 // $Id: CGAL_Ipelet_base_v7.h 0779373 2020-03-26T13:31:46+01:00 Sébastien Loriot
8 // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 //
11 // Author(s)     : Sebastien Loriot, Sylvain Pion
12 
13 
14 #ifndef CGAL_IPELET_BASE_H
15 #define CGAL_IPELET_BASE_H
16 
17 // Ipe headers use uint which is not standard.
18 #ifdef __APPLE__
19 typedef unsigned int uint;
20 #endif
21 
22 #include <ipelib.h>
23 #include <CGAL/Polygon_2.h>
24 #include <CGAL/iterator.h>
25 #include <CGAL/Triangulation_2.h>
26 #include <CGAL/grabbers.h>
27 #include <CGAL/iterator.h>
28 #include <CGAL/tuple.h>
29 #include<CGAL/Exact_circular_kernel_2.h>
30 #include <CGAL/Cartesian_converter.h>
31 #include <CGAL/assertions.h>
32 #include <CGAL/use.h>
33 
34 #include <boost/utility.hpp>
35 
36 #define CURRENTLAYER get_IpeletData()->iLayer
37 #define CURRENTATTRIBUTES get_IpeletData()->iAttributes
38 
39 namespace CGAL{
40 
41   template <class Kernel,int nbf>
42   class Ipelet_base : public ipe::Ipelet {
43   private:
44     const std::string* SubLab;
45     const std::string* HMsg;
46     std::string Name;
47     ipe::IpeletData* data_;
48     ipe::IpeletHelper* helper_;
49 
50   public:
51 
52     typedef ipe::Vector IpeVector; //ipe6 compatibility
53     typedef ipe::Curve  IpeSegmentSubPath;//ipe6 compatibility
54     typedef ipe::Matrix IpeMatrix;//ipe6 compatibility
55     typedef ipe::Path   IpePath;//ipe6 compatibility
56     //indicates if the selection should be primary or secondary. Exactly one primary selection should exist
get_selection_type()57     ipe::TSelect get_selection_type() const { return get_IpePage()->primarySelection()==-1 ? ipe::EPrimarySelected : ipe::ESecondarySelected;}
58     //ipe6 compatibility
transform_selected_objects_(const IpeMatrix & tfm)59     void transform_selected_objects_(const IpeMatrix& tfm) const {
60       for (int i=0;i<static_cast<int>(get_IpePage()->count());++i)
61         if (get_IpePage()->select(i)!=ipe::ENotSelected)
62           get_IpePage()->transform(i,tfm);
63     }
64 
delete_selected_objects_()65     void delete_selected_objects_() const {
66       for (unsigned i=static_cast<unsigned>(get_IpePage()->count());i>0;--i)
67         if (get_IpePage()->select(i-1)!=ipe::ENotSelected)
68           get_IpePage()->remove(i-1);
69     }
70 
group_selected_objects_()71     void group_selected_objects_() const {
72       ipe::Group* grp=new ipe::Group();
73       for (unsigned i=static_cast<unsigned>(get_IpePage()->count());i>0;--i)
74         if (get_IpePage()->select(i-1)!=ipe::ENotSelected){
75           grp->push_back( get_IpePage()->object(i-1)->clone() );
76           //~ grp->push_back( get_IpePage()->object(i-1) );
77           get_IpePage()->remove(i-1);
78         }
79       get_IpePage()->append(get_selection_type(),CURRENTLAYER,grp);
80     }
81 
82 
83 
84     //typedefs
85     typedef typename Kernel::FT                                               FT;
86     typedef typename CGAL::Point_2<Kernel>                                    Point_2;
87     typedef typename CGAL::Weighted_point_2<Kernel>                           Weighted_point_2;
88     typedef typename Kernel::Segment_2                                        Segment_2;
89     typedef typename Kernel::Ray_2                                            Ray_2;
90     typedef typename Kernel::Line_2                                           Line_2;
91     typedef typename Kernel::Iso_rectangle_2                                  Iso_rectangle_2;
92     typedef typename Kernel::Triangle_2                                       Triangle_2;
93     //~ typedef typename CGAL::Polygon_2<Kernel,std::list<Point_2> >              Polygon_2;
94     typedef CGAL::Polygon_2<Kernel>                                           Polygon_2;
95 
96     typedef typename Kernel::Circle_2                                         Circle_2;
97     typedef std::tuple<Circle_2,Point_2,Point_2,CGAL::Sign>           Circular_arc_2;
98 
99 
Ipelet_base(const std::string NameS,const std::string SubLabS[],const std::string HMsgS[])100     Ipelet_base(const std::string NameS,const std::string SubLabS[],const std::string HMsgS[])
101       :SubLab(&SubLabS[0]),HMsg(&HMsgS[0]),Name(NameS),data_(nullptr),helper_(nullptr){};
102 
103 
get_IpePage()104     ipe::Page* get_IpePage() const {return data_->iPage;}
get_IpeletData()105     ipe::IpeletData* get_IpeletData() const {return data_;}
get_IpeletHelper()106     ipe::IpeletHelper* get_IpeletHelper() const {return helper_;}
ipelibVersion()107     int ipelibVersion() const { return ipe::IPELIB_VERSION; }
NumFunctions()108     int NumFunctions() const { return nbf; }
Label()109     virtual const char *Label() const{ return &Name[0]; }
About()110     const char *About() const {return "https://www.cgal.org";};
SubLabel(int function)111     virtual const char *SubLabel(int function) const {return &SubLab[function][0];};
HelpMsg(int function)112     virtual const char *HelpMsg(int function) const{return &HMsg[function][0];};
run(int i,ipe::IpeletData * data,ipe::IpeletHelper * helper)113     bool run (int i, ipe::IpeletData* data, ipe::IpeletHelper* helper) {
114       data_=data;
115       helper_=helper;
116       try{
117         protected_run(i);
118         return true;
119       }
120       catch(...){
121         helper->messageBox("Error : Save your page in a file and submit it to \n https://www.cgal.org/bug_report.html",nullptr,ipe::IpeletHelper::EOkCancelButtons);
122         return false;
123       }
124     };
125 
126     virtual void protected_run(int)=0;
127 
128     void show_help(bool gen=true) const{
129       std::string hmsg;
130       hmsg="<qt><h1>"+Name+"</h1><ul>";
131       if (gen)
132         for(int i=0;i<nbf-1;++i)
133           hmsg=hmsg+"<li><i>"+SubLab[i]+"</i>: "+HMsg[i]+"</li>";
134       else
135         hmsg=hmsg+"<li>"+HMsg[0]+"</li>";
136       get_IpeletHelper()->messageBox(&hmsg[0],nullptr,ipe::IpeletHelper::EOkCancelButtons);
137       return;
138     }
139 
140 
141     //grabbers
142 
143     template <class output_iterator>
144     struct Point_grabber:public internal::Point_grabber<Kernel,output_iterator>{
Point_grabberPoint_grabber145       Point_grabber(output_iterator it):internal::Point_grabber<Kernel,output_iterator>(it){}
146     };
147 
148     template<class output_iterator>
149     boost::function_output_iterator<Point_grabber<output_iterator> >
point_grabber(output_iterator it)150     point_grabber(output_iterator it){
151       return boost::make_function_output_iterator(Point_grabber<output_iterator>(it));
152     }
153 
154 
155     template <class output_iterator>
156     struct Segment_grabber:public internal::Segment_grabber<Kernel,output_iterator>{
Segment_grabberSegment_grabber157       Segment_grabber(output_iterator it):internal::Segment_grabber<Kernel,output_iterator>(it){}
158     };
159 
160     template<class output_iterator>
161     boost::function_output_iterator<Segment_grabber<output_iterator> >
segment_grabber(output_iterator it)162     segment_grabber(output_iterator it){
163       return boost::make_function_output_iterator(Segment_grabber<output_iterator>(it));
164     }
165 
166     template <class output_iterator>
167     struct Wpoint_grabber:public internal::Wpoint_grabber<Kernel,output_iterator>{
Wpoint_grabberWpoint_grabber168       Wpoint_grabber(output_iterator it):internal::Wpoint_grabber<Kernel,output_iterator>(it){}
169     };
170 
171     template<class output_iterator>
172     boost::function_output_iterator<Wpoint_grabber<output_iterator> >
wpoint_grabber(output_iterator it)173     wpoint_grabber(output_iterator it){
174       return boost::make_function_output_iterator(Wpoint_grabber<output_iterator>(it));
175     }
176 
177     //Interaction functions
178     //------------------------------
179 
180     void
print_error_message(const char * s)181     print_error_message(const char* s) const
182     {
183       get_IpeletHelper()->message(s);
184     }
185 
186     template <class T>
187     std::pair<int,T>
request_value_from_user(std::string msg)188     request_value_from_user(std::string msg) const
189     {
190       ipe::String str;
191       std::pair<int,T> ret_val=std::make_pair(-1,T());
192       if (get_IpeletHelper()-> getString(msg.c_str(),str)){
193         if (!str.empty()){
194           ipe::Lex lex(str);
195           lex >> ret_val.second;
196           ret_val.first=1;
197         }
198         else
199           ret_val.first=0;
200       }
201       return ret_val;
202     }
203 
204     //Conversion functions
205     //------------------------------
206     Point_2
segment_endpoint(const ipe::CurveSegment & segment,ipe::Path * obj_ipe,int i)207     segment_endpoint(const ipe::CurveSegment& segment,ipe::Path* obj_ipe,int i) const
208     {
209       CGAL_precondition(i<2);
210       ipe::Vector pt_ipe = obj_ipe -> matrix() * segment.cp(i);
211       return Point_2((double)(pt_ipe.x),(double)(pt_ipe.y));//conversion into CGAL point
212     }
213 
214     Point_2
to_point_2(ipe::Object * object)215     to_point_2(ipe::Object*  object) const
216     {
217       ipe::Vector pt_ipe = object-> matrix() * object-> asReference() -> position();
218       return Point_2((double)(pt_ipe.x),(double)(pt_ipe.y));//conversion into CGAL point
219     }
220 
221     Circle_2
222     to_circle_2(ipe::Path* obj_ipe,int subpath=0) const
223     {
224       const ipe::Ellipse* ell_ipe = obj_ipe -> shape().subPath(subpath) -> asEllipse();
225       ipe::Matrix mat_ipe = obj_ipe -> matrix() * ell_ipe -> matrix();
226       FT radius = (mat_ipe*ipe::Vector(1,0)-mat_ipe.translation()).len();
227       ipe::Vector pt_ipe = mat_ipe.translation();
228       return Circle_2(Point_2(pt_ipe.x,pt_ipe.y),radius*radius);
229     }
230 
231 
232     //Picking functions
233     //------------------------------
234 
235     bool
is_only_rotated_or_scaled(const ipe::Matrix & m)236     is_only_rotated_or_scaled(const ipe::Matrix& m) const
237     {
238       return (m.a[0]==m.a[3] && m.a[1]==-m.a[2]);
239     }
240 
241     bool
242     is_IPE_circle(ipe::Object* object,int subpath=0) const
243     {
244       return ( object -> asPath() && object -> asPath() -> shape().subPath(subpath) -> asEllipse()
245         && is_only_rotated_or_scaled(object ->asPath()->matrix()));
246     }
247 
248 
249 public:
250     //declaration
251     template< class multi_output_iterator >
252     bool read_one_active_object( ipe::Object* object,
253       multi_output_iterator it_out) const;
254 
255 public:
256 
257     template< class V,class O>
258     Iso_rectangle_2
259     read_active_objects (
260       CGAL::Dispatch_or_drop_output_iterator<V,O> it_out,
261       bool deselect_all=true,
262       bool delete_selected_objects=false) const
263     {
264       ipe::Rect bbox_ipe;
265 
266       if (!get_IpePage()->hasSelection()) {
267         return Iso_rectangle_2();
268       }
269 
270       for (int i=0;i<static_cast<int>(get_IpePage()->count());++i){
271         if (get_IpePage()->select(i)==ipe::ENotSelected)
272           continue;
273 
274         bbox_ipe.addRect(get_IpePage()->bbox(i));
275 
276         //Test one function for segments, circles, circle arcs and polygons
277         bool desel_it=read_one_active_object(get_IpePage()->object(i),it_out);
278         if ( delete_selected_objects && desel_it  )
279           get_IpePage()->setSelect(i,ipe::ENotSelected);
280       }
281 
282       if (delete_selected_objects)
283         delete_selected_objects_();
284 
285       if (deselect_all)
286         get_IpePage()->deselectAll();
287 
288       Iso_rectangle_2 bbox_cgal(
289         static_cast<double>(bbox_ipe.bottomLeft().x),static_cast<double>(bbox_ipe.bottomLeft().y),
290         static_cast<double>(bbox_ipe.topRight().x),static_cast<double>(bbox_ipe.topRight().y)
291       );
292 
293         return bbox_cgal;
294     }
295 
296     //drawing functions
297     //------------------------------
298     void
299     create_polygon_with_holes(bool delete_underlying_polygons=false) const
300     {
301       std::list<ipe::SubPath*> SSPqu;
302       for (int i=0;i<static_cast<int>(get_IpePage()->count());++i){
303         if (get_IpePage()->select(i)!=ipe::ENotSelected && get_IpePage()->object(i)->asPath()->shape().subPath(0)->closed() ){
304           ipe::SubPath* ssp=new ipe::Curve(*get_IpePage()->object(i)->asPath()->shape().subPath(0)->asCurve());
305           SSPqu.push_back(ssp);
306         }
307       }
308       if (!delete_underlying_polygons)
309         get_IpePage() -> deselectAll();
310       ipe::Shape shape;// create new objects with current attributes
311       for (std::list<ipe::SubPath*>::iterator it=SSPqu.begin();it!=SSPqu.end();++it)
312         shape.appendSubPath(*it);
313       if (delete_underlying_polygons)
314         delete_selected_objects_();
315       get_IpePage()->append(get_selection_type(),CURRENTLAYER,new ipe::Path(CURRENTATTRIBUTES,shape));
316     }
317 
318     void
center_selection_in_page()319     center_selection_in_page() const
320     {
321       ipe::Vector paper_size=get_paper_size();
322       ipe::Matrix tfm (1,0,0,1,paper_size.x/2.,paper_size.y/2.);
323       for (int i=0;i<static_cast<int>(get_IpePage()->count());++i)
324         if (get_IpePage()->select(i)!=ipe::ENotSelected )
325           get_IpePage()->transform(i,tfm);
326     }
327 
328     template<class iterator>
329     ipe::Curve*
330     create_polyline(const iterator first, const iterator last,bool setclose=false) const
331     {
332       if (boost::next(first)!=last){
333         ipe::Curve* SSP_ipe = new ipe::Curve();
334         ipe::Vector Prev_pt=ipe::Vector(CGAL::to_double(first->x()),CGAL::to_double(first->y())) ;
335         for (iterator it = boost::next(first);it!=last;++it){
336           ipe::Vector Cur_pt=ipe::Vector(CGAL::to_double(it->x()),CGAL::to_double(it->y()));
337           SSP_ipe -> appendSegment(Prev_pt,Cur_pt);
338           Prev_pt=Cur_pt;
339         }
340         if (setclose)
341           SSP_ipe->setClosed(true);
342         return SSP_ipe;
343       }
344       return nullptr;
345     }
346 
347 
348     template<class iterator>
349     ipe::Path*
350     draw_polyline_in_ipe(const iterator first, const iterator last,
351                          bool setclose=false,bool deselect_all=false,
352                          bool blackfill=false,
353                          typename boost::enable_if<
354                                     boost::is_same<
355                                       typename std::iterator_traits<iterator>::value_type,
356                                       Point_2
357                                     >
358                                   >::type* =nullptr) const
359     {
360       ipe::Curve* SSP_ipe=create_polyline(first,last,setclose);
361       if (SSP_ipe!=nullptr){
362         ipe::Shape shape;
363         shape.appendSubPath(SSP_ipe);
364         ipe::Path* obj_ipe=new ipe::Path(CURRENTATTRIBUTES,shape);
365         if (blackfill){
366           obj_ipe->setPathMode(ipe::EStrokedAndFilled);
367           obj_ipe->setFill(ipe::Attribute::BLACK());
368         }
369         get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,obj_ipe);
370         return obj_ipe;
371       }
372       return nullptr;
373     }
374 
375     void draw_in_ipe(const Circle_2& C,bool deselect_all=false) const {
376       ipe::Ellipse *ellipse = new ipe::Ellipse(ipe::Matrix(sqrt(CGAL::to_double(C.squared_radius())),0,
377                                                      0,sqrt(CGAL::to_double(C.squared_radius())),
378                                                      to_double(C.center().x()),to_double(C.center().y())
379                                            )
380                                 );
381       ipe::Shape shape;
382       shape.appendSubPath(ellipse);
383       get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,new ipe::Path(CURRENTATTRIBUTES,shape));
384     }
385 
386     void
387     draw_in_ipe(const Point_2& P,bool deselect_all=false) const
388     {
389       ipe::Reference *mark = new ipe::Reference(CURRENTATTRIBUTES,CURRENTATTRIBUTES.iMarkShape, ipe::Vector(CGAL::to_double(P.x()),CGAL::to_double(P.y())));
390       get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,mark);
391     }
392 
393     void
394     draw_in_ipe(const Segment_2& S,bool deselect_all=false) const
395     {
396       ipe::Segment seg_ipe;
397       seg_ipe.iP = ipe::Vector(CGAL::to_double(S.point(0).x()),CGAL::to_double(S.point(0).y()));
398       seg_ipe.iQ = ipe::Vector(CGAL::to_double(S.point(1).x()),CGAL::to_double(S.point(1).y()));
399       get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,new ipe::Path(CURRENTATTRIBUTES,ipe::Shape(seg_ipe)));
400     }
401 
402     template<class Container>
403     void
404     draw_in_ipe(const CGAL::Polygon_2<Kernel,Container>& poly,bool deselect_all=false) const
405     {
406       std::list<Point_2> LP;
407       for (typename CGAL::Polygon_2<Kernel,Container>::iterator it=poly.vertices_begin();it!= poly.vertices_end();++it)
408         LP.push_back(*it);
409       draw_polyline_in_ipe(LP.begin(),LP.end(),true,deselect_all,false);
410     }
411 
412     void
413     draw_in_ipe(const Circular_arc_2& arc,bool deselect_all=false) const
414     {
415       ipe::Curve* SSP_ipe = new ipe::Curve;
416       ipe::Vector ipeS=ipe::Vector( CGAL::to_double(std::get<1>(arc).x()),
417                                 CGAL::to_double(std::get<1>(arc).y()));//convert ot ipe format
418       ipe::Vector ipeT=ipe::Vector( CGAL::to_double(std::get<2>(arc).x()),
419                                 CGAL::to_double(std::get<2>(arc).y()));//convert ot ipe format
420       SSP_ipe->appendArc(ipe::Matrix(sqrt(CGAL::to_double(std::get<0>(arc).squared_radius())),0,
421                                    0,(std::get<3>(arc)==CGAL::COUNTERCLOCKWISE?1:-1)*
422                                      sqrt(CGAL::to_double(std::get<0>(arc).squared_radius())),
423                                    CGAL::to_double(std::get<0>(arc).center().x()),
424                                    CGAL::to_double(std::get<0>(arc).center().y())),
425                                    ipeS,ipeT);
426       ipe::Shape shape;
427       shape.appendSubPath(SSP_ipe);
428       get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,new ipe::Path(CURRENTATTRIBUTES,shape));
429     }
430 
431 
432     void
433     draw_in_ipe(const Triangle_2& t,bool deselect_all=false) const
434     {
435       ipe::Curve* SSP_ipe = new ipe::Curve();
436       ipe::Vector P0=ipe::Vector(t[0].x(),t[0].y());
437       ipe::Vector P1=ipe::Vector(t[1].x(),t[1].y());
438       ipe::Vector P2=ipe::Vector(t[2].x(),t[2].y());
439       SSP_ipe->appendSegment(P0,P1);
440       SSP_ipe->appendSegment(P1,P2);
441       SSP_ipe->appendSegment(P2,P0);
442       SSP_ipe->setClosed(true);
443       ipe::Shape shape;
444       shape.appendSubPath(SSP_ipe);
445       get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,new ipe::Path(CURRENTATTRIBUTES,shape));
446     }
447 
448     void
449     draw_in_ipe(const Iso_rectangle_2& r,bool deselect_all=false)
450     {
451       ipe::Curve* SSP_ipe = new ipe::Curve();
452       ipe::Vector P0=ipe::Vector(r[0].x(),r[0].y());
453       ipe::Vector P1=ipe::Vector(r[1].x(),r[1].y());
454       ipe::Vector P2=ipe::Vector(r[2].x(),r[2].y());
455       ipe::Vector P3=ipe::Vector(r[3].x(),r[3].y());
456       SSP_ipe->appendSegment(P0,P1);
457       SSP_ipe->appendSegment(P1,P2);
458       SSP_ipe->appendSegment(P2,P3);
459       SSP_ipe->appendSegment(P3,P0);
460       SSP_ipe->setClosed(true);
461       ipe::Shape shape;
462       shape.appendSubPath(SSP_ipe);
463       get_IpePage()->append( (deselect_all?ipe::ENotSelected:get_selection_type()),CURRENTLAYER,new ipe::Path(CURRENTATTRIBUTES,shape));
464     }
465 
466 
467     //Drawing function with bbox : global version
468     template <class T>
469     void
470     draw_in_ipe(const T& object,const Iso_rectangle_2& bbox,bool deselect_all=false) const
471     {
472       Segment_2 s;
473       bool success=cast_into_seg(object,bbox,&s);
474       if (success)
475         draw_in_ipe(s,deselect_all);
476     }
477   private:
478     enum Type_circ_arc{SRC,TRG,OSRC,OTRG};
479   public:
480     void
481     draw_in_ipe(const Circular_arc_2& object,const Iso_rectangle_2& bbox,bool deselect_all=false) const
482     {
483       std::vector<Circular_arc_2> arc_list;
484       const Circle_2& circle=std::get<0>(object);
485       restrict_circle_to_bbox(circle,bbox,std::back_inserter(arc_list));
486       if (arc_list.empty() && bbox.has_on_bounded_side(circle.center()) ){
487         draw_in_ipe(object,deselect_all);
488         return;
489       }
490 
491       const Point_2* source=(std::get<3>(object)==CGAL::COUNTERCLOCKWISE)?
492                             &std::get<1>(object):&std::get<2>(object);
493       const Point_2* target=(std::get<3>(object)==CGAL::COUNTERCLOCKWISE)?
494                             &std::get<2>(object):&std::get<1>(object);
495       std::multimap<double,std::pair<Type_circ_arc,const Point_2*> > map_theta;
496       typedef  typename std::multimap<double,std::pair<Type_circ_arc,const Point_2*> >::iterator Map_theta_iterator;
497       Map_theta_iterator s_it=map_theta.insert(
498                                 std::make_pair(get_theta(*source,circle),std::make_pair(OSRC,source)));
499       /* Map_theta_iterator t_it=*/
500                               map_theta.insert(
501                                 std::make_pair(get_theta(*target,circle),std::make_pair(OTRG,target)));
502 
503       for (typename std::vector<Circular_arc_2>::iterator it_arc=arc_list.begin();it_arc!=arc_list.end();++it_arc){
504         const Point_2* arc_s=(std::get<3>(*it_arc)==CGAL::COUNTERCLOCKWISE)?
505                              &std::get<1>(*it_arc):&std::get<2>(*it_arc);
506         const Point_2* arc_t=(std::get<3>(*it_arc)==CGAL::COUNTERCLOCKWISE)?
507                              &std::get<2>(*it_arc):&std::get<1>(*it_arc);
508         map_theta.insert( std::make_pair(get_theta(*arc_s,circle),std::make_pair(SRC,arc_s) ) );
509         map_theta.insert( std::make_pair(get_theta(*arc_t,circle),std::make_pair(TRG,arc_t) ) );
510       }
511 
512       Map_theta_iterator next_s=s_it;
513       ++next_s; if (next_s==map_theta.end()) next_s=map_theta.begin();
514       switch (next_s->second.first){
515         case TRG:
516           draw_in_ipe(Circular_arc_2(circle,*source,*(next_s->second.second),CGAL::COUNTERCLOCKWISE));
517           break;
518         case OSRC:
519           CGAL_error();
520         case SRC:{
521           Map_theta_iterator current=next_s;
522           ++next_s; if (next_s==map_theta.end()) next_s=map_theta.begin();
523           draw_in_ipe(Circular_arc_2(circle,*(current->second.second),*(next_s->second.second),CGAL::COUNTERCLOCKWISE));
524           if(next_s->second.first==OTRG) return;
525           break;
526         }
527         case OTRG:
528           ++next_s; if (next_s==map_theta.end()) next_s=map_theta.end();
529           if (next_s->second.first==TRG){
530             draw_in_ipe(object);
531             return;
532           }
533           else
534             return;
535       }
536 
537       ++next_s; if (next_s==map_theta.end()) next_s=map_theta.begin();
538       Map_theta_iterator current=next_s;
539       ++next_s; if (next_s==map_theta.end()) next_s=map_theta.begin();
540       do{
541         if (current->second.first==OTRG) return;
542         CGAL_assertion(current->second.first==SRC);
543         draw_in_ipe(Circular_arc_2(circle,*(current->second.second),*(next_s->second.second),CGAL::COUNTERCLOCKWISE));
544         if (next_s->second.first==OTRG) return;
545         CGAL_assertion(next_s->second.first==TRG);
546         ++next_s; if (next_s==map_theta.end()) next_s=map_theta.begin();
547         current=next_s;
548         ++next_s; if (next_s==map_theta.end()) next_s=map_theta.begin();
549       }while(true);
550 
551     }
552 
553 
554     void
555     draw_in_ipe(const Circle_2& object,const Iso_rectangle_2& bbox,bool deselect_all=false) const
556     {
557       std::vector<Circular_arc_2> arc_list;
558       restrict_circle_to_bbox(object,bbox,std::back_inserter(arc_list));
559       if (arc_list.empty() && bbox.has_on_bounded_side(object.center()) )
560         draw_in_ipe(object,deselect_all);
561       else
562         draw_in_ipe(arc_list.begin(),arc_list.end(),false,deselect_all);
563     }
564 
565 
566     void
567     draw_in_ipe(const Triangle_2& object,const Iso_rectangle_2& bbox,bool deselect_all=false) const
568     {
569        for (unsigned int i=0;i!=3;++i)
570         draw_in_ipe(Segment_2(object.vertex(i),object.vertex(i+1)),bbox,deselect_all);
571     }
572 
573     void
574     draw_in_ipe(const Iso_rectangle_2& object,const Iso_rectangle_2& bbox,bool deselect_all=false) const
575     {
576       for (unsigned int i=0;i!=4;++i)
577         draw_in_ipe(Segment_2(object.vertex(i),object.vertex(i+1)),bbox,deselect_all);
578     }
579 
580     void
581     draw_in_ipe(const Polygon_2& object,const Iso_rectangle_2& bbox,bool deselect_all=false) const
582     {
583       for (typename Polygon_2::Edge_const_iterator it=object.edges_begin();it!=object.edges_end();++it)
584         draw_in_ipe(*it,bbox,deselect_all);
585     }
586 
587 
588 
589     template<class GT,class TDS>
590     void
591     draw_in_ipe(const CGAL::Triangulation_2<GT,TDS>& tri,const Iso_rectangle_2& bbox,bool deselect_all=false) const
592     {
593       typedef CGAL::Triangulation_2<GT,TDS> Triangulation;
594       typename Triangulation::Finite_edges_iterator first=tri.finite_edges_begin();
595       typename Triangulation::Finite_edges_iterator last=tri.finite_edges_end();
596       for(typename Triangulation::Finite_edges_iterator it=first;it!=last;++it)
597         draw_in_ipe(tri.segment(*it),bbox);
598       if (deselect_all)
599         get_IpePage()->deselectAll();
600     }
601 
602     void
603     draw_in_ipe(const Line_2& line,bool deselect_all=false) const
604     {
605       ipe::Vector paper_size=get_paper_size();
606       Iso_rectangle_2 bbox(0,0,paper_size.x,paper_size.y);
607       draw_in_ipe(line,bbox,deselect_all);
608     }
609 
610     void
611     draw_in_ipe(const Ray_2& ray,bool deselect_all=false)
612     {
613       ipe::Vector paper_size=get_paper_size();
614       Iso_rectangle_2 bbox(0,0,paper_size.x,paper_size.y);
615       draw_in_ipe(ray,bbox,deselect_all);
616     }
617 
618     template<class GT,class TDS>
619     void
620     draw_in_ipe(const CGAL::Triangulation_2<GT,TDS>& tri,bool deselect_all=false,bool make_grp=true) const
621     {
622       typedef CGAL::Triangulation_2<GT,TDS> Triangulation;
623       typename Triangulation::Finite_edges_iterator first=tri.finite_edges_begin();
624       typename Triangulation::Finite_edges_iterator last=tri.finite_edges_end();
625       for(typename Triangulation::Finite_edges_iterator it=first;it!=last;++it)
626         draw_in_ipe(tri.segment(*it));
627       if (make_grp)
628         group_selected_objects_();
629       if (deselect_all)
630         get_IpePage()->deselectAll();
631     }
632 
633     template<class iterator>
634     void
635     draw_in_ipe(const iterator begin,const iterator end,bool make_grp=true,bool deselect_all=false) const
636     {
637       for (iterator it=begin;it!=end;++it)
638         draw_in_ipe(*it);
639       if (make_grp and ++iterator(begin)!=end)
640         group_selected_objects_();
641       if (deselect_all)
642         get_IpePage()->deselectAll();
643     }
644 
645     template<class iterator>
646     void
647     draw_in_ipe(const iterator begin,const iterator end,const Iso_rectangle_2& bbox,bool make_grp=true,bool deselect_all=false,
648      typename boost::enable_if<  boost::mpl::or_< boost::is_same<typename std::iterator_traits<iterator>::value_type,Point_2> ,
649                                  boost::mpl::or_< boost::is_same<typename std::iterator_traits<iterator>::value_type,Segment_2> ,
650                                  boost::mpl::or_< boost::is_same<typename std::iterator_traits<iterator>::value_type,Circle_2> ,
651                                  boost::mpl::or_< boost::is_same<typename std::iterator_traits<iterator>::value_type,Circular_arc_2> ,
652                                                   boost::is_same<typename std::iterator_traits<iterator>::value_type,Polygon_2>
653                                                 > > > >
654                     >::type* = nullptr) const
655     {
656       for (iterator it=begin;it!=end;++it)
657         draw_in_ipe(*it,bbox);
658       if (make_grp and ++iterator(begin)!=end)
659         group_selected_objects_();
660       if (deselect_all)
661         get_IpePage()->deselectAll();
662     }
663 
664     private:
665 
666     ipe::Vector
get_paper_size()667     get_paper_size() const {
668         return data_->iDoc->cascade()->findLayout()->iPaperSize;
669     }
670 
671     struct Voronoi_from_tri{  //Class using stream to get the voronoi diagram
672       std::list<Ray_2> ray_list;
673       std::list<Line_2> line_list;
674       std::list<Segment_2> seg_list;
675 
676       void operator<<(const Ray_2& p){ray_list.push_back(p);}
677       void operator<<(const Line_2& p){line_list.push_back(p);}
678       void operator<<(const Segment_2& p){seg_list.push_back(p);}
679 
680     };
681 
682     template <class T,class output_iterator>
683     bool
cast_into_seg(const T & obj,const Iso_rectangle_2 & bbox,output_iterator out_it)684     cast_into_seg(const T& obj,const Iso_rectangle_2& bbox,output_iterator out_it) const{
685       CGAL::Object obj_cgal = CGAL::intersection(obj,bbox);
686       Segment_2 s;
687       bool ret=CGAL::assign(s, obj_cgal);
688       if (ret) *out_it++=s;
689       return ret;
690     }
691 
692     //Convert infinite objects into drawable segments
693     template<class iterator,class output_iterator>
694     void
cast_into_seg(const iterator first,const iterator end,const Iso_rectangle_2 & bbox,output_iterator out_it)695     cast_into_seg(const iterator first,const iterator end,
696                   const Iso_rectangle_2& bbox, output_iterator out_it) const
697     {
698       for (iterator it=first;it!=end;++it)
699         cast_into_seg(*it,bbox,out_it);
700     }
701 
702 
703 
704     void
draw_dual_(Voronoi_from_tri & v_recup,const Iso_rectangle_2 & bbox,bool makegrp)705     draw_dual_(Voronoi_from_tri& v_recup,const Iso_rectangle_2& bbox,bool makegrp) const
706     {
707       std::vector<Segment_2> seg_cont;
708       //filter degenerate segments
709       for(typename std::list<Segment_2>::iterator iteS = v_recup.seg_list.begin();iteS!=v_recup.seg_list.end();){
710         typename std::list<Segment_2>::iterator itc=iteS++;
711         if (itc->is_degenerate())
712           v_recup.seg_list.erase(itc);
713       }
714 
715       cast_into_seg(v_recup.ray_list.begin(),v_recup.ray_list.end(),bbox,std::back_inserter(seg_cont));//cast rays into segments in bbox
716       cast_into_seg(v_recup.line_list.begin(),v_recup.line_list.end(),bbox,std::back_inserter(seg_cont));//cast lines into segments in bbox
717       cast_into_seg(v_recup.seg_list.begin(),v_recup.seg_list.end(),bbox,std::back_inserter(seg_cont));//cast lines into segments in bbox
718       draw_in_ipe(seg_cont.begin(), seg_cont.end(),makegrp);
719     }
720 
721     public:
722     template<class Triangulation>
723     void
724     draw_dual_in_ipe(Triangulation& T,const Iso_rectangle_2& bbox,bool makegrp=true,bool deselect_all=false) const
725     {
726     //~ template<class GT,class TDS>
727     //~ void draw_dual_in_ipe(const CGAL::Triangulation_2<GT,TDS>& T,const Iso_rectangle_2& bbox) const{
728       Voronoi_from_tri v_recup;
729       T.draw_dual(v_recup);
730       draw_dual_(v_recup,bbox,makegrp);
731       if (deselect_all) get_IpePage()->deselectAll();
732     }
733 
734     template<class Triangulation>
735     void
736     draw_skeleton_in_ipe(Triangulation& T,const Iso_rectangle_2& bbox,
737                          bool makegrp=true,bool deselect_all=false) const
738     {
739     //~ template<class GT,class TDS>
740     //~ void draw_skeleton_in_ipe(const CGAL::Triangulation_2<GT,TDS>& T,const Iso_rectangle_2& bbox) const{
741       Voronoi_from_tri v_recup;
742       T.draw_skeleton(v_recup);
743       draw_dual_(v_recup,bbox,makegrp);
744       if (deselect_all) get_IpePage()->deselectAll();
745     }
746 
747     //Circle restriction
748   private:
get_theta(const Point_2 & point,const Circle_2 & circle)749     inline double get_theta(const Point_2& point, const Circle_2& circle) const {
750       return atan2(CGAL::to_double(point.y()-circle.center().y()),CGAL::to_double(point.x()-circle.center().x()));
751     }
752 
753     //SK objects
754     typedef CGAL::Exact_circular_kernel_2 SK;
755     typedef SK::Circle_2                  Exact_circle_2;
756     typedef SK::Point_2                   Exact_point_2;
757     typedef SK::Circular_arc_point_2      Circular_arc_point_2;
758 
759 
760 
761     //s and t are given such that if center of exact_circle is inside bbox then turn COUNTERCLOCKWISE
762     Circular_arc_2
763     build_arc(const Exact_circle_2& exact_circle,const SK::Circular_arc_point_2& s,
764               const SK::Circular_arc_point_2& t,bool sign_known=false) const
765     {
766       Point_2 sd=Point_2(CGAL::to_double(s.x()),CGAL::to_double(s.y()));
767       Point_2 td=Point_2(CGAL::to_double(t.x()),CGAL::to_double(t.y()));
768       Point_2 center(CGAL::to_double(exact_circle.center().x()),CGAL::to_double(exact_circle.center().y()));
769       CGAL::Cartesian_converter<SK,Kernel> conv;
770       Circle_2 approx_circle=conv(exact_circle);
771       if (!sign_known){
772         CGAL::Sign sign = (CGAL::orientation(sd,td,center)==CGAL::LEFT_TURN)?CGAL::POSITIVE:CGAL::NEGATIVE;
773         return std::make_tuple(approx_circle,sd,td,sign);
774       }
775       return std::make_tuple(approx_circle,sd,td,CGAL::POSITIVE);
776     }
777 
778     void
get_pair_indices(int * array,int * pair)779     get_pair_indices(int* array,int* pair) const {
780       for (int i=0;i<8;++i)
781         if (array[i]!=-1) *pair++=i;
782     }
783 
784     void
set_done(int * array,int index)785     set_done(int* array,int index) const {
786       for (int i =0;i<8;++i)
787         if (array[i]==index) array[i]=-1;
788     }
789 
790 
791     bool
indices_are_on_opposite_side(int * array)792     indices_are_on_opposite_side(int* array) const {
793         if (array[0]!=-1 || array[5]!=-1)
794           return array[2]!=-1 || array[7]!=-1;
795         if (array[1]!=-1 || array[6]!=-1)
796           return array[3]!=-1 || array[4]!=-1;
797         return false;
798     }
799 
800     int
count_points(int * array)801     count_points(int* array) const {
802       int ret=0;
803       for (int i =0;i<8;++i)
804         if (array[i]!=-1) ++ret;
805       return ret;
806     }
807 
808   public:
809     //
810     // .-----7---------2-------.
811     // |                       |
812     // 3                       6
813     // |     indices           |
814     // 4                       1
815     // |                       |
816     // .-----0---------5-------.
817     template <class Output_iterator>
818     void
restrict_circle_to_bbox(const Circle_2 & approx_circle,const Iso_rectangle_2 & bbox,Output_iterator out)819     restrict_circle_to_bbox(const Circle_2& approx_circle,
820                             const Iso_rectangle_2& bbox,Output_iterator out) const
821     {
822       CGAL::Cartesian_converter<Kernel,SK> conv;
823       Exact_circle_2 exact_circle=conv(approx_circle);
824 
825       SK::Intersect_2 inter=SK().intersect_2_object();
826       std::vector< std::pair<Circular_arc_point_2,unsigned> > points;
827       points.reserve(8);
828 
829       std::vector<CGAL::Object> ints;
830       ints.reserve(2);
831       std::pair<Circular_arc_point_2,unsigned> tmp_pt;
832 
833       int indices[8]={-1,-1,-1,-1,-1,-1,-1,-1};
834 
835       for (unsigned i=0;i!=4;++i){
836         ints.clear();
837         SK::Segment_2 S(conv(bbox[i]),conv(bbox[(i+1)%4]));
838         inter(exact_circle,SK::Line_arc_2(S),std::back_inserter(ints));
839         unsigned index=0;
840         bool ok=true;
841         switch (ints.size()){
842           case 1:
843             ok=CGAL::assign(tmp_pt,ints[0]);
844             CGAL_assertion(ok); CGAL_USE(ok);
845             points.push_back(tmp_pt);
846             index=points.size()-1;
847             indices[i]=index;
848             indices[(i+1)%4+4]=index;
849             break;
850           case 2:
851             int right_ind=i<2?0:1;
852             ok=CGAL::assign(tmp_pt,ints[right_ind]);
853             CGAL_assertion(ok); CGAL_USE(ok);
854             points.push_back(tmp_pt);
855             index=points.size()-1;
856             indices[i]=index;
857             ok=CGAL::assign(tmp_pt,ints[(right_ind+1)%2]);
858             CGAL_assertion(ok); CGAL_USE(ok);
859             points.push_back(tmp_pt);
860             index=points.size()-1;
861             indices[(i+1)%4+4]=index;
862             break;
863         }
864       }
865 
866       //corner case
867       for (unsigned i=0;i!=4;++i){
868         if (indices[i]!=-1 && indices[i+4]!=-1){
869           *out++=build_arc(exact_circle,points[ indices[i+4] ].first,points[ indices[i] ].first);
870           if (points[ indices[i] ].second==1) set_done(indices,indices[i]);
871           else indices[i]=-1;
872           if (points[ indices[i+4] ].second==1) set_done(indices,indices[i+4]);
873           else indices[i+4]=-1;
874         }
875       }
876       int rem_pt=count_points(indices);
877       if (rem_pt==4){
878         //double opposite
879         if (indices[0]!=-1){
880           *out++=build_arc(exact_circle,points[ indices[7] ].first,points[ indices[0] ].first);
881           if (indices[7]!=indices[2] && indices[0]!=indices[5])
882             *out++=build_arc(exact_circle,points[ indices[5] ].first,points[ indices[2] ].first);
883         }
884         else{
885           *out++=build_arc(exact_circle,points[ indices[6] ].first,points[ indices[3] ].first);
886           if (indices[6]!=indices[1] && indices[3]!=indices[4])
887             *out++=build_arc(exact_circle,points[ indices[4] ].first,points[ indices[1] ].first);
888         }
889         return;
890       }
891 
892       if (rem_pt==2){
893         int pair[2];
894         get_pair_indices(indices,pair);
895         if (!indices_are_on_opposite_side(indices))
896           *out++=build_arc(exact_circle,points[ indices[pair[1]] ].first,points[ indices[pair[0]] ].first,true);
897         else
898           *out++=build_arc(exact_circle,points[ indices[pair[1]] ].first,points[ indices[pair[0]] ].first);
899         return;
900       }
901       CGAL_assertion (rem_pt==0);
902     }
903   };
904 
905 
906   //definition
907   template <class Kernel,int nbf>
908   template< class multi_output_iterator >
909   bool
read_one_active_object(ipe::Object * object,multi_output_iterator it_out)910   Ipelet_base<Kernel,nbf>::read_one_active_object(ipe::Object* object,
911                                                   multi_output_iterator it_out) const
912   {
913     if (object->asGroup()){
914       bool deselect_grp=false;
915       for (ipe::Group::const_iterator it=object->asGroup()->begin();
916                                     it!=object->asGroup()->end();++it)
917       {
918         ipe::Object *obj = (*it)->clone();
919         obj->setMatrix(obj->matrix()*object->matrix());
920         bool cur=read_one_active_object(obj,it_out);
921         deselect_grp=deselect_grp || cur;
922       }
923       return deselect_grp;
924     }
925 
926     //detect Points
927     if( object->asReference() ){
928       if ( !CGAL::Is_in_tuple<Point_2,typename multi_output_iterator::Value_type_tuple>::value )
929         return true;
930       it_out++=to_point_2(object);
931       return false;
932     }
933     bool to_deselect=true;
934     //Test one function for segments, circles, circle arcs and polygons
935     if (object->asPath()){
936       //iterate on each subpath
937       to_deselect=false;
938       for (int i=0;i<object->asPath()->shape().countSubPaths();++i){
939         if(object->asPath()-> shape().subPath(i)->asCurve()){
940           std::list<Segment_2> seg_list;
941           bool is_polygon=object-> asPath() -> shape().subPath(i)->closed();
942 
943           const ipe::Curve* SSP_ipe = object -> asPath() -> shape().subPath(i) -> asCurve();
944           for(int j=0; j< SSP_ipe->countSegments();++j){
945             if (SSP_ipe -> segment(j).type()==ipe::CurveSegment::ESegment){
946               //TODO depending on if current_it  is  a polygon or not do not do the same thing
947               seg_list.push_back(Segment_2(segment_endpoint(SSP_ipe -> segment(j),object -> asPath(),0),
948                                   segment_endpoint(SSP_ipe -> segment(j),object -> asPath(),1)));
949             }
950             else{
951               //retrieve circle arcs
952               if(SSP_ipe -> segment(j).type()==ipe::CurveSegment::EArc &&
953                  is_only_rotated_or_scaled(object->asPath()->matrix()))
954               {//retreve circle arcs
955                 if ( !CGAL::Is_in_tuple<Circular_arc_2,typename multi_output_iterator::Value_type_tuple>::value ){
956                   to_deselect=true;
957                   continue;
958                 }
959                 is_polygon=false;
960                 ipe::Path* obj_ipe=object->asPath();
961                 ipe::Matrix mat=obj_ipe->matrix() * SSP_ipe->segment(j).matrix();
962                 ipe::Vector ipe_center=ipe::Vector(mat.a[4],mat.a[5]);
963                 ipe::Vector ipe_first=obj_ipe->matrix() * SSP_ipe->segment(j).cp(0);
964                 ipe::Vector ipe_last=obj_ipe->matrix() * SSP_ipe->segment(j).last();
965                 //TODO Check how object is defined
966                 Circular_arc_2 arc(
967                             Circle_2(Point_2(ipe_center.x,ipe_center.y),(ipe_first-ipe_center).len()*(ipe_first-ipe_center).len()),
968                             Point_2(ipe_first.x,ipe_first.y),
969                             Point_2(ipe_last.x,ipe_last.y),
970                             mat.a[0]*mat.a[3]-mat.a[1]*mat.a[2]<0?CGAL::CLOCKWISE:CGAL::COUNTERCLOCKWISE
971                           );
972                 it_out++=arc;
973               }
974               else
975                 to_deselect=true;
976             }
977           }
978           if (object->asPath() -> shape().subPath(i)->closed() &&
979               (SSP_ipe -> segment(0).cp(0)-SSP_ipe -> segment(SSP_ipe->countSegments()-1).cp(1)).len()!=0 ){ //for close polygon, seg
980             seg_list.push_back( Segment_2(
981                               segment_endpoint(SSP_ipe -> segment(SSP_ipe->countSegments()-1),object-> asPath(),1),
982                               segment_endpoint(SSP_ipe -> segment(0),object-> asPath(),0)
983                               ));
984           }
985           //~ if (seg_list.empty())
986             //~ to_deselect=true;
987 
988           if (is_polygon){
989             if (  !CGAL::Is_in_tuple<Polygon_2,typename multi_output_iterator::Value_type_tuple>::value  )
990               to_deselect=true;
991             else{
992               Polygon_2 polygon;
993               typename std::list<Segment_2>::iterator its=seg_list.begin();
994               for (;its!=seg_list.end();++its)
995                 polygon.push_back(its->source());
996               it_out++=polygon;
997             }
998           }
999           else{
1000             if (  !CGAL::Is_in_tuple<Segment_2,typename multi_output_iterator::Value_type_tuple>::value  )
1001               to_deselect=true;
1002             else{
1003               for (typename std::list<Segment_2>::iterator its=seg_list.begin();its!=seg_list.end();++its)
1004                 it_out++=*its;
1005             }
1006           }
1007         }
1008         else
1009           if (is_IPE_circle(object,i)){
1010             if (  !CGAL::Is_in_tuple<Circle_2,typename multi_output_iterator::Value_type_tuple>::value )
1011               to_deselect=true;
1012             else{
1013               Circle_2 C=to_circle_2(object -> asPath(),i);
1014               it_out++=C;
1015             }
1016           }
1017           else
1018             to_deselect=true; // avoid deleting no handled objects
1019       }
1020     }
1021     return to_deselect;
1022   }
1023 
1024 } //CGAL
1025 
1026 #define CGAL_IPELET(T) IPELET_DECLARE ipe::Ipelet *newIpelet(){ return new T; }
1027 
1028 #endif //CGAL_IPELET_BASE_H
1029