Browse Source

Minimal Viable Product

Confirmed working:
Greyscale-based pseudo-random point placement
Voronoi generation with Fortune's Algorithm
Voronoi cell referencing via brute-force

Issues:
-terrible performance in Voronoi cell referencing
-single thread
master
Peery 1 year ago
parent
commit
cfc56ccf03
12 changed files with 1971 additions and 0 deletions
  1. +42
    -0
      src/peery/Vorosaic.java
  2. +304
    -0
      src/peery/file/FileHandler.java
  3. +175
    -0
      src/peery/log/Log.java
  4. +9
    -0
      src/peery/log/LogLevel.java
  5. +93
    -0
      src/peery/pointgenerator/PointMap.java
  6. +114
    -0
      src/peery/voronoi/VoronoiRender.java
  7. +47
    -0
      src/peery/voronoi/simplevoronoi/Edge.java
  8. +37
    -0
      src/peery/voronoi/simplevoronoi/GraphEdge.java
  9. +45
    -0
      src/peery/voronoi/simplevoronoi/Halfedge.java
  10. +44
    -0
      src/peery/voronoi/simplevoronoi/Point.java
  11. +41
    -0
      src/peery/voronoi/simplevoronoi/Site.java
  12. +1020
    -0
      src/peery/voronoi/simplevoronoi/Voronoi.java

+ 42
- 0
src/peery/Vorosaic.java View File

@@ -0,0 +1,42 @@
package peery;

import peery.pointgenerator.PointMap;
import peery.voronoi.VoronoiRender;
import peery.voronoi.simplevoronoi.GraphEdge;
import peery.voronoi.simplevoronoi.Voronoi;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.List;

import peery.file.FileHandler;
import peery.log.Log;
import peery.log.LogLevel;

public class Vorosaic {
public static void main(String[] args) {
FileHandler fh = new FileHandler("resources");
BufferedImage img = fh.loadImage(fh.TargetImageFile);
Log.log(LogLevel.Debug, "Image size is "+img.getWidth()+"x"+img.getHeight()+"! Pixels:"+img.getWidth()*img.getHeight());
Voronoi vor = new Voronoi(1);
PointMap p = new PointMap(img);
p.placePoints(89753, 0.1);
List<GraphEdge> edges = vor.generateVoronoi(p.getXValues(), p.getYValues(), 0, p.getWidth(), 0, p.getHeight());
fh.saveImage(p.render(), new File(fh.OutputFolder+fh.fs+"Output-points.png"));
Log.log(LogLevel.Debug, edges.size()+" points have been placed!");
fh.saveImage(VoronoiRender.renderGraph(edges, p.getWidth(), p.getHeight()), new File(fh.OutputFolder+fh.fs+"Output-voronoi.png"));
Log.log(LogLevel.Info, "Starting cellMap ...");
int[][] cellMap = VoronoiRender.getCellMap(p.getPoints(), p.getWidth(), p.getHeight());
Log.log(LogLevel.Info, "Finished cellMap!");
Log.log(LogLevel.Info, "Starting cellColors ...");
int[] cellColors = VoronoiRender.getCellColors(img, p.getPoints(), cellMap);
Log.log(LogLevel.Info, "Finished cellColors!");
Log.log(LogLevel.Info, "Starting Voronoi Render ...");
fh.saveImage(VoronoiRender.renderColorVoronoi(p.getPoints(), cellColors, cellMap), new File(fh.OutputFolder+fh.fs+"Output-render.png"));
}

}

+ 304
- 0
src/peery/file/FileHandler.java View File

@@ -0,0 +1,304 @@
package peery.file;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

import javax.imageio.ImageIO;

import peery.log.Log;
import peery.log.LogLevel;

public class FileHandler {
public File sourceFolder, InputImagesFolder, TargetImageFile, OutputFolder, indexFile;
/*
* sourcePath/ -> all ressources
* sourcePath/Images/ -> Input pictures folder
* sourcePath/Target -> Target picture file
* sourcePath/Output/ -> Output picture folder
*/
public final String fs;
public FileHandler(String sourcePath){
if(!System.getProperty("os.name").startsWith("Windows")){
fs = "/";
}else{
fs = "\\";
}
this.sourceFolder = new File(sourcePath);
this.InputImagesFolder = new File(sourceFolder.getAbsolutePath()+fs+"Images");
this.TargetImageFile = new File(sourceFolder.getAbsolutePath()+fs+"Target.png");
this.OutputFolder = new File(sourceFolder.getAbsolutePath()+fs+"Output");
this.indexFile = new File(this.InputImagesFolder.getAbsolutePath()+"Index.txt");
if(!this.sourceFolder.exists()){
this.sourceFolder.mkdirs();
}
Log.initLog(this.sourceFolder.getAbsolutePath(), this);
if(fs == "\\"){
Log.log(LogLevel.Debug, "Assumed Windows like Folder declaration. Therefore using "+fs+" as a separator.");
}else{
Log.log(LogLevel.Debug, "Detected Linux or OSX.");
}
if(!this.validateFolderStructure()){
Log.log(LogLevel.Error, "Could not validate folder structure! Things are missing!");
Log.spawnReadMe(this);
System.exit(1);
}
}
public boolean validateFolderStructure(){
if(this.sourceFolder.isDirectory()){
Log.log(LogLevel.Debug, "Detected source folder at "+this.sourceFolder.getAbsolutePath());
if(this.InputImagesFolder.isDirectory()){
Log.log(LogLevel.Debug, "Detected Input folder at "+this.InputImagesFolder.getAbsolutePath());
if(this.OutputFolder.isDirectory()){
Log.log(LogLevel.Debug, "Detected Output folder at "+this.OutputFolder.getAbsolutePath());
}else{
Log.log(LogLevel.Info, "No Output folder found.");
Log.log(LogLevel.Info, "Creating one at "+this.OutputFolder.getAbsolutePath());
this.OutputFolder.mkdirs();
}
if(this.TargetImageFile.isFile()){
Log.log(LogLevel.Debug, "Detected Target Image at "+this.TargetImageFile.getAbsolutePath());
if(!this.indexFile.isDirectory()){
Log.log(LogLevel.Debug, "Found no directory blocking the index file.");
return true;
}
else{
Log.log(LogLevel.Error, "Following folder collides with the index file name: "+this.indexFile.getAbsolutePath());
return false;
}
}else{
Log.log(LogLevel.Critical, "No Target Image found! Exiting...");
return false;
}
}else{
Log.log(LogLevel.Critical, "No Input folder found.");
Log.log(LogLevel.Critical, "Creating one at "+this.InputImagesFolder.getAbsolutePath());
this.InputImagesFolder.mkdirs();
}
}else{
Log.log(LogLevel.Critical, "No source folder found (redundant check).");
Log.log(LogLevel.Critical, "Creating one at "+this.sourceFolder.getAbsolutePath());
this.sourceFolder.mkdirs();
}
Log.log(LogLevel.Critical, "Folder validation failed. There could be a permission problem "
+ "or a folder needed to be created! Please look for earlier errors.");
return false;
}
public ArrayList<String> listInputFiles(){
Log.log(LogLevel.Info, "Listing files inside "+this.InputImagesFolder.getName()+" ...");
ArrayList<String> fileList = new ArrayList<String>();
for(File f: this.InputImagesFolder.listFiles()){
if(f.isFile()){
fileList.add(f.getAbsolutePath());
}
}
return fileList;
}
public BufferedImage loadImage(File file){
Log.log(LogLevel.Info, "Loading image "+file.getName()+" ...");
if(file.isFile() && file.canRead()){
BufferedImage img;
try {
img = ImageIO.read(file);
Log.log(LogLevel.Debug, "Loaded image "+file.getName()+" !");
return img;
} catch (IOException e) {
Log.log(LogLevel.Debug, "File "+file.getPath()+" failed to load as an Image. What did I just read?");
e.printStackTrace();
return null;
}
}
else{
Log.log(LogLevel.Info, "Can't read file "+file.getPath()+" ! It could be a directory or no read permissions.");
return null;
}
}
public void saveImage(BufferedImage img, File file){
Log.log(LogLevel.Info, "Saving image as file "+file.getAbsolutePath());
Log.log(LogLevel.Info, "This could take a moment ...");
try {
ImageIO.write(img, "png", file);
} catch (IOException e) {
Log.log(LogLevel.Critical, "Couldn't write image "+file.getName()+" to file! Are write permissions missing?"
+ " Attempted to write at "+file.getAbsolutePath());
e.printStackTrace();
}
Log.log(LogLevel.Info, "Saved "+file.getName()+" !");
}
/**
* Plainly appends the given file and rgb value to the end of the index.
*
* CHECK FOR DUPLICATES BEFOREHAND.
* @param file
* @param rgb
*/
public synchronized void appendToIndex(File file, int rgb){
try{
BufferedWriter bw = new BufferedWriter(new FileWriter(indexFile, true));
bw.write(rgb+";"+file.getName()+"\n");
bw.flush();
bw.close();
Log.log(LogLevel.Info, "Wrote index entry for "+file.getName()+" !");
} catch (IOException e) {
Log.log(LogLevel.Critical, "Couldn't create or write index file "+indexFile.getAbsolutePath()+" ."
+ "Are write permissions missing?");
e.printStackTrace();
return;
}
}
/**
* Loads an index entry for a single file.
*
* Loads and parses the whole index in the background if not given the indexData.
* @param indexData
* @param file
* @return
*/
public Integer loadIndexEntry(HashMap<String, Integer> indexData, File file){
Log.log(LogLevel.Debug, "Searching for index data of "+file.getName()+" ...");
if(indexData == null){
indexData = loadIndex();
}
return indexData.get(file.getName());
}
/**
* Loads the whole index file into a Hashmap.
* The second value (file name) is used as a String-key, the rgb value is the int-value.
* @param file
* @return
*/
public HashMap<String, Integer> loadIndex(){
if(!this.indexFile.exists()){
Log.log(LogLevel.Info, "No Index file found. Nothing to load then. Creating empty one ...");
try {
indexFile.createNewFile();
} catch (IOException e) {
Log.log(LogLevel.Error, "Couldn't create Index file! Are write permissions missing?");
e.printStackTrace();
}
return null;
}
HashMap<String, Integer> indexData = new HashMap<String, Integer>();
try {
Scanner sc = new Scanner(new BufferedReader(new FileReader(this.indexFile)));
while(sc.hasNext()){
@SuppressWarnings("rawtypes")
ArrayList data = parseIndexData(sc.nextLine());
indexData.put((String)data.get(1), (int)data.get(0));
}
sc.close();
} catch (FileNotFoundException e) {
Log.log(LogLevel.Critical, "Could not open index file! Do I have read permissions?");
e.printStackTrace();
}
Log.log(LogLevel.Debug, "Sucessfully loaded index!");
return indexData;
}
public void overwriteIndex(HashMap<String, Integer> index){
try{
BufferedWriter bw = new BufferedWriter(new FileWriter(indexFile, false));
for(String key: index.keySet()){
bw.write(index.get(key)+";"+key+"\n");
}
bw.flush();
bw.close();
Log.log(LogLevel.Info, "Overwrote index file with new index!");
} catch (IOException e) {
Log.log(LogLevel.Critical, "Couldn't create or write index file "+indexFile.getAbsolutePath()+" ."
+ "Are write permissions missing?");
e.printStackTrace();
return;
}
}
/**
* Parses a line of indexData into an ArrayList containing the rgb int and the name
* @param indexData
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private ArrayList parseIndexData(String indexData){
String[] data = indexData.split(";");
ArrayList result = new ArrayList();
result.add(Integer.parseInt(data[0]));
result.add(data[1]);
return result;
}
// could use a single image variant to check synchronized to the classification (saves IO)
public Dimension loadBiggestDimension(){
File[] files = this.InputImagesFolder.listFiles();
int width = 0, height = 0, img_count = 0;
for(File f: files){
if(f.isFile() && f.canRead()){
BufferedImage img = loadImage(f);
if(img == null){
continue;
}
img_count++;
if(width < img.getWidth()){
width = img.getWidth();
}
if(height < img.getHeight()){
height = img.getHeight();
}
}
else{
Log.log(LogLevel.Info, "Can't read file"+f.toString()+"! It could be a directory or no read permissions.");
}
}
Log.log(LogLevel.Info, img_count+" image(s) were loaded...");
if(width == 0 || height == 0){
Log.log(LogLevel.Critical, "Incomplete or no dimension values! Could I load any Image?");
}
Log.log(LogLevel.Debug, "Biggest dimension is "+width+"x"+height);
return new Dimension(width, height);
}
// Would probably kill memory (and performance).
@Deprecated
public BufferedImage[] loadAllImages(){
File[] files = this.InputImagesFolder.listFiles();
ArrayList<BufferedImage> imgs = new ArrayList<BufferedImage>();
int img_count = 0;
for(File f: files){
BufferedImage img = loadImage(f);
if(img == null){
continue;
}
imgs.add(img);
img_count++;
}
if(imgs.size() == 0){
Log.log(LogLevel.Critical, "No Images found in "+this.InputImagesFolder.getAbsolutePath());
return null;
}
Log.log(LogLevel.Info, img_count+" image(s) were loaded...");
BufferedImage[] bfs = new BufferedImage[imgs.size()];
for(int i = 0; i < imgs.size(); i++){
bfs[i] = imgs.get(i);
}
return bfs;
}

}

+ 175
- 0
src/peery/log/Log.java View File

@@ -0,0 +1,175 @@
package peery.log;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import peery.file.FileHandler;

public class Log {

public static Log log;
public static final boolean silenceDebug = false,
appendEvents = false, appendErrors = false;
private final static String readmeText =
"This is the ReadMe for Mosaic.\n"
+ "The folders that were created serve following purpose: \n"
+ "resources - Is the main folder where all images (Input AND Output are stored). \n"
+ "resources%sImages - Is the Input folder for all images you want to use as \"paint\" in the Mosaic. Put all your images in there!\n"
+ "resources%sOutput - Stores all resulting Mosaic pictures. There are all named Output-{number}.png \n"
+ "resources%sTarget - Is an Image (or symbolic Link to one) of your choice. Mosaic will try to create the mosaic after this inspiration. The name MUST be \"Target\" without any file extension or it will not be recognized."
+ "resources%sERROR.log - Is a log file where all non-fatal erros are stored. Take a peek if problems occur. \n"
+ "resources%seventLog.log - Is a log for more genereal events. Like progress, events and such with time stamps. Most useful for debugging problems.";
private String location;
public final File eventFile, errorFile;
private File perfFile;
private BufferedWriter eventWriter, errorWriter, perfWriter;
private ArrayList<Long> nanoTimes;
public Log(String location, FileHandler fh){
this.location = location;
this.eventFile = new File(location+fh.fs+"eventLog.log");
this.errorFile = new File(location+fh.fs+"ERROR.log");
this.perfFile = new File(location+fh.fs+"perf.log");
this.nanoTimes = new ArrayList<Long>();
try {
if(!this.eventFile.exists()){
this.eventFile.createNewFile();
}
if(!this.errorFile.exists()){
this.errorFile.createNewFile();
}
if(!this.perfFile.exists()){
this.perfFile.createNewFile();
}
this.eventWriter = new BufferedWriter(new FileWriter(eventFile, appendEvents));
this.errorWriter = new BufferedWriter(new FileWriter(errorFile, appendErrors));
this.perfWriter = new BufferedWriter(new FileWriter(perfFile, true));
perfWriter.write("\n\n");
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
public static synchronized void initLog(String location, FileHandler fh){
if(Log.log != null){
return;
}
Log.log = new Log(location, fh);
}
public static void shutdownLog(){
if(Log.log == null){
return;
}
try {
Log.log.errorWriter.close();
Log.log.eventWriter.close();
Log.log.perfWriter.close();
Log.log = null;
} catch (IOException e) {
e.printStackTrace();
}
}
public static void log(int logLvl, String message){
Log.log(LogLevel.values()[logLvl], message);
}
public static void log(LogLevel lv, String message){
if(silenceDebug && LogLevel.Debug == lv){
return;
}
Log.log.logs(lv.ordinal(), message);
}
@SuppressWarnings("unused")
public synchronized void logs(int logLvl, String message){
String prefix = LogLevel.values()[logLvl].toString();
prefix = "["+prefix+"]";
BufferedWriter logWriter;
if(silenceDebug && logLvl == LogLevel.Debug.ordinal()){
return;
}
if(logLvl == LogLevel.Error.ordinal()){
logWriter = this.errorWriter;
}
else{
logWriter = this.eventWriter;
}
String timeStamp = new java.util.Date().toString();
String msg = "["+timeStamp+"]"+prefix+" "+message;
System.out.println(msg);
try {
logWriter.write(msg+"\n");
if(LogLevel.Info.ordinal() < logLvl){ //Saves perfomance?
logWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public synchronized void perfLog(String message){
long currentTime = System.nanoTime();
String timeStamp = new java.util.Date().toString()+"|"+currentTime;
this.nanoTimes.add(currentTime);
try {
perfWriter.write(message+"\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void finishPerfLog(){
String[] stages = {
"Indexing & Rasterization", // 2 - 1 1
"Matching", // 4 - 3 2
"Placement", // 6 - 5 3
"Saving" // 8 - 7 4
};
for(int i = 1; i <= stages.length; i++){
long duration = nanoTimes.get(i*2) - nanoTimes.get(i*2-1);
try {
perfWriter.write(stages[i-1]+": "+duration+"\n");
} catch (IOException e) {
e.printStackTrace();
}
}
try {
perfWriter.flush();
perfWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void spawnReadMe(FileHandler fh){
File readme = new File(Log.log.location+fh.fs+"README.txt");
Log.log(LogLevel.Info, "Spawning README file at "+readme.getAbsolutePath());
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(readme));
String rdme = readmeText.replaceAll("%s", fh.fs);
bw.write(rdme);
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
public static void main(String[] args){
Log.initLog("/home/mosaic/Software_Projects/EclipseWorkspace/Picture Mosaic/resources/");
Log.log(LogLevel.Debug, "Test!");
Log.log(LogLevel.Error, "TEST ERROR");
}*/
}

+ 9
- 0
src/peery/log/LogLevel.java View File

@@ -0,0 +1,9 @@
package peery.log;

public enum LogLevel {
Info,
Debug,
Critical,
Error

}

+ 93
- 0
src/peery/pointgenerator/PointMap.java View File

@@ -0,0 +1,93 @@
package peery.pointgenerator;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.util.ArrayList;
import java.util.Random;

public class PointMap {
private BufferedImage greyScale;
private double[][] pValues; // <y, x>
private ArrayList<int[]> points;
public PointMap(BufferedImage img){
greyScale = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2 = (Graphics2D) greyScale.getGraphics();
g2.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
g2.dispose();
pValues = new double[img.getWidth()][img.getHeight()];
ColorModel cm = ColorModel.getRGBdefault();
for(int y = 0; y < img.getHeight(); y++){
for(int x = 0; x < img.getWidth(); x++) {
int color = greyScale.getRGB(x, y);
pValues[x][y] = cm.getRed(color)/255.0;
//System.out.println("pValue: "+pValues[x][y]);
}
}
}
public void placePoints(int seed, double dampening) {
Random rnd = new Random(seed);
setPoints(new ArrayList<int[]>());
for(int x = 0; x < pValues.length; x++) {
for(int y = 0; y < pValues[x].length; y++) {
double r = rnd.nextDouble();
if(pValues[x][y]*dampening >= r) { //hit
int[] point = {x, y};
getPoints().add(point);
}
}
}
}
public BufferedImage render() {
BufferedImage img = new BufferedImage(greyScale.getWidth(), greyScale.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) img.getGraphics();
g2.setColor(Color.RED);
for(int[] point: getPoints()) {
g2.drawRect(point[0]-1, point[1]-1, 2, 2);
}
g2.dispose();
return img;
}
public double[] getXValues() {
double[] xValues = new double[getPoints().size()];
for(int i = 0; i < getPoints().size(); i++) {
xValues[i] = getPoints().get(i)[0];
}
return xValues;
}
public double[] getYValues() {
double[] yValues = new double[getPoints().size()];
for(int i = 0; i < getPoints().size(); i++) {
yValues[i] = getPoints().get(i)[1];
}
return yValues;
}
public int getWidth() {
return greyScale.getWidth();
}
public int getHeight() {
return greyScale.getHeight();
}

public ArrayList<int[]> getPoints() {
return points;
}

public void setPoints(ArrayList<int[]> points) {
this.points = points;
}
}

