/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.optaplanner.examples.tsp.persistence;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.optaplanner.examples.common.persistence.AbstractPngSolutionImporter;
import org.optaplanner.examples.tsp.domain.Domicile;
import org.optaplanner.examples.tsp.domain.TspSolution;
import org.optaplanner.examples.tsp.domain.Visit;
import org.optaplanner.examples.tsp.domain.location.AirLocation;
import org.optaplanner.examples.tsp.domain.location.DistanceType;
import org.optaplanner.examples.tsp.domain.location.Location;
public class TspImageStipplerImporter extends AbstractPngSolutionImporter<TspSolution> {
private static final double GRAY_MAXIMUM = 256.0 * 3.0;
public static void main(String[] args) {
new TspImageStipplerImporter().convertAll();
}
public TspImageStipplerImporter() {
super(new TspDao());
}
public TspImageStipplerImporter(boolean withoutDao) {
super(withoutDao);
}
@Override
public PngInputBuilder<TspSolution> createPngInputBuilder() {
return new TspImageStipplerInputBuilder();
}
public static class TspImageStipplerInputBuilder extends PngInputBuilder<TspSolution> {
private TspSolution tspSolution;
private int locationListSize;
@Override
public TspSolution readSolution() throws IOException {
tspSolution = new TspSolution();
tspSolution.setId(0L);
tspSolution.setName(FilenameUtils.getBaseName(inputFile.getName()));
floydSteinbergDithering();
createVisitList();
BigInteger possibleSolutionSize = factorial(tspSolution.getLocationList().size() - 1);
logger.info("TspSolution {} has {} locations with a search space of {}.",
getInputId(),
tspSolution.getLocationList().size(),
getFlooredPossibleSolutionSize(possibleSolutionSize));
return tspSolution;
}
/**
* As described by <a href="https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering">Floyd-Steinberg dithering</a>.
*/
private void floydSteinbergDithering() {
tspSolution.setDistanceType(DistanceType.AIR_DISTANCE);
tspSolution.setDistanceUnitOfMeasurement("distance");
int width = image.getWidth();
int height = image.getHeight();
double[][] errorDiffusion = new double[width][height];
List<Location> locationList = new ArrayList<>(1000);
long id = 0L;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = image.getRGB(x, y);
int r = (rgb) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb >> 16) & 0xFF;
double originalGray = (r + g + b) / GRAY_MAXIMUM;
double diffusedGray = originalGray + errorDiffusion[x][y];
double error;
if (diffusedGray <= 0.5) {
Location location = new AirLocation();
location.setId(id);
id++;
location.setLatitude(- y);
location.setLongitude(x);
locationList.add(location);
error = diffusedGray;
} else {
error = diffusedGray - 1.0;
}
if (x + 1 < width) {
errorDiffusion[x + 1][y] += error * 7.0 / 16.0;
}
if (y + 1 < height) {
if (x - 1 >= 0) {
errorDiffusion[x - 1][y + 1] += error * 3.0 / 16.0;
}
errorDiffusion[x][y + 1] += error * 5.0 / 16.0;
if (x + 1 < width) {
errorDiffusion[x + 1][y + 1] += error * 1.0 / 16.0;
}
}
}
}
tspSolution.setLocationList(locationList);
}
private void createVisitList() {
List<Location> locationList = tspSolution.getLocationList();
List<Visit> visitList = new ArrayList<>(locationList.size() - 1);
int count = 0;
for (Location location : locationList) {
if (count < 1) {
Domicile domicile = new Domicile();
domicile.setId(location.getId());
domicile.setLocation(location);
tspSolution.setDomicile(domicile);
} else {
Visit visit = new Visit();
visit.setId(location.getId());
visit.setLocation(location);
// Notice that we leave the PlanningVariable properties on null
visitList.add(visit);
}
count++;
}
tspSolution.setVisitList(visitList);
}
}
}