403Webshell
Server IP : 217.154.3.148  /  Your IP : 216.73.216.90
Web Server : Apache
System : Linux blissful-wright.217-154-3-148.plesk.page 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64
User : gracious-boyd_hfhh1h8vo9n ( 10000)
PHP Version : 8.3.31
Disable Function : opcache_get_status
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/lib/masks/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/lib/masks/pipeline.ts
import { normalizeIllumination } from "./illumination";
import { autoTuneMask } from "./autotune";
import { buildLatticeMasks, coverageOfMask, computeBandPx, leakRatio } from "./lattice";
import type { PipelineInput, PipelineResult } from "./types";

export function runMaskPipeline(input: PipelineInput): PipelineResult {
  const debug: Record<string, unknown> = {
    preset: input.preset,
    flags: input.flags,
  };

  let workingMask = Uint8Array.from(input.normalizedMask);
  let intensity = input.intensity;

  if (input.flags.enableIllumination) {
    const grid = input.grid;
    const avgCell = grid ? Math.max(grid.tileWidthPx, grid.tileHeightPx) : 0;
    const radius = Math.max(24, Math.round(avgCell * 0.6));
    const amount = 0.75;
    intensity = normalizeIllumination(intensity, input.width, input.height, { radius, amount });
    debug.illumination = { enabled: true, radius, amount };
  } else {
    debug.illumination = { enabled: false };
  }

  let leak: number | null = null;
  let interiorCoverage: number | null = null;
  let latticeWeights: Float32Array | null = null;
  let latticeAxis: Int8Array | null = null;
  let latticeLineMask: Uint8Array | null = null;
  let interiorMaskRef: Uint8Array | null = null;
  let leakExtras: {
    weights?: Float32Array;
    axisMap?: Int8Array;
    gradX?: Float32Array;
    gradY?: Float32Array;
    gradientThreshold?: number;
    angleThresholdRad?: number;
  } | null = null;
  let gradient: { gx: Float32Array; gy: Float32Array } | null = null;

  const thinFrac = Math.max(0.0, Math.min(0.5, input.flags.thinWidthFrac ?? 0.05));
  const minAreaFrac = Math.max(0.05, Math.min(0.8, input.flags.minAreaFrac ?? 0.3));

  const warnings: string[] = [];
  const cellMajor = input.grid ? Math.max(input.grid.tileWidthPx, input.grid.tileHeightPx) : 0;

  if (input.flags.enableLattice && input.grid && input.grid.vlines.length && input.grid.hlines.length) {
    const bandPx = computeBandPx(input.grid);
    const leakBandPx = leakBandWidth(input.grid);
    const { interiorMask, lineMask, weightMap, axisMap } = buildLatticeMasks(
      input.width,
      input.height,
      input.grid.vlines,
      input.grid.hlines,
      bandPx,
    );
    latticeWeights = weightMap;
    latticeAxis = axisMap;
    latticeLineMask = lineMask;
    interiorMaskRef = interiorMask;
    interiorCoverage = coverageOfMask(interiorMask);

    gradient = computeGradientVectors(intensity, input.width, input.height);
    leakExtras = {
      weights: latticeWeights,
      axisMap: latticeAxis ?? undefined,
      gradX: gradient?.gx,
      gradY: gradient?.gy,
      gradientThreshold: 12,
      angleThresholdRad: (15 * Math.PI) / 180,
    };

    if (gradient && interiorMaskRef) {
      const edgeEnergy = computeEdgeEnergy(gradient);
      const tangentSeeds = buildTangentSeedMask(
        input.width,
        input.height,
        interiorMaskRef,
        latticeWeights,
        gradient,
        {
          coherence: 0.6,
          weightThreshold: 0.45,
          magnitudeThreshold: 22,
        },
      );
      if (tangentSeeds) {
        let added = 0;
        for (let i = 0; i < workingMask.length; i++) {
          if (tangentSeeds[i] && !workingMask[i]) {
            workingMask[i] = 255;
            added += 1;
          }
        }
        debug.tangentSeeds = { enabled: true, count: added };
      } else {
        debug.tangentSeeds = { enabled: false };
      }

      const fillStats = fillComponentHoles(
        workingMask,
        input.width,
        input.height,
        interiorMaskRef,
        latticeWeights,
        edgeEnergy,
        leakBandPx,
        cellMajor,
      );
      if (fillStats) {
        debug.componentFill = fillStats;
        if (latticeLineMask && leakExtras) {
          leak = leakRatio(workingMask, latticeLineMask, leakExtras);
          (debug.componentFill as any).leakAfter = leak;
        }
      } else {
        debug.componentFill = { enabled: false, components: 0, holesFilled: 0, pixelsFilled: 0 };
      }

      const specStats = applySpecularBridges(
        workingMask,
        input.width,
        input.height,
        intensity,
        latticeWeights,
        interiorMaskRef,
        gradient,
        edgeEnergy,
      );
      debug.specularBridge = specStats;
      if (specStats.enabled && latticeLineMask && leakExtras) {
        leak = leakRatio(workingMask, latticeLineMask, leakExtras);
        (debug.specularBridge as any).leakAfter = leak;
      }
    } else {
      debug.componentFill = { enabled: false, components: 0, holesFilled: 0, pixelsFilled: 0 };
    }
    if (!debug.tangentSeeds) {
      debug.tangentSeeds = { enabled: false };
    }
    if (!debug.specularBridge) {
      debug.specularBridge = { enabled: false, pixels: 0 };
    }

    debug.lattice = {
      enabled: true,
      bandPx,
      interiorCoverage,
      vlines: input.grid.vlines.length,
      hlines: input.grid.hlines.length,
      weightMean: meanOf(weightMap),
    };

    if (input.flags.enableAutotune) {
      const tileArea = Math.max(1, input.grid.tileWidthPx * input.grid.tileHeightPx);
      const cellMajorLocal = cellMajor > 0 ? cellMajor : Math.max(input.grid.tileWidthPx, input.grid.tileHeightPx);
      let minArea = Math.max(32, Math.round(tileArea * minAreaFrac));
      let thinWidth = Math.max(2, Math.round(Math.max(1, cellMajorLocal * thinFrac)));
      const occluderCoverage = input.stats.background?.occluderCoverage ?? 0;
      if (occluderCoverage >= 0.3) {
        thinWidth = Math.max(9, thinWidth);
        minArea = Math.max(900, minArea);
      } else if (occluderCoverage >= 0.18) {
        thinWidth = Math.max(6, thinWidth);
        minArea = Math.max(650, minArea);
      } else {
        thinWidth = Math.max(4, thinWidth);
        minArea = Math.max(450, minArea);
      }
      const maxArea = Math.max(minArea, Math.round(tileArea * 1.8));
      minArea = Math.min(minArea, maxArea);
      thinWidth = Math.min(thinWidth, 12);
      const autoOptions = {
        targetLow: 0.20,
        targetHigh: 0.24,
        minArea,
        thinWidth,
        thinLength: 0.3,
        leakLimit: 0.03,
        maxDilateSteps: 6,
        u2BoostMask: input.u2BoostMask ?? undefined,
      };
      const tuned = autoTuneMask(
        workingMask,
        interiorMask,
        lineMask,
        input.width,
        input.height,
        autoOptions,
        leakExtras ?? {},
      );
      workingMask = tuned.mask;
      leak = tuned.leak;
      debug.autotune = {
        enabled: true,
        targetLow: autoOptions.targetLow,
        targetHigh: autoOptions.targetHigh,
        minArea,
        thinWidth,
        adjustments: tuned.adjustments,
        leak,
        budgetMs: input.flags.autotuneMs,
      };
    } else {
      leak = leakRatio(workingMask, lineMask, leakExtras ?? {});
      debug.autotune = { enabled: false, leak, budgetMs: input.flags.autotuneMs };
    }
  } else {
    debug.lattice = { enabled: false };
    if (input.flags.enableAutotune) {
      debug.autotune = { enabled: false, reason: "no-grid", budgetMs: input.flags.autotuneMs };
    }
  }

  if (!debug.componentFill) {
    debug.componentFill = { enabled: false, components: 0, holesFilled: 0, pixelsFilled: 0 };
  }

  if (latticeWeights) {
    const len = workingMask.length;
    for (let i = 0; i < len; i++) {
      if (workingMask[i] && latticeWeights[i] <= 0.25) {
        workingMask[i] = 0;
      }
    }
  }

  if (latticeLineMask && leakExtras && latticeWeights && gradient && latticeAxis) {
    const leakBandPx = leakBandWidth(input.grid);
    const clamp = applyLeakClamp(
      workingMask,
      input.width,
      input.height,
      latticeWeights,
      latticeAxis,
      gradient,
      leakBandPx,
      latticeLineMask,
      leakExtras,
    );
    leak = clamp.leakAfter;
    debug.leakClamp = clamp;
  } else {
    debug.leakClamp = { enabled: false };
    if (latticeLineMask && leakExtras) {
      leak = leakRatio(workingMask, latticeLineMask, leakExtras);
    }
  }

  const finalCoverage = coverageOfMask(workingMask);
  if (finalCoverage > 0.26 && finalCoverage <= 0.55) warnings.push("coverage_high");
  if (finalCoverage < 0.12 && finalCoverage >= 0.06) warnings.push("coverage_low");
  if (leak != null && leak > 0 && leak > 0.05) warnings.push("leak_warn");

  debug.coverage = {
    before: input.normalizedCoverage,
    after: finalCoverage,
    leak,
    warnings,
  };

  return {
    mask: workingMask,
    coverage: finalCoverage,
    leak,
    interiorCoverage,
    debug,
  };
}

