Skip to content

No way to get a tile's unpadded (core) bounds from a padded plan #2

Description

@jiajic

@pad is applied unconditionally wherever tile bounds are computed, so a caller can get the padded bounds or nothing. There is no way to ask a padded plan for the tile's own (unpadded) bounds short of mutating @pad and putting it back, or recomputing the grid arithmetic by hand.

Sites that bake it in:

# R/freeTilePlan.R:145
p <- x@pad
bounds <- cbind(x@bounds[, 1L] - p, x@bounds[, 2L] + p,
                x@bounds[, 3L] - p, x@bounds[, 4L] + p)

# R/spatialTilePlan.R:232
p <- x@pad; e <- x@extent
bounds <- cbind(e[[1L]] + (j_idx - 1L) * w - p, e[[1L]] + j_idx * w + p,
                e[[3L]] + (i_idx - 1L) * h - p, e[[3L]] + i_idx * h + p)

# R/intersect.R:91, 97, 120, 133 — same, for selection

getTile() takes a pad argument, but it is additive (tiles <- tiles + pad), so it cannot subtract the plan's own padding either.

Why it matters

Any algorithm where the halo exists for reading but ownership belongs to the tile proper needs both bounds from one plan:

  • padded bounds → the data to load, so results near the edge are correct
  • core bounds → which items this tile is responsible for, so neighbouring tiles do not both claim the same item

Without the second, overlapping tiles double-count. The immediate case is chunked motif enumeration (GiottoDisk#72): a tile needs a k-1 hop halo or subgraphs spanning the boundary are lost, but a subgraph must be counted by exactly one tile — the one holding its root. Same shape as any haloed convolution, watershed, or neighbour-count pass.

Today the caller has to reach past the API:

core <- tp; core@pad <- 0           # mutate a copy and hope nothing else reads it
sel_core <- intersect(core, query)
sel_halo <- intersect(tp,   query)

Suggested shape

Something injectable rather than a second slot — a .CORE sentinel or a core = TRUE switch threaded through as.polygons(), intersect() and getTile(), so one plan answers both questions without being copied or mutated:

as.polygons(tp)                # padded, unchanged default
as.polygons(tp, core = TRUE)   # the tile itself

intersect(tp, query)                # tiles whose padded bounds hit the query
intersect(tp, query, core = TRUE)   # tiles whose own bounds hit it

The information is already there in every one of those functions — p is in scope at each site and the unpadded form is the same expression with p = 0. This is an accessor gap, not a computation gap.

Applies to spatialTilePlan, freeTilePlan and pixelTilePlan alike, since all three carry pad on the shared tilePlan base.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions