/**
* 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.planet.gen;
import java.util.Random;
import jo.sm.data.SparseMatrix;
import jo.sm.data.StarMade;
import jo.sm.logic.PluginUtils;
import jo.sm.mods.IBlocksPlugin;
import jo.sm.mods.IPluginCallback;
import jo.sm.ship.data.Block;
import jo.vecmath.logic.MathUtils;
/**
* @Auther Jo Jaquinta for SMEdit Classic - version 1.0
**/
public class UndulatingPlugin implements IBlocksPlugin {
private static final int GRID = 8;
public static final String NAME = "Shape/Undulating";
public static final String DESC = "Gently rolling surface";
public static final String AUTH = "Jo Jaquinta";
public static final int[][] CLASSIFICATIONS
= {
{TYPE_PLANET, SUBTYPE_GENERATE, 26},};
@Override
public String getName() {
return NAME;
}
@Override
public String getDescription() {
return DESC;
}
@Override
public String getAuthor() {
return AUTH;
}
@Override
public Object newParameterBean() {
return new UndulatingParameters();
}
@Override
public void initParameterBean(SparseMatrix<Block> original, Object params,
StarMade sm, IPluginCallback cb) {
}
@Override
public int[][] getClassifications() {
return CLASSIFICATIONS;
}
@Override
public SparseMatrix<Block> modify(SparseMatrix<Block> original,
Object p, StarMade sm, IPluginCallback cb) {
UndulatingParameters params = (UndulatingParameters) p;
HexParams hParams = makeParams(params);
cb.setStatus("Generating terrain");
cb.startTask(hParams.fillTarget);
while (hParams.fillTarget > 0) {
fillAndMove(hParams);
cb.workTask(1);
}
cb.endTask();
findBounds(hParams);
fillGrid(hParams, params, cb);
return hParams.grid;
}
private void fillGrid(HexParams hparams, UndulatingParameters params, IPluginCallback cb) {
cb.setStatus("Filling in terrain");
cb.startTask(hparams.fieldWidth);
for (int fieldX = 0; fieldX < hparams.fieldWidth - 1; fieldX++) {
cb.workTask(1);
for (int fieldY = 0; fieldY < hparams.fieldHeight - 1; fieldY++) {
fillChunk(fieldX, fieldY, hparams, params);
}
}
cb.endTask();
}
private void fillChunk(int fieldX, int fieldY, HexParams hparams, UndulatingParameters params) {
int[] upperLeft = getFieldPosition(fieldX, fieldY, hparams, params);
int upperLeftHeight = hparams.field[fieldX][fieldY];
int upperRightHeight = hparams.field[fieldX + 1][fieldY];
int lowerLeftHeight = hparams.field[fieldX][fieldY + 1];
int[] lowerRight = getFieldPosition(fieldX + 1, fieldY + 1, hparams, params);
int lowerRightHeight = hparams.field[fieldX + 1][fieldY + 1];
for (int y = upperLeft[1]; y < lowerRight[1]; y++) {
int leftHeight = (int) MathUtils.interpolate(y, upperLeft[1], lowerRight[1], upperLeftHeight, lowerLeftHeight);
int rightHeight = (int) MathUtils.interpolate(y, upperLeft[1], lowerRight[1], upperRightHeight, lowerRightHeight);
for (int x = upperLeft[0]; x < lowerRight[0]; x++) {
int height = (int) MathUtils.interpolate(x, upperLeft[0], lowerRight[0], leftHeight, rightHeight);
fillColumn(x, y, height, hparams, params);
}
}
}
private void fillColumn(int x, int y, int hexHeight, HexParams hparams, UndulatingParameters params) {
double r = Math.sqrt(x * x + y * y);
if (r > params.getPlanetRadius()) {
return; // out of radius
}
int columnHeight = (int) MathUtils.interpolate(hexHeight, hparams.elevLow, hparams.elevHigh, 0, params.getPlanetHeight());
PluginUtils.fill(hparams.grid, x, 0, y, x, columnHeight, y, params.getFillWith(), 0);
}
private int[] getFieldPosition(int fieldX, int fieldY, HexParams hparams, UndulatingParameters params) {
int blockX = fieldX * GRID - params.getPlanetRadius();
int blockY = fieldY * GRID - params.getPlanetRadius();
return new int[]{blockX, blockY};
}
private void findBounds(HexParams hparams) {
hparams.elevHigh = hparams.field[0][0];
hparams.elevLow = hparams.field[0][0];
for (int y = 0; y < hparams.fieldHeight; y++) {
for (int x = 0; x < hparams.fieldWidth; x++) {
hparams.elevHigh = Math.max(hparams.elevHigh, hparams.field[x][y]);
hparams.elevLow = Math.min(hparams.elevLow, hparams.field[x][y]);
}
}
}
private void fillAndMove(HexParams hparams) {
// fill
fill(hparams, 0, 0, 8);
fill(hparams, 0, -1, 4);
fill(hparams, 0, 1, 4);
fill(hparams, -1, 0, 4);
fill(hparams, 1, 0, 4);
if (hparams.fillXY[0] % 2 == 0) {
fill(hparams, -1, -1, 4);
fill(hparams, 1, -1, 4);
} else {
fill(hparams, -1, 1, 4);
fill(hparams, 1, 1, 4);
}
fill(hparams, 0, -2, 2);
fill(hparams, 0, 2, 2);
fill(hparams, -2, 0, 2);
fill(hparams, 2, 0, 2);
fill(hparams, -2, -1, 2);
fill(hparams, -2, 1, 2);
fill(hparams, 2, -1, 2);
fill(hparams, 2, 1, 2);
if (hparams.fillXY[0] % 2 == 0) {
fill(hparams, -1, -1, 2);
fill(hparams, -1, 2, 2);
fill(hparams, 1, -1, 2);
fill(hparams, 1, 2, 2);
} else {
fill(hparams, -1, -2, 2);
fill(hparams, -1, 1, 2);
fill(hparams, 1, -2, 2);
fill(hparams, 1, 1, 2);
}
// move
switch (hparams.rnd.nextInt(6)) {
case 0:
hparams.fillXY[1]--;
break;
case 1:
hparams.fillXY[1]++;
break;
case 2:
hparams.fillXY[0]--;
break;
case 3:
hparams.fillXY[0]++;
break;
case 4:
hparams.fillXY[0]++;
if (hparams.fillXY[0] % 2 == 0) {
hparams.fillXY[1]--;
} else {
hparams.fillXY[1]++;
}
break;
case 5:
hparams.fillXY[0]--;
if (hparams.fillXY[0] % 2 == 0) {
hparams.fillXY[1]--;
} else {
hparams.fillXY[1]++;
}
break;
}
normalize(hparams, hparams.fillXY);
hparams.fillTarget--;
}
private void fill(HexParams hparams, int dx, int dy, int amnt) {
int[] xy = new int[]{hparams.fillXY[0] + dx, hparams.fillXY[1] + dy};
normalize(hparams, xy);
hparams.field[xy[0]][xy[1]] += amnt;
}
private void normalize(HexParams hparams, int[] xy) {
if (xy[0] < 0) {
xy[0] += hparams.fieldWidth;
} else if (xy[0] >= hparams.fieldWidth) {
xy[0] -= hparams.fieldWidth;
}
if (xy[1] < 0) {
xy[1] += hparams.fieldHeight;
} else if (xy[1] >= hparams.fieldHeight) {
xy[1] -= hparams.fieldHeight;
}
}
private HexParams makeParams(UndulatingParameters params) {
HexParams hparams = new HexParams();
hparams.grid = new SparseMatrix<Block>();
hparams.fieldWidth = params.getPlanetRadius() * 2 / GRID + 1;
hparams.fieldHeight = params.getPlanetRadius() * 2 / GRID + 1;
hparams.field = new int[hparams.fieldWidth][hparams.fieldHeight];
hparams.fillTarget = hparams.fieldWidth * hparams.fieldHeight * 2;
hparams.rnd = new Random();
hparams.fillXY = new int[]{hparams.rnd.nextInt(hparams.fieldWidth), hparams.rnd.nextInt(hparams.fieldHeight)};
return hparams;
}
class HexParams {
SparseMatrix<Block> grid;
int fieldWidth;
int fieldHeight;
int[][] field;
int fillTarget;
Random rnd;
int[] fillXY;
int elevLow;
int elevHigh;
}
}