/src/engine/canvas.cpp
C++ | 876 lines | 477 code | 142 blank | 257 comment | 41 complexity | 80e337857ce5fc839adcc479d584a4db MD5 | raw file
Possible License(s): GPL-3.0
- /**
- ATClab - scriptable Air Traffic Control simulations.
- Copyright (C) 2010 openatclab@gmail.com
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
- #ifdef _WIN32
- #include <assert.h>
- #endif
-
- #include "canvas.h"
-
- #include "aircraft_agent.h"
- #include "aircraftsprite.h"
- #include "aircraft_data.h"
- #include "canvas_tools.h"
-
- #include "aircraftparam.h"
- #include "routearcsprite.h"
- #include "scale.h"
- #include "sectorarcsprite.h"
- #include "sectorsprite.h"
- #include "transformation.h"
- #include "waypointsprite.h"
-
- #include <algorithm>
- #include <cmath>
- #include <sstream>
- //Added by qt3to4:
- #include <Q3PointArray>
-
-
- using namespace atc;
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- // atc::Canvas
- //
- //------------------------------------------------------------------------------
- // class constants
- //
-
- /*!
- * The Chunk Count defines how many blocks to break the canvas into.
- */
- const int Canvas::CHUNK_COUNT = 50;
-
- //------------------------------------------------------------------------------
- // Constrution/Destruction
- //
-
- /*!
- * Default Constructor
- */
- Canvas::Canvas(QObject *parent, const char *name)
- : Q3Canvas ( parent, name )
- , _aircraft ()
- , _2internal( 0 )
- , _scale ( 0 )
- , _brl ( 0 )
- , _vector ( 0 )
- , _conflictColour()
- , _palette ( 0 )
- , _info()
- , _infoPosition( TOPLEFT )
- {
- setBackgroundColor( SECTOR_BRUSH.color() );
- }
-
- /*!
- * Destructor
- */
- Canvas::~Canvas() {
- if ( _vector ) delete _vector;
- if ( _scale ) delete _scale;
-
- WayPointVector::const_iterator wp = _waypoints.begin();
- for ( ; wp != _waypoints.end(); ++wp )
- delete *wp;
-
- if ( _2internal ) delete _2internal;
-
- ACSpriteMap::const_iterator s = _aircraft.begin();
- for ( ; s != _aircraft.end(); ++s )
- delete s->second;
- }
-
- //--------------------------------------------------------------------------------------------------
- // [public slots] virtual
- //
-
- /*!
- *
- */
- void Canvas::add_time_info() { _info.push_back( new ClockDisplay(this) ); }
- void Canvas::add_score_info() { _info.push_back( new ScoreDisplay(this) ); }
-
- void
- Canvas::add_goal_info( const std::string& a_label,
- const std::string& a_globalRef,
- const std::string& a_text)
- {
- _info.push_back( new GoalDisplay(this, a_label, a_globalRef, a_text));
- }
-
- void Canvas::add_tool_info() { _info.push_back( new ToolDisplay(this) ); }
-
- void
- Canvas::AddSelfReferentGoalInfo()
- {
- _info.push_back(new SelfReferentGoalDisplay(this));
- }
-
- void
- Canvas::AddNormativeGoalInfo()
- {
- _info.push_back(new NormativeGoalDisplay(this));
- }
-
- void
- Canvas::AddCorrectDecisionsInfo(const std::string& a_label,
- const std::string& a_globalRef,
- const std::string& a_text)
- {
- _info.push_back(new CorrectDecisionsDisplay(this, a_label, a_globalRef, a_text));
- }
-
- void
- Canvas::AddIncorrectDecisionsInfo( const std::string& a_label,
- const std::string& a_globalRef,
- const std::string& a_text)
- {
- _info.push_back(new IncorrectDecisionsDisplay(this, a_label, a_globalRef, a_text));
- }
-
- /*!
- *
- */
- void Canvas::show_info() {
-
- int margin = 10;
- QPoint origin( margin, margin );
- QPoint offset( 0, 0 );
-
- int display_width = width();
- int display_height = height();
-
- InfoBlockIt blkIt = _info.begin();
-
- int block_width = (*blkIt)->width() + margin;
- int block_height = (*blkIt)->height() + margin;
-
- int block_count = _info.size();
-
- switch (_infoPosition) {
- case TOPLEFT:
- offset.setY( block_height );
- break;
- case TOP:
- origin.setX( ( display_width - (block_width * block_count - margin) ) / 2 );
- offset.setX( block_width );
- break;
- case TOPRIGHT:
- origin.setX( display_width - block_width );
- offset.setY( block_height );
- break;
- case LEFT:
- origin.setY( ( display_height - (block_height * block_count - margin) ) / 2 );
- offset.setY( block_height );
- break;
- case CENTER:
- origin.setX( ( display_width - (block_width * block_count - margin) ) / 2 );
- origin.setY( ( display_height - (block_height - margin) ) /2 );
- offset.setX( block_width );
- break;
- case RIGHT:
- origin.setX( display_width - block_width );
- origin.setY( ( display_height - (block_height * block_count - margin) ) / 2 );
- offset.setY( block_height );
- break;
- case BOTTOMLEFT:
- origin.setY( display_height - block_height * block_count );
- offset.setY( block_height );
- break;
- case BOTTOM:
- origin.setX( ( display_width - (block_width * block_count - margin) ) / 2 );
- origin.setY( display_height - block_height );
- offset.setX( block_width );
- break;
- case BOTTOMRIGHT:
- origin.setX( display_width - block_width );
- origin.setY( display_height - block_height * block_count );
- offset.setY( block_height );
- break;
- }
-
- QPoint insert( origin );
- for ( ; blkIt != _info.end(); ++blkIt ) {
- (*blkIt)->move( insert );
- (*blkIt)->setVisible( true );
-
- switch (_infoPosition)
- {
- case TOPLEFT:
- offset.setY((*blkIt)->height() + margin);
- break;
- case TOP:
- offset.setX((*blkIt)->width() + margin);
- break;
- case TOPRIGHT:
- offset.setY((*blkIt)->height() + margin);
- break;
- case LEFT:
- offset.setY((*blkIt)->height() + margin);
- break;
- case CENTER:
- offset.setX((*blkIt)->width() + margin);
- break;
- case RIGHT:
- offset.setY((*blkIt)->height() + margin);
- break;
- case BOTTOMLEFT:
- offset.setY((*blkIt)->height() + margin);
- break;
- case BOTTOM:
- offset.setX((*blkIt)->width() + margin);
- break;
- case BOTTOMRIGHT:
- offset.setY((*blkIt)->height() + margin);
- break;
- }
-
- insert += offset;
- }
- }
-
-
- /*!
- * Display the update counter with a specific format string.
- void Canvas::show_clock( ) {
- _clock = new QCanvasText( "--:--:--", CANVAS_FONT, this );
- _tool_name = new QCanvasText( " --- ", CANVAS_FONT, this );
-
- QRect cr( _clock->boundingRect() );
- QRect tr( _tool_name->boundingRect() );
- int xcoord = this->width() / 2;
- int ycoord = this->height() - 2 * cr.height();
-
- _clock->setColor( Qt::black );
- _clock->move( xcoord - cr.width(), ycoord );
- _clock->setVisible( true );
-
- _tool_name->setColor( Qt::blue );
- _tool_name->move( xcoord + 10, ycoord );
- _tool_name->setVisible( true );
-
- QCanvasRectangle *rc = new QCanvasRectangle( _clock->boundingRect(), this );
- rc->setPen( QPen( Qt::black, 2 ) );
- rc->show();
-
- QCanvasRectangle *rn = new QCanvasRectangle( _tool_name->boundingRect(), this );
- rn->setPen( QPen( Qt::black, 2 ) );
- rn->show();
- }
- */
-
- //------------------------------------------------------------------------------
- // [public slots] virtual
- //
-
- /*!
- * set tool
- */
- void Canvas::set_tool( QString name ) {
- emit sig_update_tool( name );
- }
-
- //------------------------------------------------------------------------------
- // [public] canvas tools interface
- //
-
- //
- // Bearing Range Line
- //
-
- /*!
- *
- */
- BearingRangeLine* Canvas::add_brl( int x, int y ) {
- // @todo:
- _brl = new BearingRangeLine( this );
- _brls.push_back( _brl );
- _brl->anchor( QPoint( x, y ) );
- return _brl;
- }
-
- /*!
- *
- */
- BearingRangeLine* Canvas::add_brl( AircraftSprite *s ) {
- // @todo:
- _brl = new BearingRangeLine( this );
- _brls.push_back( _brl );
- _brl->anchor( s );
- return _brl;
- }
-
- /*!
- *
- */
- BearingRangeLine* Canvas::add_brl( WayPointSprite *s ) {
- _brl = new BearingRangeLine( this );
- _brls.push_back( _brl );
- _brl->anchor( s );
- return _brl;
- }
-
- /*!
- *
- */
- void Canvas::hook_brl( int x, int y ) {
- _brl->hook( QPoint( x, y ) );
- }
-
- /*!
- *
- */
- void Canvas::hook_brl( AircraftSprite *s ) {
- _brl->hook( s );
- }
-
- /*!
- *
- */
- void Canvas::hook_brl( WayPointSprite *s ) {
- _brl->hook( s );
- }
-
- /*!
- *
- */
- void Canvas::select_brl( BearingRangeLine *brl ) {
- _brl = brl;
- }
-
- /*!
- *
- */
- void Canvas::remove_brl( BearingRangeLine *brl ) {
- _brls.remove( brl );
- delete( brl );
- update();
- }
-
- //
- // Scale
- //
-
- /*!
- * Add a scale indicator to the canvas
- */
- void Canvas::add_scale(
- int x, int y, int xextent, int yextent, int interval, int tick_size
- ){
- _add_scale( xextent, yextent, interval, tick_size );
- _scale->move_by( to_internal_length( x ), to_internal_length( y ) );
- }
-
- /*!
- *
- */
- void Canvas::add_scale(
- int xextent, int yextent, int interval, int tick_size,
- GridPosition position
- ) {
- _add_scale( xextent, yextent, interval, tick_size );
-
- int margin = 50;
- int xpos = to_internal_length( xextent ) + margin;
- int ypos = to_internal_length( yextent ) + margin;
-
- QPoint insert( xpos, ypos );
-
- switch (position) {
- case TOPLEFT:
- break;
- case TOP:
- insert.setX( width() / 2 );
- break;
- case TOPRIGHT:
- insert.setX( width() - xpos );
- break;
- case LEFT:
- insert.setY( height() / 2 );
- break;
- case CENTER:
- insert.setX( width() / 2 );
- insert.setY( height() / 2 );
- break;
- case RIGHT:
- insert.setX( width() - xpos );
- insert.setY( height() / 2 );
- break;
- case BOTTOMLEFT:
- insert.setY( height() - ypos );
- break;
- case BOTTOM:
- insert.setX( width() / 2 );
- insert.setY( height() - ypos );
- break;
- case BOTTOMRIGHT:
- insert.setX( width() - xpos );
- insert.setY( height() - ypos );
- break;
- };
- _scale->move_by( insert );
- }
-
- /*!
- * Add a scale indicator to the canvas at the origin ( probably patially offscreen ).
- */
- void Canvas::_add_scale( int xextent, int yextent, int interval, int tick_size ) {
- _scale = new ScaleXHair(
- this
- , to_internal_length( xextent ), to_internal_length( yextent )
- , to_internal_length( interval ), to_internal_length( tick_size )
- );
- }
-
- /*!
- *
- */
- void Canvas::move_scale( QPoint pt ) {
- _scale->move_by( pt.x(), pt.y() );
- }
-
- //
- // vector
- //
-
- /*!
- *
- */
- VectorTool* Canvas::vector_tool() { return _vector; }
-
- /*!
- *
- */
- VectorTool* Canvas::add_vector( AircraftSprite *s ) {
- _vector = new VectorTool( this, s );
- return _vector;
- }
-
- /*!
- *
- */
- void Canvas::move_vector( QPoint pt ) { _vector->set_end( pt.x(), pt.y() ); }
-
- /*!
- *
- */
- void Canvas::cancel_vector() {
- delete _vector;
- _vector = 0;
- }
-
-
- /*!
- *
- */
- void Canvas::toggle_srprobe( int count ) {
- ACSpriteMap::iterator ac = _aircraft.begin();
- for ( ; ac != _aircraft.end(); ++ac ) {
- for ( int i = 0; i < count; ++i ) {
- ac->second->toggle_probe();
- }
- }
- }
-
- //
- //
- //
-
- /*!
- *
- */
- void Canvas::set_conflict_colour( Colour c ) { _conflictColour = c; }
-
- /*!
- *
- */
- QColor Canvas::conflict_colour() { return _conflictColour.colour; }
-
- /*!
- *
- */
- bool Canvas::conflict_blink() { return _conflictColour.blink; }
-
- /*!
- *
- */
- void Canvas::set_palette( Palette* p ) { _palette = p; }
-
- /*!
- *
- */
- QColor Canvas::colour_lookup( ControlState s ) { return (*_palette)[ s ].colour; }
-
- /*!
- *
- */
- bool Canvas::blink_lookup( ControlState s ) { return (*_palette)[ s ].blink; }
-
-
- //--------------------------------------------------------------------------------------------------
- // [public] transformation interface
- //
-
- /*!
- *
- */
- double Canvas::to_internal_length( double len ) const {
- return _2internal->length( len );
- }
-
- /*!
- *
- */
- std::pair< double, double >
- Canvas::to_internal_point( const double x, const double y ) const {
- return _2internal->point( x, y );
- }
-
- /*!
- *
- */
- QPoint
- Canvas::to_internal_qpoint( const double x, const double y ) const {
- return _2internal->qpoint( x, y );
- }
-
- /*!
- *
- */
- double Canvas::to_user_length( double len ) const {
- return _2internal->inverse_length( len );
- }
-
- /*!
- *
- */
- std::pair< double, double >
- Canvas::to_user_point( const double x, const double y ) const {
- return _2internal->inverse_point( x, y );
- }
-
- /*!
- *
- */
- QPoint
- Canvas::to_user_qpoint( const double x, const double y ) const {
- return _2internal->inverse_qpoint( x, y );
- }
-
- //------------------------------------------------------------------------------
- // [public] initialization (map objects - integer)
- //
- /*!
- *
- */
- void Canvas::add_active_sector( const AreaDefinition &def ) {
- Q3PointArray pts;
- build_sector( new ActiveSectorSprite( this ), area_points( def, pts ) );
- }
-
-
- /*!
- *
- */
- void Canvas::add_sector( const AreaDefinition &def ) {
- Q3PointArray pts;
- build_sector( new SectorSprite( this ), area_points( def, pts ) );
- }
-
- /*!
- * add a weather system
- */
- void Canvas::add_weather( const AreaDefinition& def ) {
- Q3PointArray pts;
- area_points( def, pts );
-
- Q3CanvasPolygon *poly = new Q3CanvasPolygon( this );
- poly->setPoints( pts );
- poly->setBrush( WEATHER_BRUSH );
- poly->setZ( WEATHER_Z );
- poly->show();
- }
-
- /*!
- */
- void Canvas::add_waypoint( const std::string& name, double x, double y ) {
- _waypoints.push_back(
- new WayPointSprite( name, _2internal->qpoint( x, y ), this )
- );
- }
-
- /*!
- */
- void Canvas::add_route( const RouteList &pts ) {
- if ( 2 > pts.size() ) return;
-
- RouteList::const_iterator pt = pts.begin();
- QPoint p0 = _2internal->qpoint( pt->x, pt->y );
- QPoint p1;
- ++pt;
- for( ; pt != pts.end(); ++pt ) {
- p1 = _2internal->qpoint( pt->x, pt->y );
-
- RouteArcSprite *s = new RouteArcSprite( this );
- s->setPoints( p0.x(), p0.y(), p1.x(), p1.y() );
- s->show();
-
- p0 = p1;
- }
- }
-
-
- //------------------------------------------------------------------------------
- // [public] initialization (sky objects - double precision)
- //
-
- /*!
- * Add an aircraft to the canvas
- *
- * Creates a new aircraft sprite which is a controller for the aircraft agent.
- * A list of aircraft sprites is maintained for update purposes.
- */
- void Canvas::add_aircraft( AircraftAgent *ac ) {
- AircraftSprite *s = new AircraftSprite( this, ac );
- _aircraft.insert( std::make_pair( ac->callsign(), s ) );
-
- s->set_visible( true );
-
- connect( ac, SIGNAL( updated() ), s, SLOT( invalidate() ) );
- connect( ac, SIGNAL( waypoint() ), s, SLOT( next_waypoint() ) );
- connect( ac, SIGNAL( deactivated() ), s, SLOT( destroy() ) );
-
- connect( ac, SIGNAL( activated() ), s, SLOT( update() ) );
- }
-
-
- //------------------------------------------------------------------------------
- // public [slots]
- //
-
- /*!
- * Update all aircraft
- */
- void Canvas::update_aircraft() {
- ACSpriteMap::iterator ac = _aircraft.begin();
- for ( ; ac != _aircraft.end(); ++ac ) {
- ac->second->update();
- }
-
- BRLList::iterator brl = _brls.begin();
- for ( ; brl != _brls.end(); ++brl ) {
- (*brl)->update();
- }
-
- if ( _vector )
- _vector->update();
-
- update();
- }
-
-
- //------------------------------------------------------------------------------
- // public: [virtual]
- //
-
- /*!
- * Resize and Retune the Canvas from zero sized initial state.
- */
- void Canvas::resize( const QRect &map ) {
- if ( _2internal )
- throw canvas_error( "canvas previously initialized" );
-
- _2internal = new Transformation();
- QRect r = _2internal->calculate( map, CANVAS_SIZE_HINT );
-
- // automatically retune on resize
- // aiming for approx CHUNK_COUNT chunks.
-
- int w = r.width();
- int h = r.height();
-
- int dim = (w > h ? w : h);
- int ln2 = int( log((double)(dim / CHUNK_COUNT)) / log(2.0) ) + 1;
- int chksz = (int)pow(2.0, ln2);
- retune(chksz); // quick since no resize yet.
-
- Q3Canvas::resize(w, h);
-
- if ( !_info.empty() ) show_info();
- }
-
-
- //------------------------------------------------------------------------------
- // [private]
- //
-
- /*!
- *
- */
- void Canvas::build_sector( SectorSprite *ss, const Q3PointArray &points ) {
- //
- // create sector
- //
-
- ss->setPoints( points );
- ss->show();
-
- //
- // process the canvas points to create sector outline
- //
-
- QPoint p1( points.at( points.size() - 1 ) );
- for ( int i = 0; i < points.size(); ++i ) {
-
- QPoint p2( points[i] );
-
- SectorArcSprite *sas = new SectorArcSprite( this );
- sas->setPoints( p1.x(), p1.y(), p2.x(), p2.y() );
- sas->show();
-
- p1 = p2;
- }
- }
-
- /*!
- *
- */
- Q3PointArray& Canvas::area_points(
- const AreaDefinition &area_def
- , Q3PointArray &pts_ref
- ) {
- // validate point array
- if ( ! pts_ref.isNull() ) {
- throw canvas_error( "Cannot process area: point array is not empty!" );
- }
-
- // process area's descriptors
- AreaDefinition::const_iterator it = area_def.begin();
- for ( ; it != area_def.end(); ++it) {
-
- switch ( (*it)->rtti ) {
-
- case ( AreaDescriptor::RTTI_VERTEX ): {
- QPoint p( _2internal->qpoint( (*it)->x, (*it)->y ) );
- pts_ref.putPoints( pts_ref.size(), 1, p.x(), p.y() );
- break;
- }
- case ( AreaDescriptor::RTTI_ARC ): {
- ArcDescriptor *arc = static_cast< ArcDescriptor* >( *it );
-
- QPoint p0 = _2internal->qpoint( arc->x, arc->y );
- int r = (int)( _2internal->length( arc->r ) );
-
- if ( 0 == pts_ref.size() ) {
- // assume 360 single area arc descriptor
- // @todo: assert( only area descriptor )
- pts_ref.makeEllipse( p0.x() - r, p0.y() - r , 2 * r, 2 * r );
- }
- else {
- ++it; // need next point also to limit arc
-
- assert( it != area_def.end() );
-
- QPoint pA( pts_ref.at( pts_ref.size() - 1 ) );
- QPoint pB( (*it)->x, (*it)->y );
-
- bool minor = true; // TODO:??
-
- double bA = bearing(p0, pA);
- double bB = bearing(p0, pB);
-
- if ( (bB - bA > 180 && minor) || (bB - bA < 180 && !minor) ) {
- double tmp = bA;
- bA = bB;
- bB = tmp;
- }
-
- double angle = bB - bA;
-
- Q3PointArray arcPts;
- arcPts.makeArc(
- p0.x() - r, p0.y() - r, 2 * r, 2 * r,
- (floor)(bA * 16 + 0.5), (floor)(angle * 16 + 0.5)
- );
-
- // Note: do not include pA or pB in final pts.
- pts_ref.putPoints( pts_ref.size() - 1, arcPts.size(), arcPts );
- }
-
- break;
- }
- case ( AreaDescriptor::RTTI_ELLIPSE ): {
- EllipseDescriptor *e
- = static_cast< EllipseDescriptor* >( *it );
-
- QPoint p0 = _2internal->qpoint( e->x, e->y );
- int w = (int)( _2internal->length( e->w ) );
- int h = (int)( _2internal->length( e->h ) );
- int a = ( e->a );
-
- // @todo: assert only one area descriptor (this)
- pts_ref.makeEllipse( p0.x() - w/2, p0.y() - h/2 , w, h );
-
- if ( a ) {
- QMatrix matrix;
- matrix.rotate( -a );
- pts_ref.translate( -p0.x(), -p0.y() );
- pts_ref = matrix.map( pts_ref );
- pts_ref.translate( p0.x(), p0.y() );
- }
-
- break;
- }
- default:
- throw canvas_error("Unknown area descriptor!");
- }
- }
- return pts_ref;
- }
-
- /*!
- * Calculate the bearing from _*canvas*_ point p1 to point p2 (degrees)
- *
- * The points are already converted to canvas space which has an upside down
- * geometry ie +ve y is down. Bearings are still however measured in clockwize
- * rotation from the y-axis;
- *
- */
- double Canvas::bearing(QPoint p1, QPoint p2)const
- {
- double dy = p2.y() - p1.y();
- double dx = p2.x() - p1.x();
-
- double a =
- PI -
- (
- dx != 0
- ? atan(dy / dx)
- : (dy < 0 ? - PI/2 : PI/2)
- );
-
- if ( dx >= 0 ) a += PI * ( dy < 0 ? -1 : 1);
- // slightly confusing because canvas space (like screen) is upside down
-
- return a * 180 / PI;
- }
-
-
- ////////////////////////////////////////////////////////////////////////////////