[Polycasso-commit] SF.net SVN: polycasso:[243] trunk/polycasso
Brought to you by:
dbrosius
|
From: <dbr...@us...> - 2011-08-03 06:00:45
|
Revision: 243
http://polycasso.svn.sourceforge.net/polycasso/?rev=243&view=rev
Author: dbrosius
Date: 2011-08-03 06:00:34 +0000 (Wed, 03 Aug 2011)
Log Message:
-----------
add support for proxies
Modified Paths:
--------------
trunk/polycasso/.classpath
trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultImageGenerator.java
trunk/polycasso/src/com/mebigfatguy/polycasso/Feedback.java
trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationHandler.java
trunk/polycasso/src/com/mebigfatguy/polycasso/ImageGenerator.java
trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementResult.java
trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementType.java
trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementTypeStats.java
trunk/polycasso/src/com/mebigfatguy/polycasso/Improver.java
trunk/polycasso/src/com/mebigfatguy/polycasso/PainterFrame.java
trunk/polycasso/src/com/mebigfatguy/polycasso/Polycasso.java
trunk/polycasso/src/com/mebigfatguy/polycasso/PolycassoBundle.java
trunk/polycasso/src/com/mebigfatguy/polycasso/RandomImageFinder.java
trunk/polycasso/src/com/mebigfatguy/polycasso/Settings.java
trunk/polycasso/src/com/mebigfatguy/polycasso/SettingsDialog.java
trunk/polycasso/src/com/mebigfatguy/polycasso/URLFetcher.java
trunk/polycasso/src/com/mebigfatguy/polycasso/resource.properties
Added Paths:
-----------
trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultFeedback.java
trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultScore.java
trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationMember.java
trunk/polycasso/src/com/mebigfatguy/polycasso/ProxyDialog.java
trunk/polycasso/src/com/mebigfatguy/polycasso/Score.java
trunk/polycasso/src/com/mebigfatguy/polycasso/SwingUtils.java
Modified: trunk/polycasso/.classpath
===================================================================
--- trunk/polycasso/.classpath 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/.classpath 2011-08-03 06:00:34 UTC (rev 243)
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/commons-io-1.4.jar"/>
<classpathentry kind="lib" path="lib/forms-1.2.1.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="classes"/>
</classpath>
Copied: trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultFeedback.java (from rev 238, trunk/polycasso/src/com/mebigfatguy/polycasso/Feedback.java)
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultFeedback.java (rev 0)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultFeedback.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -0,0 +1,140 @@
+/*
+ * polycasso - Cubism Artwork generator
+ * Copyright 2009-2011 MeBigFatGuy.com
+ * Copyright 2009-2011 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.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.WritableRaster;
+
+/**
+ * an immutable class for processing a test image against target image for closeness.
+ */
+public class DefaultFeedback implements Feedback {
+
+ private byte[] targetBuffer;
+ private int width, height;
+
+ /**
+ * creates a feedback object with a given targetImage. Caches the image bytes in
+ * member variables.
+ */
+ public DefaultFeedback() {
+ }
+
+ /**
+ * caches information about the target image
+ *
+ * @param targetImage the target image that will be the judge of test images
+ */
+ @Override
+ public void setTargetImage(BufferedImage targetImage) {
+ WritableRaster raster = targetImage.getRaster();
+ width = targetImage.getWidth();
+ height = targetImage.getHeight();
+ DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
+ targetBuffer = dbb.getData();
+ }
+
+ /**
+ * returns a score of how close the test image is to the target
+ * which is the square of the error to the target image
+ *
+ * @param testImage the image to score
+ * @param previousScore the score of the generated image from which this image was created
+ * @param changedArea the area of changed between the parent generated image and this one
+ *
+ * @return a score that represents its closeness to ideal
+ */
+ @Override
+ public Score calculateScore(BufferedImage testImage, Score previousScore, Rectangle changedArea) {
+
+ DefaultScore score = (previousScore != null) ? (DefaultScore)previousScore.clone() : new DefaultScore();
+
+ WritableRaster raster = testImage.getRaster();
+ DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
+ byte[] testBuffer = dbb.getData();
+
+ boolean needAutoRecalc = (previousScore == null) || (changedArea == null);
+ score.overallScore = 0L;
+
+ for (int y = 0; y < DefaultScore.NUM_DIVISIONS; y++) {
+ int gridHeight = (height / DefaultScore.NUM_DIVISIONS);
+ int gridTop = y * gridHeight;
+ int gridBottom;
+ if (y < (DefaultScore.NUM_DIVISIONS - 1)) {
+ gridBottom = gridTop + gridHeight;
+ } else {
+ gridBottom = height;
+ }
+
+ if (needAutoRecalc || ((changedArea.y <= gridBottom) && ((changedArea.y + changedArea.height) >= gridTop))) {
+ for (int x = 0; x < DefaultScore.NUM_DIVISIONS; x++) {
+ int gridWidth = (width / DefaultScore.NUM_DIVISIONS);
+ int gridLeft = x * gridWidth;
+ int gridRight;
+ if (x < (DefaultScore.NUM_DIVISIONS - 1)) {
+ gridRight = gridLeft + gridWidth;
+ } else {
+ gridRight = width;
+ }
+
+ if (needAutoRecalc || ((changedArea.x <= gridRight) && ((changedArea.x + changedArea.width) >= gridLeft))) {
+
+ long gridError = 0L;
+ for (int gy = gridTop; gy < gridBottom; gy++) {
+ int pixelStart = (gy * width * 4) + (gridLeft * 4);
+ int pixelEnd = pixelStart + (gridRight - gridLeft) * 4;
+
+ //index 0 is alpha, start at 1 (blue)
+ for (int i = pixelStart + 1; i < pixelEnd; i++) {
+ int blue1 = targetBuffer[i] & 0x0FF;
+ int blue2 = testBuffer[i++] & 0x0FF;
+ long blueError = blue1 - blue2;
+ blueError *= blueError;
+
+ int green1 = targetBuffer[i] & 0x0FF;
+ int green2 = testBuffer[i++] & 0x0FF;
+ long greenError = green1 - green2;
+ greenError *= greenError;
+
+ int red1 = targetBuffer[i] & 0x0FF;
+ int red2 = testBuffer[i++] & 0x0FF;
+ long redError = red1 - red2;
+ redError *= redError;
+
+ gridError += redError + greenError + blueError;
+ }
+ }
+ score.gridScores[x][y] = gridError;
+ score.overallScore += gridError;
+ } else {
+ score.overallScore += score.gridScores[x][y];
+ }
+ }
+ } else {
+ for (int x = 0; x < DefaultScore.NUM_DIVISIONS; x++) {
+ score.overallScore += score.gridScores[x][y];
+ }
+ }
+ }
+
+ return score;
+ }
+}
\ No newline at end of file
Property changes on: trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultFeedback.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:mimetype
+ text/plain
Added: svn:eol-style
+ native
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultImageGenerator.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultImageGenerator.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultImageGenerator.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -4,17 +4,17 @@
* Copyright 2009-2011 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
+ * 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.
+ * 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;
@@ -33,240 +33,253 @@
/**
* class that generates test images iteratively looking for the best image that matches a target.
- * The images are generated from semi-transparent polygons that are improved upon over time.
+ * The images are generated from semi-transparent polygons that are improved upon over time.
* This class generates multiple images in parallel to keep multicore processors busy.
*/
public class DefaultImageGenerator implements ImageGenerator, Runnable {
- private Set<ImageGeneratedListener> listeners = new HashSet<ImageGeneratedListener>();
- private Settings settings;
- private BufferedImage targetImage;
- private GenerationHandler generationHandler;
- private Dimension imageSize;
- private Feedback feedback;
- private Thread[] t = null;
- private Object startStopLock = new Object();
-
- /**
- * creates an ImageGenerator for the given target image, and size
- * @param confSettings the configuration settings
- * @param image the target image
- * @param size the dimension of the image
- */
- public DefaultImageGenerator(Settings confSettings, Image image, Dimension size) {
- settings = confSettings;
- imageSize = trimSize(size, settings.getMaxImageSize());
- targetImage = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
-
- Graphics g = targetImage.getGraphics();
- try {
- g.drawImage(image, 0, 0, imageSize.width, imageSize.height, Color.WHITE, null);
- generationHandler = new GenerationHandler(settings, imageSize);
- feedback = new Feedback(targetImage);
- } finally {
- g.dispose();
- }
- }
-
- /**
- * retrieves the scaled target iamge
- *
- * @return the target image
- */
- public BufferedImage getTargetImage() {
- return targetImage;
- }
-
- /**
- * returns the image size that is being generated. This size might be different the original image
- * if the size is bigger then the max setting.
- *
- * @return the image size
- */
- public Dimension getImageSize() {
- return imageSize;
- }
-
- /**
- * allows interested parties to register to receive events when a new best image has been
- * found.
- *
- * @param listener the listener that is interested in events
- */
- public void addImageGeneratedListener(ImageGeneratedListener listener) {
- listeners.add(listener);
- }
-
- /**
- * allows uninterested parties to unregister to receive events when a new best image is
- * found
- *
- * @param listener the listener that is no longer needed
- */
- public void removeImageGeneratedListener(ImageGeneratedListener listener) {
- listeners.remove(listener);
- }
-
- /**
- * informs all listeners that a new best image has been found
- *
- * @param image the new best image
- */
- public void fireImageGenerated(Image image) {
- ImageGeneratedEvent event = new ImageGeneratedEvent(this, image);
- for (ImageGeneratedListener listener : listeners) {
- listener.imageGenerated(event);
- }
- }
-
- /**
- * starts up threads to start looking for images that are closest to the target
- */
- public void startGenerating() {
- synchronized(startStopLock) {
- if (t == null) {
-
- populateGenerationZeroElite();
- t = new Thread[Runtime.getRuntime().availableProcessors() + 1];
- for (int i = 0; i < t.length; i++) {
- t[i] = new Thread(this);
- t[i].setName("Improver : " + i);
- t[i].start();
- }
- }
- }
- }
-
- /**
- * shuts down threads that were looking for images
- */
- public void stopGenerating() {
- synchronized(startStopLock) {
- if (t != null) {
- try {
- for (int i = 0; i < t.length; i++) {
- t[i].interrupt();
- }
- for (int i = 0; i < t.length; i++) {
- t[i].join();
- }
- } catch (InterruptedException ie) {
- } finally {
- t = null;
- }
- }
- }
- }
-
- /**
- * completes the image by transforming the polygon image to the real image
- */
- public void complete() {
- synchronized(startStopLock) {
- if (t != null) {
- stopGenerating();
- t = new Thread[1];
- t[0] = new Thread(new ImageCompleter(this, targetImage, generationHandler.getBestMember().data, imageSize));
- t[0].start();
- }
- }
- }
-
- /**
- * retrieves the best set of polygons for drawing the image so far
- *
- * @return the best set of polygons
- */
- public PolygonData[] getBestData() {
- return generationHandler.getBestMember().data;
- }
- /**
- * the runnable interface implementation to repeatedly improve upon the image and check to
- * see if it is closer to the target image. Images are created in batches of settings.numCompetingImages
- * and the best one (if better than the parent) is selected as the new best.
- */
- public void run() {
- try {
- BufferedImage image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
- Graphics2D g2d = (Graphics2D)image.getGraphics();
- try {
- Composite srcOpaque = AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f);
- Improver improver = new Improver(settings, generationHandler, imageSize);
-
- while (!Thread.interrupted()) {
- ImprovementType type = improver.improveRandomly();
-
- List<PolygonData> data = improver.getData();
- imagePolygonData(g2d, data, srcOpaque);
-
- long delta = feedback.calculateDelta(image);
-
- boolean wasSuccessful;
-
- ImprovementResult result = generationHandler.addPolygonData(delta, data.toArray(new PolygonData[data.size()]));
- switch (result) {
- case BEST:
- fireImageGenerated(image);
- wasSuccessful = true;
- image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
- g2d.dispose();
- g2d = (Graphics2D)image.getGraphics();
- break;
-
- case ELITE:
- wasSuccessful = true;
- break;
-
- default:
- wasSuccessful = false;
- }
-
- improver.typeWasSuccessful(type, wasSuccessful);
- }
- } finally {
- g2d.dispose();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void imagePolygonData(Graphics2D g2d, List<PolygonData> polygonData, Composite srcOpaque) {
- g2d.setColor(Color.BLACK);
+ private final Set<ImageGeneratedListener> listeners = new HashSet<ImageGeneratedListener>();
+ private final Settings settings;
+ private final BufferedImage targetImage;
+ private GenerationHandler generationHandler;
+ private final Dimension imageSize;
+ private Feedback feedback;
+ private Thread[] t = null;
+ private final Object startStopLock = new Object();
+
+ /**
+ * creates an ImageGenerator for the given target image, and size
+ * @param confSettings the configuration settings
+ * @param image the target image
+ * @param size the dimension of the image
+ */
+ public DefaultImageGenerator(Settings confSettings, Image image, Dimension size) {
+ settings = confSettings;
+ imageSize = trimSize(size, settings.getMaxImageSize());
+ targetImage = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
+
+ Graphics g = targetImage.getGraphics();
+ try {
+ g.drawImage(image, 0, 0, imageSize.width, imageSize.height, Color.WHITE, null);
+ generationHandler = new GenerationHandler(settings, imageSize);
+ feedback = new DefaultFeedback();
+ feedback.setTargetImage(targetImage);
+ } finally {
+ g.dispose();
+ }
+ }
+
+ /**
+ * retrieves the scaled target iamge
+ *
+ * @return the target image
+ */
+ @Override
+ public BufferedImage getTargetImage() {
+ return targetImage;
+ }
+
+ /**
+ * returns the image size that is being generated. This size might be different the original image
+ * if the size is bigger then the max setting.
+ *
+ * @return the image size
+ */
+ @Override
+ public Dimension getImageSize() {
+ return imageSize;
+ }
+
+ /**
+ * allows interested parties to register to receive events when a new best image has been
+ * found.
+ *
+ * @param listener the listener that is interested in events
+ */
+ @Override
+ public void addImageGeneratedListener(ImageGeneratedListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * allows uninterested parties to unregister to receive events when a new best image is
+ * found
+ *
+ * @param listener the listener that is no longer needed
+ */
+ @Override
+ public void removeImageGeneratedListener(ImageGeneratedListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * informs all listeners that a new best image has been found
+ *
+ * @param image the new best image
+ */
+ @Override
+ public void fireImageGenerated(Image image) {
+ ImageGeneratedEvent event = new ImageGeneratedEvent(this, image);
+ for (ImageGeneratedListener listener : listeners) {
+ listener.imageGenerated(event);
+ }
+ }
+
+ /**
+ * starts up threads to start looking for images that are closest to the target
+ */
+ @Override
+ public void startGenerating() {
+ synchronized(startStopLock) {
+ if (t == null) {
+
+ populateGenerationZeroElite();
+ t = new Thread[Runtime.getRuntime().availableProcessors() + 1];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = new Thread(this);
+ t[i].setName("Improver : " + i);
+ t[i].start();
+ }
+ }
+ }
+ }
+
+ /**
+ * shuts down threads that were looking for images
+ */
+ @Override
+ public void stopGenerating() {
+ synchronized(startStopLock) {
+ if (t != null) {
+ try {
+ for (Thread element : t) {
+ element.interrupt();
+ }
+ for (Thread element : t) {
+ element.join();
+ }
+ } catch (InterruptedException ie) {
+ } finally {
+ t = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * completes the image by transforming the polygon image to the real image
+ */
+ @Override
+ public void complete() {
+ synchronized(startStopLock) {
+ if (t != null) {
+ stopGenerating();
+ t = new Thread[1];
+ t[0] = new Thread(new ImageCompleter(this, targetImage, generationHandler.getBestMember().getData(), imageSize));
+ t[0].start();
+ }
+ }
+ }
+
+ /**
+ * retrieves the best set of polygons for drawing the image so far
+ *
+ * @return the best set of polygons
+ */
+ @Override
+ public PolygonData[] getBestData() {
+ return generationHandler.getBestMember().getData();
+ }
+ /**
+ * the runnable interface implementation to repeatedly improve upon the image and check to
+ * see if it is closer to the target image. Images are created in batches of settings.numCompetingImages
+ * and the best one (if better than the parent) is selected as the new best.
+ */
+ @Override
+ public void run() {
+ try {
+ BufferedImage image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
+ Graphics2D g2d = (Graphics2D)image.getGraphics();
+ try {
+ Composite srcOpaque = AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f);
+ Improver improver = new Improver(settings, generationHandler, imageSize);
+
+ while (!Thread.interrupted()) {
+ ImprovementType type = improver.improveRandomly();
+
+ List<PolygonData> data = improver.getData();
+ imagePolygonData(g2d, data, srcOpaque);
+
+ GenerationMember parentMember = improver.getParentGenerationMember();
+ Score delta = feedback.calculateScore(image, (parentMember != null) ? parentMember.getScore() : null, improver.getChangedArea());
+
+ boolean wasSuccessful;
+
+ ImprovementResult result = generationHandler.addPolygonData(delta, data.toArray(new PolygonData[data.size()]));
+ switch (result) {
+ case BEST:
+ fireImageGenerated(image);
+ wasSuccessful = true;
+ image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
+ g2d.dispose();
+ g2d = (Graphics2D)image.getGraphics();
+ break;
+
+ case ELITE:
+ wasSuccessful = true;
+ break;
+
+ default:
+ wasSuccessful = false;
+ }
+
+ improver.typeWasSuccessful(type, wasSuccessful);
+ }
+ } finally {
+ g2d.dispose();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void imagePolygonData(Graphics2D g2d, List<PolygonData> polygonData, Composite srcOpaque) {
+ g2d.setColor(Color.BLACK);
g2d.setComposite(srcOpaque);
g2d.fillRect(0, 0, imageSize.width, imageSize.height);
-
+
for (PolygonData pd : polygonData) {
pd.draw(g2d);
}
- }
-
- private void populateGenerationZeroElite() {
+ }
+
+ private void populateGenerationZeroElite() {
Composite srcOpaque = AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f);
- for (int i = 0; i < settings.getEliteSize(); i++) {
- BufferedImage image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
- List<PolygonData> polygons = new ArrayList<PolygonData>();
- PolygonData pd = PolygonData.randomPoly(imageSize, settings.getMaxPoints());
- polygons.add(pd);
- Graphics2D g2d = (Graphics2D)image.getGraphics();
- try {
+ for (int i = 0; i < settings.getEliteSize(); i++) {
+ BufferedImage image = new BufferedImage(imageSize.width, imageSize.height, BufferedImage.TYPE_4BYTE_ABGR);
+ List<PolygonData> polygons = new ArrayList<PolygonData>();
+ PolygonData pd = PolygonData.randomPoly(imageSize, settings.getMaxPoints());
+ polygons.add(pd);
+ Graphics2D g2d = (Graphics2D)image.getGraphics();
+ try {
imagePolygonData(g2d, polygons, srcOpaque);
- long delta = feedback.calculateDelta(image);
+ Score delta = feedback.calculateScore(image, null, null);
generationHandler.addPolygonData(delta, polygons.toArray(new PolygonData[polygons.size()]));
- } finally {
- g2d.dispose();
- }
- }
- }
-
- private Dimension trimSize(Dimension origSize, Dimension maxSize) {
- if ((origSize.width < maxSize.width) && (origSize.height < maxSize.height))
- return origSize;
-
- double hFrac = (double)maxSize.width / (double)origSize.width;
- double vFrac = (double)maxSize.height/ (double)origSize.height;
-
- double frac = (hFrac < vFrac) ? hFrac : vFrac;
-
- return new Dimension((int)(frac * origSize.width), (int)(frac * origSize.height));
- }
+ } finally {
+ g2d.dispose();
+ }
+ }
+ }
+
+ private Dimension trimSize(Dimension origSize, Dimension maxSize) {
+ if ((origSize.width < maxSize.width) && (origSize.height < maxSize.height)) {
+ return origSize;
+ }
+
+ double hFrac = (double)maxSize.width / (double)origSize.width;
+ double vFrac = (double)maxSize.height/ (double)origSize.height;
+
+ double frac = (hFrac < vFrac) ? hFrac : vFrac;
+
+ return new Dimension((int)(frac * origSize.width), (int)(frac * origSize.height));
+ }
}
Added: trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultScore.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultScore.java (rev 0)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultScore.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -0,0 +1,112 @@
+package com.mebigfatguy.polycasso;
+
+/**
+ * a default implementation of a score for the error in a image against a target image
+ * this score maintains a grid of scores representing scores in sections of the image, and
+ * then rolls up these scores to an overall score
+ */
+public class DefaultScore implements Score {
+
+ static final int NUM_DIVISIONS = 4;
+
+ /**
+ * a worst case score
+ */
+ public static final DefaultScore MAX_SCORE = new DefaultScore(Long.MAX_VALUE);
+
+ long gridScores[][] = new long[NUM_DIVISIONS][NUM_DIVISIONS];
+ long overallScore;
+
+ /**
+ * constructs an empty score
+ */
+ public DefaultScore() {
+ overallScore = 0;
+ }
+
+ /**
+ * constructs a score with the specified delta, spreads the score across all grids
+ *
+ * @param delta the delta score
+ */
+ public DefaultScore(long delta) {
+ overallScore = delta;
+
+ long divider = NUM_DIVISIONS * NUM_DIVISIONS;
+ long gridScore = overallScore / divider;
+
+ for (int y = 0; y < NUM_DIVISIONS; y++) {
+ for (int x = 0; x < NUM_DIVISIONS; x++) {
+ gridScores[x][y] = gridScore;
+ }
+ }
+ }
+
+ /**
+ * returns the sum of the square of pixel errors
+ * @return the delta between a generate image and the target
+ */
+ @Override
+ public long getDelta() {
+ return overallScore;
+ }
+
+ /**
+ * compares this core to another
+ *
+ * @param o the score to compare to
+ * @return whether the two scores have the same delta
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DefaultScore)) {
+ return false;
+ }
+
+ return getDelta() == ((DefaultScore) o).getDelta();
+ }
+
+ /**
+ * clones this object
+ *
+ * @return a clone
+ */
+ @Override
+ public Object clone() {
+ DefaultScore clonedScore;
+
+ try {
+ clonedScore = (DefaultScore) super.clone();
+ } catch (CloneNotSupportedException cnse) {
+ clonedScore = new DefaultScore();
+ clonedScore.overallScore = overallScore;
+ }
+
+ clonedScore.gridScores = new long[NUM_DIVISIONS][NUM_DIVISIONS];
+ for (int i = 0; i < NUM_DIVISIONS; i++) {
+ System.arraycopy(gridScores[i], 0, clonedScore.gridScores[i], 0, NUM_DIVISIONS);
+ }
+
+ return clonedScore;
+ }
+
+ /**
+ * generates a hash code for this score
+ *
+ * @return the hash code of this score
+ */
+ @Override
+ public int hashCode() {
+ return (int) overallScore;
+ }
+
+ /**
+ * returns a string representation of the score
+ *
+ * @return the score as a string
+ */
+ @Override
+ public String toString() {
+ return String.valueOf(overallScore);
+ }
+}
Property changes on: trunk/polycasso/src/com/mebigfatguy/polycasso/DefaultScore.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:mimetype
+ text/plain
Added: svn:eol-style
+ native
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/Feedback.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/Feedback.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/Feedback.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -18,65 +18,29 @@
*/
package com.mebigfatguy.polycasso;
+import java.awt.Rectangle;
import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferByte;
-import java.awt.image.WritableRaster;
/**
- * an immutable class for processing a test image against target image for closeness.
+ * an immutable calculater of a generated images score compared to a target image
*/
-public class Feedback {
+public interface Feedback {
- private byte[] targetBuffer;
- private int size;
-
- /**
- * creates a feedback object with a given targetImage. Caches the image bytes in
- * member variables.
- *
- * @param targetImage the target image that will be the judge of test images
- */
- public Feedback(BufferedImage targetImage) {
- WritableRaster raster = targetImage.getRaster();
- DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
- targetBuffer = dbb.getData();
- size = dbb.getSize();
- }
-
- /**
- * returns a score of how close the test image is to the target
- * which is the square of the error to the target image
- *
- * @param testImage the image to score
- * @return a value that represents its closeness to ideal
- */
- public long calculateDelta(BufferedImage testImage) {
- WritableRaster raster = testImage.getRaster();
- DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer();
- byte[] testBuffer = dbb.getData();
-
- long error = 0L;
-
- //index 0 is alpha, start at 1 (blue)
- for (int i = 1; i < size; i++) {
- int blue1 = targetBuffer[i] & 0x0FF;
- int blue2 = testBuffer[i++] & 0x0FF;
- long blueError = blue1 - blue2;
- blueError *= blueError;
-
- int green1 = targetBuffer[i] & 0x0FF;
- int green2 = testBuffer[i++] & 0x0FF;
- long greenError = green1 - green2;
- greenError *= greenError;
-
- int red1 = targetBuffer[i] & 0x0FF;
- int red2 = testBuffer[i++] & 0x0FF;
- long redError = red1 - red2;
- redError *= redError;
-
- error += redError + greenError + blueError;
- }
-
- return error;
- }
+ /**
+ * sets the target image to compare against
+ * @param targetImage the real target image
+ */
+ void setTargetImage(BufferedImage targetImage);
+
+ /**
+ * calculates the score of a generated image against a target image, as
+ * the sum of the square of the pixel error
+ *
+ * @param testImage the generated image to test
+ * @param sourceScore the score of the parent test image from which this test image was generated
+ * @param changedArea the area of changed between the parent generated image and this one
+ *
+ * @return the score of this generated image
+ */
+ Score calculateScore(BufferedImage testImage, Score sourceScore, Rectangle changedArea);
}
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationHandler.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationHandler.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationHandler.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -19,8 +19,8 @@
package com.mebigfatguy.polycasso;
import java.awt.Dimension;
+import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
@@ -28,56 +28,16 @@
/**
* class that maintains the set of polygon data for this generation of images
*/
-public class GenerationHandler {
+public class GenerationHandler implements Serializable {
- /**
- * class that holds a sample set of polygons and it's score
- */
- public static class Member implements Comparable<Member> {
-
- long score;
- PolygonData[] data;
-
- Member(long polyScore, PolygonData[] polyData) {
- score = polyScore;
- data = polyData;
- }
+ private static final long serialVersionUID = 2375492293685052783L;
- @Override
- public int compareTo(Member o) {
- if (score > o.score)
- return 1;
- else if (score < o.score)
- return -1;
-
- return data.length - o.data.length;
- }
-
- @Override
- public int hashCode() {
- return (int)score * 1000;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Member) {
- return score == ((Member)o).score;
- }
- return false;
- }
-
- @Override
- public String toString() {
- return "(" + score + ": " + Arrays.toString(data) + ")";
- }
- }
-
- private List<Member> generation;
+ private List<GenerationMember> generation;
private Random random;
private Settings settings;
private int generationNumber;
private double annealingValue;
- private Member bestMember;
+ private GenerationMember bestMember;
private double eliteCutOff;
private int generationBests;
private int generationElites;
@@ -92,9 +52,9 @@
random = new Random();
generationNumber = 0;
settings = confSettings;
- bestMember = new Member(Long.MAX_VALUE, new PolygonData[0]);
+ bestMember = new GenerationMember(DefaultScore.MAX_SCORE, new PolygonData[0]);
eliteCutOff = Long.MAX_VALUE;
- generation = new ArrayList<Member>(settings.getGenerationSize() + 10);
+ generation = new ArrayList<GenerationMember>(settings.getGenerationSize() + 10);
annealingValue = settings.getStartTemperature() * settings.getStartTemperature() * imageSize.height * imageSize.width;
generationBests = 0;
generationElites = 0;
@@ -109,20 +69,20 @@
*
* @return whether this is the best polygon set so far
*/
- public ImprovementResult addPolygonData(long score, PolygonData[] polygonData) {
+ public ImprovementResult addPolygonData(Score score, PolygonData[] polygonData) {
synchronized(generation) {
- Member newMember = new Member(score, polygonData);
+ GenerationMember newMember = new GenerationMember(score, polygonData);
generation.add(newMember);
if (generation.size() >= settings.getGenerationSize()) {
processGeneration();
} else {
Collections.sort(generation);
}
- if (score < bestMember.score) {
+ if (score.getDelta() < bestMember.getScore().getDelta()) {
bestMember = newMember;
generationBests++;
return ImprovementResult.BEST;
- } else if (score < eliteCutOff) {
+ } else if (score.getDelta() < eliteCutOff) {
generationElites++;
return ImprovementResult.ELITE;
}
@@ -132,25 +92,25 @@
}
/**
- * pick a random polygon sample 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 polygon set
- */
- public PolygonData[] getRandomPolygonData(boolean elite) {
- synchronized(generation) {
- int size = elite ? (settings.getEliteSize() % generation.size()) : generation.size();
+ * 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).data;
- }
+ if (size == 0)
+ return null;
+
+ int r = random.nextInt(size);
+
+ int idx = (int)(r * ((double) r / (double) size));
+
+ return generation.get(idx);
+ }
}
/**
@@ -158,7 +118,7 @@
*
* @return the best polygon set
*/
- public Member getBestMember() {
+ public GenerationMember getBestMember() {
synchronized(generation) {
return bestMember;
}
@@ -167,25 +127,28 @@
private void processGeneration() {
int eliteSize = settings.getEliteSize();
- Collections.<GenerationHandler.Member>sort(generation);
+ Collections.<GenerationMember>sort(generation);
int sz = generation.size();
- List<Member> nextGeneration = new ArrayList<Member>(settings.getGenerationSize() + 10);
+ List<GenerationMember> nextGeneration = new ArrayList<GenerationMember>(settings.getGenerationSize() + 10);
for (int i = 0; i < eliteSize; i++) {
nextGeneration.add(generation.get(i));
}
if (settings.isUseAnnealing() && (annealingValue > 0.01)) {
- int annealingReplacements = 0;
+ 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;
- Member candidate = generation.get(candidateIndex);
- Member elite = generation.get(i);
- long delta = candidate.score - elite.score;
+ 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);
- annealingReplacements++;
+ if (Polycasso.DEBUG) {
+ annealingReplacements++;
+ }
}
}
@@ -196,10 +159,10 @@
generation = nextGeneration;
- eliteCutOff = generation.get(eliteSize-1).score;
+ 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).score);
+ System.out.println("Generation " + generationNumber + " had " + generationBests + " bests and " + generationElites + " elites. Best Score: " + generation.get(0).getScore());
}
generationBests = 0;
generationElites = 0;
Added: trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationMember.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationMember.java (rev 0)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationMember.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -0,0 +1,81 @@
+/*
+ * polycasso - Cubism Artwork generator
+ * Copyright 2009-2011 MeBigFatGuy.com
+ * Copyright 2009-2011 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.util.Arrays;
+
+/**
+ * class that holds a sample set of polygons and it's score
+ */
+public class GenerationMember implements Comparable<GenerationMember> {
+
+ private Score score;
+ private PolygonData[] data;
+
+ GenerationMember(Score polyScore, PolygonData[] polyData) {
+ score = polyScore;
+ data = polyData;
+ }
+
+ /**
+ * returns the score for this member
+ * @return the score
+ */
+ public Score getScore() {
+ return score;
+ }
+
+ /**
+ * returns the polygon data for this member
+ *
+ * @return the polygon data
+ */
+ public PolygonData[] getData() {
+ return data;
+ }
+
+ @Override
+ public int compareTo(GenerationMember o) {
+ long delta = score.getDelta() - o.score.getDelta();
+ if (delta > 0)
+ return 1;
+ else if (delta < 0)
+ return -1;
+
+ return data.length - o.data.length;
+ }
+
+ @Override
+ public int hashCode() {
+ return score.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof GenerationMember) {
+ return score.getDelta() == ((GenerationMember)o).score.getDelta();
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + score + ": " + Arrays.toString(data) + ")";
+ }
+}
\ No newline at end of file
Property changes on: trunk/polycasso/src/com/mebigfatguy/polycasso/GenerationMember.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:mimetype
+ text/plain
Added: svn:eol-style
+ native
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/ImageGenerator.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/ImageGenerator.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/ImageGenerator.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -28,21 +28,63 @@
public interface ImageGenerator {
+ /**
+ * starts up threads to start looking for images that are closest to the target
+ */
void startGenerating();
+ /**
+ * shuts down threads that were looking for images
+ */
void stopGenerating();
+ /**
+ * retrieves the scaled target iamge
+ *
+ * @return the target image
+ */
BufferedImage getTargetImage();
+ /**
+ * returns the image size that is being generated. This size might be different the original image
+ * if the size is bigger then the max setting.
+ *
+ * @return the image size
+ */
Dimension getImageSize();
+ /**
+ * retrieves the best set of polygons for drawing the image so far
+ *
+ * @return the best set of polygons
+ */
PolygonData[] getBestData();
+ /**
+ * completes the image by transforming the polygon image to the real image
+ */
void complete();
+ /**
+ * allows interested parties to register to receive events when a new best image has been
+ * found.
+ *
+ * @param listener the listener that is interested in events
+ */
void addImageGeneratedListener(ImageGeneratedListener listener);
+ /**
+ * allows uninterested parties to unregister to receive events when a new best image is
+ * found
+ *
+ * @param listener the listener that is no longer needed
+ */
void removeImageGeneratedListener(ImageGeneratedListener listener);
+ /**
+ * informs all listeners that a new best image has been found
+ *
+ * @param image the new best image
+ */
void fireImageGenerated(Image image);
}
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementResult.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementResult.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementResult.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -18,8 +18,20 @@
*/
package com.mebigfatguy.polycasso;
+/**
+ * an enum that describes the result of an attempted improvement on an image
+ */
public enum ImprovementResult {
+ /**
+ * this improvement is better than last generations best image
+ */
BEST,
+ /**
+ * this improvement is better than the worst elite image from last generation
+ */
ELITE,
+ /**
+ * this improvement didn't improve any on the elite set from last generation
+ */
FAIL;
}
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementType.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementType.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementType.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -74,11 +74,11 @@
* adjust the transparency of a random existing polygon
*/
ChangeAlpha,
- /*
+ /**
* change the color to white
*/
White,
- /*
+ /**
* change the color to black
*/
Black,
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementTypeStats.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementTypeStats.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/ImprovementTypeStats.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -29,7 +29,7 @@
*/
public class ImprovementTypeStats {
- private static final int MAX_FAILURE_RUN = 25;
+ private static final int MAX_FAILURE_RUN = 50;
private static class Stats {
public int successes = 1;
public int totals = 1;
Modified: trunk/polycasso/src/com/mebigfatguy/polycasso/Improver.java
===================================================================
--- trunk/polycasso/src/com/mebigfatguy/polycasso/Improver.java 2011-07-29 03:36:02 UTC (rev 242)
+++ trunk/polycasso/src/com/mebigfatguy/polycasso/Improver.java 2011-08-03 06:00:34 UTC (rev 243)
@@ -36,9 +36,11 @@
private Settings settings;
private GenerationHandler generationHandler;
private Dimension imageSize;
+ private Random r;
private List<PolygonData> polygons = null;
+ private Rectangle changedArea;
+ private GenerationMember changedMember;
private ImprovementTypeStats stats;
- private Random r;
/**
* create an improver using a specified image size
@@ -80,14 +82,11 @@
* @return the improvement type used to alter the data
*/
public ImprovementType improveRandomly() {
-
- {
- PolygonData[] randomData = generationHandler.getRandomPolygonData(false);
- if (randomData != null)
- polygons = new ArrayList<PolygonData>(Arrays.asList(randomData.clone()));
- else
- polygons = new ArrayList<PolygonData>();
- }
+ changedMember = generationHandler.getRandomMember(false);
+ if (changedMember != null)
+ polygons = new ArrayList<PolygonData>(Arrays.asList(changedMember.getData().clone()));
+ else
+ polygons = new ArrayList<PolygonData>();
ImprovementType type = (polygons.isEmpty()) ? ImprovementType.AddPolygon : stats.getRandomImprovementType();
@@ -96,6 +95,7 @@
if (polygons.size() < settings.getMaxPolygons()) {
PolygonData pd = PolygonData.randomPoly(imageSize, settings.getMaxPoints());
polygons.add(pd);
+ changedArea = pd.getPolygon().getBounds();
} else {
randomCompleteChange();
type = ImprovementType.CompleteChange;
@@ -106,6 +106,7 @@
case RemovePolygon: {
int idx = r.nextInt(polygons.size());
+ changedArea = polygons.get(idx).getPolygon().getBounds();
polygons.remove(idx);
}
break;
@@ -114,7 +115,8 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
- if (polygon.npoints <= settings.getMaxPoints()) {
+ changedArea = polygon.getBounds();
+ if (polygon.npoints < settings.getMaxPoints()) {
polygon.addPoint(0, 0);
int insPos = r.nextInt(polygon.npoints);
int lastPt = (insPos + polygon.npoints - 1) % polygon.npoints;
@@ -125,10 +127,13 @@
int x = r.nextInt(maxX) + Math.min(polygon.xpoints[lastPt], polygon.xpoints[insPos]);
int y = r.nextInt(maxY) + Math.min(polygon.ypoints[lastPt], polygon.ypoints[insPos]);
- System.arraycopy(polygon.xpoints, insPos, polygon.xpoints, insPos + 1, polygon.npoints - insPos - 1);
+ int numCopyPts = polygon.npoints - insPos - 1;
+ System.arraycopy(polygon.xpoints, insPos, polygon.xpoints, insPos + 1, numCopyPts);
polygon.xpoints[insPos] = x;
- System.arraycopy(polygon.ypoints, insPos, polygon.ypoints, insPos + 1, polygon.npoints - insPos - 1);
+ System.arraycopy(polygon.ypoints, insPos, polygon.ypoints, insPos + 1, numCopyPts);
polygon.ypoints[insPos] = y;
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
} else {
@@ -143,12 +148,16 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
+ changedArea = polygon.getBounds();
if (polygon.npoints > 3) {
int delPos = r.nextInt(polygon.npoints);
- System.arraycopy(polygon.xpoints, delPos+1, polygon.xpoints, delPos, polygon.npoints - delPos - 1);
- System.arraycopy(polygon.ypoints, delPos+1, polygon.ypoints, delPos, polygon.npoints - delPos - 1);
+ int numPtCopy = polygon.npoints - delPos - 1;
+ System.arraycopy(polygon.xpoints, delPos+1, polygon.xpoints, delPos, numPtCopy);
+ System.arraycopy(polygon.ypoints, delPos+1, polygon.ypoints, delPos, numPtCopy);
polygon.npoints--;
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
} else {
randomCompleteChange();
@@ -162,14 +171,18 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
+ changedArea = polygon.getBounds();
int movePos = r.nextInt(polygon.npoints);
int maxMovement = settings.getMaxPtMovement();
- int moveX = r.nextInt(maxMovement * 2) - maxMovement;
- int moveY = r.nextInt(maxMovement * 2) - maxMovement;
+ int dblMax = maxMovement << 1;
+ int moveX = r.nextInt(dblMax) - maxMovement;
+ int moveY = r.nextInt(dblMax) - maxMovement;
polygon.xpoints[movePos] += moveX;
polygon.ypoints[movePos] += moveY;
clipToRange(0, imageSize.width, polygon.xpoints[movePos]);
clipToRange(0, imageSize.height, polygon.ypoints[movePos]);
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
}
break;
@@ -178,6 +191,7 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
+ changedArea = polygon.getBounds();
int rectifyPos = r.nextInt(polygon.npoints);
int targetPos = (rectifyPos == 0) ? polygon.npoints - 1 : (rectifyPos - 1);
@@ -187,6 +201,8 @@
} else {
polygon.ypoints[rectifyPos] = polygon.ypoints[targetPos];
}
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
}
break;
@@ -194,6 +210,7 @@
case ReorderPoly: {
if (polygons.size() > 2) {
PolygonData pd = polygons.remove(r.nextInt(polygons.size()));
+ changedArea = pd.getPolygon().getBounds();
polygons.add(r.nextInt(polygons.size()), pd);
} else {
randomCompleteChange();
@@ -207,6 +224,7 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
+ changedArea = polygon.getBounds();
Rectangle bbox = polygon.getBounds();
double midX = bbox.getCenterX();
@@ -217,6 +235,8 @@
polygon.xpoints[i] += (polygon.xpoints[i] < midX) ? shrinkFactor : -shrinkFactor;
polygon.ypoints[i] += (polygon.ypoints[i] < midY) ? shrinkFactor : -shrinkFactor;
}
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
}
break;
@@ -225,8 +245,8 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
+ changedArea = polygon.getBounds();
Rectangle bbox = polygon.getBounds();
-
double midX = bbox.getCenterX();
double midY = bbox.getCenterY();
@@ -237,6 +257,8 @@
polygon.xpoints[i] = clipToRange(0, imageSize.width, polygon.xpoints[i]);
polygon.ypoints[i] = clipToRange(0, imageSize.height, polygon.ypoints[i]);
}
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
}
break;
@@ -245,15 +267,19 @@
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
Polygon polygon = pd.getPolygon();
+ changedArea = polygon.getBounds();
int maxMovement = settings.getMaxPtMovement();
- int shiftX = r.nextInt(2 * maxMovement) + maxMovement;
- int shiftY = r.nextInt(2 * maxMovement) + maxMovement;
+ int dblMax = maxMovement << 1;
+ int shiftX = r.nextInt(dblMax) + maxMovement;
+ int shiftY = r.nextInt(dblMax) + maxMovement;
for (int i = 0; i < polygon.npoints; i++) {
polygon.xpoints[i] += shiftX;
polygon.ypoints[i] += shiftY;
polygon.xpoints[i] = clipToRange(0, imageSize.width, polygon.xpoints[i]);
polygon.ypoints[i] = clipToRange(0, imageSize.height, polygon.ypoints[i]);
- }
+ }
+ polygon.invalidate();
+ changedArea.union(polygon.getBounds());
polygons.set(idx, pd);
}
break;
@@ -261,26 +287,28 @@
case ChangeColor: {
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
+ changedArea = pd.getPolygon().getBounds();
Color color = pd.getColor();
int comp = r.nextInt(3);
int maxChange = settings.getMaxColorChange();
+ int dblChange = maxChange << 1;
switch (comp) {
case 0: {
- int newColor = color.getRed() + (r.nextInt(2 * maxChange) - maxChange);
+ int newColor = color.getRed() + (r.nextInt(dblChange) - maxChange);
newColor = clipToRange(0, 255, newColor);
pd.setColor(new Color(newColor, color.getGreen(), color.getBlue()));
}
break;
case 1: {
- int newColor = color.getGreen() + (r.nextInt(2 * maxChange) - maxChange);
+ int newColor = color.getGreen() + (r.nextInt(dblChange) - maxChange);
newColor = clipToRange(0, 255, newColor);
pd.setColor(new Color(color.getRed(), newColor, color.getBlue()));
}
break;
case 2: {
- int newColor = color.getBlue() + (r.nextInt(2 * maxChange) - maxChange);
+ int newColor = color.getBlue() + (r.nextInt(dblChange) - maxChange);
newColor = clipToRange(0, 255, newColor);
pd.setColor(new Color(color.getRed(), color.getGreen(), newColor));
}
@@ -293,6 +321,7 @@
case White: {
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
+ changedArea = pd.getPolygon().getBounds();
pd.setColor(Color.WHITE);
pd.setAlpha(1);
polygons.set(idx, pd);
@@ -302,6 +331,7 @@
case Black: {
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
+ changedArea = pd.getPolygon().getBounds();
pd.setColor(Color.BLACK);
pd.setAlpha(1);
polygons.set(idx, pd);
@@ -311,35 +341,44 @@
case ChangeAlpha:
int idx = r.nextInt(polygons.size());
PolygonData pd = (PolygonData)polygons.get(idx).clone();
+ changedArea = pd.getPolygon().getBounds();
pd.setAlpha(r.nextFloat());
polygons.set(idx, pd);
break;
case Breed: {
- PolygonData[] copyData = generationHandler.getRandomPolygonData(false);
- if ((copyData == null) || (copyData.length == 0)) {
+ GenerationMember copyMember = generationHandler.getRandomMember(false);
+ if ((copyMember == null) || (copyMember.getData().length == 0)) {
randomCompleteChange();
} else {
+ PolygonData[] copyData = copyMember.getData();
idx = r.nextInt(copyData.length);
if (idx >= polygons.size()) {
polygons.add(copyData[idx]);
+ changedArea = copyData[idx].getPolygon().getBounds();
} else {
+ changedArea = polygons.get(idx).getPolygon().getBounds();
polygons.set(idx, copyData[idx]);
+ changedArea.union(polygons.get(idx).getPolygon().getBounds());
}
}
}
break;
case BreedElite: {
- PolygonData[] copyData = generationHandler.getRandomPolygonData(true);
- if ((copyData == null) || (copyData.length == 0)) {
+ GenerationMember copyMember = generationHandler.getRandomMember(true);
+ if ((copyMember == null) || (copyMember.getData().length == 0)) {
randomCompleteChange();
} else {
+ PolygonData[] copyData = copyMember.getData();
idx = r.nextInt(copyData.length);
if (idx >= polygons.size()) {
polygons.add(copyData[idx]);
+ changedArea = copyData[idx].getPolygon().getBounds();
} else {
+ changedArea = polygons.get(idx).getPolygon().getBounds();
polygons.set(idx, copyData[idx]);
+ changedArea.union(polygons.get(idx).getPolygon().getBounds());
}
}
}
@@ -355,11 +394,30 @@
}
/**
+ * returns the member image from which this generated image was created
+ *
+ * @return the parent generated image member
+ */
+ public GenerationMember getParentGenerationMember() {
+ return changedMember;
+ }
+
+ /**
+ * returns the area that was changed in this image
+ *
+ * @return the rectangular bounds that was changed, or null for a complete change
+ */
+ public Rectangle getChangedArea() {
+ return changedArea;
+ }
+
+ /**
* generates a random polygon change (all values)
*/
private void randomCompleteChange() {
int idx = r.nextInt(polygons.size());
polygons.set(idx, PolygonData.randomPoly(imageSize, settings.getMaxPoints()));
+ changedArea = null;
}
/**
Modified: trunk/polyca...
[truncated message content] |