function computeGradientVectors(gray: Uint8Array, width: number, height: number) {
  const len = gray.length;
  const gx = new Float32Array(len);
  const gy = new Float32Array(len);
  const gxK = [-1, 0, 1, -2, 0, 2, -1, 0, 1];
  const gyK = [-1, -2, -1, 0, 0, 0, 1, 2, 1];
  for (let y = 1; y < height - 1; y++) {
    for (let x = 1; x < width - 1; x++) {
      let sx = 0;
      let sy = 0;
      let idxK = 0;
      for (let dy = -1; dy <= 1; dy++) {
        const yy = y + dy;
        for (let dx = -1; dx <= 1; dx++, idxK++) {
          const xx = x + dx;
          const value = gray[yy * width + xx];
          sx += value * gxK[idxK];
          sy += value * gyK[idxK];
        }
      }
      const idx = y * width + x;
      gx[idx] = sx;
      gy[idx] = sy;
    }
  }
  return { gx, gy };
}

function meanOf(values: Float32Array): number {
  if (!values?.length) return 0;
  let sum = 0;
  for (let i = 0; i < values.length; i++) sum += values[i];
  return sum / values.length;
}

function leakBandWidth(grid: PipelineInput["grid"]): number {
  if (!grid) return 3;
  const grout = Math.max(1, grid.groutPx || 1);
  return Math.min(6, Math.max(2, Math.round(grout * 1.5)));
}

type LeakClampStats = {
  enabled: boolean;
  removed?: number;
  weakened?: number;
  leakBefore?: number;
  leakAfter?: number;
};

function applyLeakClamp(
  mask: Uint8Array,
  width: number,
  height: number,
  weights: Float32Array,
  axis: Int8Array,
  gradient: { gx: Float32Array; gy: Float32Array },
  bandPx: number,
  lineMask: Uint8Array,
  leakExtras: {
    weights?: Float32Array;
    axisMap?: Int8Array;
    gradX?: Float32Array;
    gradY?: Float32Array;
    gradientThreshold?: number;
    angleThresholdRad?: number;
  },
  leakLimit = 0.03,
): LeakClampStats {
  const leakBefore = leakRatio(mask, lineMask, leakExtras);
  if (leakBefore <= leakLimit) {
    return { enabled: false, leakBefore, leakAfter: leakBefore };
  }

  const cosMax = Math.cos((15 * Math.PI) / 180);
  const gxArr = gradient.gx;
  const gyArr = gradient.gy;
  const len = mask.length;
  let removed = 0;
  let weakened = 0;
  for (let i = 0; i < len; i++) {
    if (!mask[i]) continue;
    const w = weights[i];
    if (w > 0.35) continue;
    const orient = axis[i];
    if (orient < 0) {
      mask[i] = 0;
      removed += 1;
      continue;
    }
    const gx = gxArr[i];
    const gy = gyArr[i];
    const mag = Math.hypot(gx, gy);
    if (mag < 1e-6) {
      mask[i] = 0;
      removed += 1;
      continue;
    }
    const normalX = orient === 0 ? 1 : 0;
    const normalY = orient === 1 ? 1 : 0;
    const dot = Math.abs((gx * normalX + gy * normalY) / mag);
    if (dot > cosMax || w < 0.2) {
      mask[i] = 0;
      removed += 1;
    } else {
      weakened += 1;
    }
  }

  const leakAfter = leakRatio(mask, lineMask, leakExtras);
  return { enabled: true, removed, weakened, leakBefore, leakAfter };
}

