Skip to content

Duplicate Code: Shared Setup Logic in coordinates.ts Utility Functions #128

@github-actions

Description

@github-actions

Analysis of commit 846ffc9

Assignee: @copilot

Summary

Two duplication patterns were found in fidnii/src/utils/coordinates.ts:

  1. normalizedToWorld / worldToNormalized share ~18 identical lines of axis-index setup and error-handling code.
  2. clampPixelCoord / roundPixelCoord / floorPixelCoord / ceilPixelCoord all apply a single Math.* function element-wise to a [number, number, number] triple — 4 structurally identical functions totalling ~37 lines.

Duplication Details

Pattern 1 — Shared Axis-Index Setup in normalizedToWorld / worldToNormalized

  • Severity: Medium
  • Occurrences: 2
  • Locations:
    • fidnii/src/utils/coordinates.ts lines 131–163 (normalizedToWorld)
    • fidnii/src/utils/coordinates.ts lines 173–207 (worldToNormalized)
  • Duplicated block (~18 lines, identical in both functions):
const shape = ngffImage.data.shape
const dims = ngffImage.dims

const yIdx = dims.indexOf("y")
const xIdx = dims.indexOf("x")

if (yIdx === -1 || xIdx === -1) {
  const missingAxes = [xIdx === -1 ? "x" : null, yIdx === -1 ? "y" : null]
    .filter((axis): axis is string => axis !== null)
    .join(", ")
  throw new Error(
    `NgffImage is missing required spatial dimension(s): \$\{missingAxes}`,
  )
}

const zIdx = dims.indexOf("z")
const dimZ = zIdx !== -1 ? shape[zIdx] : 1
const dimY = shape[yIdx]
const dimX = shape[xIdx]

Refactoring suggestion: Extract to a private helper, e.g.:

function getSpatialDims(ngffImage: NgffImage): {
  dimX: number; dimY: number; dimZ: number
} { ... }

Both normalizedToWorld and worldToNormalized call this helper and use the returned values.


Pattern 2 — Four Identical Element-Wise Pixel-Coordinate Mappers

  • Severity: Low-Medium
  • Occurrences: 4
  • Locations:
    • fidnii/src/utils/coordinates.ts lines 216–225 (clampPixelCoord)
    • fidnii/src/utils/coordinates.ts lines 233–241 (roundPixelCoord)
    • fidnii/src/utils/coordinates.ts lines 249–257 (floorPixelCoord)
    • fidnii/src/utils/coordinates.ts lines 265–273 (ceilPixelCoord)
  • Structural template (same for every function except the Math.* call):
export function (name)PixelCoord(
  pixelCoord: [number, number, number],
  // (clampPixelCoord also takes `shape` as a second param)
): [number, number, number] {
  return [
    Math.(op)(pixelCoord[0], ...),
    Math.(op)(pixelCoord[1], ...),
    Math.(op)(pixelCoord[2], ...),
  ]
}

Refactoring suggestion: Extract a generic helper:

function mapPixelCoord(
  pixelCoord: [number, number, number],
  fn: (v: number, i: number) => number,
): [number, number, number] {
  return [fn(pixelCoord[0], 0), fn(pixelCoord[1], 1), fn(pixelCoord[2], 2)]
}

Then each public function becomes a one-liner wrapper.


Impact Analysis

  • Maintainability: Both patterns require parallel edits whenever the logic changes — for Pattern 1, any update to the axis-validation error message or default-z handling must be copied to both functions.
  • Bug Risk: Inconsistent fixes are likely; a bug fix applied to one function may be missed in the other.
  • Code Bloat: ~55 lines can be reduced to ~20 after refactoring.

Implementation Checklist

  • Review duplication findings
  • Extract getSpatialDims helper for normalizedToWorld / worldToNormalized
  • Extract mapPixelCoord helper (or inline Array.from map) for the four element-wise functions
  • Verify no behaviour change (existing Playwright tests cover these paths)
  • Run bun run check to confirm linting passes

Analysis Metadata

  • Analyzed Files: fidnii/src/utils/coordinates.ts (primary), fidnii/src/OMEZarrNVImage.ts, fidnii/src/ClipPlanes.ts, fidnii/src/utils/orientation.ts
  • Detection Method: Serena semantic code analysis + pattern search
  • Commit: 846ffc9ba09295d0427772a86fddb822b9d7d307
  • Analysis Date: 2026-04-16

Generated by Duplicate Code Detector

To install this agentic workflow, run

gh aw add github/gh-aw/.github/workflows/duplicate-code-detector.md@33cd6c7f1fee588654ef19def2e6a4174be66197

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions