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