function computeEdgeEnergy(gradient: { gx: Float32Array; gy: Float32Array }): Float32Array {
  const { gx, gy } = gradient;
  const len = gx.length;
  const out = new Float32Array(len);
  for (let i = 0; i < len; i++) {
    const mag = Math.hypot(gx[i], gy[i]);
    out[i] = Math.min(1, mag / 1024);
  }
  return out;
}

type ComponentFillStats = {
  enabled: boolean;
  components: number;
  holesFilled: number;
  pixelsFilled: number;
};

function fillComponentHoles(
  mask: Uint8Array,
  width: number,
  height: number,
  interiorMask: Uint8Array,
  weights: Float32Array | null,
  edgeEnergy: Float32Array,
  bandPx: number,
  cellSize: number,
): ComponentFillStats | null {
  const len = mask.length;
  if (len === 0) return null;
  const visited = new Uint8Array(len);
  const queue = new Int32Array(len);
  const componentIndices: number[] = [];

  let components = 0;
  let holesFilled = 0;
  let pixelsFilled = 0;

  const maxHoleRatio = 0.35;
  const minComponentArea = Math.max(64, Math.round(cellSize * cellSize * 0.05));
  const minHoleArea = 12;

  for (let i = 0; i < len; i++) {
    if (!mask[i] || visited[i]) continue;

    componentIndices.length = 0;
    let head = 0;
    let tail = 0;
    queue[tail++] = i;
    visited[i] = 1;

    let minX = width - 1;
    let maxX = 0;
    let minY = height - 1;
    let maxY = 0;

    while (head < tail) {
      const current = queue[head++];
      componentIndices.push(current);
      const x = current % width;
      const y = (current / width) | 0;
      if (x < minX) minX = x;
      if (x > maxX) maxX = x;
      if (y < minY) minY = y;
      if (y > maxY) maxY = y;

      const tryPush = (idx: number) => {
        if (!mask[idx] || visited[idx]) return;
        visited[idx] = 1;
        queue[tail++] = idx;
      };

      if (x > 0) tryPush(current - 1);
      if (x + 1 < width) tryPush(current + 1);
      if (y > 0) tryPush(current - width);
      if (y + 1 < height) tryPush(current + width);
    }

    const compArea = componentIndices.length;
    if (compArea < minComponentArea) continue;

    components += 1;

    const regionMinX = Math.max(0, minX - 1);
    const regionMinY = Math.max(0, minY - 1);
    const regionMaxX = Math.min(width - 1, maxX + 1);
    const regionMaxY = Math.min(height - 1, maxY + 1);
    const regionW = regionMaxX - regionMinX + 1;
    const regionH = regionMaxY - regionMinY + 1;
    const regionSize = regionW * regionH;

    const regionComp = new Uint8Array(regionSize);
    const regionCandidate = new Uint8Array(regionSize);
    const regionVisited = new Uint8Array(regionSize);
    const regionQueue = new Int32Array(regionSize);

    const toLocal = (globalIdx: number) => {
      const gx = globalIdx % width;
      const gy = (globalIdx / width) | 0;
      return (gy - regionMinY) * regionW + (gx - regionMinX);
    };

    for (const idx of componentIndices) {
      const local = toLocal(idx);
      regionComp[local] = 1;
    }

    for (let ry = 0; ry < regionH; ry++) {
      const gy = regionMinY + ry;
      for (let rx = 0; rx < regionW; rx++) {
        const gx = regionMinX + rx;
        const globalIdx = gy * width + gx;
        if (!interiorMask[globalIdx]) continue;
        if (weights && weights[globalIdx] <= 0.5) continue;
        regionCandidate[ry * regionW + rx] = 1;
      }
    }

    let rHead = 0;
    let rTail = 0;

    const markVisited = (local: number) => {
      regionVisited[local] = 1;
      regionQueue[rTail++] = local;
    };

    const pushNeighbors = (local: number) => {
      const x = local % regionW;
      const y = (local / regionW) | 0;
      const tryPush = (lx: number, ly: number) => {
        if (lx < 0 || ly < 0 || lx >= regionW || ly >= regionH) return;
        const idxLocal = ly * regionW + lx;
        if (regionVisited[idxLocal]) return;
        if (regionComp[idxLocal]) return;
        if (!regionCandidate[idxLocal]) return;
        markVisited(idxLocal);
      };
      tryPush(x - 1, y);
      tryPush(x + 1, y);
      tryPush(x, y - 1);
      tryPush(x, y + 1);
    };

    for (let rx = 0; rx < regionW; rx++) {
      const top = rx;
      const bottom = (regionH - 1) * regionW + rx;
      if (!regionComp[top] && regionCandidate[top] && !regionVisited[top]) markVisited(top);
      if (!regionComp[bottom] && regionCandidate[bottom] && !regionVisited[bottom]) markVisited(bottom);
    }
    for (let ry = 0; ry < regionH; ry++) {
      const left = ry * regionW;
      const right = ry * regionW + (regionW - 1);
      if (!regionComp[left] && regionCandidate[left] && !regionVisited[left]) markVisited(left);
      if (!regionComp[right] && regionCandidate[right] && !regionVisited[right]) markVisited(right);
    }

    while (rHead < rTail) {
      const local = regionQueue[rHead++];
      pushNeighbors(local);
    }

    for (let local = 0; local < regionSize; local++) {
      if (regionComp[local] || !regionCandidate[local] || regionVisited[local]) continue;

      rHead = 0;
      rTail = 0;
      regionQueue[rTail++] = local;
      regionVisited[local] = 1;

      const holeLocals: number[] = [];
      holeLocals.push(local);

      while (rHead < rTail) {
        const current = regionQueue[rHead++];
        const cx = current % regionW;
        const cy = (current / regionW) | 0;
        const tryPush = (lx: number, ly: number) => {
          if (lx < 0 || ly < 0 || lx >= regionW || ly >= regionH) return;
          const idxLocal = ly * regionW + lx;
          if (regionVisited[idxLocal]) return;
          if (regionComp[idxLocal]) return;
          if (!regionCandidate[idxLocal]) return;
          regionVisited[idxLocal] = 1;
          regionQueue[rTail++] = idxLocal;
          holeLocals.push(idxLocal);
        };
        tryPush(cx - 1, cy);
        tryPush(cx + 1, cy);
        tryPush(cx, cy - 1);
        tryPush(cx, cy + 1);
      }

      const holeArea = holeLocals.length;
      if (holeArea < minHoleArea) continue;
      if (holeArea > compArea * maxHoleRatio) continue;

      let energySum = 0;
      for (const localIdx of holeLocals) {
        const gx = regionMinX + (localIdx % regionW);
        const gy = regionMinY + ((localIdx / regionW) | 0);
        const globalIdx = gy * width + gx;
        energySum += edgeEnergy[globalIdx];
      }
      const meanEnergy = energySum / holeArea;
      if (meanEnergy > 0.08) continue;

      for (const localIdx of holeLocals) {
        const gx = regionMinX + (localIdx % regionW);
        const gy = regionMinY + ((localIdx / regionW) | 0);
        const globalIdx = gy * width + gx;
        if (!mask[globalIdx]) {
          mask[globalIdx] = 255;
          pixelsFilled += 1;
        }
      }
      holesFilled += 1;
    }
  }

  if (holesFilled === 0 && pixelsFilled === 0) return null;
  return { enabled: true, components, holesFilled, pixelsFilled };
}

type SpecularStats = { enabled: boolean; pixels: number };

function applySpecularBridges(
  mask: Uint8Array,
  width: number,
  height: number,
  intensity: Uint8Array,
  weights: Float32Array | null,
  interiorMask: Uint8Array,
  gradient: { gx: Float32Array; gy: Float32Array },
  edgeEnergy: Float32Array,
): SpecularStats {
  const len = mask.length;
  if (len === 0) return { enabled: false, pixels: 0 };
  let added = 0;

  for (let idx = 0; idx < len; idx++) {
    if (!interiorMask[idx]) continue;
    if (weights && weights[idx] <= 0.5) continue;
    if (intensity[idx] < 220) continue;
    if (edgeEnergy[idx] > 0.05) continue;

    let tx = -gradient.gy[idx];
    let ty = gradient.gx[idx];
    let norm = Math.hypot(tx, ty);
    if (norm < 1e-6) continue;
    tx /= norm;
    ty /= norm;
    const stepX = clampStep(tx);
    const stepY = clampStep(ty);
    if (stepX === 0 && stepY === 0) continue;

    const x = idx % width;
    const y = (idx / width) | 0;
    const trySet = (sx: number, sy: number) => {
      if (sx < 0 || sy < 0 || sx >= width || sy >= height) return;
      const id = sy * width + sx;
      if (!interiorMask[id]) return;
      if (weights && weights[id] <= 0.5) return;
      if (!mask[id]) {
        mask[id] = 255;
        added += 1;
      }
    };

    trySet(x + stepX, y + stepY);
    trySet(x - stepX, y - stepY);
  }

  return { enabled: added > 0, pixels: added };
}

function buildTangentSeedMask(
  width: number,
  height: number,
  interiorMask: Uint8Array,
  weights: Float32Array | null,
  gradient: { gx: Float32Array; gy: Float32Array },
  opts: { coherence?: number; weightThreshold?: number; magnitudeThreshold?: number },
): Uint8Array | null {
  const coherenceThreshold = opts.coherence ?? 0.6;
  const weightThreshold = opts.weightThreshold ?? 0.45;
  const magnitudeThreshold = opts.magnitudeThreshold ?? 22;
  const { gx, gy } = gradient;
  const len = width * height;
  const seeds = new Uint8Array(len);
  let seeded = 0;
  const radius = 1;

  const idx = (x: number, y: number) => y * width + x;

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const center = idx(x, y);
      if (!interiorMask[center]) continue;
      if (weights && weights[center] < weightThreshold) continue;

      let sxx = 0;
      let sxy = 0;
      let syy = 0;
      let samples = 0;

      for (let dy = -radius; dy <= radius; dy++) {
        const yy = y + dy;
        if (yy < 0 || yy >= height) continue;
        for (let dx = -radius; dx <= radius; dx++) {
          const xx = x + dx;
          if (xx < 0 || xx >= width) continue;
          const p = idx(xx, yy);
          const gxp = gx[p];
          const gyp = gy[p];
          sxx += gxp * gxp;
          sxy += gxp * gyp;
          syy += gyp * gyp;
          samples += 1;
        }
      }

      if (samples === 0) continue;
      sxx /= samples;
      sxy /= samples;
      syy /= samples;

      const trace = sxx + syy;
      const diff = Math.sqrt(Math.max(0, (sxx - syy) * (sxx - syy) + 4 * sxy * sxy));
      const lambda1 = 0.5 * (trace + diff);
      const lambda2 = 0.5 * (trace - diff);
      const coherence = (lambda1 - lambda2) / (lambda1 + lambda2 + 1e-5);
      if (!Number.isFinite(coherence) || coherence < coherenceThreshold) continue;

      const gxBase = gx[center];
      const gyBase = gy[center];
      const magnitude = Math.hypot(gxBase, gyBase);
      if (magnitude < magnitudeThreshold) continue;

      let orientation = 0.5 * Math.atan2(2 * sxy, sxx - syy);
      if (!Number.isFinite(orientation)) orientation = 0;
      let tangentAngle = orientation + Math.PI / 2;

      let tx = Math.cos(tangentAngle);
      let ty = Math.sin(tangentAngle);
      let norm = Math.hypot(tx, ty);
      if (norm < 1e-6) {
        const gradAngle = Math.atan2(gyBase, gxBase);
        tx = -Math.sin(gradAngle);
        ty = Math.cos(gradAngle);
        norm = Math.hypot(tx, ty);
      }
      if (norm < 1e-6) continue;
      tx /= norm;
      ty /= norm;

      const stepX = clampStep(tx);
      const stepY = clampStep(ty);
      const negStepX = -stepX;
      const negStepY = -stepY;

      const setSeed = (sx: number, sy: number) => {
        if (sx < 0 || sy < 0 || sx >= width || sy >= height) return;
        const id = idx(sx, sy);
        if (!interiorMask[id]) return;
        if (weights && weights[id] < weightThreshold) return;
        if (!seeds[id]) {
          seeds[id] = 255;
          seeded += 1;
        }
      };

      setSeed(x, y);
      if (stepX !== 0 || stepY !== 0) {
        setSeed(x + stepX, y + stepY);
        setSeed(x + negStepX, y + negStepY);
      }
    }
  }

  return seeded > 0 ? seeds : null;
}

function clampStep(value: number): number {
  if (value > 0.66) return 1;
  if (value < -0.66) return -1;
  return 0;
}

Youez - 2016 - github.com/yon3zu
LinuXploit