/**
* Copyright 2014
* SMEdit https://github.com/StarMade/SMEdit
* SMTools https://github.com/StarMade/SMTools
*
* 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 jo.sm.plugins.ship.imp;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import jo.sm.data.BlockTypes;
import jo.sm.data.SparseMatrix;
import jo.sm.logic.StarMadeLogic;
import jo.sm.logic.utils.IntegerUtils;
import jo.sm.logic.utils.ResourceUtils;
import jo.sm.logic.utils.ShortUtils;
import jo.sm.logic.utils.XMLUtils;
import jo.sm.mods.IPluginCallback;
import jo.sm.ship.data.Block;
import jo.sm.ship.logic.ShipLogic;
import jo.sm.ui.BlockTypeColors;
import jo.util.Paths;
import jo.vecmath.Color3f;
import jo.vecmath.Point2f;
import jo.vecmath.Point3f;
import jo.vecmath.Point3i;
import jo.vecmath.ext.Hull3f;
import jo.vecmath.ext.Triangle3f;
import jo.vecmath.logic.MathUtils;
import jo.vecmath.logic.Point2fLogic;
import jo.vecmath.logic.Point3fLogic;
import jo.vecmath.logic.Point3iLogic;
import jo.vecmath.logic.ext.Hull3fLogic;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* @Auther Jo Jaquinta for SMEdit Classic - version 1.0
**/
public class PlotLogic {
private static short[] HULL_IDS = null;
private static int[] HULL_RGBS = null;
private static long mLastRead = 0;
private static final Logger log = Logger.getLogger(PlotLogic.class.getName());
public static void mapHull(SparseMatrix<Block> modified, Hull3f hull,
Point3f scale, Point3i lowerGrid, Point3i upperGrid, IPluginCallback cb) {
Point3i center;
center = Point3iLogic.interpolate(lowerGrid, upperGrid, .5f);
cb.startTask(hull.getTriangles().size());
for (Triangle3f t : hull.getTriangles()) {
Point3i iA = mapPoint(t.getA(), scale, center);
Point3i iB = mapPoint(t.getB(), scale, center);
Point3i iC = mapPoint(t.getC(), scale, center);
if ((t.getAUV() != null) && (t.getBUV() != null) && (t.getCUV() != null) && (t.getTexture() != null)) {
drawTriangle(modified, iA, iB, iC, t.getAUV(), t.getBUV(), t.getCUV(), t.getTexture());
} else if (t.getColor() != null) {
short color;
color = mapColor(t.getColor());
drawTriangle(modified, iA, iB, iC, color);
} else {
drawTriangle(modified, iA, iB, iC, BlockTypes.HULL_COLOR_GREY_ID);
}
cb.workTask(1);
}
ShipLogic.ensureCore(modified);
cb.endTask();
}
private static short uvToColor(Point2f uv, BufferedImage img) {
uv.x -= Math.floor(uv.x);
uv.y -= Math.floor(uv.y);
int x;
x = (int) MathUtils.interpolate(uv.x, 0, 1, 0, img.getWidth() - 1);
int y;
y = (int) MathUtils.interpolate(uv.y, 0, 1, 0, img.getHeight() - 1);
int rgb;
rgb = img.getRGB(x, y);
return mapColor(rgb);
}
private static Point3i mapPoint(Point3f f, Point3f scale, Point3i center) {
Point3f fA;
fA = new Point3f(f);
fA = Point3fLogic.scale(fA, scale);
Point3i iA;
iA = new Point3i(fA);
iA.sub(center);
iA.x += 8;
iA.y += 8;
iA.z += 8;
return iA;
}
public static void drawTriangle(SparseMatrix<Block> grid, Point3i a, Point3i b, Point3i c, short color) {
// Brute force and ignorance method
// Anything smarter seems to leave holes
doDrawTriangle(grid, a, b, c, color);
doDrawTriangle(grid, b, c, a, color);
doDrawTriangle(grid, c, a, b, color);
}
private static void doDrawTriangle(SparseMatrix<Block> grid,
Point3i fulcrum, Point3i target1, Point3i target2, short color) {
List<Point3i> iterpolate;
iterpolate = new ArrayList<>();
plotLine(target1, target2, iterpolate);
Set<Point3i> area;
area = new HashSet<>();
//log.log(Level.INFO, "interpolate over");
for (Point3i i : iterpolate) {
//log.log(Level.INFO, " "+i);
plotLine(i, fulcrum, area);
}
plotArea(grid, area, color);
}
public static void drawTriangle(SparseMatrix<Block> grid, Point3i a, Point3i b, Point3i c, Point2f auv, Point2f buv, Point2f cuv, BufferedImage img) {
// Brute force and ignorance method
// Anything smarter seems to leave holes
doDrawTriangle(grid, a, b, c, auv, buv, cuv, img);
doDrawTriangle(grid, b, c, a, buv, cuv, auv, img);
doDrawTriangle(grid, c, a, b, cuv, auv, buv, img);
}
private static void doDrawTriangle(SparseMatrix<Block> grid,
Point3i fulcrum, Point3i target1, Point3i target2,
Point2f fulcrumUV, Point2f target1UV, Point2f target2UV,
BufferedImage img) {
List<Point3i> iterpolate;
iterpolate = new ArrayList<>();
plotLine(target1, target2, iterpolate);
for (int i = 0; i < iterpolate.size(); i++) {
Point3i p = iterpolate.get(i);
Point2f puv = Point2fLogic.interpolate(target1UV, target2UV, i / (float) (iterpolate.size() - 1));
drawLine(grid, p, fulcrum, puv, fulcrumUV, img);
}
}
private static void plotArea(SparseMatrix<Block> grid, Collection<Point3i> area,
short color) {
for (Point3i p : area) {
grid.set(p, new Block(color));
}
}
public static void drawLine(SparseMatrix<Block> grid, Point3i a, Point3i b, short color) {
List<Point3i> plot;
plot = new ArrayList<>();
plotLine(a, b, plot);
plotArea(grid, plot, color);
}
public static void drawLine(SparseMatrix<Block> grid, Point3i a, Point3i b, Point2f auv, Point2f buv, BufferedImage img) {
List<Point3i> plot;
plot = new ArrayList<>();
plotLine(a, b, plot);
for (int i = 0; i < plot.size(); i++) {
Point3i p = plot.get(i);
Point2f uv = Point2fLogic.interpolate(auv, buv, i / (float) (plot.size() - 1));
short color;
color = uvToColor(uv, img);
grid.set(p, new Block(color));
}
}
private static void plotLine(Point3i a, Point3i b, Collection<Point3i> plot) {
Point3f vector;
vector = new Point3f(b);
Point3f p;
p = new Point3f(a);
vector.sub(p);
float mag;
mag = Point3fLogic.mag(vector);
int steps;
steps = (int) mag;
vector.scale(1 / mag);
Point3i last;
last = null;
for (int i = 0; i < steps; i++) {
last = new Point3i(p);
plot.add(last);
p.add(vector);
}
if (!b.equals(last)) {
plot.add(new Point3i(b));
}
}
public static float getScale(Hull3f hull, int longestDimension, Point3i lowerGrid, Point3i upperGrid, Point3i offset) {
Point3f lowerModel;
lowerModel = new Point3f();
Point3f upperModel;
upperModel = new Point3f();
Hull3fLogic.getBounds(hull, lowerModel, upperModel);
//log.log(Level.INFO, "Model Bounds: " + lowerModel + " -- " + upperModel);
float longestModel;
longestModel = Math.max(upperModel.x - lowerModel.x, Math.max(upperModel.y - lowerModel.y, upperModel.z - lowerModel.z));
float scale;
scale = longestDimension / longestModel;
//log.log(Level.INFO, "Scale: " + scale + " (" + longestDimension + "/" + longestModel);
lowerGrid.x = (int) Math.floor(lowerModel.x * scale);
lowerGrid.y = (int) Math.floor(lowerModel.y * scale);
lowerGrid.z = (int) Math.floor(lowerModel.z * scale);
upperGrid.x = (int) Math.ceil(upperModel.x * scale);
upperGrid.y = (int) Math.ceil(upperModel.y * scale);
upperGrid.z = (int) Math.ceil(upperModel.z * scale);
//log.log(Level.INFO, "Grid Bounds: " + lowerGrid + " -- " + upperGrid);
return scale;
}
public static int distance(int rgb1, int rgb2) {
float [] buffer1 = new float[3];
float [] buffer2 = new float[3];
Color c1 = new Color(rgb1);
Color c2 = new Color(rgb2);
c1.getRGBColorComponents(buffer1);
c2.getRGBColorComponents(buffer2);
int distSquare = 0;
for (int i = 0; i < 3; i++) {
distSquare += (buffer1[i] - buffer2[i]) * (buffer1[i] - buffer2[i]);
}
return distSquare;
}
private static void loadColors() {
File plugins = new File(Paths.getPluginsDirectory());
File colorMap;
colorMap = new File(plugins, "color_map.xml");
if (colorMap.exists()) {
if (colorMap.lastModified() <= mLastRead) {
return;
}
try {
readColorFile(new FileInputStream(colorMap));
mLastRead = colorMap.lastModified();
return;
} catch (FileNotFoundException e) {
log.log(Level.WARNING, "File Input Stream failed!", e);
}
}
if (HULL_IDS == null) {
readColorFile(ResourceUtils.loadSystemResourceStream("color_map.xml", PlotLogic.class));
}
}
private static void readColorFile(InputStream is) {
Document doc;
doc = XMLUtils.readStream(is);
if (doc == null) {
return;
}
List<Short> hullIDs;
hullIDs = new ArrayList<>();
List<Integer> hullRGBs;
hullRGBs = new ArrayList<>();
for (Node n : XMLUtils.findNodes(doc, "colors/color")) {
int rgb;
rgb = Integer.parseInt(XMLUtils.getAttribute(n, "rgb"), 16);
short block;
String smBlock;
smBlock = XMLUtils.getAttribute(n, "block");
if (BlockTypeColors.mBlockTypes.containsKey(smBlock)) {
block = ShortUtils.parseShort(BlockTypeColors.mBlockTypes.getProperty(smBlock));
} else {
block = ShortUtils.parseShort(smBlock);
}
hullIDs.add(block);
hullRGBs.add(rgb);
}
HULL_IDS = ShortUtils.toShortArray(hullIDs.toArray());
HULL_RGBS = IntegerUtils.toArray(hullRGBs.toArray());
}
public static short mapColor(int rgb) {
loadColors();
int best;
best = 0;
int value;
value = distance(rgb, HULL_RGBS[best]);
for (int i = 1; i < HULL_RGBS.length; i++) {
int v;
v = distance(rgb, HULL_RGBS[i]);
if (v < value) {
best = i;
value = v;
}
}
return HULL_IDS[best];
}
public static short mapColor(Color3f c) {
int r;
r = (int) (c.x * 255);
int g;
g = (int) (c.y * 255);
int b;
b = (int) (c.z * 255);
int rgb;
rgb = (r << 16) | (g << 8) | (b);
short color;
color = mapColor(rgb);
//log.log(Level.INFO, c+" -> "+r+","+g+","+b+" -> "+Integer.toHexString(rgb)+" -> "+color);
return color;
}
public static Hull3f makeTorus(float resolution) {
Hull3f torus;
torus = new Hull3f();
int longSteps;
longSteps = (int) (2 * Math.PI * 1 / resolution);
float longIncrement;
longIncrement = (float) (2 * Math.PI / longSteps);
int shortSteps;
shortSteps = (int) (2 * Math.PI * .25 / resolution);
float shortIncrement;
shortIncrement = (float) (2 * Math.PI / shortSteps);
for (int longSegment = 0; longSegment < longSteps; longSegment++) {
Point3f longCenter1;
longCenter1 = new Point3f(.75f, 0, 0);
Point3fLogic.rotateBy(longCenter1, 0, longSegment * longIncrement, 0);
Point3f longCenter2;
longCenter2 = new Point3f(.75f, 0, 0);
Point3fLogic.rotateBy(longCenter2, 0, (longSegment + 1) * longIncrement, 0);
for (int shortSegment = 0; shortSegment < shortSteps; shortSegment++) {
Point3f shortSpur11;
shortSpur11 = new Point3f(.25f, 0, 0);
Point3fLogic.rotateBy(shortSpur11, 0, 0, shortSegment * shortIncrement);
Point3fLogic.rotateBy(shortSpur11, 0, longSegment * longIncrement, 0);
shortSpur11.add(longCenter1);
Point3f shortSpur12;
shortSpur12 = new Point3f(.25f, 0, 0);
Point3fLogic.rotateBy(shortSpur12, 0, 0, (shortSegment + 1) * shortIncrement);
Point3fLogic.rotateBy(shortSpur12, 0, longSegment * longIncrement, 0);
shortSpur12.add(longCenter1);
Point3f shortSpur21;
shortSpur21 = new Point3f(.25f, 0, 0);
Point3fLogic.rotateBy(shortSpur21, 0, 0, shortSegment * shortIncrement);
Point3fLogic.rotateBy(shortSpur21, 0, longSegment * longIncrement, 0);
shortSpur21.add(longCenter2);
Point3f shortSpur22;
shortSpur22 = new Point3f(.25f, 0, 0);
Point3fLogic.rotateBy(shortSpur22, 0, 0, (shortSegment + 1) * shortIncrement);
Point3fLogic.rotateBy(shortSpur22, 0, longSegment * longIncrement, 0);
shortSpur22.add(longCenter2);
addTriangle(torus, shortSpur11, shortSpur12, shortSpur22, resolution);
addTriangle(torus, shortSpur11, shortSpur21, shortSpur22, resolution);
}
}
return torus;
}
public static Hull3f makeSphere(float resolution, boolean half) {
Hull3f sphere;
sphere = new Hull3f();
Point3f fore;
fore = new Point3f(0, 0, 1);
Point3f aft;
aft = new Point3f(0, 0, -1);
Point3f dorsal;
dorsal = new Point3f(0, 1, 0);
Point3f ventral;
ventral = new Point3f(0, -1, 0);
Point3f starboard;
starboard = new Point3f(1, 0, 0);
Point3f port;
port = new Point3f(-1, 0, 0);
if (!half) {
addTriangle(sphere, fore, port, dorsal, resolution);
addTriangle(sphere, port, aft, dorsal, resolution);
addTriangle(sphere, aft, starboard, dorsal, resolution);
addTriangle(sphere, starboard, fore, dorsal, resolution);
}
addTriangle(sphere, fore, port, ventral, resolution);
addTriangle(sphere, port, aft, ventral, resolution);
addTriangle(sphere, aft, starboard, ventral, resolution);
addTriangle(sphere, starboard, fore, ventral, resolution);
return sphere;
}
private static void addTriangle(Hull3f sphere, Point3f p1, Point3f p2,
Point3f p3, float resolution) {
//log.log(Level.INFO, "Adding "+p1+", "+p2+", "+p3);
if ((p1.distance(p2) < resolution) || (p2.distance(p3) < resolution) || (p3.distance(p1) < resolution)) {
Triangle3f t;
t = new Triangle3f(p1, p2, p3);
sphere.getTriangles().add(t);
} else {
Point3f p12;
p12 = new Point3f(p1);
p12.add(p2);
Point3fLogic.normalize(p12);
Point3f p23;
p23 = new Point3f(p2);
p23.add(p3);
Point3fLogic.normalize(p23);
Point3f p31;
p31 = new Point3f(p3);
p31.add(p1);
Point3fLogic.normalize(p31);
addTriangle(sphere, p1, p12, p31, resolution);
addTriangle(sphere, p2, p23, p12, resolution);
addTriangle(sphere, p3, p31, p23, resolution);
addTriangle(sphere, p12, p23, p31, resolution);
}
}
}