/* COPYRIGHT Esri - https://js.arcgis.com/5.0.18/LICENSE.txt */
import{MixExternalColor as e}from"../util/MixExternalColor.glsl.js";import{NoPerspectiveRead as t}from"../util/NoPerspective.glsl.js";import{addCameraPosition as o,addPixelRatio as i}from"../util/View.glsl.js";import{Float4PassUniform as r}from"../../shaderModules/Float4PassUniform.js";import{FloatBindUniform as l}from"../../shaderModules/FloatBindUniform.js";import{FloatPassUniform as a}from"../../shaderModules/FloatPassUniform.js";import{glsl as p,If as s}from"../../shaderModules/glsl.js";import{Texture2DPassUniform as n}from"../../shaderModules/Texture2DPassUniform.js";import{isImagePattern as d}from"../../../materials/lineStippleUtils.js";import{computeTextureSize as c}from"../../../materials/stippleTextureRepository.js";import{ensureColor4 as f}from"../../../shaders/ensureColor4.js";function m(d,c){if(!c.stippleEnabled)return void d.fragment.code.add(p`float getStippleAlpha(float lineWidth) { return 1.0; }
void discardByStippleAlpha(float stippleAlpha, float threshold) {}
vec4 blendStipple(vec4 color, float stippleAlpha) { return color; }`);const m=!(c.draped&&c.stipplePreferContinuous),{vertex:u,fragment:v}=d;c.draped||(o(u,c),u.uniforms.add(new l("worldToScreenPerDistanceRatio",({camera:e})=>1/e.perScreenPixelRatio)).code.add(p`float computeWorldToScreenRatio(vec3 segmentCenter) {
float segmentDistanceToCamera = length(segmentCenter - cameraPosition);
return worldToScreenPerDistanceRatio / segmentDistanceToCamera;
}`)),d.varyings.add("vStippleDistance","float"),d.varyings.add("vStippleDistanceLimits","vec2"),d.varyings.add("vStipplePatternStretch","float"),u.code.add(p`
    float discretizeWorldToScreenRatio(float worldToScreenRatio) {
      float step = ${p.float(g)};

      float discreteWorldToScreenRatio = log(worldToScreenRatio);
      discreteWorldToScreenRatio = ceil(discreteWorldToScreenRatio / step) * step;
      discreteWorldToScreenRatio = exp(discreteWorldToScreenRatio);
      return discreteWorldToScreenRatio;
    }
  `),i(u),u.code.add(p`
    vec2 computeStippleDistanceLimits(float startPseudoScreen, float segmentLengthPseudoScreen, float segmentLengthScreen, float patternLength) {

      // First check if the segment is long enough to support fully screen space patterns.
      // Force sparse mode for segments that are very large in screen space even if it is not allowed,
      // to avoid imprecision from calculating with large floats.
      if (segmentLengthPseudoScreen >= ${m?"patternLength":"1e4"}) {
        // Round the screen length to get an integer number of pattern repetitions (minimum 1).
        float repetitions = segmentLengthScreen / (patternLength * pixelRatio);
        float flooredRepetitions = max(1.0, floor(repetitions + 0.5));
        float segmentLengthScreenRounded = flooredRepetitions * patternLength;

        float stretch = repetitions / flooredRepetitions;

        // We need to impose a lower bound on the stretch factor to prevent the dots from merging together when there is only 1 repetition.
        // 0.75 is the lowest possible stretch value for flooredRepetitions > 1, so it makes sense as lower bound.
        vStipplePatternStretch = max(0.75, stretch);

        return vec2(0.0, segmentLengthScreenRounded);
      }
      return vec2(startPseudoScreen, startPseudoScreen + segmentLengthPseudoScreen);
    }
  `),v.uniforms.add(new n("stipplePatternTexture",e=>e.stippleTexture),new a("stipplePatternPixelSizeInv",e=>1/S(e))),c.stippleOffColorEnabled&&v.uniforms.add(new r("stippleOffColor",e=>f(e.stippleOffColor))),d.include(t),c.worldSizedImagePattern?(d.varyings.add("vStippleV","float"),d.fragment.include(e),v.code.add(p`vec4 getStippleColor(out bool isClamped) {
vec2 aaCorrectedLimits = vStippleDistanceLimits + vec2(1.0, -1.0) / gl_FragCoord.w;
isClamped = vStippleDistance < aaCorrectedLimits.x || vStippleDistance > aaCorrectedLimits.y;
float u = vStippleDistance * stipplePatternPixelSizeInv;
float v = vStippleV == -1.0 ? 0.5 : vStippleV;
return texture(stipplePatternTexture, vec2(u, v));
}
vec4 getStippleColor() {
bool ignored;
return getStippleColor(ignored);
}
float getStippleSDF() {
vec4 color = getStippleColor();
return color.a == 0.0 ? -0.5 : 0.5;
}
float getStippleAlpha(float lineWidth) {
return getStippleColor().a;
}
vec4 blendStipple(vec4 color, float stippleAlpha) {
vec4 stippleColor = getStippleColor();
int mixMode  = 1;
vec3 col = mixExternalColor(color.rgb, vec3(1.0), stippleColor.rgb, mixMode);
float opacity = mixExternalOpacity(color.a, 1.0, stippleColor.a, mixMode);
return vec4(col, opacity);
}`)):v.code.add(p`
    float getStippleSDF(out bool isClamped) {
      float stippleDistanceClamped = noPerspectiveRead(clamp(vStippleDistance, vStippleDistanceLimits.x, vStippleDistanceLimits.y));
      float lineSizeInv = noPerspectiveRead(vLineSizeInv);

      vec2 aaCorrectedLimits = vStippleDistanceLimits + vec2(1.0, -1.0) / gl_FragCoord.w;
      isClamped = vStippleDistance < aaCorrectedLimits.x || vStippleDistance > aaCorrectedLimits.y;

      float u = stippleDistanceClamped * stipplePatternPixelSizeInv * lineSizeInv;
      u = fract(u);

      float sdf = texture(stipplePatternTexture, vec2(u, 0.5)).r;

      return (sdf - 0.5) * vStipplePatternStretch + 0.5;
    }

    float getStippleSDF() {
      bool ignored;
      return getStippleSDF(ignored);
    }

    float getStippleAlpha(float lineWidth) {
      bool isClamped;
      float stippleSDF = getStippleSDF(isClamped);
      float antiAliasedResult = clamp(stippleSDF * lineWidth + 0.5, 0.0, 1.0);
      return isClamped ? floor(antiAliasedResult + 0.5) : antiAliasedResult;
    }

    vec4 blendStipple(vec4 color, float stippleAlpha) {
      return ${c.stippleOffColorEnabled?"mix(color, stippleOffColor, stippleAlpha)":"vec4(color.rgb, color.a * stippleAlpha)"};
    }
  `),v.code.add(p`
    void discardByStippleAlpha(float stippleAlpha, float threshold) {
     ${s(!c.stippleOffColorEnabled,"if (stippleAlpha < threshold) { discard; }")}
    }
  `)}function S(e){const t=e.stipplePattern;return d(t)?t.length:t?c(t)/t.pixelRatio:1}const g=.4;export{m as LineStipple,S as computePixelSize};
