PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/engine/canvas.cpp

https://code.google.com/p/atclab/
C++ | 876 lines | 477 code | 142 blank | 257 comment | 41 complexity | 80e337857ce5fc839adcc479d584a4db MD5 | raw file
Possible License(s): GPL-3.0
  1. /**
  2. ATClab - scriptable Air Traffic Control simulations.
  3. Copyright (C) 2010 openatclab@gmail.com
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. **/
  15. #ifdef _WIN32
  16. #include <assert.h>
  17. #endif
  18. #include "canvas.h"
  19. #include "aircraft_agent.h"
  20. #include "aircraftsprite.h"
  21. #include "aircraft_data.h"
  22. #include "canvas_tools.h"
  23. #include "aircraftparam.h"
  24. #include "routearcsprite.h"
  25. #include "scale.h"
  26. #include "sectorarcsprite.h"
  27. #include "sectorsprite.h"
  28. #include "transformation.h"
  29. #include "waypointsprite.h"
  30. #include <algorithm>
  31. #include <cmath>
  32. #include <sstream>
  33. //Added by qt3to4:
  34. #include <Q3PointArray>
  35. using namespace atc;
  36. ////////////////////////////////////////////////////////////////////////////////
  37. //
  38. // atc::Canvas
  39. //
  40. //------------------------------------------------------------------------------
  41. // class constants
  42. //
  43. /*!
  44. * The Chunk Count defines how many blocks to break the canvas into.
  45. */
  46. const int Canvas::CHUNK_COUNT = 50;
  47. //------------------------------------------------------------------------------
  48. // Constrution/Destruction
  49. //
  50. /*!
  51. * Default Constructor
  52. */
  53. Canvas::Canvas(QObject *parent, const char *name)
  54. : Q3Canvas ( parent, name )
  55. , _aircraft ()
  56. , _2internal( 0 )
  57. , _scale ( 0 )
  58. , _brl ( 0 )
  59. , _vector ( 0 )
  60. , _conflictColour()
  61. , _palette ( 0 )
  62. , _info()
  63. , _infoPosition( TOPLEFT )
  64. {
  65. setBackgroundColor( SECTOR_BRUSH.color() );
  66. }
  67. /*!
  68. * Destructor
  69. */
  70. Canvas::~Canvas() {
  71. if ( _vector ) delete _vector;
  72. if ( _scale ) delete _scale;
  73. WayPointVector::const_iterator wp = _waypoints.begin();
  74. for ( ; wp != _waypoints.end(); ++wp )
  75. delete *wp;
  76. if ( _2internal ) delete _2internal;
  77. ACSpriteMap::const_iterator s = _aircraft.begin();
  78. for ( ; s != _aircraft.end(); ++s )
  79. delete s->second;
  80. }
  81. //--------------------------------------------------------------------------------------------------
  82. // [public slots] virtual
  83. //
  84. /*!
  85. *
  86. */
  87. void Canvas::add_time_info() { _info.push_back( new ClockDisplay(this) ); }
  88. void Canvas::add_score_info() { _info.push_back( new ScoreDisplay(this) ); }
  89. void
  90. Canvas::add_goal_info( const std::string& a_label,
  91. const std::string& a_globalRef,
  92. const std::string& a_text)
  93. {
  94. _info.push_back( new GoalDisplay(this, a_label, a_globalRef, a_text));
  95. }
  96. void Canvas::add_tool_info() { _info.push_back( new ToolDisplay(this) ); }
  97. void
  98. Canvas::AddSelfReferentGoalInfo()
  99. {
  100. _info.push_back(new SelfReferentGoalDisplay(this));
  101. }
  102. void
  103. Canvas::AddNormativeGoalInfo()
  104. {
  105. _info.push_back(new NormativeGoalDisplay(this));
  106. }
  107. void
  108. Canvas::AddCorrectDecisionsInfo(const std::string& a_label,
  109. const std::string& a_globalRef,
  110. const std::string& a_text)
  111. {
  112. _info.push_back(new CorrectDecisionsDisplay(this, a_label, a_globalRef, a_text));
  113. }
  114. void
  115. Canvas::AddIncorrectDecisionsInfo( const std::string& a_label,
  116. const std::string& a_globalRef,
  117. const std::string& a_text)
  118. {
  119. _info.push_back(new IncorrectDecisionsDisplay(this, a_label, a_globalRef, a_text));
  120. }
  121. /*!
  122. *
  123. */
  124. void Canvas::show_info() {
  125. int margin = 10;
  126. QPoint origin( margin, margin );
  127. QPoint offset( 0, 0 );
  128. int display_width = width();
  129. int display_height = height();
  130. InfoBlockIt blkIt = _info.begin();
  131. int block_width = (*blkIt)->width() + margin;
  132. int block_height = (*blkIt)->height() + margin;
  133. int block_count = _info.size();
  134. switch (_infoPosition) {
  135. case TOPLEFT:
  136. offset.setY( block_height );
  137. break;
  138. case TOP:
  139. origin.setX( ( display_width - (block_width * block_count - margin) ) / 2 );
  140. offset.setX( block_width );
  141. break;
  142. case TOPRIGHT:
  143. origin.setX( display_width - block_width );
  144. offset.setY( block_height );
  145. break;
  146. case LEFT:
  147. origin.setY( ( display_height - (block_height * block_count - margin) ) / 2 );
  148. offset.setY( block_height );
  149. break;
  150. case CENTER:
  151. origin.setX( ( display_width - (block_width * block_count - margin) ) / 2 );
  152. origin.setY( ( display_height - (block_height - margin) ) /2 );
  153. offset.setX( block_width );
  154. break;
  155. case RIGHT:
  156. origin.setX( display_width - block_width );
  157. origin.setY( ( display_height - (block_height * block_count - margin) ) / 2 );
  158. offset.setY( block_height );
  159. break;
  160. case BOTTOMLEFT:
  161. origin.setY( display_height - block_height * block_count );
  162. offset.setY( block_height );
  163. break;
  164. case BOTTOM:
  165. origin.setX( ( display_width - (block_width * block_count - margin) ) / 2 );
  166. origin.setY( display_height - block_height );
  167. offset.setX( block_width );
  168. break;
  169. case BOTTOMRIGHT:
  170. origin.setX( display_width - block_width );
  171. origin.setY( display_height - block_height * block_count );
  172. offset.setY( block_height );
  173. break;
  174. }
  175. QPoint insert( origin );
  176. for ( ; blkIt != _info.end(); ++blkIt ) {
  177. (*blkIt)->move( insert );
  178. (*blkIt)->setVisible( true );
  179. switch (_infoPosition)
  180. {
  181. case TOPLEFT:
  182. offset.setY((*blkIt)->height() + margin);
  183. break;
  184. case TOP:
  185. offset.setX((*blkIt)->width() + margin);
  186. break;
  187. case TOPRIGHT:
  188. offset.setY((*blkIt)->height() + margin);
  189. break;
  190. case LEFT:
  191. offset.setY((*blkIt)->height() + margin);
  192. break;
  193. case CENTER:
  194. offset.setX((*blkIt)->width() + margin);
  195. break;
  196. case RIGHT:
  197. offset.setY((*blkIt)->height() + margin);
  198. break;
  199. case BOTTOMLEFT:
  200. offset.setY((*blkIt)->height() + margin);
  201. break;
  202. case BOTTOM:
  203. offset.setX((*blkIt)->width() + margin);
  204. break;
  205. case BOTTOMRIGHT:
  206. offset.setY((*blkIt)->height() + margin);
  207. break;
  208. }
  209. insert += offset;
  210. }
  211. }
  212. /*!
  213. * Display the update counter with a specific format string.
  214. void Canvas::show_clock( ) {
  215. _clock = new QCanvasText( "--:--:--", CANVAS_FONT, this );
  216. _tool_name = new QCanvasText( " --- ", CANVAS_FONT, this );
  217. QRect cr( _clock->boundingRect() );
  218. QRect tr( _tool_name->boundingRect() );
  219. int xcoord = this->width() / 2;
  220. int ycoord = this->height() - 2 * cr.height();
  221. _clock->setColor( Qt::black );
  222. _clock->move( xcoord - cr.width(), ycoord );
  223. _clock->setVisible( true );
  224. _tool_name->setColor( Qt::blue );
  225. _tool_name->move( xcoord + 10, ycoord );
  226. _tool_name->setVisible( true );
  227. QCanvasRectangle *rc = new QCanvasRectangle( _clock->boundingRect(), this );
  228. rc->setPen( QPen( Qt::black, 2 ) );
  229. rc->show();
  230. QCanvasRectangle *rn = new QCanvasRectangle( _tool_name->boundingRect(), this );
  231. rn->setPen( QPen( Qt::black, 2 ) );
  232. rn->show();
  233. }
  234. */
  235. //------------------------------------------------------------------------------
  236. // [public slots] virtual
  237. //
  238. /*!
  239. * set tool
  240. */
  241. void Canvas::set_tool( QString name ) {
  242. emit sig_update_tool( name );
  243. }
  244. //------------------------------------------------------------------------------
  245. // [public] canvas tools interface
  246. //
  247. //
  248. // Bearing Range Line
  249. //
  250. /*!
  251. *
  252. */
  253. BearingRangeLine* Canvas::add_brl( int x, int y ) {
  254. // @todo:
  255. _brl = new BearingRangeLine( this );
  256. _brls.push_back( _brl );
  257. _brl->anchor( QPoint( x, y ) );
  258. return _brl;
  259. }
  260. /*!
  261. *
  262. */
  263. BearingRangeLine* Canvas::add_brl( AircraftSprite *s ) {
  264. // @todo:
  265. _brl = new BearingRangeLine( this );
  266. _brls.push_back( _brl );
  267. _brl->anchor( s );
  268. return _brl;
  269. }
  270. /*!
  271. *
  272. */
  273. BearingRangeLine* Canvas::add_brl( WayPointSprite *s ) {
  274. _brl = new BearingRangeLine( this );
  275. _brls.push_back( _brl );
  276. _brl->anchor( s );
  277. return _brl;
  278. }
  279. /*!
  280. *
  281. */
  282. void Canvas::hook_brl( int x, int y ) {
  283. _brl->hook( QPoint( x, y ) );
  284. }
  285. /*!
  286. *
  287. */
  288. void Canvas::hook_brl( AircraftSprite *s ) {
  289. _brl->hook( s );
  290. }
  291. /*!
  292. *
  293. */
  294. void Canvas::hook_brl( WayPointSprite *s ) {
  295. _brl->hook( s );
  296. }
  297. /*!
  298. *
  299. */
  300. void Canvas::select_brl( BearingRangeLine *brl ) {
  301. _brl = brl;
  302. }
  303. /*!
  304. *
  305. */
  306. void Canvas::remove_brl( BearingRangeLine *brl ) {
  307. _brls.remove( brl );
  308. delete( brl );
  309. update();
  310. }
  311. //
  312. // Scale
  313. //
  314. /*!
  315. * Add a scale indicator to the canvas
  316. */
  317. void Canvas::add_scale(
  318. int x, int y, int xextent, int yextent, int interval, int tick_size
  319. ){
  320. _add_scale( xextent, yextent, interval, tick_size );
  321. _scale->move_by( to_internal_length( x ), to_internal_length( y ) );
  322. }
  323. /*!
  324. *
  325. */
  326. void Canvas::add_scale(
  327. int xextent, int yextent, int interval, int tick_size,
  328. GridPosition position
  329. ) {
  330. _add_scale( xextent, yextent, interval, tick_size );
  331. int margin = 50;
  332. int xpos = to_internal_length( xextent ) + margin;
  333. int ypos = to_internal_length( yextent ) + margin;
  334. QPoint insert( xpos, ypos );
  335. switch (position) {
  336. case TOPLEFT:
  337. break;
  338. case TOP:
  339. insert.setX( width() / 2 );
  340. break;
  341. case TOPRIGHT:
  342. insert.setX( width() - xpos );
  343. break;
  344. case LEFT:
  345. insert.setY( height() / 2 );
  346. break;
  347. case CENTER:
  348. insert.setX( width() / 2 );
  349. insert.setY( height() / 2 );
  350. break;
  351. case RIGHT:
  352. insert.setX( width() - xpos );
  353. insert.setY( height() / 2 );
  354. break;
  355. case BOTTOMLEFT:
  356. insert.setY( height() - ypos );
  357. break;
  358. case BOTTOM:
  359. insert.setX( width() / 2 );
  360. insert.setY( height() - ypos );
  361. break;
  362. case BOTTOMRIGHT:
  363. insert.setX( width() - xpos );
  364. insert.setY( height() - ypos );
  365. break;
  366. };
  367. _scale->move_by( insert );
  368. }
  369. /*!
  370. * Add a scale indicator to the canvas at the origin ( probably patially offscreen ).
  371. */
  372. void Canvas::_add_scale( int xextent, int yextent, int interval, int tick_size ) {
  373. _scale = new ScaleXHair(
  374. this
  375. , to_internal_length( xextent ), to_internal_length( yextent )
  376. , to_internal_length( interval ), to_internal_length( tick_size )
  377. );
  378. }
  379. /*!
  380. *
  381. */
  382. void Canvas::move_scale( QPoint pt ) {
  383. _scale->move_by( pt.x(), pt.y() );
  384. }
  385. //
  386. // vector
  387. //
  388. /*!
  389. *
  390. */
  391. VectorTool* Canvas::vector_tool() { return _vector; }
  392. /*!
  393. *
  394. */
  395. VectorTool* Canvas::add_vector( AircraftSprite *s ) {
  396. _vector = new VectorTool( this, s );
  397. return _vector;
  398. }
  399. /*!
  400. *
  401. */
  402. void Canvas::move_vector( QPoint pt ) { _vector->set_end( pt.x(), pt.y() ); }
  403. /*!
  404. *
  405. */
  406. void Canvas::cancel_vector() {
  407. delete _vector;
  408. _vector = 0;
  409. }
  410. /*!
  411. *
  412. */
  413. void Canvas::toggle_srprobe( int count ) {
  414. ACSpriteMap::iterator ac = _aircraft.begin();
  415. for ( ; ac != _aircraft.end(); ++ac ) {
  416. for ( int i = 0; i < count; ++i ) {
  417. ac->second->toggle_probe();
  418. }
  419. }
  420. }
  421. //
  422. //
  423. //
  424. /*!
  425. *
  426. */
  427. void Canvas::set_conflict_colour( Colour c ) { _conflictColour = c; }
  428. /*!
  429. *
  430. */
  431. QColor Canvas::conflict_colour() { return _conflictColour.colour; }
  432. /*!
  433. *
  434. */
  435. bool Canvas::conflict_blink() { return _conflictColour.blink; }
  436. /*!
  437. *
  438. */
  439. void Canvas::set_palette( Palette* p ) { _palette = p; }
  440. /*!
  441. *
  442. */
  443. QColor Canvas::colour_lookup( ControlState s ) { return (*_palette)[ s ].colour; }
  444. /*!
  445. *
  446. */
  447. bool Canvas::blink_lookup( ControlState s ) { return (*_palette)[ s ].blink; }
  448. //--------------------------------------------------------------------------------------------------
  449. // [public] transformation interface
  450. //
  451. /*!
  452. *
  453. */
  454. double Canvas::to_internal_length( double len ) const {
  455. return _2internal->length( len );
  456. }
  457. /*!
  458. *
  459. */
  460. std::pair< double, double >
  461. Canvas::to_internal_point( const double x, const double y ) const {
  462. return _2internal->point( x, y );
  463. }
  464. /*!
  465. *
  466. */
  467. QPoint
  468. Canvas::to_internal_qpoint( const double x, const double y ) const {
  469. return _2internal->qpoint( x, y );
  470. }
  471. /*!
  472. *
  473. */
  474. double Canvas::to_user_length( double len ) const {
  475. return _2internal->inverse_length( len );
  476. }
  477. /*!
  478. *
  479. */
  480. std::pair< double, double >
  481. Canvas::to_user_point( const double x, const double y ) const {
  482. return _2internal->inverse_point( x, y );
  483. }
  484. /*!
  485. *
  486. */
  487. QPoint
  488. Canvas::to_user_qpoint( const double x, const double y ) const {
  489. return _2internal->inverse_qpoint( x, y );
  490. }
  491. //------------------------------------------------------------------------------
  492. // [public] initialization (map objects - integer)
  493. //
  494. /*!
  495. *
  496. */
  497. void Canvas::add_active_sector( const AreaDefinition &def ) {
  498. Q3PointArray pts;
  499. build_sector( new ActiveSectorSprite( this ), area_points( def, pts ) );
  500. }
  501. /*!
  502. *
  503. */
  504. void Canvas::add_sector( const AreaDefinition &def ) {
  505. Q3PointArray pts;
  506. build_sector( new SectorSprite( this ), area_points( def, pts ) );
  507. }
  508. /*!
  509. * add a weather system
  510. */
  511. void Canvas::add_weather( const AreaDefinition& def ) {
  512. Q3PointArray pts;
  513. area_points( def, pts );
  514. Q3CanvasPolygon *poly = new Q3CanvasPolygon( this );
  515. poly->setPoints( pts );
  516. poly->setBrush( WEATHER_BRUSH );
  517. poly->setZ( WEATHER_Z );
  518. poly->show();
  519. }
  520. /*!
  521. */
  522. void Canvas::add_waypoint( const std::string& name, double x, double y ) {
  523. _waypoints.push_back(
  524. new WayPointSprite( name, _2internal->qpoint( x, y ), this )
  525. );
  526. }
  527. /*!
  528. */
  529. void Canvas::add_route( const RouteList &pts ) {
  530. if ( 2 > pts.size() ) return;
  531. RouteList::const_iterator pt = pts.begin();
  532. QPoint p0 = _2internal->qpoint( pt->x, pt->y );
  533. QPoint p1;
  534. ++pt;
  535. for( ; pt != pts.end(); ++pt ) {
  536. p1 = _2internal->qpoint( pt->x, pt->y );
  537. RouteArcSprite *s = new RouteArcSprite( this );
  538. s->setPoints( p0.x(), p0.y(), p1.x(), p1.y() );
  539. s->show();
  540. p0 = p1;
  541. }
  542. }
  543. //------------------------------------------------------------------------------
  544. // [public] initialization (sky objects - double precision)
  545. //
  546. /*!
  547. * Add an aircraft to the canvas
  548. *
  549. * Creates a new aircraft sprite which is a controller for the aircraft agent.
  550. * A list of aircraft sprites is maintained for update purposes.
  551. */
  552. void Canvas::add_aircraft( AircraftAgent *ac ) {
  553. AircraftSprite *s = new AircraftSprite( this, ac );
  554. _aircraft.insert( std::make_pair( ac->callsign(), s ) );
  555. s->set_visible( true );
  556. connect( ac, SIGNAL( updated() ), s, SLOT( invalidate() ) );
  557. connect( ac, SIGNAL( waypoint() ), s, SLOT( next_waypoint() ) );
  558. connect( ac, SIGNAL( deactivated() ), s, SLOT( destroy() ) );
  559. connect( ac, SIGNAL( activated() ), s, SLOT( update() ) );
  560. }
  561. //------------------------------------------------------------------------------
  562. // public [slots]
  563. //
  564. /*!
  565. * Update all aircraft
  566. */
  567. void Canvas::update_aircraft() {
  568. ACSpriteMap::iterator ac = _aircraft.begin();
  569. for ( ; ac != _aircraft.end(); ++ac ) {
  570. ac->second->update();
  571. }
  572. BRLList::iterator brl = _brls.begin();
  573. for ( ; brl != _brls.end(); ++brl ) {
  574. (*brl)->update();
  575. }
  576. if ( _vector )
  577. _vector->update();
  578. update();
  579. }
  580. //------------------------------------------------------------------------------
  581. // public: [virtual]
  582. //
  583. /*!
  584. * Resize and Retune the Canvas from zero sized initial state.
  585. */
  586. void Canvas::resize( const QRect &map ) {
  587. if ( _2internal )
  588. throw canvas_error( "canvas previously initialized" );
  589. _2internal = new Transformation();
  590. QRect r = _2internal->calculate( map, CANVAS_SIZE_HINT );
  591. // automatically retune on resize
  592. // aiming for approx CHUNK_COUNT chunks.
  593. int w = r.width();
  594. int h = r.height();
  595. int dim = (w > h ? w : h);
  596. int ln2 = int( log((double)(dim / CHUNK_COUNT)) / log(2.0) ) + 1;
  597. int chksz = (int)pow(2.0, ln2);
  598. retune(chksz); // quick since no resize yet.
  599. Q3Canvas::resize(w, h);
  600. if ( !_info.empty() ) show_info();
  601. }
  602. //------------------------------------------------------------------------------
  603. // [private]
  604. //
  605. /*!
  606. *
  607. */
  608. void Canvas::build_sector( SectorSprite *ss, const Q3PointArray &points ) {
  609. //
  610. // create sector
  611. //
  612. ss->setPoints( points );
  613. ss->show();
  614. //
  615. // process the canvas points to create sector outline
  616. //
  617. QPoint p1( points.at( points.size() - 1 ) );
  618. for ( int i = 0; i < points.size(); ++i ) {
  619. QPoint p2( points[i] );
  620. SectorArcSprite *sas = new SectorArcSprite( this );
  621. sas->setPoints( p1.x(), p1.y(), p2.x(), p2.y() );
  622. sas->show();
  623. p1 = p2;
  624. }
  625. }
  626. /*!
  627. *
  628. */
  629. Q3PointArray& Canvas::area_points(
  630. const AreaDefinition &area_def
  631. , Q3PointArray &pts_ref
  632. ) {
  633. // validate point array
  634. if ( ! pts_ref.isNull() ) {
  635. throw canvas_error( "Cannot process area: point array is not empty!" );
  636. }
  637. // process area's descriptors
  638. AreaDefinition::const_iterator it = area_def.begin();
  639. for ( ; it != area_def.end(); ++it) {
  640. switch ( (*it)->rtti ) {
  641. case ( AreaDescriptor::RTTI_VERTEX ): {
  642. QPoint p( _2internal->qpoint( (*it)->x, (*it)->y ) );
  643. pts_ref.putPoints( pts_ref.size(), 1, p.x(), p.y() );
  644. break;
  645. }
  646. case ( AreaDescriptor::RTTI_ARC ): {
  647. ArcDescriptor *arc = static_cast< ArcDescriptor* >( *it );
  648. QPoint p0 = _2internal->qpoint( arc->x, arc->y );
  649. int r = (int)( _2internal->length( arc->r ) );
  650. if ( 0 == pts_ref.size() ) {
  651. // assume 360 single area arc descriptor
  652. // @todo: assert( only area descriptor )
  653. pts_ref.makeEllipse( p0.x() - r, p0.y() - r , 2 * r, 2 * r );
  654. }
  655. else {
  656. ++it; // need next point also to limit arc
  657. assert( it != area_def.end() );
  658. QPoint pA( pts_ref.at( pts_ref.size() - 1 ) );
  659. QPoint pB( (*it)->x, (*it)->y );
  660. bool minor = true; // TODO:??
  661. double bA = bearing(p0, pA);
  662. double bB = bearing(p0, pB);
  663. if ( (bB - bA > 180 && minor) || (bB - bA < 180 && !minor) ) {
  664. double tmp = bA;
  665. bA = bB;
  666. bB = tmp;
  667. }
  668. double angle = bB - bA;
  669. Q3PointArray arcPts;
  670. arcPts.makeArc(
  671. p0.x() - r, p0.y() - r, 2 * r, 2 * r,
  672. (floor)(bA * 16 + 0.5), (floor)(angle * 16 + 0.5)
  673. );
  674. // Note: do not include pA or pB in final pts.
  675. pts_ref.putPoints( pts_ref.size() - 1, arcPts.size(), arcPts );
  676. }
  677. break;
  678. }
  679. case ( AreaDescriptor::RTTI_ELLIPSE ): {
  680. EllipseDescriptor *e
  681. = static_cast< EllipseDescriptor* >( *it );
  682. QPoint p0 = _2internal->qpoint( e->x, e->y );
  683. int w = (int)( _2internal->length( e->w ) );
  684. int h = (int)( _2internal->length( e->h ) );
  685. int a = ( e->a );
  686. // @todo: assert only one area descriptor (this)
  687. pts_ref.makeEllipse( p0.x() - w/2, p0.y() - h/2 , w, h );
  688. if ( a ) {
  689. QMatrix matrix;
  690. matrix.rotate( -a );
  691. pts_ref.translate( -p0.x(), -p0.y() );
  692. pts_ref = matrix.map( pts_ref );
  693. pts_ref.translate( p0.x(), p0.y() );
  694. }
  695. break;
  696. }
  697. default:
  698. throw canvas_error("Unknown area descriptor!");
  699. }
  700. }
  701. return pts_ref;
  702. }
  703. /*!
  704. * Calculate the bearing from _*canvas*_ point p1 to point p2 (degrees)
  705. *
  706. * The points are already converted to canvas space which has an upside down
  707. * geometry ie +ve y is down. Bearings are still however measured in clockwize
  708. * rotation from the y-axis;
  709. *
  710. */
  711. double Canvas::bearing(QPoint p1, QPoint p2)const
  712. {
  713. double dy = p2.y() - p1.y();
  714. double dx = p2.x() - p1.x();
  715. double a =
  716. PI -
  717. (
  718. dx != 0
  719. ? atan(dy / dx)
  720. : (dy < 0 ? - PI/2 : PI/2)
  721. );
  722. if ( dx >= 0 ) a += PI * ( dy < 0 ? -1 : 1);
  723. // slightly confusing because canvas space (like screen) is upside down
  724. return a * 180 / PI;
  725. }
  726. ////////////////////////////////////////////////////////////////////////////////