Update to TwoColorImage Java Class

Free Update that uses a ConcurrentHashMap to store 2D draw calls, Free Starter Class MIT License

package graphics;

import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import data.BooleanMatrix;

public class TwoColorImage {
	BooleanMatrix pixels;
	
	public int getWidth() {
		return pixels.getWidth();
	}
	
	public int getHeight() {
		return pixels.getHeight();
	}

	public TwoColorImage(int width, int height) {
		pixels = new BooleanMatrix(width, height, false);
	}
	
	public TwoColorImage(BooleanMatrix pixels) {
		this.pixels = pixels;
	}
	
	public BooleanMatrix getPixels() {
		return pixels;
	}
	
	public void turnPixelOn(int x, int y) {
		if (x<pixels.getWidth() && y<pixels.getHeight()) {
			if (null!=inProgressSet) {
				inProgressSet.add(new Point(x,y));
			}
			pixels.setElement(x, y, true);
		}
	}
	
	public void turnPixelOff(int x, int y) {
		if (x<pixels.getWidth() && y<pixels.getHeight()) {
			pixels.setElement(x, y, false);
		}
	}
	
	private class Point {
		public Point(int x, int y) {
			this.x=x;
			this.y=y;
		}
		
		int x;
		int y;
	}
	
	private static ConcurrentHashMap<String, Vector<Point>> processedCache = new ConcurrentHashMap<>();
	private Vector<Point> inProgressSet;
	
	private void startRecording() {
		inProgressSet = new Vector<>();
	}
	
	private void stopRecording(String cacheName) {
		processedCache.put(cacheName, inProgressSet);
		inProgressSet = null;
	}
	
	private void drawFromCache(Vector<Point> cachedSet) {
		inProgressSet=null;
		cachedSet.stream().forEach((point)->{
			turnPixelOn(point.x, point.y);
		});
	}
	
	public void drawLine(int x1, int y1, int x2, int y2) {
		String cacheName="drawLine("+x1+","+y1+","+x2+","+y2+")"+"["+getWidth()+","+getHeight()+"]";
		Vector<Point> cachedVersion = processedCache.get(cacheName);
		if (null!=cachedVersion) {
			drawFromCache(cachedVersion);
			return;
		}
		
		startRecording();
		int yDifference = y2-y1;
		int xDifference = x2-x1;
		
		if (xDifference<0) {
			// Switch points, draw left to right
			int x1Temp=x1;
			int x2Temp=x2;
			int y1Temp=y1;
			int y2Temp=y2;
			
			x1=x2Temp;
			y1=y2Temp;
			x2=x1Temp;
			y2=y1Temp;
			
			xDifference = x2-x1;
		}
		
		if (x2>getWidth()-1) {
			x2=getWidth()-1;
		}
		
		if (y2>getHeight()-1) {
			y2=getHeight()-1;
		}
		
		double riseOverRun = (double) yDifference/xDifference;
		double b = y1-(riseOverRun*x1);
		
		turnPixelOn(x1, y1); // Draw start point
		
		if (y1==y2) {
			for (int x=x1+1; x<x2-1; x++) {
				turnPixelOn(x, y1);
			}
		} else if (x1==x2) {
			for (int y=y1+1; y<y2-1; y++) {
				turnPixelOn(x1, y);
			}
	    } else if (y1<y2){		
			double computedY;
			double integerY;
			double usedXs=x1;
			double usedYs=y1;
			for (int x=x1; x<x2; x++) {
				computedY = (riseOverRun*x)+b;
				integerY = (int) computedY;
				
				if (x>usedXs) {
					turnPixelOn((int)x, (int)integerY);
					usedXs=x;
				}					
			}
		} else if (y2<y1) {
			double computedY;
			double integerY;
			double usedXs=x1;
			double usedYs=y1;
			for (double x=x1; x<x2; x+=1) {
				computedY = (riseOverRun*x)+b;
				computedY = Math.abs(computedY);
				integerY = (int) computedY;
				integerY = Math.abs(integerY);
				
				if (x>usedXs) {
					turnPixelOn((int) x, (int) integerY);
					usedXs=x;
				}					
			}			
		}
		
		turnPixelOn(x2, y2); // Draw end point
		stopRecording(cacheName);	
	}
	
	public void drawCircle(double x, double y, double radius) {
		
		String cacheName = "drawCircle("+String.format("%.1f", x)+","
				                        +String.format("%.1f", y)+","
				                        +String.format("%.1f", radius)+")"
				                        +"["+getWidth()+","+getHeight()+"]";

		Vector<Point> cachedVersion = processedCache.get(cacheName);
		if (null!=cachedVersion) {
			drawFromCache(cachedVersion);
			return;
		}		
		
		startRecording();
		double xPosition;
		double yPosition;
		double modLevel;
		
		if (radius<200) {
			modLevel=0.01;
		} else if (radius<600) {
			modLevel=0.005;
		} else {
			modLevel=0.001;
		}
		
		for (double radians=0; radians<6.28; radians+=modLevel) {
			xPosition=y+(radius*Math.cos(radians));
			yPosition=y+(radius*Math.sin(radians));
			turnPixelOn((int) xPosition, (int) yPosition);
		}
		stopRecording(cacheName);
	}
	
	public void drawRectangeCoordinates(int x1, int y1, int x2, int y2) {		
		String cacheName="drawLine("+x1+","+y1+","+x2+","+y2+")"+"["+getWidth()+","+getHeight()+"]";
		Vector<Point> cachedVersion = processedCache.get(cacheName);
		if (null!=cachedVersion) {
			drawFromCache(cachedVersion);
			return;
		}
		
		startRecording();
		for (int x=x1; x<=x2; x++) {
			for (int y=y1; y<=y2; y++) {
				if (x==x1 || x==x2 || y==y1 || y==y2) {
					turnPixelOn(x, y);
				}
			}
		}
		stopRecording(cacheName);
	}
	
	public void drawRectangeFilledCoordinates(int x1, int y1, int x2, int y2) {
		for (int x=x1; x<=x2; x++) {
			for (int y=y1; y<=y2; y++) {
				turnPixelOn(x, y);
			}
		}
	}
}

HashMap Cache has potential to grow thus considering how I can optimize JVM Memory. I don’t have access to Profiling, people with Professional version of IntelliJ could look at this, currently showing 2GB on my Task Manager.

Published by techinfodebug

Flex and Java Developer, Christian, Art, Music, Video, and Vlogging

Leave a comment