// Copyright (c) 2013 INRIA Sophia Antipolis - Mediterranee, (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org) // // $URL$ // $Id$ // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Olivier Devillers #include #include #include #include "include/CGAL_ipelets/pencils.h" #include namespace CGAL_hyperbolic{ typedef CGAL::Exact_circular_kernel_2 Kernel; // -------------------------------------------------------------------- const std::string sublabel[] = { "Line through two points", "Segment through two points", "Bisector of two points", "Circle by center and point", "Circle center", "Help" }; const std::string helpmsg[] = { "Draw the hyperbolic line through two points in Poincare disk", "Draw the hyperbolic segment through two points in Poincare disk", "Draw the hyperbolic bisector of two points in Poincare disk", "Draw the hyperbolic circle given the center (primary selection) and a point in Poincare disk", "Draw the hyperbolic center given a circle (primary selection) in Poincare disk", }; class hyperbolicIpelet : public CGAL::Ipelet_base { public: hyperbolicIpelet() :CGAL::Ipelet_base("Hyperbolic",sublabel,helpmsg){} void protected_run(int); }; // -------------------------------------------------------------------- void hyperbolicIpelet::protected_run(int fn) { Circle_2 circ; //constructed circle: Circle_2 p1,p2; Circle_2 poincare,selected; if (fn==5) { show_help(); return; } std::list pt_list,pt_list1; std::list cir_list,cir_list1; int i=get_IpePage()->primarySelection(); if (i<0) { print_error_message(("No mark or circle selected")); return; } read_one_active_object(get_IpePage()->object(i),CGAL::dispatch_or_drop_output( std::back_inserter(pt_list1), std::back_inserter(cir_list1))); Iso_rectangle_2 bbox= read_active_objects( CGAL::dispatch_or_drop_output( std::back_inserter(pt_list), std::back_inserter(cir_list) ) ); std::list::iterator it1=pt_list1.begin(); std::list::iterator cit1=cir_list1.begin(); std::list::iterator it=pt_list.begin(); std::list::iterator cit=cir_list.begin(); if (fn!=4){ if (pt_list.empty() || cir_list.empty()){ print_error_message(("Two marks and a circle have to be selected")); return; } }else{ if (cir_list.empty()){ print_error_message(("Two circles have to be selected")); return; } } poincare=*cit;++cit; if(fn==4){ if( (cit==cir_list.end()) || (cit1==cir_list1.end())){ print_error_message(("Two circles have to be selected")); return; } if (*cit1==poincare) poincare=*cit; selected=*cit1; }else{ p1=Circle_2(*it,0); ++it; if (it!=pt_list.end()) { p2=Circle_2(*it,0); ++it; }else{ print_error_message(("Two marks and a circle have to be selected")); return; } if( (it!=pt_list.end())||(cit!=cir_list.end())){ print_error_message(("Only two marks and a circle have to be selected")); return; } } if (fn==3){//primary selection must be a point (p1) if (pt_list1.empty()){ print_error_message(("Primary selection must be a mark (center)")); return; } if (*it1 != p1.center()) { //swap circ = p1; p1 = p2; p2 = circ; } if (*it1 != p1.center()) { print_error_message(("Primary selection must be a mark (center)")); return; } } switch(fn){ case 0: // Circle orthogonal to p1, p2, and poincare circ = compute_circle_orthogonal(p1,p2,poincare); break; //goto clip case 1: // Circle orthogonal to p1, p2, and poincare circ = compute_circle_orthogonal(p1,p2,poincare); if (orientation(poincare.center(),p1.center(),p2.center())>0) draw_in_ipe(Circular_arc_2(circ,p2.center(),p1.center(),circ.orientation())); else if (orientation(poincare.center(),p1.center(),p2.center())==0){ print_error_message( "degenerate case, hyperbolic line is on a diameter of Poincare disk"); draw_in_ipe(Segment_2(p1.center(),p2.center())); }else draw_in_ipe(Circular_arc_2(circ,p1.center(),p2.center(),circ.orientation())); return; case 2: // Circle of pencil generated by p1 p2 orthogonal to poincare circ = compute_circle_in_pencil(poincare,p1,p2); break; //goto clip case 3: // Circle of pencil p1 poincare through p2 circ = compute_circle_in_pencil(p2,poincare,p1); draw_in_ipe(circ); return; case 4: // Zere radius circle of pencil selected poincare inside // translate so that Poincare : x^2+y^2=A // and selected : x^2+y^2 -2ax -2by +a^2+ b^2=C // look for l so that l.Poincare + selected has zero radius double a=CGAL::to_double(selected.center().x())-CGAL::to_double(poincare.center().x()); double b=CGAL::to_double(selected.center().y())-CGAL::to_double(poincare.center().y()); double C=CGAL::to_double(selected.squared_radius()); double A=CGAL::to_double(poincare.squared_radius()); double B=A+C-a*a-b*b; double delta=B*B-4*A*C; double l=(-B+sqrt(delta))/2/A; l = 1/(1+l); Point_2 center=poincare.center()+ (l*(selected.center()-poincare.center())); draw_in_ipe(center); return; } //end of switch // detect degenerate case if (circ==Circle_2()){ Kernel::Vector_2 v; if (fn==2) v= Kernel::Vector_2 (p2.center().y()-p1.center().y(),p2.center().x()-p1.center().x()); else v=p2.center()-p1.center(); Kernel::FT sqr_length=poincare.squared_radius() / v.squared_length(); double length = sqrt( CGAL::to_double(sqr_length) ); v = Kernel::FT(length)*v; Point_2 q1=poincare.center()+ v; Point_2 q2=poincare.center()- v; print_error_message( "degenerate case, hyperbolic line is a diameter of Poincare disk"); Kernel::Segment_2 s(q1,q2); draw_in_ipe(s); return; } // clip circ by poincare std::vector< CGAL::Object > result; Kernel::Circular_arc_point_2 L,R; std::pair the_pair; CGAL::intersection(circ, poincare, std::back_inserter(result)); assert (result.size()==2); assign(the_pair, result[0]); L = the_pair.first; assign(the_pair, result[1]); R = the_pair.first; Point_2 LL(CGAL::to_double(L.x()),CGAL::to_double(L.y())); Point_2 RR(CGAL::to_double(R.x()),CGAL::to_double(R.y())); assert( LL.x() <= RR.x()); Circular_arc_2 arc; if ( orientation(poincare.center(),circ.center(),LL) >0) arc = Circular_arc_2(circ,LL,RR,circ.orientation()); else arc = Circular_arc_2(circ,RR,LL,circ.orientation()); draw_in_ipe( arc ); } } CGAL_IPELET(CGAL_hyperbolic::hyperbolicIpelet)