Skip to content

Instantly share code, notes, and snippets.

@chausen
Created April 3, 2025 04:15
Show Gist options
  • Save chausen/b154b1563c63285ad78ad4e64d1807f4 to your computer and use it in GitHub Desktop.
Save chausen/b154b1563c63285ad78ad4e64d1807f4 to your computer and use it in GitHub Desktop.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;
public class CrosshatchExample {
// A simple data structure to hold a region’s pixel coordinates plus a “density” value
public static class Region {
private List<Point> pixelCoordinates;
private float density; // e.g. 0.1 = sparse, 0.9 = dense
public Region(List<Point> pixelCoordinates, float density) {
this.pixelCoordinates = pixelCoordinates;
this.density = density;
}
public List<Point> getPixelCoordinates() {
return pixelCoordinates;
}
public float getDensity() {
return density;
}
}
public static void main(String[] args) {
int width = 400;
int height = 400;
// Create a BufferedImage where we will draw everything
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// Get a Graphics2D context so we can render shapes, lines, etc.
Graphics2D g = image.createGraphics();
try {
// White background
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// Example: Suppose we have a few Region objects
// (In a real scenario, these would come from your API or somewhere else)
Region regionA = new Region(
List.of( // some pixel coordinates for region A
new Point(50, 50),
new Point(50, 51),
new Point(51, 50),
new Point(51, 51),
// ... etc ...
new Point(100, 100)
),
0.3f // A fairly sparse crosshatch
);
Region regionB = new Region(
List.of( // some pixel coordinates for region B
new Point(150, 150),
new Point(150, 151),
new Point(151, 150),
// ... etc ...
new Point(200, 200)
),
0.8f // A dense crosshatch
);
// We’ll gather these in a list
List<Region> regions = List.of(regionA, regionB);
// Define a single color for all crosshatches
Color hatchColor = Color.BLACK;
// For each region, draw crosshatches
for (Region region : regions) {
drawCrossHatch(g, region, hatchColor);
}
// (You would now write the image to disk, or return it in a response, etc.)
// ImageIO.write(image, "PNG", new File("output.png"));
} catch (Exception e) {
e.printStackTrace();
} finally {
// Dispose of the Graphics2D context
g.dispose();
}
}
/**
* Draws crosshatches in the given region using the specified color.
* The region’s density determines how close the lines are.
*/
private static void drawCrossHatch(Graphics2D g, Region region, Color hatchColor) {
float density = region.getDensity();
// Map density [0.0, 1.0] to a spacing range (smaller spacing => higher density)
// For example, let’s say spacing can go from 3 pixels (dense) up to 20 pixels (sparse).
// You can tweak the numbers based on your needs.
int minSpacing = 3;
int maxSpacing = 20;
int spacing = (int) ((1 - density) * (maxSpacing - minSpacing) + minSpacing);
// Optional: get the bounding rectangle of the region so we can iterate systematically
Rectangle bounds = getBounds(region.getPixelCoordinates());
// Set the color for the crosshatches
g.setColor(hatchColor);
// First diagonal (/)
for (int y = bounds.y; y <= bounds.y + bounds.height; y += spacing) {
// We'll draw a line from left to right across the bounding region
// We'll only draw where it intersects the region's pixels.
drawDiagonalLine(g, region, bounds.x, y, +1, spacing);
}
// Second diagonal (\)
for (int y = bounds.y; y <= bounds.y + bounds.height; y += spacing) {
// We'll draw in the opposite direction
drawDiagonalLine(g, region, bounds.x + bounds.width, y, -1, spacing);
}
}
/**
* Helper method: draws a diagonal line across the region if the region’s pixels are present.
* direction = +1 for one diagonal (/), -1 for the other diagonal (\).
*/
private static void drawDiagonalLine(Graphics2D g,
Region region,
int startX,
int startY,
int direction,
int spacing) {
// We can step in increments until we’re out of the bounding box.
// direction +1 means x++ each step, direction -1 means x-- each step.
// We'll move along the diagonal until we exit the region’s bounding area.
// For simplicity, let's define a maximum length for a single diagonal
int maxLength = 1000; // or compute from bounding rectangle if you want
// We'll collect line segments that are valid (within region’s pixel set).
// This is just a demonstration approach.
int x = startX;
int y = startY;
// For quick membership checking, store region pixels in a HashSet
// (You could do it outside and pass in to avoid repeated creation).
// This is optional but helps if you want to only draw lines on real region pixels.
java.util.Set<Point> pixelSet = new java.util.HashSet<>(region.getPixelCoordinates());
Point prev = null;
for (int i = 0; i < maxLength; i++) {
if (!pixelSet.contains(new Point(x, y))) {
// If we’re out of region pixels, break or skip
// In a real scenario, you might want to “continue” until we jump back in region
// This is just a simplistic approach that stops if we leave the region.
if (prev != null) {
// We can finalize the line segment if we had started one
// g.drawLine(prev.x, prev.y, x, y);
}
break;
}
if (prev == null) {
// Start a new line segment
prev = new Point(x, y);
} else {
// Extend the line segment
g.drawLine(prev.x, prev.y, x, y);
prev = new Point(x, y);
}
// Move to the next diagonal pixel
x += direction;
y -= 1; // for slope -1 or +1 diagonal
}
}
/**
* Returns the bounding rectangle that encloses all pixel coordinates in the region.
*/
private static Rectangle getBounds(List<Point> points) {
if (points.isEmpty()) return new Rectangle(0,0,0,0);
int minX = points.stream().mapToInt(p -> p.x).min().orElse(0);
int maxX = points.stream().mapToInt(p -> p.x).max().orElse(0);
int minY = points.stream().mapToInt(p -> p.y).min().orElse(0);
int maxY = points.stream().mapToInt(p -> p.y).max().orElse(0);
return new Rectangle(minX, minY, (maxX - minX), (maxY - minY));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment