void SphereDisplay::DrawTrack(const Sensor& sensor,
                              const ViewArea& radarView,
                              const Track& track,
                              bool negate_z)
{
    if (!radarView.IsActive())
        return;

    GFXColor color = sensor.GetColor(track);

    Vector position = track.GetPosition();
    if (negate_z) position.z=-position.z;
    if (position.z < 0){
        static bool  negate_z       =
            XMLSupport::parse_bool( vs_config->getVariable( "graphics", "hud", "show_negative_blips_as_positive", "true" ));
        if (negate_z)
            position.z=-position.z;
        else                                    
            position.z = .125;
    }
    const float trackSize = 2.0;

    // FIXME: Jitter only on boundary, not in center
    if (sensor.InsideNebula())
    {
        Jitter(0.02, 0.04, position);
    }
    else
    {
        const bool isNebula = (track.GetType() == Track::Type::Nebula);
        const bool isEcmActive = track.HasActiveECM();
        if (isNebula || isEcmActive)
        {
            float error = 0.02 * trackSize;
            Jitter(error, error, position);
        }
    }

    // The magnitude is used to calculate the unit vector. With subtle scaling
    // of the magnitude we generate a unit vector whose length will vary from
    // innerSphere to 1.0, depending on the distance to the object. Combined
    // with the OpenGL z-buffering, this will ensure that close tracks are drawn
    // on top of distant tracks.
    float magnitude = position.Magnitude();
    float scaleFactor = 0.0; // [0; 1] where 0 = border, 1 = center
    const float maxRange = sensor.GetMaxRange();
    if (magnitude <= maxRange)
    {
        // [innerSphere; 1]
        scaleFactor = (1.0 - innerSphere) * (maxRange - magnitude) / maxRange;
        magnitude /= (1.0 - scaleFactor);
    }
    Vector scaledPosition = Vector(-position.x, position.y, position.z) / magnitude;

    Vector head = radarView.Scale(scaledPosition);
    
    GFXColor headColor = color;
    if (sensor.UseThreatAssessment())
    {
        float dangerRate = GetDangerRate(sensor.IdentifyThreat(track));
        if (dangerRate > 0.0)
        {
            // Blinking track
            headColor.a *= cosf(dangerRate * radarTime);
        }
    }
    // Fade out dying ships
    if (track.IsExploding())
    {
        headColor.a *= (1.0 - track.ExplodingProgress());
    }

    GFXColorf(headColor);
    if (sensor.IsTracking(track))
    {
        DrawTargetMarker(head, trackSize);
    }
    GFXPointSize(trackSize);
    GFXBegin(GFXPOINT);
    GFXVertexf(head);
    GFXEnd();
}
void BubbleDisplay::DrawTrack(const Sensor& sensor,
                              const ViewArea& radarView,
                              const Track& track)
{
    if (!radarView.IsActive())
        return;

    GFXColor color = sensor.GetColor(track);

    Vector position = track.GetPosition();
    if (position.z < 0)
        position.z = -position.z;

    float magnitude = position.Magnitude();
    float scaleFactor = 0.0; // [0; 1] where 0 = border, 1 = center
    float maxRange = sensor.GetMaxRange();
    if (magnitude <= maxRange)
    {
        // [innerSphere; outerSphere]
        scaleFactor = (outerSphere - innerSphere) * ((maxRange - magnitude) / maxRange);
        magnitude /= (1.0 - scaleFactor);
    }

    if (sensor.InsideNebula())
    {
        magnitude /= (1.0 - 0.04 * Jitter(0.0, 1.0));
    }
    Vector scaledPosition = sphereZoom * Vector(-position.x, position.y, position.z) / magnitude;

    Vector head = radarView.Scale(scaledPosition);

    GFXColor headColor = color;

    headColor.a *= 0.2 + scaleFactor * (1.0 - 0.2); // [0;1] => [0.1;1]
    if (sensor.UseThreatAssessment())
    {
        float dangerRate = GetDangerRate(sensor.IdentifyThreat(track));
        if (dangerRate > 0.0)
        {
            // Blinking blip
            headColor.a *= cosf(dangerRate * radarTime);
        }
    }

    // Fade out dying ships
    if (track.IsExploding())
    {
        headColor.a *= (1.0 - track.ExplodingProgress());
    }

    float trackSize = std::max(1.0f, std::log10(track.GetSize()));
    if (track.GetType() != Track::Type::Cargo)
        trackSize += 1.0;

    if (sensor.IsTracking(track))
    {
        currentTargetMarkerSize = trackSize;
        DrawTargetMarker(head, headColor, trackSize);
    }

    const bool isNebula = (track.GetType() == Track::Type::Nebula);
    const bool isEcmActive = track.HasActiveECM();
    if (isNebula || isEcmActive)
    {
        // Vary size between 50% and 150%
        trackSize *= Jitter(0.5, 1.0);
    }

    impl->getPointBuffer(trackSize).insert(GFXColorVertex(head, headColor));
}