Created
April 9, 2025 05:47
-
-
Save chausen/7f914189009818662f8237a24ff76fd3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.*; | |
import java.awt.image.BufferedImage; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Set; | |
public class LayeredCrosshatchDemo { | |
// Suppose we have enumerated density levels | |
public enum DensityLevel { | |
LOW, | |
MEDIUM, | |
HIGH | |
} | |
// A region’s data structure: set of pixel coordinates + which density it should be | |
public static class Region { | |
private Set<Point> pixelCoordinates; | |
private DensityLevel densityLevel; | |
public Region(Set<Point> pixels, DensityLevel densityLevel) { | |
this.pixelCoordinates = pixels; | |
this.densityLevel = densityLevel; | |
} | |
public Set<Point> getPixelCoordinates() { | |
return pixelCoordinates; | |
} | |
public DensityLevel getDensityLevel() { | |
return densityLevel; | |
} | |
} | |
public static void main(String[] args) { | |
int width = 600; | |
int height = 400; | |
// 1) Generate 3 separate "layer" images, one for each density | |
// All have ARGB so we can do transparency (alpha) | |
BufferedImage layerLow = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |
BufferedImage layerMedium = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |
BufferedImage layerHigh = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |
// 2) For each layer, fill with crosshatching at the appropriate spacing | |
// but fill the entire image with that pattern. We'll mask out unwanted areas later. | |
drawCrosshatchesFull(layerLow, 15); // bigger spacing => sparser | |
drawCrosshatchesFull(layerMedium, 8); | |
drawCrosshatchesFull(layerHigh, 4); // smaller spacing => denser | |
// 3) Construct some example Region data | |
Region regionA = makeRegion( 50,50, 100,60, DensityLevel.LOW); // a rectangle for demonstration | |
Region regionB = makeRegion(150,80, 80,50, DensityLevel.MEDIUM); | |
Region regionC = makeRegion(300,120, 50, 60, DensityLevel.HIGH); | |
List<Region> allRegions = List.of(regionA, regionB, regionC); | |
// 4) For each region, we "unmask" the corresponding layer in its pixelCoordinates | |
// and set the rest to transparent. Or equivalently, we can set all pixels | |
// outside the region to 0 alpha in that layer. | |
// We'll do it by clearing the alpha outside of the region. | |
maskLayersByRegion(allRegions, layerLow, layerMedium, layerHigh); | |
// 5) Finally, create the final image with white background (or anything). | |
BufferedImage finalImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | |
Graphics2D gFinal = finalImage.createGraphics(); | |
try { | |
// White background | |
gFinal.setColor(Color.WHITE); | |
gFinal.fillRect(0, 0, width, height); | |
// Draw each layer on top | |
gFinal.drawImage(layerLow, 0, 0, null); | |
gFinal.drawImage(layerMedium, 0, 0, null); | |
gFinal.drawImage(layerHigh, 0, 0, null); | |
// At this point, finalImage is complete. | |
// You can write it out or display it, e.g.: | |
// ImageIO.write(finalImage, "PNG", new File("crosshatchedResult.png")); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} finally { | |
gFinal.dispose(); | |
} | |
} | |
/** | |
* Draws crosshatches across the entire buffered image at the given spacing. | |
* We'll assume black crosshatches on a transparent background. | |
* (i.e., everything else is 0 alpha). | |
*/ | |
private static void drawCrosshatchesFull(BufferedImage layer, int spacing) { | |
int width = layer.getWidth(); | |
int height = layer.getHeight(); | |
Graphics2D g = layer.createGraphics(); | |
try { | |
// Start with a fully transparent background | |
g.setComposite(AlphaComposite.Clear); | |
g.fillRect(0, 0, width, height); | |
// Now switch to "SrcOver" so we can draw the lines in black | |
g.setComposite(AlphaComposite.SrcOver); | |
// Optionally set stroke to 1 pixel or thinner | |
g.setStroke(new BasicStroke(1.0f)); | |
g.setColor(Color.BLACK); | |
// For "/" diagonals | |
for (int y = 0; y <= height; y += spacing) { | |
drawOneDiagonalLineFull(g, 0, y, width, height, +1); | |
} | |
for (int x = 0; x <= width; x += spacing) { | |
drawOneDiagonalLineFull(g, x, height, width, height, +1); | |
} | |
// For "\" diagonals | |
for (int y = 0; y <= height; y += spacing) { | |
drawOneDiagonalLineFull(g, 0, y, width, height, -1); | |
} | |
for (int x = 0; x <= width; x += spacing) { | |
drawOneDiagonalLineFull(g, x, 0, width, height, -1); | |
} | |
} finally { | |
g.dispose(); | |
} | |
} | |
/** | |
* Draws one diagonal line from (startX, startY) in either "/" or "\" direction | |
* all the way across the image. | |
*/ | |
private static void drawOneDiagonalLineFull(Graphics2D g, | |
int startX, int startY, | |
int width, int height, | |
int direction) { | |
// direction = +1 => "/" | |
// direction = -1 => "\" | |
// We'll step in increments of 1 pixel until we're out of the image bounds | |
int x = startX; | |
int y = startY; | |
int stepCount = width + height; // worst case diagonal length | |
for (int i = 0; i < stepCount; i++) { | |
// If out of bounds, break | |
if (x < 0 || x >= width || y < 0 || y >= height) { | |
break; | |
} | |
// Draw a tiny line from (x, y) to (x, y). | |
// Actually, we can just do g.drawLine(x, y, x, y). | |
g.drawLine(x, y, x, y); | |
// Move diagonally | |
x += 1 * direction; // +/-1 in the x-direction | |
y -= 1; // always up 1 in y for "/" | |
// or if direction=-1 => that changes the diagonal slope | |
// (this is the same logic we used before, just in "full layer" context) | |
} | |
} | |
/** | |
* For each region in the list, keep the crosshatching in the appropriate layer | |
* within region pixels, and set everything else to transparent in that layer. | |
*/ | |
private static void maskLayersByRegion(List<Region> regions, | |
BufferedImage layerLow, | |
BufferedImage layerMedium, | |
BufferedImage layerHigh) { | |
// We'll do a quick pass creating a large sets of region pixels | |
// for each density. | |
Set<Point> lowSet = new HashSet<>(); | |
Set<Point> mediumSet = new HashSet<>(); | |
Set<Point> highSet = new HashSet<>(); | |
for (Region region : regions) { | |
switch (region.getDensityLevel()) { | |
case LOW -> lowSet.addAll(region.getPixelCoordinates()); | |
case MEDIUM -> mediumSet.addAll(region.getPixelCoordinates()); | |
case HIGH -> highSet.addAll(region.getPixelCoordinates()); | |
} | |
} | |
// Now we’ll mask each layer by iterating over every pixel. | |
maskLayer(layerLow, lowSet); | |
maskLayer(layerMedium, mediumSet); | |
maskLayer(layerHigh, highSet); | |
} | |
/** | |
* Sets all pixels *not in the given set* to fully transparent (ARGB=0) | |
* so that only region pixels remain. | |
*/ | |
private static void maskLayer(BufferedImage layer, Set<Point> regionPoints) { | |
int width = layer.getWidth(); | |
int height = layer.getHeight(); | |
// For quick membership checks: | |
// regionPoints is a set of (x,y). | |
// If you have extremely large images, you might prefer a scanline approach. | |
// But let's keep it straightforward for demonstration. | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
if (!regionPoints.contains(new Point(x, y))) { | |
// Set alpha to 0 | |
layer.setRGB(x, y, 0x00000000); | |
} | |
} | |
} | |
} | |
/** | |
* Helper method to create a simple rectangular region. | |
* This is just for the demo; in your code, you'd have real shapes. | |
*/ | |
private static Region makeRegion(int startX, int startY, int w, int h, DensityLevel density) { | |
Set<Point> coords = new HashSet<>(); | |
for (int y = startY; y < startY + h; y++) { | |
for (int x = startX; x < startX + w; x++) { | |
coords.add(new Point(x, y)); | |
} | |
} | |
return new Region(coords, density); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I NEED A PROGRAMMER