Skip to content

Coaster asset crop borders#391

Open
amilich wants to merge 4 commits intomainfrom
cursor/coaster-asset-crop-borders-34b6
Open

Coaster asset crop borders#391
amilich wants to merge 4 commits intomainfrom
cursor/coaster-asset-crop-borders-34b6

Conversation

@amilich
Copy link
Owner

@amilich amilich commented Feb 2, 2026

Fixes visible grid line artifacts in coaster sprite sheets by correcting off-color boundary pixels.

Several coaster sprite sheets contained anti-aliased or artifact pixels at the boundaries between individual sprites. These pixels were not the pure red background color, leading to visible "skinny line borders" or grid lines when the sprites were rendered. This PR introduces a script to identify and replace these off-color boundary pixels with the correct background red.


Open in Cursor Open in Web


Note

Low Risk
Low risk: adds a standalone asset-processing script that only modifies local image files when run, without affecting runtime code paths.

Overview
Adds scripts/fix-coaster-borders.mjs, a CLI script that repairs coaster sprite-sheet “grid line” artifacts by sampling the red background color, flood-filling background-like edge regions to normalize color, then overwriting sprite boundary strips for configured sheet grids.

The script backs up original PNG/WebP files, rewrites the fixed PNGs in place, and regenerates matching WebP outputs with consistent compression settings.

Written by Cursor Bugbot for commit 58e9644. This will update automatically on new commits. Configure here.

Cursor Bugbot found 2 potential issues for commit 58e9644

- Remove anti-aliased boundary artifacts from 5 sprite sheets
- Affected assets: infrastructure, path_furniture, theme_classic, theme_modern, trees
- Fixed ~355,000 boundary pixels with incorrect colors
- Add fix-coaster-borders.mjs script for future use
- Regenerated corresponding WebP files

Co-authored-by: andrew <andrew@anysphere.co>
@cursor
Copy link

cursor bot commented Feb 2, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@vercel
Copy link
Contributor

vercel bot commented Feb 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
isometric-city Ready Ready Preview, Comment Feb 2, 2026 7:04am

Request Review

@amilich amilich marked this pull request as ready for review February 2, 2026 04:57
@assert-app
Copy link

assert-app bot commented Feb 2, 2026

Your pull request is now ready for review with Assert.

Open Review →


Stop waiting for your code to break. Ship with confidence using Assert.

Co-authored-by: andrew <andrew@anysphere.co>
// Read image
const image = sharp(pngPath);
const metadata = await image.metadata();
const { width, height, channels } = metadata;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent channel source causes pixel stride mismatch

Low Severity · Potential Edge Case

The channels value used for pixel operations comes from metadata (line 92), but the raw buffer's actual layout is determined by info.channels from toBuffer() (line 158). All getPixel and setPixel calls use metadata.channels for stride calculation, but the buffer data follows info.channels. If these values differ, pixel index calculations become incorrect, causing the script to read/write wrong positions and corrupt the image data.

Additional Locations (1)

Fix in Cursor Fix in Web

// Create backup directory if needed
if (!existsSync(BACKUP_DIR)) {
const { mkdir } = await import('fs/promises');
await mkdir(BACKUP_DIR, { recursive: true });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary dynamic import for mkdir

Low Severity · Code Quality

mkdir is dynamically imported from fs/promises inside createBackups(), but fs/promises is already statically imported at line 14. Add mkdir to the existing static import instead: import { copyFile, mkdir } from 'fs/promises';

Fix in Cursor Fix in Web

Co-authored-by: andrew <andrew@anysphere.co>
Co-authored-by: andrew <andrew@anysphere.co>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

linePixelsCleared++;
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boundary clearing destroys all pixels unconditionally

Medium Severity · Logic Bug

The boundary clearing code at lines 178-200 unconditionally replaces all pixels within the boundary strips with background color, without checking if they are background-like first. This differs from the flood fill approach which correctly uses isBackgroundLike() to only modify background-like pixels. Any sprite content that extends to or within BOUNDARY_CLEAR_RADIUS pixels of grid boundaries will be destroyed and replaced with background red, potentially clipping sprites that have content near their cell edges.

Fix in Cursor Fix in Web


// Determine background similarity threshold from edge pixels
const edgeDiffs = [];
const edgeIsRedDominant = (r, g, b) => r > 120 && r >= g + 20 && r >= b + 20;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated red-dominant pixel check logic

Low Severity · Code Quality

The edgeIsRedDominant function at line 115 duplicates the exact same logic as redDominant on line 55 inside isBackgroundLike: r > 120 && r >= g + 20 && r >= b + 20. Extract this into a standalone isRedDominant(r, g, b) helper function and reuse it in both places.

Fix in Cursor Fix in Web

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants