/*
* polycasso - Cubism Artwork generator
* Copyright 2009-2017 MeBigFatGuy.com
* Copyright 2009-2017 Dave Brosius
* Inspired by work by Roger Alsing
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations
* under the License.
*/
package com.mebigfatguy.polycasso;
import java.awt.Dimension;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
/**
* class that maintains the set of polygon data for this generation of images
*/
public class GenerationHandler implements Serializable {
private static final long serialVersionUID = 2375492293685052783L;
private List<GenerationMember> generation;
private final Random random;
private final Settings settings;
private int generationNumber;
private double annealingValue;
private GenerationMember bestMember;
private double eliteCutOff;
private int generationBests;
private int generationElites;
/**
* constructs a handler for managing successive generations of image samples
*
* @param confSettings
* settings to use for generation and elite size
* @param imageSize
* the size of the target image
*/
public GenerationHandler(Settings confSettings, Dimension imageSize) {
random = new Random();
generationNumber = 0;
settings = confSettings;
bestMember = new GenerationMember(DefaultScore.MAX_SCORE, new PolygonData[0]);
eliteCutOff = Long.MAX_VALUE;
generation = new ArrayList<GenerationMember>(settings.getGenerationSize() + 10);
annealingValue = settings.getStartTemperature() * settings.getStartTemperature() * imageSize.height * imageSize.width;
generationBests = 0;
generationElites = 0;
}
/**
* add a sample polygon set to this generation with a given score
*
* @param score
* the deviation from perfection this set calculates
*
* @param polygonData
* the polygons that draw the image
*
* @return whether this is the best polygon set so far
*/
public ImprovementResult addPolygonData(Score score, PolygonData[] polygonData) {
GenerationMember newMember = new GenerationMember(score, polygonData);
synchronized (generation) {
generation.add(newMember);
if (generation.size() >= settings.getGenerationSize()) {
processGeneration();
} else {
Collections.sort(generation);
}
if (score.getDelta() < bestMember.getScore().getDelta()) {
bestMember = newMember;
generationBests++;
return ImprovementResult.BEST;
} else if (score.getDelta() < eliteCutOff) {
generationElites++;
return ImprovementResult.ELITE;
}
}
return ImprovementResult.FAIL;
}
/**
* pick a random member either from the general pool or elite pool skew the results towards the elite
*
* @param elite
* whether to pick from the elite pool or not
* @return a random member
*/
public GenerationMember getRandomMember(boolean elite) {
synchronized (generation) {
int size = elite ? (settings.getEliteSize() % generation.size()) : generation.size();
if (size == 0) {
return null;
}
int r = random.nextInt(size);
int idx = (int) (r * ((double) r / (double) size));
return generation.get(idx);
}
}
/**
* returns the best polygon set to draw the picture
*
* @return the best polygon set
*/
public GenerationMember getBestMember() {
synchronized (generation) {
return bestMember;
}
}
private void processGeneration() {
int eliteSize = settings.getEliteSize();
Collections.<GenerationMember> sort(generation);
int sz = generation.size();
List<GenerationMember> nextGeneration = new ArrayList<GenerationMember>(settings.getGenerationSize() + 10);
for (int i = 0; i < eliteSize; i++) {
nextGeneration.add(generation.get(i));
}
if ((annealingValue > 0.01) && settings.isUseAnnealing()) {
int annealingReplacements = 0;
/* always keep the best, so start at 1 */
for (int i = 1; i < eliteSize; i++) {
int candidateIndex = random.nextInt(sz - eliteSize) + eliteSize;
GenerationMember candidate = generation.get(candidateIndex);
GenerationMember elite = generation.get(i);
long delta = candidate.getScore().getDelta() - elite.getScore().getDelta();
if (delta < annealingValue) {
nextGeneration.set(i, candidate);
if (Polycasso.DEBUG) {
annealingReplacements++;
}
}
}
if (Polycasso.DEBUG) {
System.out.println(
"Generation " + generationNumber + " had " + annealingReplacements + " annealing replacements with annealing value: " + annealingValue);
}
}
generation = nextGeneration;
eliteCutOff = generation.get(eliteSize - 1).getScore().getDelta();
if (Polycasso.DEBUG) {
System.out.println("Generation " + generationNumber + " had " + generationBests + " bests and " + generationElites + " elites. Best Score: "
+ generation.get(0).getScore());
}
generationBests = 0;
generationElites = 0;
generationNumber++;
annealingValue *= (1.0 - settings.getCoolingRate());
}
}