#version 440

layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec2 inA;   // A B and C: control points in logical coordinates
layout(location = 2) in vec2 inB;
layout(location = 3) in vec2 inC;
layout(location = 4) in vec3 normalExt; // x and y: normal vector; z: strokeWidth multiplier (default 1)

layout(location = 0) out vec4 P;    // stroke edge
layout(location = 1) out vec2 A;    // A B and C: control points in logical coordinates
layout(location = 2) out vec2 B;
layout(location = 3) out vec2 C;
layout(location = 4) out vec4 HGOW; // H and G: args to solveDepressedCubic(); O: offset; W: adj strokeWidth

#if defined(LINEARGRADIENT)
layout(location = 5) out float gradTabIndex;
#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
layout(location = 5) out vec2 coord;
#endif

layout(std140, binding = 0) uniform buf {
#if QSHADER_VIEW_COUNT >= 2
    mat4 qt_Matrix[QSHADER_VIEW_COUNT];
#else
    mat4 qt_Matrix;
#endif

    float matrixScale;
    float opacity;
    float devicePixelRatio;
    float strokeWidth;

#if defined(LINEARGRADIENT)
    vec2 gradientStart;
    vec2 gradientEnd;
#elif defined(RADIALGRADIENT)
    vec2 translationPoint;
    vec2 focalToCenter;
    float centerRadius;
    float focalRadius;
    float reserved3;
    float reserved4;
#elif defined(CONICALGRADIENT)
    vec2 translationPoint;
    float angle;
    float reserved3;
#else
    vec4 strokeColor;
#endif

    float debug;
    float reserved5;
    float reserved6;
    float reserved7;

} ubuf;

#define SQRT2 1.41421356237

float qdot(vec2 a, vec2 b)
{
    return a.x * b.x + a.y * b.y;
}

void main()
{
    vec4 normalVector = vec4(normalExt.xy, 0.0, 0.0);
    // If STROKE_EXPANDING is _not_ defined, vertexCoord starts out in
    // logical coordinates, already positioned outwards past an edge of the stroke.
    P = vertexCoord + normalVector * SQRT2 / ubuf.matrixScale;
    float adjStrokeWidth = abs(ubuf.strokeWidth); // passed to fragment shader in HGOW.w

#if defined(STROKE_EXPANDING)
    // vertexCoord starts out in logical coordinates, in the center of the stroke.
    // Move the vertex by stroke width * normalVector to fill the stroke width;
    // and in case of a cosmetic stroke, divide by matrixScale to undo the scaling.
    // In the case of a miter joint, the tip vertices need to be moved farther,
    // so the length of normalVector may be > 1.
    P += normalVector * adjStrokeWidth; // expand to stroke width

    // Negative ubuf.strokeWidth means we want a "cosmetic pen", which means
    // we make the stroke triangles wider or narrower to adjust for the difference
    // between zoomed stroke width and cosmetic stroke width.
    if (ubuf.strokeWidth < 0.) { // cosmetic stroke
        adjStrokeWidth *= ubuf.devicePixelRatio / ubuf.matrixScale;
        float strokeDiff = max(ubuf.devicePixelRatio / ubuf.matrixScale, adjStrokeWidth) *
                (ubuf.matrixScale / ubuf.devicePixelRatio - 1) / 2.;
        P -= normalVector * strokeDiff; // cosmetic adjustment
    }

    // adjust stroke width by the given multiplier (usually 1)
    adjStrokeWidth *= normalExt.z;
#endif // if the shader is expected to expand the stroke by moving vertices outwards

#if defined(LINEARGRADIENT)
    vec2 gradVec = ubuf.gradientEnd - ubuf.gradientStart;
    gradTabIndex = dot(gradVec, P.xy - ubuf.gradientStart) / dot(gradVec, gradVec);
#elif defined(RADIALGRADIENT) || defined(CONICALGRADIENT)
    coord = P.xy - ubuf.translationPoint;
#endif


    A = inA;
    B = inB;
    C = inC;

    // Find the parameters H, G for the depressed cubic
    // t^2+H*t+G=0
    // that results from the equation
    // Q'(s).(p-Q(s)) = 0
    // O (HGOW.z) is the static offset between s and t:
    // s = t - b/(3a)
    // use it to get back the parameter t

    // this is a constant for the curve
    float a = -2. * qdot(A, A);
    // this is a constant for the curve
    float b = -3. * qdot(A, B);
    //this is linear in p so it can be put into the shader with vertex data
    float c = 2. * qdot(A, P.xy) - qdot(B, B) - 2. * qdot(A, C);
    //this is linear in p so it can be put into the shader with vertex data
    float d = qdot(B, P.xy) - qdot(B, C);
    // convert to depressed cubic.
    // both functions are linear in c and d and thus linear in p
    float H = (3. * a * c - b * b) / (3. * a * a);
    float G = (2. * b * b * b - 9. * a * b * c + 27. * a * a * d) / (27. * a * a * a);
    HGOW = vec4(H, G, b / (3 * a), adjStrokeWidth);

#if QSHADER_VIEW_COUNT >= 2
    gl_Position = ubuf.qt_Matrix[gl_ViewIndex] * P;
#else
    gl_Position = ubuf.qt_Matrix * P;
#endif
}
