double Projector::findPA( SkyObject *o, float x, float y ) const { //Find position angle of North using a test point displaced to the north //displace by 100/zoomFactor radians (so distance is always 100 pixels) //this is 5730/zoomFactor degrees KStarsData *data = KStarsData::Instance(); double newDec = o->dec().Degrees() + 5730.0/m_vp.zoomFactor; if ( newDec > 90.0 ) newDec = 90.0; SkyPoint test( o->ra().Hours(), newDec ); if ( m_vp.useAltAz ) test.EquatorialToHorizontal( data->lst(), data->geo()->lat() ); Vector2f t = toScreenVec( &test ); float dx = t.x() - x; float dy = y - t.y(); //backwards because QWidget Y-axis increases to the bottom float north; if ( dy ) { north = atan2f( dx, dy )*180.0/dms::PI; } else { north = (dx > 0.0 ? -90.0 : 90.0); } return ( north + o->pa() ); }
QPointF Projector::toScreen(const SkyPoint* o, bool oRefract, bool* onVisibleHemisphere) const { return KSUtils::vecToPoint( toScreenVec(o, oRefract, onVisibleHemisphere) ); }
QVector< Vector2f > Projector::groundPoly(SkyPoint* labelpoint, bool *drawLabel) const { KStarsData *data = KStarsData::Instance(); QVector<Vector2f> ground; static const QString horizonLabel = i18n("Horizon"); float marginLeft, marginRight, marginTop, marginBot; SkyLabeler::Instance()->getMargins( horizonLabel, &marginLeft, &marginRight, &marginTop, &marginBot ); //daz is 1/2 the width of the sky in degrees double daz = 90.; if ( m_vp.useAltAz ) { daz = 0.5*m_vp.width*57.3/m_vp.zoomFactor; //center to edge, in degrees if ( type() == SkyMap::Orthographic ) { daz = daz * 1.4; } daz = qMin(90.0, daz); } double faz = m_vp.focus->az().Degrees(); double az1 = faz -daz; double az2 = faz +daz; bool allGround = true; bool allSky = true; double inc = 1.0; //Add points along horizon for(double az = az1; az <= az2 + inc; az += inc) { SkyPoint p = pointAt(az,data); bool visible = false; Vector2f o = toScreenVec(&p, false, &visible); if( visible ) { ground.append( o ); //Set the label point if this point is onscreen if ( labelpoint && o.x() < marginRight && o.y() > marginTop && o.y() < marginBot ) *labelpoint = p; if ( o.y() > 0. ) allGround = false; if ( o.y() < m_vp.height ) allSky = false; } } if( allSky ) { if( drawLabel) *drawLabel = false; return QVector<Vector2f>(); } if( allGround ) { ground.clear(); ground.append( Vector2f( -10., -10. ) ); ground.append( Vector2f( m_vp.width +10., -10. ) ); ground.append( Vector2f( m_vp.width +10., m_vp.height +10. ) ); ground.append( Vector2f( -10., m_vp.height +10. ) ); if( drawLabel) *drawLabel = false; return ground; } //In Gnomonic projection, or if sufficiently zoomed in, we can complete //the ground polygon by simply adding offscreen points //FIXME: not just gnomonic if ( daz < 25.0 || type() == SkyMap::Gnomonic ) { ground.append( Vector2f( m_vp.width + 10.f, ground.last().y() ) ); ground.append( Vector2f( m_vp.width + 10.f, m_vp.height + 10.f ) ); ground.append( Vector2f( -10.f, m_vp.height + 10.f ) ); ground.append( Vector2f( -10.f, ground.first().y() ) ); } else { double r = m_vp.zoomFactor*radius(); double t1 = atan2( -1.*(ground.last().y() - 0.5*m_vp.height), ground.last().x() - 0.5*m_vp.width )/dms::DegToRad; double t2 = t1 - 180.; for ( double t=t1; t >= t2; t -= inc ) { //step along circumference dms a( t ); double sa(0.), ca(0.); a.SinCos( sa, ca ); ground.append( Vector2f( 0.5*m_vp.width + r*ca, 0.5*m_vp.height - r*sa) ); } } if( drawLabel) *drawLabel = true; return ground; }
Vector2f Projector::clipLineVec( SkyPoint *p1, SkyPoint *p2 ) const { /* ASSUMES p1 was not clipped but p2 was. * Return the QPoint that barely clips in the line twixt p1 and p2. */ //TODO: iteration = ceil( 0.5*log2( w^2 + h^2) )?? // also possibly rewrite this // --hdevalence int iteration = 15; // For "perfect" clipping: // 2^interations should be >= max pixels/line bool isVisible = true; // so we start at midpoint SkyPoint mid; Vector2f oMid; double x, y, z, dx, dy, dz, ra, dec; int newx, newy, oldx, oldy; oldx = oldy = -10000; // any old value that is not the first omid toXYZ( p1, &x, &y, &z ); // -jbb printf("\np1: %6.4f %6.4f %6.4f\n", x, y, z); toXYZ( p2, &dx, &dy, &dz ); // -jbb printf("p2: %6.4f %6.4f %6.4f\n", dx, dy, dz); dx -= x; dy -= y; dz -= z; // Successive approximation to point on line that just clips. while(iteration-- > 0) { dx *= .5; dy *= .5; dz *= .5; if ( ! isVisible ) { // move back toward visible p1 x -= dx; y -= dy; z -= dz; } else { // move out toward clipped p2 x += dx; y += dy; z += dz; } // -jbb printf(" : %6.4f %6.4f %6.4f\n", x, y, z); // [x, y, z] => [ra, dec] ra = atan2( y, x ); dec = asin( z / sqrt(x*x + y*y + z*z) ); mid = SkyPoint( ra * 12. / dms::PI, dec * 180. / dms::PI ); mid.EquatorialToHorizontal( m_data->lst(), m_data->geo()->lat() ); oMid = toScreenVec( &mid, false, &isVisible ); //AND the result with checkVisibility to clip things going below horizon isVisible &= checkVisibility(&mid); newx = (int) oMid.x(); newy = (int) oMid.y(); // -jbb printf("new x/y: %4d %4d", newx, newy); if ( (oldx == newx) && (oldy == newy) ) { break; } oldx = newx; oldy = newy; } return oMid; }
QVector< Vector2f > EquirectangularProjector::groundPoly(SkyPoint* labelpoint, bool* drawLabel) const { float x0 = m_vp.width/2.; float y0 = m_vp.width/2.; if( m_vp.useAltAz ) { float dX = m_vp.zoomFactor*M_PI; float dY = m_vp.zoomFactor*M_PI; SkyPoint belowFocus; belowFocus.setAz( m_vp.focus->az().Degrees() ); belowFocus.setAlt( 0.0 ); Vector2f obf = toScreenVec( &belowFocus, false ); //If the horizon is off the bottom edge of the screen, //we can return immediately if ( obf.y() > m_vp.height ) { if( drawLabel ) *drawLabel = false; return QVector<Vector2f>(); } //We can also return if the horizon is off the top edge, //as long as the ground poly is not being drawn if ( obf.y() < 0. && m_vp.fillGround == false ) { if( drawLabel ) *drawLabel = false; return QVector<Vector2f>(); } QVector<Vector2f> ground; //Construct the ground polygon, which is a simple rectangle in this case ground << Vector2f( x0 - dX, obf.y() ) << Vector2f( x0 + dX, obf.y() ) << Vector2f( x0 + dX, y0 + dY ) << Vector2f( x0 - dX, y0 + dY ); if( labelpoint ) { QPointF pLabel( x0 -dX -50., obf.y() ); KStarsData *data = KStarsData::Instance(); *labelpoint = fromScreen(pLabel, data->lst(), data->geo()->lat()); } if( drawLabel ) *drawLabel = true; return ground; } else { float dX = m_vp.zoomFactor*M_PI/2; float dY = m_vp.zoomFactor*M_PI/2; QVector<Vector2f> ground; static const QString horizonLabel = i18n("Horizon"); float marginLeft, marginRight, marginTop, marginBot; SkyLabeler::Instance()->getMargins( horizonLabel, &marginLeft, &marginRight, &marginTop, &marginBot ); double daz = 90.; double faz = m_vp.focus->az().Degrees(); double az1 = faz -daz; double az2 = faz +daz; bool allGround = true; bool allSky = true; double inc = 1.0; //Add points along horizon for(double az = az1; az <= az2 + inc; az += inc) { SkyPoint p = pointAt(az); bool visible = false; Vector2f o = toScreenVec(&p, false, &visible); if( visible ) { ground.append( o ); //Set the label point if this point is onscreen if ( labelpoint && o.x() < marginRight && o.y() > marginTop && o.y() < marginBot ) *labelpoint = p; if ( o.y() > 0. ) allGround = false; if ( o.y() < m_vp.height ) allSky = false; } } if( allSky ) { if( drawLabel) *drawLabel = false; return QVector<Vector2f>(); } if( allGround ) { ground.clear(); ground.append( Vector2f( x0 - dX, y0 - dY ) ); ground.append( Vector2f( x0 + dX, y0 - dY ) ); ground.append( Vector2f( x0 + dX, y0 + dY ) ); ground.append( Vector2f( x0 - dX, y0 + dY ) ); if( drawLabel) *drawLabel = false; return ground; } if( labelpoint ) { QPointF pLabel( x0 -dX -50., ground.last().y() ); KStarsData *data = KStarsData::Instance(); *labelpoint = fromScreen(pLabel, data->lst(), data->geo()->lat()); } if( drawLabel ) *drawLabel = true; //Now add points along the ground ground.append( Vector2f( x0 + dX, ground.last().y() ) ); ground.append( Vector2f( x0 + dX, y0 + dY ) ); ground.append( Vector2f( x0 - dX, y0 + dY ) ); ground.append( Vector2f( x0 - dX, ground.first().y() ) ); return ground; } }