/**
 * vue-cesium - https://github.com/zouyaoji/vue-cesium
 * Vue 2.x components for CesiumJS.
 *
 * @package VueCesium
 * @author zouyaoji <370681295@qq.com>
 * @version 2.3.12
 * @license MIT
 * @homepage https://zouyaoji.top/vue-cesium
 * @copyright (c) 2018-2022, zouyaoji <370681295@qq.com>
 */
var fullscreenVert = "\nattribute vec3 position;\nattribute vec2 st;\n\nvarying vec2 textureCoordinate;\n\nvoid main() {\n    textureCoordinate = st;\n    gl_Position = vec4(position, 1.0);\n}\n";
var getWindFrag = "\n// the size of UV textures: width = lon, height = lat*lev\nuniform sampler2D U; // eastward wind\nuniform sampler2D V; // northward wind\n\nuniform sampler2D currentParticlesPosition; // (lon, lat, lev)\n\nuniform vec3 dimension; // (lon, lat, lev)\nuniform vec3 minimum; // minimum of each dimension\nuniform vec3 maximum; // maximum of each dimension\nuniform vec3 interval; // interval of each dimension\n\nvarying vec2 v_textureCoordinates;\n\nvec2 mapPositionToNormalizedIndex2D(vec3 lonLatLev) {\n    // ensure the range of longitude and latitude\n    lonLatLev.x = mod(lonLatLev.x, 360.0);\n    lonLatLev.y = clamp(lonLatLev.y, -90.0, 90.0);\n\n    vec3 index3D = vec3(0.0);\n    index3D.x = (lonLatLev.x - minimum.x) / interval.x;\n    index3D.y = (lonLatLev.y - minimum.y) / interval.y;\n    index3D.z = (lonLatLev.z - minimum.z) / interval.z;\n\n    // the st texture coordinate corresponding to (col, row) index\n    // example\n    // data array is [0, 1, 2, 3, 4, 5], width = 3, height = 2\n    // the content of texture will be\n    // t 1.0\n    //    |  3 4 5\n    //    |\n    //    |  0 1 2\n    //   0.0------1.0 s\n\n    vec2 index2D = vec2(index3D.x, index3D.z * dimension.y + index3D.y);\n    vec2 normalizedIndex2D = vec2(index2D.x / dimension.x, index2D.y / (dimension.y * dimension.z));\n    return normalizedIndex2D;\n}\n\nfloat getWind(sampler2D windTexture, vec3 lonLatLev) {\n    vec2 normalizedIndex2D = mapPositionToNormalizedIndex2D(lonLatLev);\n    float result = texture2D(windTexture, normalizedIndex2D).r;\n    return result;\n}\n\nconst mat4 kernelMatrix = mat4(\n    0.0, -1.0, 2.0, -1.0, // first column\n    2.0, 0.0, -5.0, 3.0, // second column\n    0.0, 1.0, 4.0, -3.0, // third column\n    0.0, 0.0, -1.0, 1.0 // fourth column\n);\nfloat oneDimensionInterpolation(float t, float p0, float p1, float p2, float p3) {\n    vec4 tVec4 = vec4(1.0, t, t * t, t * t * t);\n    tVec4 = tVec4 / 2.0;\n    vec4 pVec4 = vec4(p0, p1, p2, p3);\n    return dot((tVec4 * kernelMatrix), pVec4);\n}\n\nfloat calculateB(sampler2D windTexture, float t, float lon, float lat, float lev) {\n    float lon0 = floor(lon) - 1.0 * interval.x;\n    float lon1 = floor(lon);\n    float lon2 = floor(lon) + 1.0 * interval.x;\n    float lon3 = floor(lon) + 2.0 * interval.x;\n\n    float p0 = getWind(windTexture, vec3(lon0, lat, lev));\n    float p1 = getWind(windTexture, vec3(lon1, lat, lev));\n    float p2 = getWind(windTexture, vec3(lon2, lat, lev));\n    float p3 = getWind(windTexture, vec3(lon3, lat, lev));\n\n    return oneDimensionInterpolation(t, p0, p1, p2, p3);\n}\n\nfloat interpolateOneTexture(sampler2D windTexture, vec3 lonLatLev) {\n    float lon = lonLatLev.x;\n    float lat = lonLatLev.y;\n    float lev = lonLatLev.z;\n\n    float lat0 = floor(lat) - 1.0 * interval.y;\n    float lat1 = floor(lat);\n    float lat2 = floor(lat) + 1.0 * interval.y;\n    float lat3 = floor(lat) + 2.0 * interval.y;\n\n    vec2 coef = lonLatLev.xy - floor(lonLatLev.xy);\n    float b0 = calculateB(windTexture, coef.x, lon, lat0, lev);\n    float b1 = calculateB(windTexture, coef.x, lon, lat1, lev);\n    float b2 = calculateB(windTexture, coef.x, lon, lat2, lev);\n    float b3 = calculateB(windTexture, coef.x, lon, lat3, lev);\n\n    return oneDimensionInterpolation(coef.y, b0, b1, b2, b3);\n}\n\nvec3 bicubic(vec3 lonLatLev) {\n    // https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm\n    float u = interpolateOneTexture(U, lonLatLev);\n    float v = interpolateOneTexture(V, lonLatLev);\n    float w = 0.0;\n    return vec3(u, v, w);\n}\n\nconst float h = 1.0;\nvec3 rungeKutta4(vec3 lonLatLev) {\n    vec3 p1 = lonLatLev;\n    vec3 k1 = bicubic(p1);\n\n    vec3 p2 = p1;\n    p2.xy = p2.xy + 0.5 * h * k1.xy;\n    vec3 k2 = bicubic(p2);\n\n    vec3 p3 = p1;\n    p3.xy = p3.xy + 0.5 * h * k2.xy;\n    vec3 k3 = bicubic(p3);\n\n    vec3 p4 = p1;\n    p4.xy = p4.xy + h * k3.xy;\n    vec3 k4 = bicubic(p4);\n\n    vec3 rk4 = (k1 + 2.0 * k2 + 2.0 * k3 + k4) / 6.0;\n    return rk4;\n}\n\nvoid main() {\n    // texture coordinate must be normalized\n    vec3 lonLatLev = texture2D(currentParticlesPosition, v_textureCoordinates).rgb;\n    vec3 windVector = rungeKutta4(lonLatLev);\n    gl_FragColor = vec4(windVector, 0.0);\n}\n";
var postProcessingPositionFrag = "\nuniform sampler2D nextParticlesPosition;\nuniform sampler2D nextParticlesSpeed; // (u, v, w, normalization)\n\n// range (min, max)\nuniform vec2 lonRange;\nuniform vec2 latRange;\nuniform vec2 lonDataRange;\nuniform vec2 latDataRange;\n\nuniform float randomCoef; // use to improve the pseudo-random generator\nuniform float dropRate; // drop rate is a chance a particle will restart at random position to avoid degeneration\nuniform float dropRateBump;\n\nvarying vec2 v_textureCoordinates;\n\n// pseudo-random generator\nconst vec3 randomConstants = vec3(12.9898, 78.233, 4375.85453);\nconst vec2 normalRange = vec2(0.0, 1.0);\nfloat rand(vec2 seed, vec2 range) {\n    vec2 randomSeed = randomCoef * seed;\n    float temp = dot(randomConstants.xy, randomSeed);\n    temp = fract(sin(temp) * (randomConstants.z + temp));\n    return temp * (range.y - range.x) + range.x;\n}\n\nvec3 generateRandomParticle(vec2 seed, float lev) {\n    // ensure the longitude is in [0, 360]\n    float randomLon = mod(rand(seed, lonRange), 360.0);\n    float randomLat = rand(-seed, latRange);\n\n    return vec3(randomLon, randomLat, lev);\n}\n\nbool particleOutbound(vec3 particle) {\n    return particle.y < -90.0 || particle.y > 90.0 || particle.y < latDataRange.x || particle.y > latDataRange.y || particle.x < lonDataRange.x || particle.x > lonDataRange.y;\n}\n\nvoid main() {\n    vec3 nextParticle = texture2D(nextParticlesPosition, v_textureCoordinates).rgb;\n    vec4 nextSpeed = texture2D(nextParticlesSpeed, v_textureCoordinates);\n    float particleDropRate = dropRate + dropRateBump * nextSpeed.a;\n\n    vec2 seed1 = nextParticle.xy + v_textureCoordinates;\n    vec2 seed2 = nextSpeed.xy + v_textureCoordinates;\n    vec3 randomParticle = generateRandomParticle(seed1, nextParticle.z);\n    float randomNumber = rand(seed2, normalRange);\n\n    if (randomNumber < particleDropRate || particleOutbound(nextParticle)) {\n        gl_FragColor = vec4(randomParticle, 1.0); // 1.0 means this is a random particle\n    } else {\n        gl_FragColor = vec4(nextParticle, 0.0);\n    }\n}\n";
var postProcessingSpeedFrag = "\nuniform sampler2D postProcessingPosition;\nuniform sampler2D nextParticlesSpeed;\n\nvarying vec2 v_textureCoordinates;\n\nvoid main() {\n    vec4 randomParticle = texture2D(postProcessingPosition, v_textureCoordinates);\n    vec4 particleSpeed = texture2D(nextParticlesSpeed, v_textureCoordinates);\n\n    if (randomParticle.a > 0.0) {\n        gl_FragColor = vec4(0.0);\n    } else {\n        gl_FragColor = particleSpeed;\n    }\n}\n";
var screenDrawFrag = "\nuniform sampler2D trailsColorTexture;\nuniform sampler2D trailsDepthTexture;\n\nvarying vec2 textureCoordinate;\n\nvoid main() {\n    vec4 trailsColor = texture2D(trailsColorTexture, textureCoordinate);\n    float trailsDepth = texture2D(trailsDepthTexture, textureCoordinate).r;\n    float globeDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, textureCoordinate));\n\n    if (trailsDepth < globeDepth) {\n        gl_FragColor = trailsColor;\n    } else {\n        gl_FragColor = vec4(0.0);\n    }\n}\n";
var segmentDrawFrag = "\nuniform sampler2D colorTable;\n\nvarying float speedNormalization;\n\nvoid main() {\n    gl_FragColor = texture2D(colorTable, vec2(speedNormalization, 0.0));\n}\n";
var segmentDrawVert = "\nattribute vec2 st;\n// it is not normal itself, but used to control normal\nattribute vec3 normal; // (point to use, offset sign, not used component)\n\nuniform sampler2D currentParticlesPosition;\nuniform sampler2D postProcessingPosition;\nuniform sampler2D postProcessingSpeed;\n\nuniform float particleHeight;\n\nuniform float aspect;\nuniform float pixelSize;\nuniform float lineWidth;\n\nvarying float speedNormalization;\n\nvec3 convertCoordinate(vec3 lonLatLev) {\n    // WGS84 (lon, lat, lev) -> ECEF (x, y, z)\n    // see https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#From_geodetic_to_ECEF_coordinates for detail\n\n    // WGS 84 geometric constants\n    float a = 6378137.0; // Semi-major axis\n    float b = 6356752.3142; // Semi-minor axis\n    float e2 = 6.69437999014e-3; // First eccentricity squared\n\n    float latitude = radians(lonLatLev.y);\n    float longitude = radians(lonLatLev.x);\n\n    float cosLat = cos(latitude);\n    float sinLat = sin(latitude);\n    float cosLon = cos(longitude);\n    float sinLon = sin(longitude);\n\n    float N_Phi = a / sqrt(1.0 - e2 * sinLat * sinLat);\n    float h = particleHeight; // it should be high enough otherwise the particle may not pass the terrain depth test\n\n    vec3 cartesian = vec3(0.0);\n    cartesian.x = (N_Phi + h) * cosLat * cosLon;\n    cartesian.y = (N_Phi + h) * cosLat * sinLon;\n    cartesian.z = ((b * b) / (a * a) * N_Phi + h) * sinLat;\n    return cartesian;\n}\n\nvec4 calcProjectedCoord(vec3 lonLatLev) {\n    // the range of longitude in Cesium is [-180, 180] but the range of longitude in the NetCDF file is [0, 360]\n    // [0, 180] is corresponding to [0, 180] and [180, 360] is corresponding to [-180, 0]\n    lonLatLev.x = mod(lonLatLev.x + 180.0, 360.0) - 180.0;\n    vec3 particlePosition = convertCoordinate(lonLatLev);\n    vec4 projectedCoord = czm_modelViewProjection * vec4(particlePosition, 1.0);\n    return projectedCoord;\n}\n\nvec4 calcOffset(vec4 currentProjectedCoord, vec4 nextProjectedCoord, float offsetSign) {\n    vec2 aspectVec2 = vec2(aspect, 1.0);\n    vec2 currentXY = (currentProjectedCoord.xy / currentProjectedCoord.w) * aspectVec2;\n    vec2 nextXY = (nextProjectedCoord.xy / nextProjectedCoord.w) * aspectVec2;\n\n    float offsetLength = lineWidth / 2.0;\n    vec2 direction = normalize(nextXY - currentXY);\n    vec2 normalVector = vec2(-direction.y, direction.x);\n    normalVector.x = normalVector.x / aspect;\n    normalVector = offsetLength * normalVector;\n\n    vec4 offset = vec4(offsetSign * normalVector, 0.0, 0.0);\n    return offset;\n}\n\nvoid main() {\n    vec2 particleIndex = st;\n\n    vec3 currentPosition = texture2D(currentParticlesPosition, particleIndex).rgb;\n    vec4 nextPosition = texture2D(postProcessingPosition, particleIndex);\n\n    vec4 currentProjectedCoord = vec4(0.0);\n    vec4 nextProjectedCoord = vec4(0.0);\n    if (nextPosition.w > 0.0) {\n      currentProjectedCoord = calcProjectedCoord(currentPosition);\n      nextProjectedCoord = calcProjectedCoord(currentPosition);\n    } else {\n        currentProjectedCoord = calcProjectedCoord(currentPosition);\n      nextProjectedCoord = calcProjectedCoord(nextPosition.xyz);\n    }\n\n    float pointToUse = normal.x; // -1 is currentProjectedCoord and +1 is nextProjectedCoord\n    float offsetSign = normal.y;\n\n    vec4 offset = pixelSize * calcOffset(currentProjectedCoord, nextProjectedCoord, offsetSign);\n    if (pointToUse < 0.0) {\n        gl_Position = currentProjectedCoord + offset;\n    } else {\n        gl_Position = nextProjectedCoord + offset;\n    }\n\n    speedNormalization = texture2D(postProcessingSpeed, particleIndex).a;\n}\n";
var trailDrawFrag = "\nuniform sampler2D segmentsColorTexture;\nuniform sampler2D segmentsDepthTexture;\n\nuniform sampler2D currentTrailsColor;\nuniform sampler2D trailsDepthTexture;\n\nuniform float fadeOpacity;\n\nvarying vec2 textureCoordinate;\n\nvoid main() {\n    vec4 pointsColor = texture2D(segmentsColorTexture, textureCoordinate);\n    vec4 trailsColor = texture2D(currentTrailsColor, textureCoordinate);\n\n    trailsColor = floor(fadeOpacity * 255.0 * trailsColor) / 255.0; // make sure the trailsColor will be strictly decreased\n\n    float pointsDepth = texture2D(segmentsDepthTexture, textureCoordinate).r;\n    float trailsDepth = texture2D(trailsDepthTexture, textureCoordinate).r;\n    float globeDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, textureCoordinate));\n\n    gl_FragColor = vec4(0.0);\n    if (pointsDepth < globeDepth) {\n      gl_FragColor = gl_FragColor + pointsColor;\n    }\n    if (trailsDepth < globeDepth) {\n      gl_FragColor = gl_FragColor + trailsColor;\n    }\n    gl_FragDepthEXT = min(pointsDepth, trailsDepth);\n}\n";
var updatePositionFrag = "\nuniform sampler2D currentParticlesPosition; // (lon, lat, lev)\nuniform sampler2D currentParticlesSpeed; // (u, v, w, normalization)\n\nvarying vec2 v_textureCoordinates;\n\nvec2 lengthOfLonLat(vec3 lonLatLev) {\n    // unit conversion: meters -> longitude latitude degrees\n    // see https://en.wikipedia.org/wiki/Geographic_coordinate_system#Length_of_a_degree for detail\n\n    // Calculate the length of a degree of latitude and longitude in meters\n    float latitude = radians(lonLatLev.y);\n\n    float term1 = 111132.92;\n    float term2 = 559.82 * cos(2.0 * latitude);\n    float term3 = 1.175 * cos(4.0 * latitude);\n    float term4 = 0.0023 * cos(6.0 * latitude);\n    float latLength = term1 - term2 + term3 - term4;\n\n    float term5 = 111412.84 * cos(latitude);\n    float term6 = 93.5 * cos(3.0 * latitude);\n    float term7 = 0.118 * cos(5.0 * latitude);\n    float longLength = term5 - term6 + term7;\n\n    return vec2(longLength, latLength);\n}\n\nvoid updatePosition(vec3 lonLatLev, vec3 speed) {\n    vec2 lonlatLengthgth = lengthOfLonLat(lonLatLev);\n    float u = speed.x / lonlatLengthgth.x;\n    float v = speed.y / lonlatLengthgth.y;\n    float w = 0.0;\n    vec3 windVectorInLonLatLev = vec3(u, v, w);\n\n    vec3 nextParticle = lonLatLev + windVectorInLonLatLev;\n\n    gl_FragColor = vec4(nextParticle, 0.0);\n}\n\nvoid main() {\n    // texture coordinate must be normalized\n    vec3 lonLatLev = texture2D(currentParticlesPosition, v_textureCoordinates).rgb;\n    vec3 speed = texture2D(currentParticlesSpeed, v_textureCoordinates).rgb;\n\n    updatePosition(lonLatLev, speed);\n}\n";
var updateSpeedFrag = "\nuniform sampler2D currentParticlesSpeed; // (u, v, w, normalization)\nuniform sampler2D particlesWind;\n\n// use to calculate the relative speed\nuniform vec2 uSpeedRange; // (min, max);\nuniform vec2 vSpeedRange;\nuniform float pixelSize;\nuniform float speedFactor;\n\nvarying vec2 v_textureCoordinates;\n\nvec4 calcRelativeSpeed(vec3 speed) {\n    vec3 percent = vec3(0.0);\n    percent.x = (speed.x - uSpeedRange.x) / (uSpeedRange.y - uSpeedRange.x);\n    percent.y = (speed.y - vSpeedRange.x) / (vSpeedRange.y - vSpeedRange.x);\n    float normalization = length(percent);\n\n    float minRelativeSpeed = -speedFactor * pixelSize;\n    float maxRelativeSpeed = speedFactor * pixelSize;\n\n    vec3 relativeSpeed = vec3(0.0);\n    relativeSpeed.x = mix(minRelativeSpeed, maxRelativeSpeed, percent.x);\n    relativeSpeed.y = mix(minRelativeSpeed, maxRelativeSpeed, percent.y);\n\n    return vec4(relativeSpeed, normalization);\n}\n\nvoid main() {\n    // texture coordinate must be normalized\n    vec3 currentSpeed = texture2D(currentParticlesSpeed, v_textureCoordinates).rgb;\n    vec3 windVector = texture2D(particlesWind, v_textureCoordinates).rgb;\n\n    vec4 nextSpeed = calcRelativeSpeed(windVector);\n    gl_FragColor = nextSpeed;\n}\n";

export { fullscreenVert, getWindFrag, postProcessingPositionFrag, postProcessingSpeedFrag, screenDrawFrag, segmentDrawFrag, segmentDrawVert, trailDrawFrag, updatePositionFrag, updateSpeedFrag };