+ 114
- 0
src/peery/voronoi/VoronoiRender.java View File

@@ -0,0 +1,114 @@
package peery.voronoi;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.util.ArrayList;
import java.util.List;

import peery.log.Log;
import peery.log.LogLevel;
import peery.voronoi.simplevoronoi.GraphEdge;

public class VoronoiRender {
public static BufferedImage renderGraph(List<GraphEdge> edges, int width, int height) {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) img.getGraphics();
g2.setColor(Color.BLACK);
g2.drawRect(0, 0, width, height);
g2.setColor(Color.RED);
for(GraphEdge edge: edges) {
g2.drawLine((int)edge.x1, (int)edge.y1, (int)edge.x2, (int)edge.y2);
}
g2.dispose();
return img;
}
//int = pointIndex in points
/**
* Brute-force algorithm to determine the closest point in 'points' to the pixel (x,y)
* @param points ArrayList of points to choose from
* @param x x-coordinate for the pixel
* @param y y-coodinate for the pixel
* @return Index of the closest point in 'points'
*/
private static int getCellIndex(ArrayList<int[]> points, int x, int y) {
ArrayList<Double> distances = new ArrayList<Double>();
for(int i = 0; i < points.size(); i++) {
double d = Math.sqrt(Math.pow(points.get(i)[0]-x, 2) + Math.pow(points.get(i)[1]-y, 2));
distances.add(d);
}
int smallestI = 0;
double smallestD = distances.get(0);
for(int i = 0; i < distances.size(); i++) {
if(smallestD > distances.get(i)) {
smallestD = distances.get(i);
smallestI = i;
}
}
return smallestI;
}
//int[x][y] = pointIndex in points
public static int[][] getCellMap(ArrayList<int[]> points, int width, int height){
int[][] map = new int[width][height];
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
map[x][y] = getCellIndex(points, x, y);
}
Log.log(LogLevel.Debug, "Finished cell index row y:"+y+"/"+height+"!");
}
return map;
}
//int[pointIndex in points] = average RGB
public static int[] getCellColors(BufferedImage img, ArrayList<int[]> points, int[][] cellMap) {
int[][] colors = new int[points.size()][4]; // 0 = red, 1 = green , 2 = blue, amount
ColorModel cm = ColorModel.getRGBdefault();
int[] amount = new int[points.size()];
for(int x = 0; x < cellMap.length; x++) {
for(int y = 0; y < cellMap[x].length; y++) {
int color = img.getRGB(x, y);
int red = cm.getRed(color);
int green = cm.getGreen(color);
int blue = cm.getBlue(color);
colors[cellMap[x][y]][0] += red;
colors[cellMap[x][y]][1] += green;
colors[cellMap[x][y]][2] += blue;
colors[cellMap[x][y]][3] += 1;
}
}
int[] colorMap = new int[points.size()];
for(int i = 0; i < colorMap.length; i++) {
int red = colors[i][0] / colors[i][3]; //red / amount
int green = colors[i][1] / colors[i][3]; // green / amount
int blue = colors[i][2] / colors[i][3]; // blue / amount
colorMap[i] = new Color(red, green, blue).getRGB();
}
return colorMap;
}
public static BufferedImage renderColorVoronoi(ArrayList<int[]> points, int[] colorMap, int[][] cellMap) {
int width = cellMap.length;
int height = cellMap[0].length;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
img.setRGB(x, y, colorMap[cellMap[x][y]]);
}
}
return img;
}

}

+ 47
- 0
src/peery/voronoi/simplevoronoi/Edge.java View File

@@ -0,0 +1,47 @@
/*
Copyright 2011 James Humphreys. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/

package peery.voronoi.simplevoronoi;

/**
*
* @author James Humphreys
*/
class Edge
{
public double a = 0, b = 0, c = 0;
Site[] ep; // JH: End points?
Site[] reg; // JH: Sites this edge bisects?
int edgenbr;

Edge()
{
ep = new Site[2];
reg = new Site[2];
}
}

+ 37
- 0
src/peery/voronoi/simplevoronoi/GraphEdge.java View File

@@ -0,0 +1,37 @@
/*
Copyright 2011 James Humphreys. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/

package peery.voronoi.simplevoronoi;

public class GraphEdge
{
public double x1, y1, x2, y2;

public int site1;
public int site2;
}

+ 45
- 0
src/peery/voronoi/simplevoronoi/Halfedge.java View File

@@ -0,0 +1,45 @@
/*
Copyright 2011 James Humphreys. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/

package peery.voronoi.simplevoronoi;

public class Halfedge
{
Halfedge ELleft, ELright;
Edge ELedge;
boolean deleted;
int ELpm;
Site vertex;
double ystar;
Halfedge PQnext;

public Halfedge()
{
PQnext = null;
}
}

+ 44
- 0
src/peery/voronoi/simplevoronoi/Point.java View File

@@ -0,0 +1,44 @@
/*
Copyright 2011 James Humphreys. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/

package peery.voronoi.simplevoronoi;

class Point
{
double x, y;

public Point()
{
}

public void setPoint(double x, double y)
{
this.x = x;
this.y = y;
}
}

+ 41
- 0
src/peery/voronoi/simplevoronoi/Site.java View File

@@ -0,0 +1,41 @@
/*
Copyright 2011 James Humphreys. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.

THIS SOFTWARE IS PROVIDED BY James Humphreys ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of James Humphreys.
*/

package peery.voronoi.simplevoronoi;

// used both for sites and for vertices
public class Site
{
Point coord;
int sitenbr;

public Site()
{
coord = new Point();
}
}

+ 1020
- 0
src/peery/voronoi/simplevoronoi/Voronoi.java
File diff suppressed because it is too large
View File


Loading…
Cancel
Save