Removed bad libraries, added LWJGL and Slick-Util, added
mightypork.utils, some work on the framework.
This commit is contained in:
@@ -0,0 +1,417 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileLock;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import mightypork.rogue.input.Keys;
|
||||
import mightypork.rogue.screens.ScreenSplash;
|
||||
import mightypork.rogue.sounds.SoundManager;
|
||||
import mightypork.rogue.threads.ThreadSaveScreenshot;
|
||||
import mightypork.rogue.threads.ThreadScreenshotTrigger;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.logging.LogInstance;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.time.TimerDelta;
|
||||
import mightypork.utils.time.TimerInterpolating;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
|
||||
|
||||
public class App {
|
||||
|
||||
/** instance */
|
||||
public static App inst;
|
||||
/** Current delta time (secs since last render) */
|
||||
public static double currentDelta = 0;
|
||||
|
||||
private static DisplayMode windowDisplayMode = null;
|
||||
|
||||
/** current screen */
|
||||
public static Screen screen = null;
|
||||
|
||||
/** Flag that screenshot is scheduled to be taken next loop */
|
||||
public static boolean scheduledScreenshot = false;
|
||||
|
||||
|
||||
private static boolean lockInstance()
|
||||
{
|
||||
if (Config.SINGLE_INSTANCE == false) return true; // bypass lock
|
||||
|
||||
final File lockFile = new File(Paths.WORKDIR, ".lock");
|
||||
try {
|
||||
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
|
||||
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
|
||||
if (fileLock != null) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
fileLock.release();
|
||||
randomAccessFile.close();
|
||||
lockFile.delete();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to remove lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to create and/or lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is if FS
|
||||
*
|
||||
* @return is in fs
|
||||
*/
|
||||
public static boolean isFullscreen()
|
||||
{
|
||||
return Display.isFullscreen();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
|
||||
|
||||
inst = new App();
|
||||
|
||||
try {
|
||||
inst.start();
|
||||
} catch (Throwable t) {
|
||||
onCrash(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show crash report dialog with error stack trace.
|
||||
*
|
||||
* @param error
|
||||
*/
|
||||
public static void onCrash(Throwable error)
|
||||
{
|
||||
Log.e("The game has crashed.");
|
||||
|
||||
Log.e(error);
|
||||
|
||||
try {
|
||||
inst.deinit();
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quit to OS
|
||||
*/
|
||||
public void exit()
|
||||
{
|
||||
deinit();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get current screen
|
||||
*
|
||||
* @return screen
|
||||
*/
|
||||
public Screen getScreen()
|
||||
{
|
||||
return screen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get screen size
|
||||
*
|
||||
* @return size
|
||||
*/
|
||||
public Coord getSize()
|
||||
{
|
||||
return new Coord(Display.getWidth(), Display.getHeight());
|
||||
}
|
||||
|
||||
|
||||
private void init() throws LWJGLException
|
||||
{
|
||||
// initialize main logger
|
||||
LogInstance li = Log.create("runtime", Paths.LOGS, 10);
|
||||
li.enable(Config.LOGGING_ENABLED);
|
||||
li.enableSysout(Config.LOG_TO_STDOUT);
|
||||
|
||||
// initialize display
|
||||
Display.setDisplayMode(windowDisplayMode = new DisplayMode(Const.WINDOW_SIZE_X, Const.WINDOW_SIZE_Y));
|
||||
Display.setResizable(true);
|
||||
Display.setVSyncEnabled(true);
|
||||
Display.setTitle(Const.TITLEBAR);
|
||||
Display.create();
|
||||
|
||||
if (Config.START_IN_FS) {
|
||||
switchFullscreen();
|
||||
Display.update();
|
||||
}
|
||||
|
||||
// initialize inputs
|
||||
Mouse.create();
|
||||
Keyboard.create();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
|
||||
// initialize sound system
|
||||
SoundManager.get().setListener(Const.LISTENER_POS);
|
||||
SoundManager.get().setMasterVolume(1F);
|
||||
|
||||
// start async screenshot trigger listener
|
||||
(new ThreadScreenshotTrigger()).start();
|
||||
}
|
||||
|
||||
|
||||
private void start() throws LWJGLException
|
||||
{
|
||||
|
||||
if (!lockInstance()) {
|
||||
System.out.println("Working directory is locked.\nOnly one instance can run at a time.");
|
||||
|
||||
//@formatter:off
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
"The game is already running.",
|
||||
"Instance error",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
//@formatter:on
|
||||
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
init();
|
||||
mainLoop();
|
||||
deinit();
|
||||
}
|
||||
|
||||
|
||||
private void deinit()
|
||||
{
|
||||
Display.destroy();
|
||||
Mouse.destroy();
|
||||
Keyboard.destroy();
|
||||
SoundManager.get().destroy();
|
||||
AL.destroy();
|
||||
}
|
||||
|
||||
/** timer */
|
||||
private TimerDelta timerRender;
|
||||
private TimerInterpolating timerGui;
|
||||
|
||||
private int timerAfterResize = 0;
|
||||
|
||||
|
||||
private void mainLoop()
|
||||
{
|
||||
screen = new ScreenSplash();
|
||||
|
||||
screen.init();
|
||||
|
||||
timerRender = new TimerDelta();
|
||||
timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE);
|
||||
|
||||
while (!Display.isCloseRequested()) {
|
||||
glLoadIdentity();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
timerGui.sync();
|
||||
|
||||
int ticks = timerGui.getSkipped();
|
||||
|
||||
if (ticks >= 1) {
|
||||
screen.updateGui();
|
||||
timerGui.startNewFrame();
|
||||
}
|
||||
|
||||
currentDelta = timerRender.getDelta();
|
||||
|
||||
// RENDER
|
||||
screen.render(currentDelta);
|
||||
SoundManager.get().update(currentDelta);
|
||||
|
||||
Display.update();
|
||||
|
||||
if (scheduledScreenshot) {
|
||||
takeScreenshot();
|
||||
scheduledScreenshot = false;
|
||||
}
|
||||
|
||||
if (Keys.justPressed(Keyboard.KEY_F11)) {
|
||||
Log.f2("F11, toggle fullscreen.");
|
||||
switchFullscreen();
|
||||
screen.onFullscreenChange();
|
||||
Keys.destroyChangeState(Keyboard.KEY_F11);
|
||||
}
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_Q)) {
|
||||
Log.f2("Ctrl+Q, force quit.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_Q);
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
// if (Keyboard.isKeyDown(Keyboard.KEY_M)) {
|
||||
// Log.f2("Ctrl+M, go to main menu.");
|
||||
// Keys.destroyChangeState(Keyboard.KEY_M);
|
||||
// replaceScreen(new ScreenMenuMain());
|
||||
// }
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_F)) {
|
||||
Log.f2("Ctrl+F, switch fullscreen.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_F);
|
||||
switchFullscreen();
|
||||
screen.onFullscreenChange();
|
||||
}
|
||||
}
|
||||
|
||||
if (Display.wasResized()) {
|
||||
screen.onWindowResize();
|
||||
timerAfterResize = 0;
|
||||
} else { // ensure screen has even size
|
||||
timerAfterResize++;
|
||||
if (timerAfterResize > Const.FPS_GUI_UPDATE * 0.3) {
|
||||
timerAfterResize = 0;
|
||||
int x = Display.getX();
|
||||
int y = Display.getY();
|
||||
|
||||
int w = Display.getWidth();
|
||||
int h = Display.getHeight();
|
||||
if (w % 2 != 0 || h % 2 != 0) {
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode = new DisplayMode(w - w % 2, h - h % 2));
|
||||
screen.onWindowResize();
|
||||
Display.setLocation(x, y);
|
||||
} catch (LWJGLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Display.sync(Const.FPS_RENDER);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Your graphics card driver does not support fullscreen properly.", t);
|
||||
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
} catch (LWJGLException e) {
|
||||
Log.e("Error going back from corrupted fullscreen.");
|
||||
onCrash(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// UPDATE LOOP END
|
||||
|
||||
/**
|
||||
* Do take a screenshot
|
||||
*/
|
||||
public void takeScreenshot()
|
||||
{
|
||||
//Effects.play("gui.screenshot");
|
||||
|
||||
glReadBuffer(GL_FRONT);
|
||||
int width = Display.getDisplayMode().getWidth();
|
||||
int height = Display.getDisplayMode().getHeight();
|
||||
int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
(new ThreadSaveScreenshot(buffer, width, height, bpp)).start();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace screen
|
||||
*
|
||||
* @param newScreen new screen
|
||||
*/
|
||||
public void replaceScreen(Screen newScreen)
|
||||
{
|
||||
screen = newScreen;
|
||||
screen.init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace screen, don't init it
|
||||
*
|
||||
* @param newScreen new screen
|
||||
*/
|
||||
public void replaceScreenNoInit(Screen newScreen)
|
||||
{
|
||||
screen = newScreen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle FS if possible
|
||||
*/
|
||||
public void switchFullscreen()
|
||||
{
|
||||
try {
|
||||
if (!Display.isFullscreen()) {
|
||||
Log.f3("Entering fullscreen.");
|
||||
// save window resize
|
||||
windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight());
|
||||
|
||||
Display.setDisplayMode(Display.getDesktopDisplayMode());
|
||||
Display.setFullscreen(true);
|
||||
Display.update();
|
||||
//
|
||||
//
|
||||
// DisplayMode mode = Display.getDesktopDisplayMode(); //findDisplayMode(WIDTH, HEIGHT);
|
||||
// Display.setDisplayModeAndFullscreen(mode);
|
||||
} else {
|
||||
Log.f3("Leaving fullscreen.");
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
Display.update();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Failed to toggle fullscreen mode.", t);
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
Display.update();
|
||||
} catch (Throwable t1) {
|
||||
onCrash(t1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import mightypork.utils.files.PropertyManager;
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Main Config class
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
private static PropertyManager mgr;
|
||||
|
||||
// opts
|
||||
public static final int def_LAST_RUN_VERSION = 0;
|
||||
public static int LAST_RUN_VERSION;
|
||||
|
||||
public static final boolean def_START_IN_FS = false;
|
||||
public static boolean START_IN_FS;
|
||||
|
||||
// property keys
|
||||
private static final String PK_LAST_RUN_VERSION = "status.last_run_version";
|
||||
private static final String PK_START_IN_FS = "cfg.start_in_fullscreen";
|
||||
|
||||
|
||||
/**
|
||||
* Prepare config manager and load user settings
|
||||
*/
|
||||
public static void init()
|
||||
{
|
||||
Log.f2("Initializing configuration manager.");
|
||||
|
||||
String comment = Const.APP_NAME + " config file";
|
||||
|
||||
mgr = new PropertyManager(Paths.CONFIG, comment);
|
||||
|
||||
mgr.cfgNewlineBeforeComments(true);
|
||||
mgr.cfgSeparateSections(true);
|
||||
|
||||
mgr.putInteger(PK_LAST_RUN_VERSION, def_LAST_RUN_VERSION);
|
||||
mgr.putBoolean(PK_START_IN_FS, def_START_IN_FS);
|
||||
|
||||
load(); // load what has been "put"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save changed fields to config file
|
||||
*/
|
||||
public static void save()
|
||||
{
|
||||
mgr.setValue(PK_LAST_RUN_VERSION, Const.VERSION);
|
||||
mgr.setValue(PK_START_IN_FS, START_IN_FS);
|
||||
|
||||
mgr.apply();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load config file and assign values to fields
|
||||
*/
|
||||
public static void load()
|
||||
{
|
||||
mgr.apply();
|
||||
|
||||
LAST_RUN_VERSION = mgr.getInteger(PK_LAST_RUN_VERSION);
|
||||
START_IN_FS = mgr.getBoolean(PK_START_IN_FS);
|
||||
}
|
||||
|
||||
// options that can't be configured via config file
|
||||
|
||||
public static boolean LOGGING_ENABLED = true;
|
||||
public static boolean LOG_TO_STDOUT = true;
|
||||
public static boolean SINGLE_INSTANCE = true;
|
||||
|
||||
public static boolean LOG_FONTS = false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Application constants
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Const {
|
||||
|
||||
// STRINGS
|
||||
public static final int VERSION = 1;
|
||||
|
||||
public static final String APP_NAME = "Rogue";
|
||||
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
|
||||
|
||||
// AUDIO
|
||||
public static final Coord LISTENER_POS = Coord.ZERO;
|
||||
|
||||
public static final int FPS_RENDER = 200; // max
|
||||
public static final long FPS_GUI_UPDATE = 60;
|
||||
|
||||
// INITIAL WINDOW SIZE
|
||||
public static final int WINDOW_SIZE_X = 1024;
|
||||
public static final int WINDOW_SIZE_Y = 768;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
|
||||
public class CrashHandler implements UncaughtExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e)
|
||||
{
|
||||
App.onCrash(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import mightypork.utils.files.OsUtils;
|
||||
|
||||
|
||||
public class Paths {
|
||||
|
||||
private static final String APPDIR_NAME = "rogue";
|
||||
|
||||
public static final File WORKDIR = OsUtils.getWorkDir(APPDIR_NAME);
|
||||
public static final File LOGS = OsUtils.getWorkDir(APPDIR_NAME, "logs");
|
||||
public static final File SCREENSHOTS = OsUtils.getWorkDir(APPDIR_NAME, "screenshots");
|
||||
public static final File CONFIG = new File(WORKDIR, "config.ini");
|
||||
|
||||
public static final String DIR_EFFECTS = "res/sounds/effects/";
|
||||
public static final String DIR_MUSIC = "res/sounds/music/";
|
||||
public static final String DIR_LOOPS = "res/sounds/loops/";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import mightypork.rogue.animations.GUIRenderer;
|
||||
import mightypork.rogue.input.InputHandler;
|
||||
import mightypork.rogue.input.Keys;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Vec;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
|
||||
|
||||
/**
|
||||
* Screen class.<br>
|
||||
* Screen animates 3D world, while contained panels render 2D overlays, process
|
||||
* inputs and run the game logic.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public abstract class Screen implements GUIRenderer, InputHandler {
|
||||
|
||||
/** RNG */
|
||||
protected static Random rand = new Random();
|
||||
|
||||
|
||||
/**
|
||||
* handle fullscreen change
|
||||
*/
|
||||
@Override
|
||||
public final void onFullscreenChange()
|
||||
{
|
||||
onWindowResize();
|
||||
onViewportChanged();
|
||||
}
|
||||
|
||||
|
||||
protected abstract void onViewportChanged();
|
||||
|
||||
|
||||
/**
|
||||
* handle window resize.
|
||||
*/
|
||||
public final void onWindowResize()
|
||||
{
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
Coord s = App.inst.getSize();
|
||||
|
||||
glViewport(0, 0, s.xi(), s.yi());
|
||||
|
||||
glOrtho(0, s.x, 0, s.y, -1000, 1000);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glLoadIdentity();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
//glDisable(GL_DEPTH_TEST);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize screen
|
||||
*/
|
||||
public void init()
|
||||
{
|
||||
onWindowResize();
|
||||
|
||||
initScreen();
|
||||
|
||||
// SETUP LIGHTS
|
||||
glDisable(GL_LIGHTING);
|
||||
|
||||
// OTHER SETTINGS
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glClearDepth(1f);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
glShadeModel(GL_SMOOTH);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Here you can initialize the screen.
|
||||
*/
|
||||
public abstract void initScreen();
|
||||
|
||||
|
||||
/**
|
||||
* Update tick
|
||||
*/
|
||||
@Override
|
||||
public final void updateGui()
|
||||
{
|
||||
Mouse.poll();
|
||||
Keyboard.poll();
|
||||
checkInputEvents();
|
||||
|
||||
onGuiUpdate();
|
||||
}
|
||||
|
||||
|
||||
protected abstract void onGuiUpdate();
|
||||
|
||||
|
||||
/**
|
||||
* Render screen
|
||||
*
|
||||
* @param delta delta time (position between two update ticks, to allow
|
||||
* super-smooth animations)
|
||||
*/
|
||||
@Override
|
||||
public final void render(double delta)
|
||||
{
|
||||
glPushAttrib(GL_ENABLE_BIT);
|
||||
|
||||
// draw the directly rendered 3D stuff
|
||||
render3D();
|
||||
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
|
||||
protected abstract void render3D();
|
||||
|
||||
|
||||
/**
|
||||
* Check input events and process them.
|
||||
*/
|
||||
private final void checkInputEvents()
|
||||
{
|
||||
while (Keyboard.next()) {
|
||||
int key = Keyboard.getEventKey();
|
||||
boolean down = Keyboard.getEventKeyState();
|
||||
char c = Keyboard.getEventCharacter();
|
||||
Keys.onKey(key, down);
|
||||
onKey(key, c, down);
|
||||
}
|
||||
while (Mouse.next()) {
|
||||
int button = Mouse.getEventButton();
|
||||
boolean down = Mouse.getEventButtonState();
|
||||
Coord delta = new Coord(Mouse.getEventDX(), Mouse.getEventDY());
|
||||
Coord pos = new Coord(Mouse.getEventX(), Mouse.getEventY());
|
||||
int wheeld = Mouse.getEventDWheel();
|
||||
|
||||
onMouseButton(button, down, wheeld, pos, delta);
|
||||
}
|
||||
|
||||
int xc = Mouse.getX();
|
||||
int yc = Mouse.getY();
|
||||
int xd = Mouse.getDX();
|
||||
int yd = Mouse.getDY();
|
||||
int wd = Mouse.getDWheel();
|
||||
|
||||
if (Math.abs(xd) > 0 || Math.abs(yd) > 0 || Math.abs(wd) > 0) {
|
||||
onMouseMove(new Coord(xc, yc), new Vec(xd, yd), wd);
|
||||
}
|
||||
|
||||
handleKeyStates();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public abstract void onKey(int key, char c, boolean down);
|
||||
|
||||
|
||||
@Override
|
||||
public abstract void onMouseButton(int button, boolean down, int wheeld, Coord pos, Coord delta);
|
||||
|
||||
|
||||
@Override
|
||||
public abstract void handleKeyStates();
|
||||
|
||||
|
||||
@Override
|
||||
public abstract void onMouseMove(Coord coord, Vec vec, int wd);
|
||||
|
||||
|
||||
/**
|
||||
* Render background 2D (all is ready for rendering)
|
||||
*
|
||||
* @param delta delta time
|
||||
*/
|
||||
protected abstract void render2D(double delta);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package mightypork.rogue.animations;
|
||||
|
||||
|
||||
/**
|
||||
* Empty animation (no effect)
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class EmptyAnimator implements GUIRenderer {
|
||||
|
||||
/**
|
||||
* New empty animation
|
||||
*/
|
||||
public EmptyAnimator() {}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateGui()
|
||||
{}
|
||||
|
||||
|
||||
@Override
|
||||
public void render(double delta)
|
||||
{}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFullscreenChange()
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package mightypork.rogue.animations;
|
||||
|
||||
|
||||
public interface GUIRenderer {
|
||||
|
||||
public void updateGui();
|
||||
|
||||
|
||||
public void render(double delta);
|
||||
|
||||
|
||||
public void onFullscreenChange();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package mightypork.rogue.fonts;
|
||||
|
||||
|
||||
/**
|
||||
* Alignment
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class Align {
|
||||
|
||||
public static final int LEFT = -1;
|
||||
public static final int RIGHT = 1;
|
||||
public static final int TOP = 1;
|
||||
public static final int BOTTOM = -1;
|
||||
public static final int UP = 1;
|
||||
public static final int DOWN = -1;
|
||||
public static final int CENTER = 0;
|
||||
public static final int MIDDLE = 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
package mightypork.rogue.fonts;
|
||||
|
||||
|
||||
import java.awt.Font;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
import mightypork.rogue.Config;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Remade universal font manager for Sector.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class FontManager {
|
||||
|
||||
private static final boolean DEBUG = Config.LOG_FONTS;
|
||||
|
||||
/**
|
||||
* Glyph tables.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Glyphs {
|
||||
|
||||
//@formatter:off
|
||||
/** all glyphs */
|
||||
public static final String all =
|
||||
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]" +
|
||||
"^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤" +
|
||||
"¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäå" +
|
||||
"æçèéêëìíîïðñòóôõö÷øùúûüýþÿ";
|
||||
|
||||
/** letters and numbers, sufficient for basic messages etc. NO SPACE */
|
||||
public static final String alnum_nospace =
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
/** letters and numbers, sufficient for basic messages etc. */
|
||||
public static final String alnum =
|
||||
" "+alnum_nospace;
|
||||
|
||||
/** letters and numbers with the most basic punctuation signs */
|
||||
public static final String basic_text =
|
||||
" .-,.?!:;_"+alnum_nospace;
|
||||
|
||||
/** letters */
|
||||
public static final String alpha =
|
||||
" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
/** numbers */
|
||||
public static final String numbers =
|
||||
" 0123456789.-,:";
|
||||
|
||||
/** non-standard variants of alnum */
|
||||
public static final String alnum_extra =
|
||||
" ŒÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜŸÝßàáâãäåæçèéêëìíîïðñòóôõöøùúû" +
|
||||
"üýþÿĚŠČŘŽŤŇĎŮěščřžťňďůŁłđ";
|
||||
|
||||
/** signs and punctuation */
|
||||
public static final String signs =
|
||||
" !\"#$%&§'()*+,-./:;<=>?@[\\]^_{|}~";
|
||||
|
||||
/** extra signs and punctuation */
|
||||
public static final String signs_extra =
|
||||
" ¥€£¢`ƒ„…†‡ˆ‰‹‘’“”•›¡¤¦¨ª«¬¯°±²³´µ¶·¸¹º»¼½¾¿÷™©®→↓←↑";
|
||||
|
||||
|
||||
/** basic character set. */
|
||||
public static final String basic = alnum + signs;
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
* Font style
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static enum Style
|
||||
{
|
||||
/** Normal */
|
||||
NORMAL,
|
||||
/** Italic */
|
||||
ITALIC,
|
||||
/** Stronger italic to left. */
|
||||
LEFT,
|
||||
/** Stronger italic to right */
|
||||
RIGHT,
|
||||
/** Monospace type */
|
||||
MONO,
|
||||
/** Bold */
|
||||
BOLD,
|
||||
/** Bold & Italic */
|
||||
BOLD_I,
|
||||
/** Bold & Left */
|
||||
BOLD_L,
|
||||
/** Bold & Right */
|
||||
BOLD_R,
|
||||
/** Heavy style, stronger than bold. */
|
||||
HEAVY,
|
||||
/** Light (lighter than normal) */
|
||||
LIGHT,
|
||||
/** narrow style, similar to Light */
|
||||
NARROW,
|
||||
/** Wide style, like Bold but with thinner lines */
|
||||
WIDE,
|
||||
/** Outline variant of normal */
|
||||
OUTLINE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloaded font identifier [name, size, style]
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class FontId {
|
||||
|
||||
/** font size (pt) */
|
||||
public float size = 24;
|
||||
/** font name, registered with registerFile */
|
||||
public String name = "";
|
||||
/** font style. The given style must be in a file. */
|
||||
public Style style;
|
||||
|
||||
/** Set of glyphs in this ID */
|
||||
public String glyphs = "";
|
||||
|
||||
/** Index for faster comparision of glyph ids. */
|
||||
public int glyphset_id = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Preloaded font identifier
|
||||
*
|
||||
* @param name font name (registerFile)
|
||||
* @param size font size (pt)
|
||||
* @param style font style
|
||||
* @param glyphs glyphs to load
|
||||
*/
|
||||
public FontId(String name, double size, Style style, String glyphs) {
|
||||
this.name = name;
|
||||
this.size = (float) size;
|
||||
this.style = style;
|
||||
|
||||
if (glyphs.equals(Glyphs.basic)) {
|
||||
glyphset_id = 1;
|
||||
} else if (glyphs.equals(Glyphs.alnum)) {
|
||||
glyphset_id = 2;
|
||||
} else if (glyphs.equals(Glyphs.basic_text)) {
|
||||
glyphset_id = 3;
|
||||
} else if (glyphs.equals(Glyphs.numbers)) {
|
||||
glyphset_id = 4;
|
||||
} else if (glyphs.equals(Glyphs.alpha)) {
|
||||
glyphset_id = 5;
|
||||
} else if (glyphs.equals(Glyphs.all)) {
|
||||
glyphset_id = 6;
|
||||
} else if (glyphs.equals(Glyphs.alnum_extra)) {
|
||||
glyphset_id = 7;
|
||||
} else if (glyphs.equals(Glyphs.signs)) {
|
||||
glyphset_id = 8;
|
||||
} else if (glyphs.equals(Glyphs.signs_extra)) {
|
||||
glyphset_id = 9;
|
||||
} else {
|
||||
this.glyphs = glyphs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
|
||||
if (obj instanceof FontId) {
|
||||
if (obj == this) return true;
|
||||
FontId id2 = ((FontId) obj);
|
||||
boolean flag = true;
|
||||
flag &= id2.size == size;
|
||||
flag &= id2.name.equals(name);
|
||||
flag &= id2.style == style;
|
||||
flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
|
||||
return flag;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Group of styles of one font.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class FontFamily extends HashMap<Style, String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Table of font files. name → {style:file,style:file,style:file...}
|
||||
*/
|
||||
private static HashMap<String, FontFamily> fontFiles = new HashMap<String, FontFamily>();
|
||||
|
||||
|
||||
/**
|
||||
* Register font file.
|
||||
*
|
||||
* @param path resource path (res/fonts/...)
|
||||
* @param name font name (for binding)
|
||||
* @param style font style in this file
|
||||
*/
|
||||
public static void registerFile(String path, String name, Style style)
|
||||
{
|
||||
if (fontFiles.containsKey(name)) {
|
||||
if (fontFiles.get(name) != null) {
|
||||
fontFiles.get(name).put(style, path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// insert new table of styles to font name.
|
||||
FontFamily family = new FontFamily();
|
||||
family.put(style, path);
|
||||
fontFiles.put(name, family);
|
||||
}
|
||||
|
||||
/** Counter of loaded fonts */
|
||||
public static int loadedFontCounter = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Preload font if needed, get preloaded font.<br>
|
||||
* If needed file is not available, throws runtime exception.
|
||||
*
|
||||
* @param name font name (registerFile)
|
||||
* @param size font size (pt)
|
||||
* @param style font style
|
||||
* @param glyphs glyphs needed
|
||||
* @return the loaded font.
|
||||
*/
|
||||
public static LoadedFont loadFont(String name, double size, Style style, String glyphs)
|
||||
{
|
||||
return loadFont(name, size, style, glyphs, 9, 8, Coord.ONE, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Preload font if needed, get preloaded font.<br>
|
||||
* If needed file is not available, throws runtime exception.
|
||||
*
|
||||
* @param name font name (registerFile)
|
||||
* @param size font size (pt)
|
||||
* @param style font style
|
||||
* @param glyphs glyphs needed
|
||||
* @param correctLeft left horizontal correction
|
||||
* @param correctRight right horizontal correction
|
||||
* @param scale font scale (changing aspect ratio)
|
||||
* @param clipTop top clip (0-1) - top part of the font to be cut off
|
||||
* @param clipBottom bottom clip (0-1) - bottom part of the font to be cut
|
||||
* off
|
||||
* @return the loaded font.
|
||||
*/
|
||||
public static LoadedFont loadFont(String name, double size, Style style, String glyphs, int correctLeft, int correctRight, Coord scale, double clipTop, double clipBottom)
|
||||
{
|
||||
String resourcePath;
|
||||
try {
|
||||
resourcePath = fontFiles.get(name).get(style);
|
||||
if (resourcePath == null) {
|
||||
Log.w("Font [" + name + "] does not have variant " + style + ".\nUsing NORMAL instead.");
|
||||
resourcePath = fontFiles.get(name).get(Style.NORMAL);
|
||||
if (resourcePath == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
throw new RuntimeException("Font loading failed: no font file registered for name \"" + name + "\".");
|
||||
}
|
||||
|
||||
InputStream in = ResourceLoader.getResourceAsStream(resourcePath);
|
||||
|
||||
Font awtFont;
|
||||
try {
|
||||
awtFont = Font.createFont(Font.TRUETYPE_FONT, in);
|
||||
} catch (Exception e) {
|
||||
Log.e("Loading of font " + resourcePath + " failed.", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
awtFont = awtFont.deriveFont((float) size); // set font size
|
||||
LoadedFont font = new LoadedFont(awtFont, true, glyphs);
|
||||
|
||||
font.setCorrection(correctLeft, correctRight);
|
||||
font.setClip(clipTop, clipBottom);
|
||||
font.setScale(scale.x, scale.y);
|
||||
|
||||
loadedFontCounter++;
|
||||
|
||||
if (DEBUG) Log.f3("Font from file \"" + resourcePath + "\" preloaded.");
|
||||
|
||||
return font;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package mightypork.rogue.fonts;
|
||||
|
||||
|
||||
import static mightypork.rogue.fonts.FontManager.Style.*;
|
||||
import mightypork.rogue.fonts.FontManager.Glyphs;
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Global font preloader
|
||||
*
|
||||
* @author Rapus
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class Fonts {
|
||||
|
||||
public static LoadedFont splash_info;
|
||||
|
||||
public static LoadedFont tooltip;
|
||||
public static LoadedFont gui;
|
||||
public static LoadedFont gui_title;
|
||||
public static LoadedFont program_number;
|
||||
public static LoadedFont menu_button;
|
||||
public static LoadedFont menu_title;
|
||||
public static LoadedFont tiny;
|
||||
|
||||
|
||||
private static void registerFileNames()
|
||||
{
|
||||
FontManager.registerFile("res/fonts/4feb.ttf", "4feb", NORMAL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load fonts needed for splash.
|
||||
*/
|
||||
public static void loadForSplash()
|
||||
{
|
||||
registerFileNames();
|
||||
|
||||
gui = FontManager.loadFont("4feb", 24, NORMAL, Glyphs.basic).setCorrection(8, 7);
|
||||
splash_info = FontManager.loadFont("4feb", 42, NORMAL, "Loading.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Preload all fonts we will use in the game
|
||||
*/
|
||||
public static void load()
|
||||
{
|
||||
tooltip = FontManager.loadFont("4feb", 24, NORMAL, Glyphs.basic_text).setCorrection(8, 7);
|
||||
gui_title = FontManager.loadFont("4feb", 30, NORMAL, Glyphs.basic_text);
|
||||
menu_button = FontManager.loadFont("4feb", 36, NORMAL, Glyphs.basic_text);
|
||||
menu_title = FontManager.loadFont("4feb", 34, NORMAL, Glyphs.basic_text);
|
||||
program_number = FontManager.loadFont("4feb", 28, NORMAL, Glyphs.numbers);
|
||||
tiny = FontManager.loadFont("4feb", 20, NORMAL, Glyphs.basic_text);
|
||||
|
||||
Log.i("Fonts loaded = " + FontManager.loadedFontCounter);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
package mightypork.rogue.fonts;
|
||||
|
||||
|
||||
import static mightypork.rogue.fonts.Align.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import mightypork.rogue.Config;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.color.RGB;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.CoordI;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.util.glu.GLU;
|
||||
|
||||
|
||||
/**
|
||||
* A TrueType font implementation originally for Slick, edited for Bobjob's
|
||||
* Engine
|
||||
*
|
||||
* @original author James Chambers (Jimmy)
|
||||
* @original author Jeremy Adams (elias4444)
|
||||
* @original author Kevin Glass (kevglass)
|
||||
* @original author Peter Korzuszek (genail)
|
||||
* @new version edited by David Aaron Muhar (bobjob)
|
||||
* @new version edited by MightyPork
|
||||
*/
|
||||
public class LoadedFont {
|
||||
|
||||
private static final boolean DEBUG = Config.LOG_FONTS;
|
||||
|
||||
/** Map of user defined font characters (Character <-> IntObject) */
|
||||
private Map<Character, CharStorageEntry> chars = new HashMap<Character, CharStorageEntry>(100);
|
||||
|
||||
/** Boolean flag on whether AntiAliasing is enabled or not */
|
||||
private boolean antiAlias;
|
||||
|
||||
/** Font's size */
|
||||
private int fontSize = 0;
|
||||
|
||||
/** Font's height */
|
||||
private int fontHeight = 0;
|
||||
|
||||
/** Texture used to cache the font 0-255 characters */
|
||||
private int fontTextureID;
|
||||
|
||||
/** Default font texture width */
|
||||
private int textureWidth = 2048;
|
||||
|
||||
/** Default font texture height */
|
||||
private int textureHeight = 2048;
|
||||
|
||||
/** A reference to Java's AWT Font that we create our font texture from */
|
||||
private Font font;
|
||||
|
||||
/** The font metrics for our Java AWT font */
|
||||
private FontMetrics fontMetrics;
|
||||
|
||||
private int correctL = 9, correctR = 8;
|
||||
|
||||
private double defScaleX = 1, defScaleY = 1;
|
||||
private double clipVerticalT = 0;
|
||||
private double clipVerticalB = 0;
|
||||
|
||||
private class CharStorageEntry {
|
||||
|
||||
/** Character's width */
|
||||
public int width;
|
||||
|
||||
/** Character's height */
|
||||
public int height;
|
||||
|
||||
/** Character's stored x position */
|
||||
public int texPosX;
|
||||
|
||||
/** Character's stored y position */
|
||||
public int texPosY;
|
||||
}
|
||||
|
||||
|
||||
public LoadedFont(Font font, boolean antiAlias, String charsNeeded) {
|
||||
this.font = font;
|
||||
this.fontSize = font.getSize() + 3;
|
||||
this.antiAlias = antiAlias;
|
||||
|
||||
createSet(charsNeeded.toCharArray());
|
||||
|
||||
fontHeight -= 1;
|
||||
if (fontHeight <= 0) fontHeight = 1;
|
||||
}
|
||||
|
||||
|
||||
public void setCorrection(boolean on)
|
||||
{
|
||||
if (on) {
|
||||
correctL = 9;//2
|
||||
correctR = 8;//1
|
||||
} else {
|
||||
correctL = 0;
|
||||
correctR = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private BufferedImage getFontImage(char ch)
|
||||
{
|
||||
// Create a temporary image to extract the character's size
|
||||
BufferedImage tempfontImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
|
||||
if (antiAlias == true) {
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
}
|
||||
g.setFont(font);
|
||||
fontMetrics = g.getFontMetrics();
|
||||
int charwidth = fontMetrics.charWidth(ch) + 8;
|
||||
|
||||
if (charwidth <= 0) {
|
||||
charwidth = 7;
|
||||
}
|
||||
int charheight = fontMetrics.getHeight() + 3;
|
||||
if (charheight <= 0) {
|
||||
charheight = fontSize;
|
||||
}
|
||||
|
||||
// Create another image holding the character we are creating
|
||||
BufferedImage fontImage;
|
||||
fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D gt = (Graphics2D) fontImage.getGraphics();
|
||||
if (antiAlias == true) {
|
||||
gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
}
|
||||
gt.setFont(font);
|
||||
|
||||
gt.setColor(Color.WHITE);
|
||||
int charx = 3;
|
||||
int chary = 1;
|
||||
gt.drawString(String.valueOf(ch), (charx), (chary) + fontMetrics.getAscent());
|
||||
|
||||
return fontImage;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void createSet(char[] charsToLoad)
|
||||
{
|
||||
try {
|
||||
class LoadedGlyph {
|
||||
|
||||
public char c;
|
||||
public BufferedImage image;
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
|
||||
public LoadedGlyph(char c, BufferedImage image) {
|
||||
this.image = image;
|
||||
this.c = c;
|
||||
this.width = image.getWidth();
|
||||
this.height = image.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
List<LoadedGlyph> glyphs = new ArrayList<LoadedGlyph>();
|
||||
List<Character> loaded = new ArrayList<Character>();
|
||||
for (char ch : charsToLoad) {
|
||||
if (!loaded.contains(ch)) {
|
||||
glyphs.add(new LoadedGlyph(ch, getFontImage(ch)));
|
||||
loaded.add(ch);
|
||||
}
|
||||
}
|
||||
|
||||
Coord canvas = new Coord(128, 128);
|
||||
double lineHeight = 0;
|
||||
Coord begin = new Coord(0, 0);
|
||||
boolean needsLarger = false;
|
||||
|
||||
while (true) {
|
||||
needsLarger = false;
|
||||
|
||||
for (LoadedGlyph glyph : glyphs) {
|
||||
if (begin.x + glyph.width > canvas.x) {
|
||||
begin.y += lineHeight;
|
||||
lineHeight = 0;
|
||||
begin.x = 0;
|
||||
}
|
||||
|
||||
if (lineHeight < glyph.height) {
|
||||
lineHeight = glyph.height;
|
||||
}
|
||||
|
||||
if (begin.y + lineHeight > canvas.y) {
|
||||
needsLarger = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// draw.
|
||||
begin.x += glyph.width;
|
||||
}
|
||||
|
||||
if (needsLarger) {
|
||||
canvas.x *= 2;
|
||||
canvas.y *= 2;
|
||||
begin.setTo(0, 0);
|
||||
lineHeight = 0;
|
||||
} else {
|
||||
if (DEBUG) Log.f3("Preparing texture " + canvas.x + "x" + canvas.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
textureWidth = (int) canvas.x;
|
||||
textureHeight = (int) canvas.y;
|
||||
|
||||
BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = (Graphics2D) imgTemp.getGraphics();
|
||||
|
||||
g.setColor(new Color(0, 0, 0, 1));
|
||||
g.fillRect(0, 0, textureWidth, textureHeight);
|
||||
|
||||
int rowHeight = 0;
|
||||
int positionX = 0;
|
||||
int positionY = 0;
|
||||
|
||||
for (LoadedGlyph glyph : glyphs) {
|
||||
CharStorageEntry storedChar = new CharStorageEntry();
|
||||
|
||||
storedChar.width = glyph.width;
|
||||
storedChar.height = glyph.height;
|
||||
|
||||
if (positionX + storedChar.width >= textureWidth) {
|
||||
positionX = 0;
|
||||
positionY += rowHeight;
|
||||
rowHeight = 0;
|
||||
}
|
||||
|
||||
storedChar.texPosX = positionX;
|
||||
storedChar.texPosY = positionY;
|
||||
|
||||
if (storedChar.height > fontHeight) {
|
||||
fontHeight = storedChar.height;
|
||||
}
|
||||
|
||||
if (storedChar.height > rowHeight) {
|
||||
rowHeight = storedChar.height;
|
||||
}
|
||||
|
||||
// Draw it here
|
||||
g.drawImage(glyph.image, positionX, positionY, null);
|
||||
|
||||
positionX += storedChar.width;
|
||||
|
||||
chars.put(glyph.c, storedChar);
|
||||
}
|
||||
|
||||
fontTextureID = loadImage(imgTemp);
|
||||
|
||||
imgTemp = null;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to create font.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void drawQuad(double drawX, double drawY, double drawX2, double drawY2, CharStorageEntry charObj)
|
||||
{
|
||||
double srcX = charObj.texPosX + charObj.width;
|
||||
double srcY = charObj.texPosY + charObj.height;
|
||||
double srcX2 = charObj.texPosX;
|
||||
double srcY2 = charObj.texPosY;
|
||||
double DrawWidth = drawX2 - drawX;
|
||||
double DrawHeight = drawY2 - drawY;
|
||||
double TextureSrcX = srcX / textureWidth;
|
||||
double TextureSrcY = srcY / textureHeight;
|
||||
double SrcWidth = srcX2 - srcX;
|
||||
double SrcHeight = srcY2 - srcY;
|
||||
double RenderWidth = (SrcWidth / textureWidth);
|
||||
double RenderHeight = (SrcHeight / textureHeight);
|
||||
|
||||
drawY -= DrawHeight * clipVerticalB;
|
||||
|
||||
GL11.glTexCoord2d(TextureSrcX, TextureSrcY);
|
||||
GL11.glVertex2d(drawX, drawY);
|
||||
GL11.glTexCoord2d(TextureSrcX, TextureSrcY + RenderHeight);
|
||||
GL11.glVertex2d(drawX, drawY + DrawHeight);
|
||||
GL11.glTexCoord2d(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
|
||||
GL11.glVertex2d(drawX + DrawWidth, drawY + DrawHeight);
|
||||
GL11.glTexCoord2d(TextureSrcX + RenderWidth, TextureSrcY);
|
||||
GL11.glVertex2d(drawX + DrawWidth, drawY);
|
||||
}
|
||||
|
||||
|
||||
public int getWidth(String whatchars)
|
||||
{
|
||||
if (whatchars == null) whatchars = "";
|
||||
int totalwidth = 0;
|
||||
CharStorageEntry charStorage = null;
|
||||
char currentChar = 0;
|
||||
for (int i = 0; i < whatchars.length(); i++) {
|
||||
currentChar = whatchars.charAt(i);
|
||||
|
||||
charStorage = chars.get(currentChar);
|
||||
|
||||
if (charStorage != null) {
|
||||
totalwidth += charStorage.width - correctL;
|
||||
}
|
||||
}
|
||||
return (int) (totalwidth * defScaleX);
|
||||
}
|
||||
|
||||
|
||||
public int getHeight()
|
||||
{
|
||||
return (int) (fontHeight * defScaleY * (1 - clipVerticalT - clipVerticalB));
|
||||
}
|
||||
|
||||
|
||||
public int getLineHeight()
|
||||
{
|
||||
return getHeight();
|
||||
}
|
||||
|
||||
|
||||
public void drawString(double x, double y, String text, double scaleX, double scaleY, RGB color)
|
||||
{
|
||||
drawString(x, y, text, 0, text.length() - 1, scaleX, scaleY, color, LEFT);
|
||||
}
|
||||
|
||||
|
||||
public void drawString(double x, double y, String text, double scaleX, double scaleY, RGB color, int align)
|
||||
{
|
||||
drawString(x, y, text, 0, text.length() - 1, scaleX, scaleY, color, align);
|
||||
}
|
||||
|
||||
|
||||
private void drawString(double x, double y, String text, int startIndex, int endIndex, double scaleX, double scaleY, RGB color, int align)
|
||||
{
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
|
||||
scaleX *= defScaleX;
|
||||
scaleY *= defScaleY;
|
||||
|
||||
CharStorageEntry charStorage = null;
|
||||
int charCurrent;
|
||||
|
||||
int totalwidth = 0;
|
||||
int i = startIndex, d = 1, c = correctL;
|
||||
float startY = 0;
|
||||
|
||||
switch (align) {
|
||||
case RIGHT: {
|
||||
d = -1;
|
||||
c = correctR;
|
||||
|
||||
while (i < endIndex) {
|
||||
if (text.charAt(i) == '\n') startY -= getHeight();
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CENTER: {
|
||||
for (int l = startIndex; l <= endIndex; l++) {
|
||||
charCurrent = text.charAt(l);
|
||||
if (charCurrent == '\n') break;
|
||||
|
||||
charStorage = chars.get((char) charCurrent);
|
||||
if (charStorage != null) {
|
||||
totalwidth += charStorage.width - correctL;
|
||||
}
|
||||
}
|
||||
totalwidth /= -2;
|
||||
break;
|
||||
}
|
||||
case LEFT:
|
||||
default: {
|
||||
d = 1;
|
||||
c = correctL;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GL11.glPushAttrib(GL_ENABLE_BIT);
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
|
||||
GL11.glColor4d(color.r, color.g, color.b, color.a);
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
|
||||
while (i >= startIndex && i <= endIndex) {
|
||||
charCurrent = text.charAt(i);
|
||||
|
||||
charStorage = chars.get(new Character((char) charCurrent));
|
||||
|
||||
if (charStorage != null) {
|
||||
if (d < 0) totalwidth += (charStorage.width - c) * d;
|
||||
if (charCurrent == '\n') {
|
||||
startY -= getHeight() * d;
|
||||
totalwidth = 0;
|
||||
if (align == CENTER) {
|
||||
for (int l = i + 1; l <= endIndex; l++) {
|
||||
charCurrent = text.charAt(l);
|
||||
if (charCurrent == '\n') break;
|
||||
|
||||
charStorage = chars.get((char) charCurrent);
|
||||
|
||||
totalwidth += charStorage.width - correctL;
|
||||
}
|
||||
totalwidth /= -2;
|
||||
}
|
||||
//if center get next lines total width/2;
|
||||
} else {
|
||||
//@formatter:off
|
||||
drawQuad(
|
||||
(totalwidth + charStorage.width) * scaleX + x,
|
||||
startY * scaleY + y, totalwidth * scaleX + x,
|
||||
(startY + charStorage.height) * scaleY + y,
|
||||
charStorage
|
||||
);
|
||||
//@formatter:on
|
||||
|
||||
if (d > 0) totalwidth += (charStorage.width - c) * d;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i += d;
|
||||
}
|
||||
GL11.glEnd();
|
||||
GL11.glPopAttrib();
|
||||
}
|
||||
|
||||
|
||||
public static int loadImage(BufferedImage bufferedImage)
|
||||
{
|
||||
try {
|
||||
short width = (short) bufferedImage.getWidth();
|
||||
short height = (short) bufferedImage.getHeight();
|
||||
//textureLoader.bpp = bufferedImage.getColorModel().hasAlpha() ? (byte)32 : (byte)24;
|
||||
int bpp = (byte) bufferedImage.getColorModel().getPixelSize();
|
||||
ByteBuffer byteBuffer;
|
||||
DataBuffer db = bufferedImage.getData().getDataBuffer();
|
||||
if (db instanceof DataBufferInt) {
|
||||
int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData();
|
||||
byte newI[] = new byte[intI.length * 4];
|
||||
for (int i = 0; i < intI.length; i++) {
|
||||
byte b[] = intToByteArray(intI[i]);
|
||||
int newIndex = i * 4;
|
||||
|
||||
newI[newIndex] = b[1];
|
||||
newI[newIndex + 1] = b[2];
|
||||
newI[newIndex + 2] = b[3];
|
||||
newI[newIndex + 3] = b[0];
|
||||
}
|
||||
|
||||
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(newI);
|
||||
} else {
|
||||
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(((DataBufferByte) (bufferedImage.getData().getDataBuffer())).getData());
|
||||
}
|
||||
byteBuffer.flip();
|
||||
|
||||
int internalFormat = GL11.GL_RGBA8, format = GL11.GL_RGBA;
|
||||
IntBuffer textureId = BufferUtils.createIntBuffer(1);
|
||||
|
||||
GL11.glGenTextures(textureId);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0));
|
||||
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
|
||||
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
|
||||
GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
|
||||
GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, internalFormat, width, height, format, GL11.GL_UNSIGNED_BYTE, byteBuffer);
|
||||
return textureId.get(0);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isSupported(String fontname)
|
||||
{
|
||||
Font font[] = getFonts();
|
||||
for (int i = font.length - 1; i >= 0; i--) {
|
||||
if (font[i].getName().equalsIgnoreCase(fontname)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static Font[] getFonts()
|
||||
{
|
||||
return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
|
||||
}
|
||||
|
||||
|
||||
public static Font getFont(String fontname, int style, float size)
|
||||
{
|
||||
Font result = null;
|
||||
GraphicsEnvironment graphicsenvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
for (Font font : graphicsenvironment.getAllFonts()) {
|
||||
if (font.getName().equalsIgnoreCase(fontname)) {
|
||||
result = font.deriveFont(style, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] intToByteArray(int value)
|
||||
{
|
||||
return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
|
||||
}
|
||||
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
IntBuffer scratch = BufferUtils.createIntBuffer(1);
|
||||
scratch.put(0, fontTextureID);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GL11.glDeleteTextures(scratch);
|
||||
}
|
||||
|
||||
|
||||
public LoadedFont setScale(double x, double y)
|
||||
{
|
||||
defScaleX = x;
|
||||
defScaleY = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public LoadedFont setClip(double clipRatioTop, double clipRatioBottom)
|
||||
{
|
||||
clipVerticalT = clipRatioTop;
|
||||
clipVerticalB = clipRatioBottom;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public LoadedFont setCorrection(int correctionLeft, int correctionRight)
|
||||
{
|
||||
correctL = correctionLeft;
|
||||
correctR = correctionRight;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw string with font.
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param text text to draw
|
||||
* @param color render color
|
||||
* @param align (-1,0,1)
|
||||
*/
|
||||
public void draw(double x, double y, String text, RGB color, int align)
|
||||
{
|
||||
drawString(x, y, text, 1, 1, color, align);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw string with font.
|
||||
*
|
||||
* @param pos coord
|
||||
* @param text text to draw
|
||||
* @param color render color
|
||||
* @param align (-1,0,1)
|
||||
*/
|
||||
public void draw(Coord pos, String text, RGB color, int align)
|
||||
{
|
||||
drawString(pos.x, pos.y, text, 1, 1, color, align);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw string with font.
|
||||
*
|
||||
* @param pos coord
|
||||
* @param text text to draw
|
||||
* @param color render color
|
||||
* @param align (-1,0,1)
|
||||
*/
|
||||
public void draw(CoordI pos, String text, RGB color, int align)
|
||||
{
|
||||
drawString(pos.x, pos.y, text, 1, 1, color, align);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
|
||||
{
|
||||
drawFuzzy(pos, text, align, textColor, blurColor, blurSize, true);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
|
||||
{
|
||||
drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, true);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
|
||||
{
|
||||
drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, smooth);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
glTranslated(pos.x, pos.y, pos.z);
|
||||
|
||||
//shadow
|
||||
int sh = blurSize;
|
||||
|
||||
int l = glGenLists(1);
|
||||
|
||||
glNewList(l, GL_COMPILE);
|
||||
draw(0, 0, text, blurColor, align);
|
||||
glEndList();
|
||||
|
||||
for (int xx = -sh; xx <= sh; xx += (smooth ? 1 : sh)) {
|
||||
for (int yy = -sh; yy <= sh; yy += (smooth ? 1 : sh)) {
|
||||
if (xx == 0 && yy == 0) continue;
|
||||
glPushMatrix();
|
||||
glTranslated(xx, yy, 0);
|
||||
glCallList(l);
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteLists(l, 1);
|
||||
|
||||
draw(0, 0, text, textColor, align);
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package mightypork.rogue.input;
|
||||
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Vec;
|
||||
|
||||
|
||||
/**
|
||||
* Input event handler
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface InputHandler {
|
||||
|
||||
/**
|
||||
* Called each update tick, if the mouse position was changed.
|
||||
*
|
||||
* @param pos mouse position
|
||||
* @param move mouse motion
|
||||
* @param wheelDelta mouse wheel delta
|
||||
*/
|
||||
public void onMouseMove(Coord pos, Vec move, int wheelDelta);
|
||||
|
||||
|
||||
/**
|
||||
* Mouse event handler.
|
||||
*
|
||||
* @param button button which caused this event
|
||||
* @param down true = down, false = up
|
||||
* @param wheelDelta number of steps the wheel turned since last event
|
||||
* @param pos mouse position
|
||||
* @param deltaPos delta mouse position
|
||||
*/
|
||||
public void onMouseButton(int button, boolean down, int wheelDelta, Coord pos, Coord deltaPos);
|
||||
|
||||
|
||||
/**
|
||||
* Key event handler.
|
||||
*
|
||||
* @param key key index, constant Keyboard.KEY_???
|
||||
* @param c character typed, if any
|
||||
* @param down true = down, false = up
|
||||
*/
|
||||
public void onKey(int key, char c, boolean down);
|
||||
|
||||
|
||||
/**
|
||||
* In this method screen can handle static inputs, that is:
|
||||
* Keyboard.isKeyDown, Mouse.isButtonDown etc.
|
||||
*/
|
||||
public void handleKeyStates();
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package mightypork.rogue.input;
|
||||
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
|
||||
/**
|
||||
* Key state handler
|
||||
*/
|
||||
public class Keys {
|
||||
|
||||
private static boolean[] prevKeys;
|
||||
private static boolean[] keys;
|
||||
|
||||
|
||||
/**
|
||||
* initialize key state handler
|
||||
*/
|
||||
private static void init()
|
||||
{
|
||||
|
||||
if (keys == null) {
|
||||
keys = new boolean[Keyboard.KEYBOARD_SIZE];
|
||||
}
|
||||
|
||||
if (prevKeys == null) {
|
||||
prevKeys = new boolean[Keyboard.KEYBOARD_SIZE];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* method called when key event was detected in Screen class.
|
||||
*
|
||||
* @param key
|
||||
* @param down
|
||||
*/
|
||||
public static void onKey(int key, boolean down)
|
||||
{
|
||||
|
||||
init();
|
||||
|
||||
prevKeys[key] = keys[key];
|
||||
keys[key] = down;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if key is down
|
||||
*
|
||||
* @param key key index
|
||||
* @return is down
|
||||
*/
|
||||
public static boolean isDown(int key)
|
||||
{
|
||||
|
||||
init();
|
||||
|
||||
return keys[key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if key is up
|
||||
*
|
||||
* @param key key index
|
||||
* @return is up
|
||||
*/
|
||||
public static boolean isUp(int key)
|
||||
{
|
||||
|
||||
init();
|
||||
|
||||
return !keys[key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if key was just pressed (changed state since last event on this
|
||||
* key)
|
||||
*
|
||||
* @param key key index
|
||||
* @return true if changed state to DOWN
|
||||
*/
|
||||
public static boolean justPressed(int key)
|
||||
{
|
||||
|
||||
init();
|
||||
|
||||
return !prevKeys[key] && keys[key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if key was just released (changed state since last event on this
|
||||
* key)
|
||||
*
|
||||
* @param key key index
|
||||
* @return true if changed state to UP
|
||||
*/
|
||||
public static boolean justReleased(int key)
|
||||
{
|
||||
|
||||
init();
|
||||
|
||||
return prevKeys[key] && !keys[key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroy "just" flag for a key.
|
||||
*
|
||||
* @param key key index
|
||||
*/
|
||||
public static void destroyChangeState(int key)
|
||||
{
|
||||
|
||||
init();
|
||||
|
||||
prevKeys[key] = keys[key];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package mightypork.rogue.screens;
|
||||
|
||||
|
||||
import mightypork.rogue.Screen;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Vec;
|
||||
|
||||
|
||||
public class ScreenSplash extends Screen {
|
||||
|
||||
@Override
|
||||
protected void onViewportChanged()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initScreen()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onGuiUpdate()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void render3D()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onKey(int key, char c, boolean down)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMouseButton(int button, boolean down, int wheeld, Coord pos, Coord delta)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleKeyStates()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMouseMove(Coord coord, Vec vec, int wd)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void render2D(double delta)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package mightypork.rogue.sounds;
|
||||
|
||||
|
||||
import mightypork.utils.objects.Mutable;
|
||||
|
||||
|
||||
public abstract class AudioPlayer {
|
||||
|
||||
/** base gain for sfx */
|
||||
private double baseGain = 1;
|
||||
|
||||
/** the track */
|
||||
private AudioX track;
|
||||
|
||||
/** base pitch for sfx */
|
||||
private double basePitch = 1;
|
||||
|
||||
/** dedicated volume control */
|
||||
private Mutable<Float> gainMultiplier = null;
|
||||
|
||||
|
||||
public AudioPlayer(AudioX track, double baseGain, Mutable<Float> gainMultiplier) {
|
||||
this(track, 1, baseGain, gainMultiplier);
|
||||
}
|
||||
|
||||
|
||||
public AudioPlayer(AudioX track, double basePitch, double baseGain, Mutable<Float> gainMultiplier) {
|
||||
this.track = track;
|
||||
|
||||
this.baseGain = baseGain;
|
||||
this.basePitch = basePitch;
|
||||
|
||||
this.gainMultiplier = gainMultiplier;
|
||||
}
|
||||
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
track.release();
|
||||
track = null;
|
||||
}
|
||||
|
||||
|
||||
protected AudioX getAudio()
|
||||
{
|
||||
return track;
|
||||
}
|
||||
|
||||
|
||||
protected float getGain(double multiplier)
|
||||
{
|
||||
return (float) (baseGain * gainMultiplier.get() * multiplier);
|
||||
}
|
||||
|
||||
|
||||
protected float getPitch(double multiplier)
|
||||
{
|
||||
return (float) (basePitch * multiplier);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get if audio is valid
|
||||
*
|
||||
* @return is valid
|
||||
*/
|
||||
protected boolean canPlay()
|
||||
{
|
||||
return (track != null);
|
||||
}
|
||||
|
||||
|
||||
public void load()
|
||||
{
|
||||
if (canPlay()) track.load();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package mightypork.rogue.sounds;
|
||||
|
||||
|
||||
import mightypork.utils.files.FileUtils;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
import org.newdawn.slick.openal.Audio;
|
||||
import org.newdawn.slick.openal.SoundStore;
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper class for slick audio
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class AudioX implements Audio {
|
||||
|
||||
private enum PlayMode
|
||||
{
|
||||
EFFECT, MUSIC;
|
||||
};
|
||||
|
||||
private Audio audio = null;
|
||||
private float pauseLoopPosition = 0;
|
||||
private boolean looping = false;
|
||||
private boolean paused = false;
|
||||
private PlayMode mode = PlayMode.EFFECT;
|
||||
private float pitch = 1;
|
||||
private float gain = 1;
|
||||
|
||||
private String resourcePath;
|
||||
|
||||
|
||||
/**
|
||||
* Pause loop (remember position and stop playing) - if was looping
|
||||
*/
|
||||
public void pauseLoop()
|
||||
{
|
||||
if (!ensureLoaded()) return;
|
||||
|
||||
if (isPlaying() && looping) {
|
||||
pauseLoopPosition = audio.getPosition();
|
||||
stop();
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resume loop (if was paused)
|
||||
*
|
||||
* @return source ID
|
||||
*/
|
||||
public int resumeLoop()
|
||||
{
|
||||
if (!ensureLoaded()) return -1;
|
||||
|
||||
int source = -1;
|
||||
if (looping && paused) {
|
||||
if (mode == PlayMode.MUSIC) {
|
||||
source = audio.playAsMusic(pitch, gain, true);
|
||||
} else {
|
||||
source = audio.playAsSoundEffect(pitch, gain, true);
|
||||
}
|
||||
audio.setPosition(pauseLoopPosition);
|
||||
paused = false;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create deferred primitive audio player
|
||||
*
|
||||
* @param resourceName resource to load when needed
|
||||
*/
|
||||
public AudioX(String resourceName) {
|
||||
this.audio = null;
|
||||
this.resourcePath = resourceName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if can play, if not, try to load sound.
|
||||
*
|
||||
* @return can now play
|
||||
*/
|
||||
private boolean ensureLoaded()
|
||||
{
|
||||
load();
|
||||
|
||||
return audio != null;
|
||||
}
|
||||
|
||||
|
||||
public void load()
|
||||
{
|
||||
if (audio != null) return; // already loaded
|
||||
if (resourcePath == null) return; // not loaded, but can't load anyway
|
||||
|
||||
try {
|
||||
String ext = FileUtils.getExtension(resourcePath);
|
||||
|
||||
// java 6 can't use String switch :(
|
||||
if (ext.equalsIgnoreCase("ogg")) {
|
||||
audio = SoundStore.get().getOgg(resourcePath);
|
||||
} else if (ext.equalsIgnoreCase("wav")) {
|
||||
audio = SoundStore.get().getWAV(resourcePath);
|
||||
} else if (ext.equalsIgnoreCase("aif")) {
|
||||
audio = SoundStore.get().getAIF(resourcePath);
|
||||
} else if (ext.equalsIgnoreCase("mod")) {
|
||||
audio = SoundStore.get().getMOD(resourcePath);
|
||||
} else {
|
||||
resourcePath = null; // don't try next time
|
||||
Log.e("Invalid audio file extension: " + resourcePath);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("Could not load " + resourcePath, e);
|
||||
resourcePath = null; // don't try next time
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if (!ensureLoaded()) return;
|
||||
|
||||
audio.stop();
|
||||
paused = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getBufferID()
|
||||
{
|
||||
if (!ensureLoaded()) return -1;
|
||||
|
||||
return audio.getBufferID();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPlaying()
|
||||
{
|
||||
if (!ensureLoaded()) return false;
|
||||
|
||||
return audio.isPlaying();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPaused()
|
||||
{
|
||||
if (!ensureLoaded()) return false;
|
||||
|
||||
return audio.isPaused();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop)
|
||||
{
|
||||
return playAsSoundEffect(pitch, gain, loop, SoundManager.get().listener);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z)
|
||||
{
|
||||
if (!ensureLoaded()) return -1;
|
||||
|
||||
this.pitch = pitch;
|
||||
this.gain = gain;
|
||||
looping = loop;
|
||||
mode = PlayMode.EFFECT;
|
||||
return audio.playAsSoundEffect(pitch, gain, loop, x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Play this sound as a sound effect
|
||||
*
|
||||
* @param pitch The pitch of the play back
|
||||
* @param gain The gain of the play back
|
||||
* @param loop True if we should loop
|
||||
* @param pos The position of the sound
|
||||
* @return The ID of the source playing the sound
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, Coord pos)
|
||||
{
|
||||
return playAsSoundEffect(pitch, gain, loop, (float) pos.x, (float) pos.y, (float) pos.z);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int playAsMusic(float pitch, float gain, boolean loop)
|
||||
{
|
||||
this.pitch = pitch;
|
||||
this.gain = gain;
|
||||
looping = loop;
|
||||
mode = PlayMode.MUSIC;
|
||||
return audio.playAsMusic(pitch, gain, loop);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setPosition(float position)
|
||||
{
|
||||
return audio.setPosition(position);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getPosition()
|
||||
{
|
||||
return audio.getPosition();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void release()
|
||||
{
|
||||
audio.release();
|
||||
audio = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package mightypork.rogue.sounds;
|
||||
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.objects.Mutable;
|
||||
|
||||
|
||||
public class EffectPlayer extends AudioPlayer {
|
||||
|
||||
public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable<Float> gainMultiplier) {
|
||||
super(track, (float) basePitch, (float) baseGain, gainMultiplier);
|
||||
}
|
||||
|
||||
|
||||
public int play(double pitch, double gain)
|
||||
{
|
||||
if (!canPlay()) return -1;
|
||||
|
||||
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false);
|
||||
}
|
||||
|
||||
|
||||
public int play(double gain)
|
||||
{
|
||||
return play(1, gain);
|
||||
}
|
||||
|
||||
|
||||
public int play(double pitch, double gain, Coord pos)
|
||||
{
|
||||
if (!canPlay()) return -1;
|
||||
|
||||
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false, pos);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package mightypork.rogue.sounds;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
import mightypork.utils.objects.Mutable;
|
||||
|
||||
|
||||
/**
|
||||
* Volume multiplex
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class JointVolume extends Mutable<Float> {
|
||||
|
||||
private Mutable<Float>[] volumes;
|
||||
|
||||
|
||||
/**
|
||||
* CReate joint volume with master gain of 1
|
||||
*
|
||||
* @param volumes individual volumes to join
|
||||
*/
|
||||
public JointVolume(Mutable<Float>... volumes) {
|
||||
super(1F);
|
||||
this.volumes = volumes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get combined gain (multiplied)
|
||||
*/
|
||||
@Override
|
||||
public Float get()
|
||||
{
|
||||
float f = super.get();
|
||||
for (Mutable<Float> v : volumes)
|
||||
f *= v.get();
|
||||
|
||||
return Calc.clampf(f, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set master gain
|
||||
*/
|
||||
@Override
|
||||
public void set(Float o)
|
||||
{
|
||||
super.set(o);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package mightypork.rogue.sounds;
|
||||
|
||||
|
||||
import mightypork.utils.objects.Mutable;
|
||||
import mightypork.utils.time.AnimDouble;
|
||||
import mightypork.utils.time.Pauseable;
|
||||
import mightypork.utils.time.Updateable;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
|
||||
public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
|
||||
|
||||
private int sourceID = -1;
|
||||
|
||||
/** animator for fade in and fade out */
|
||||
private AnimDouble fadeAnim = new AnimDouble(0);
|
||||
|
||||
private double lastUpdateGain = 0;
|
||||
|
||||
/** flag that track is paused */
|
||||
private boolean paused = true;
|
||||
|
||||
/** Default fadeIn time */
|
||||
private double inTime = 1;
|
||||
|
||||
/** Default fadeOut time */
|
||||
private double outTime = 1;
|
||||
|
||||
|
||||
public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Float> gainMultiplier) {
|
||||
super(track, (float) pitch, (float) baseGain, gainMultiplier);
|
||||
|
||||
paused = true;
|
||||
}
|
||||
|
||||
|
||||
public void setFadeTimes(double in, double out)
|
||||
{
|
||||
inTime = in;
|
||||
outTime = out;
|
||||
}
|
||||
|
||||
|
||||
private void initLoop()
|
||||
{
|
||||
if (!canPlay() && sourceID == -1) {
|
||||
sourceID = getAudio().playAsSoundEffect(getPitch(1), getGain(1), true);
|
||||
getAudio().pauseLoop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void pause()
|
||||
{
|
||||
if (!canPlay() || paused) return;
|
||||
|
||||
initLoop();
|
||||
|
||||
getAudio().pauseLoop();
|
||||
paused = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPaused()
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resume()
|
||||
{
|
||||
if (!canPlay() || paused) return;
|
||||
|
||||
initLoop();
|
||||
|
||||
sourceID = getAudio().resumeLoop();
|
||||
paused = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (!canPlay() || paused) return;
|
||||
|
||||
initLoop();
|
||||
|
||||
fadeAnim.update(delta);
|
||||
|
||||
double gain = getGain(fadeAnim.getCurrentValue());
|
||||
if (!paused && gain != lastUpdateGain) {
|
||||
AL10.alSourcef(sourceID, AL10.AL_GAIN, (float) gain);
|
||||
lastUpdateGain = gain;
|
||||
}
|
||||
|
||||
if (gain == 0 && !paused) pause(); // pause on zero volume
|
||||
}
|
||||
|
||||
|
||||
public void fadeIn(double secs)
|
||||
{
|
||||
if (!canPlay()) return;
|
||||
|
||||
resume();
|
||||
fadeAnim.fadeIn(secs);
|
||||
}
|
||||
|
||||
|
||||
public void fadeOut(double secs)
|
||||
{
|
||||
if (!canPlay()) return;
|
||||
|
||||
fadeAnim.fadeOut(secs);
|
||||
}
|
||||
|
||||
|
||||
public void fadeIn()
|
||||
{
|
||||
fadeIn(inTime);
|
||||
}
|
||||
|
||||
|
||||
public void fadeOut()
|
||||
{
|
||||
fadeOut(outTime);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package mightypork.rogue.sounds;
|
||||
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import mightypork.utils.math.Calc.Buffers;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.objects.Mutable;
|
||||
import mightypork.utils.time.Updateable;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.newdawn.slick.openal.SoundStore;
|
||||
|
||||
|
||||
/**
|
||||
* Preloaded sounds.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class SoundManager implements Updateable {
|
||||
|
||||
private static SoundManager inst = new SoundManager();
|
||||
public Mutable<Float> masterVolume = new Mutable<Float>(1F);
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mutable<Float> effectsVolume = new JointVolume(masterVolume);
|
||||
@SuppressWarnings("unchecked")
|
||||
public Mutable<Float> loopsVolume = new JointVolume(masterVolume);
|
||||
public Coord listener = new Coord(Coord.ZERO);
|
||||
private Map<String, EffectPlayer> effects = new HashMap<String, EffectPlayer>();
|
||||
private Map<String, LoopPlayer> loops = new HashMap<String, LoopPlayer>();
|
||||
|
||||
|
||||
/**
|
||||
* Singleton constructor
|
||||
*/
|
||||
private SoundManager() {
|
||||
SoundStore.get().setMaxSources(256);
|
||||
SoundStore.get().init();
|
||||
}
|
||||
|
||||
|
||||
public static SoundManager get()
|
||||
{
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
public void addEffect(String key, String resource, float pitch, float gain)
|
||||
{
|
||||
EffectPlayer p = new EffectPlayer(new AudioX(resource), pitch, gain, effectsVolume);
|
||||
effects.put(key, p);
|
||||
}
|
||||
|
||||
|
||||
public void addLoop(String key, String resource, float pitch, float gain, double fadeIn, double fadeOut)
|
||||
{
|
||||
LoopPlayer p = new LoopPlayer(new AudioX(resource), pitch, gain, loopsVolume);
|
||||
p.setFadeTimes(fadeIn, fadeOut);
|
||||
loops.put(key, p);
|
||||
}
|
||||
|
||||
|
||||
public LoopPlayer getLoop(String key)
|
||||
{
|
||||
LoopPlayer p = loops.get(key);
|
||||
if (p == null) {
|
||||
throw new IllegalArgumentException("Requesting unknown sound loop \"" + key + "\".");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
public EffectPlayer getEffect(String key)
|
||||
{
|
||||
EffectPlayer p = effects.get(key);
|
||||
if (p == null) {
|
||||
throw new IllegalArgumentException("Requesting unknown sound effect \"" + key + "\".");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
public void fadeOutAllLoops()
|
||||
{
|
||||
for (LoopPlayer p : loops.values()) {
|
||||
p.fadeOut();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void fadeInLoop(String key)
|
||||
{
|
||||
getLoop(key).fadeIn();
|
||||
}
|
||||
|
||||
|
||||
public void fadeInLoop(String key, double seconds)
|
||||
{
|
||||
getLoop(key).fadeIn(seconds);
|
||||
}
|
||||
|
||||
|
||||
public void pauseLoop(String key)
|
||||
{
|
||||
getLoop(key).pause();
|
||||
}
|
||||
|
||||
|
||||
public void pauseAllLoops()
|
||||
{
|
||||
for (LoopPlayer p : loops.values()) {
|
||||
p.pause();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void resumeLoop(String key)
|
||||
{
|
||||
getLoop(key).resume();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set listener pos
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public void setListener(Coord pos)
|
||||
{
|
||||
listener.setTo(pos);
|
||||
FloatBuffer buf3 = Buffers.alloc(3);
|
||||
FloatBuffer buf6 = Buffers.alloc(6);
|
||||
buf3.clear();
|
||||
Buffers.fill(buf3, (float) pos.x, (float) pos.y, (float) pos.z);
|
||||
AL10.alListener(AL10.AL_POSITION, buf3);
|
||||
buf3.clear();
|
||||
Buffers.fill(buf3, 0, 0, 0);
|
||||
AL10.alListener(AL10.AL_VELOCITY, buf3);
|
||||
buf6.clear();
|
||||
Buffers.fill(buf6, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
|
||||
AL10.alListener(AL10.AL_ORIENTATION, buf6);
|
||||
buf3 = buf6 = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
for (LoopPlayer lp : loops.values()) {
|
||||
lp.update(delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setMasterVolume(float f)
|
||||
{
|
||||
masterVolume.set(f);
|
||||
}
|
||||
|
||||
|
||||
public void setEffectsVolume(float f)
|
||||
{
|
||||
effectsVolume.set(f);
|
||||
}
|
||||
|
||||
|
||||
public void setMusicVolume(float f)
|
||||
{
|
||||
loopsVolume.set(f);
|
||||
}
|
||||
|
||||
|
||||
public void destroy()
|
||||
{
|
||||
SoundStore.get().clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package mightypork.rogue.textures;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
import org.newdawn.slick.opengl.Texture;
|
||||
import org.newdawn.slick.opengl.TextureLoader;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Texture manager
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class TextureManager {
|
||||
|
||||
private static Texture lastBinded = null;
|
||||
|
||||
|
||||
/**
|
||||
* Load texture
|
||||
*
|
||||
* @param resourcePath
|
||||
* @return the loaded texture
|
||||
*/
|
||||
public static Texture load(String resourcePath)
|
||||
{
|
||||
try {
|
||||
String ext = resourcePath.substring(resourcePath.length() - 4);
|
||||
|
||||
Texture texture = TextureLoader.getTexture(ext.toUpperCase(), ResourceLoader.getResourceAsStream(resourcePath));
|
||||
|
||||
if (texture != null) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
Log.w("Texture " + resourcePath + " could not be loaded.");
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
Log.e("Loading of texture " + resourcePath + " failed.", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind texture
|
||||
*
|
||||
* @param texture the texture
|
||||
* @throws RuntimeException if not loaded yet
|
||||
*/
|
||||
public static void bind(Texture texture) throws RuntimeException
|
||||
{
|
||||
if (texture != lastBinded) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.getTextureID());
|
||||
lastBinded = texture;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unbind all
|
||||
*/
|
||||
public static void unbind()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
lastBinded = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package mightypork.rogue.textures;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import org.newdawn.slick.opengl.Texture;
|
||||
|
||||
|
||||
/**
|
||||
* Texture loading class
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
// unrelevant
|
||||
public class Textures {
|
||||
|
||||
protected static Texture buttons_menu;
|
||||
protected static Texture buttons_small;
|
||||
|
||||
// patterns need raw access (are repeated)
|
||||
public static Texture steel_small_dk;
|
||||
public static Texture steel_small_lt;
|
||||
public static Texture steel_big_dk;
|
||||
public static Texture steel_big_lt;
|
||||
public static Texture steel_big_scratched;
|
||||
public static Texture steel_brushed;
|
||||
public static Texture steel_rivet_belts;
|
||||
public static Texture water;
|
||||
|
||||
// particles need direct access
|
||||
public static Texture snow;
|
||||
public static Texture leaves;
|
||||
public static Texture rain;
|
||||
public static Texture circle;
|
||||
|
||||
protected static Texture logo;
|
||||
protected static Texture widgets;
|
||||
public static Texture program;
|
||||
protected static Texture icons;
|
||||
|
||||
public static Texture map_tiles;
|
||||
public static Texture map_shading;
|
||||
|
||||
// spotted
|
||||
public static Texture turtle_blue;
|
||||
public static Texture turtle_green;
|
||||
public static Texture turtle_purple;
|
||||
public static Texture turtle_yellow;
|
||||
|
||||
// misc
|
||||
public static Texture turtle_red;
|
||||
public static Texture turtle_brown_dk;
|
||||
public static Texture turtle_brown_lt;
|
||||
public static Texture turtle_brown_orn;
|
||||
// a shadow sheet
|
||||
public static Texture turtle_shadows;
|
||||
|
||||
public static Texture food;
|
||||
public static Texture food_shadows;
|
||||
|
||||
private static final String GUI = "res/images/gui/";
|
||||
private static final String PATTERNS = "res/images/patterns/";
|
||||
private static final String PROGRAM = "res/images/program/";
|
||||
private static final String TILES = "res/images/tiles/";
|
||||
private static final String SPRITES = "res/images/sprites/";
|
||||
private static final String PARTICLES = "res/images/particles/";
|
||||
|
||||
|
||||
/**
|
||||
* Load what's needed for splash
|
||||
*/
|
||||
public static void loadForSplash()
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
logo = TextureManager.load(GUI + "logo.png");
|
||||
steel_small_dk = TextureManager.load(PATTERNS + "steel.png");
|
||||
|
||||
Tx.initForSplash();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load textures
|
||||
*/
|
||||
public static void load()
|
||||
{
|
||||
// gui
|
||||
buttons_menu = TextureManager.load(GUI + "buttons_menu.png");
|
||||
buttons_small = TextureManager.load(GUI + "buttons_small.png");
|
||||
widgets = TextureManager.load(GUI + "widgets.png");
|
||||
icons = TextureManager.load(GUI + "icons.png");
|
||||
|
||||
// patterns
|
||||
|
||||
steel_small_lt = TextureManager.load(PATTERNS + "steel_light.png"); // XXX Unused texture
|
||||
steel_big_dk = TextureManager.load(PATTERNS + "steel_big.png");
|
||||
steel_big_lt = TextureManager.load(PATTERNS + "steel_big_light.png");
|
||||
steel_big_scratched = TextureManager.load(PATTERNS + "steel_big_scratched.png");
|
||||
steel_brushed = TextureManager.load(PATTERNS + "steel_brushed.png");
|
||||
steel_rivet_belts = TextureManager.load(PATTERNS + "rivet_belts.png");
|
||||
water = TextureManager.load(PATTERNS + "water.png");
|
||||
|
||||
// particles
|
||||
snow = TextureManager.load(PARTICLES + "snow.png");
|
||||
leaves = TextureManager.load(PARTICLES + "leaves.png");
|
||||
rain = TextureManager.load(PARTICLES + "rain.png");
|
||||
circle = TextureManager.load(PARTICLES + "circle.png");
|
||||
|
||||
// program tiles
|
||||
program = TextureManager.load(PROGRAM + "program.png");
|
||||
|
||||
// map tiles
|
||||
map_tiles = TextureManager.load(TILES + "tiles.png");
|
||||
map_shading = TextureManager.load(TILES + "shading.png");
|
||||
|
||||
// turtle
|
||||
turtle_blue = TextureManager.load(SPRITES + "HD-blue.png");
|
||||
turtle_yellow = TextureManager.load(SPRITES + "HD-yellow.png");
|
||||
turtle_green = TextureManager.load(SPRITES + "HD-green.png");
|
||||
turtle_purple = TextureManager.load(SPRITES + "HD-purple.png");
|
||||
turtle_red = TextureManager.load(SPRITES + "HD-red.png");
|
||||
|
||||
turtle_brown_dk = TextureManager.load(SPRITES + "HD-brown-dk.png");
|
||||
turtle_brown_lt = TextureManager.load(SPRITES + "HD-brown-lt.png");
|
||||
turtle_brown_orn = TextureManager.load(SPRITES + "HD-brown-ornamental.png");
|
||||
turtle_shadows = TextureManager.load(SPRITES + "HD-shadow.png");
|
||||
|
||||
food = TextureManager.load(SPRITES + "food.png");
|
||||
food_shadows = TextureManager.load(SPRITES + "food-shadow.png");
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
Tx.init();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package mightypork.rogue.textures;
|
||||
|
||||
|
||||
/**
|
||||
* List of texture quads for GUIs
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public class Tx {
|
||||
|
||||
// logo
|
||||
public static TxQuad LOGO;
|
||||
|
||||
|
||||
public static void initForSplash()
|
||||
{
|
||||
// splash logo
|
||||
LOGO = TxQuad.fromSize(Textures.logo, 15, 9, 226, 132);
|
||||
}
|
||||
|
||||
|
||||
public static void init()
|
||||
{
|
||||
// title image (word art)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package mightypork.rogue.textures;
|
||||
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Rect;
|
||||
|
||||
import org.newdawn.slick.opengl.Texture;
|
||||
|
||||
|
||||
/**
|
||||
* Texture Quad (describing a part of a texture)
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class TxQuad {
|
||||
|
||||
/** The texture */
|
||||
public Texture tx;
|
||||
/** Coords in texture (pixels) */
|
||||
public Rect uvs;
|
||||
/** Quad size */
|
||||
public Coord size;
|
||||
|
||||
|
||||
/**
|
||||
* Create TxQuad from left top coord and rect size
|
||||
*
|
||||
* @param tx texture
|
||||
* @param x1 left top X
|
||||
* @param y1 left top Y
|
||||
* @param width area width
|
||||
* @param height area height
|
||||
* @return new TxQuad
|
||||
*/
|
||||
public static TxQuad fromSize(Texture tx, int x1, int y1, int width, int height)
|
||||
{
|
||||
return new TxQuad(tx, x1, y1, x1 + width, y1 + height);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param tx Texture
|
||||
* @param uvs Rect of texturwe UVs (pixels - from left top)
|
||||
*/
|
||||
public TxQuad(Texture tx, Rect uvs) {
|
||||
this.tx = tx;
|
||||
this.uvs = uvs.copy();
|
||||
this.size = uvs.getSize();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make of coords
|
||||
*
|
||||
* @param tx texture
|
||||
* @param x1 x1
|
||||
* @param y1 y1
|
||||
* @param x2 x2
|
||||
* @param y2 y2
|
||||
*/
|
||||
public TxQuad(Texture tx, int x1, int y1, int x2, int y2) {
|
||||
this.tx = tx;
|
||||
this.uvs = new Rect(x1, y1, x2, y2);
|
||||
this.size = uvs.getSize();
|
||||
}
|
||||
|
||||
|
||||
public TxQuad copy()
|
||||
{
|
||||
return new TxQuad(tx, uvs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package mightypork.rogue.threads;
|
||||
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import mightypork.rogue.Paths;
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Thread for saving screenshot
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ThreadSaveScreenshot extends Thread {
|
||||
|
||||
private ByteBuffer buffer;
|
||||
private int width;
|
||||
private int height;
|
||||
private int bpp;
|
||||
|
||||
|
||||
/**
|
||||
* Save screenshot thread
|
||||
*
|
||||
* @param buffer byte buffer with image data
|
||||
* @param width screen width
|
||||
* @param height screen height
|
||||
* @param bpp bits per pixel
|
||||
*/
|
||||
public ThreadSaveScreenshot(ByteBuffer buffer, int width, int height, int bpp) {
|
||||
this.buffer = buffer;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.bpp = bpp;
|
||||
}
|
||||
|
||||
|
||||
private String getUniqueScreenshotName()
|
||||
{
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
|
||||
return df.format(new Date());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
String fname = getUniqueScreenshotName();
|
||||
|
||||
// generate unique filename
|
||||
File file;
|
||||
int index = 0;
|
||||
while (true) {
|
||||
file = new File(Paths.SCREENSHOTS, fname + (index > 0 ? "-" + index : "") + ".png");
|
||||
if (!file.exists()) break;
|
||||
index++;
|
||||
}
|
||||
|
||||
Log.f3("Saving screenshot to file: " + file);
|
||||
|
||||
String format = "PNG";
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
// convert to a buffered image
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
int i = (x + (width * y)) * bpp;
|
||||
int r = buffer.get(i) & 0xFF;
|
||||
int g = buffer.get(i + 1) & 0xFF;
|
||||
int b = buffer.get(i + 2) & 0xFF;
|
||||
image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
|
||||
}
|
||||
}
|
||||
|
||||
// save to disk
|
||||
try {
|
||||
ImageIO.write(image, format, file);
|
||||
} catch (IOException e) {
|
||||
Log.e("Failed to save screenshot.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package mightypork.rogue.threads;
|
||||
|
||||
|
||||
import mightypork.rogue.App;
|
||||
import mightypork.rogue.input.Keys;
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
|
||||
/**
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ThreadScreenshotTrigger extends Thread {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true) {
|
||||
if (Keys.justPressed(Keyboard.KEY_F2)) {
|
||||
Log.f2("F2, taking screenshot.");
|
||||
App.scheduledScreenshot = true;
|
||||
Keys.destroyChangeState(Keyboard.KEY_F2);
|
||||
}
|
||||
try {
|
||||
sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,919 @@
|
||||
package mightypork.rogue.util;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import mightypork.rogue.textures.TextureManager;
|
||||
import mightypork.rogue.textures.TxQuad;
|
||||
import mightypork.utils.math.color.HSV;
|
||||
import mightypork.utils.math.color.RGB;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Rect;
|
||||
|
||||
import org.newdawn.slick.opengl.Texture;
|
||||
|
||||
|
||||
/**
|
||||
* Render utilities
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class RenderUtils {
|
||||
|
||||
/**
|
||||
* Render quad 2D
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
*/
|
||||
public static void quadCoord(Coord min, Coord max)
|
||||
{
|
||||
quadCoord(min.x, min.y, max.x, max.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with absolute coords (from screen bottom)
|
||||
*
|
||||
* @param left left x
|
||||
* @param bottom bottom y
|
||||
* @param right right x
|
||||
* @param top top y
|
||||
*/
|
||||
public static void quadCoord(double left, double bottom, double right, double top)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2d(left, top);
|
||||
glVertex2d(right, top);
|
||||
glVertex2d(right, bottom);
|
||||
glVertex2d(left, bottom);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with coloured border
|
||||
*
|
||||
* @param min min
|
||||
* @param max max
|
||||
* @param border border width
|
||||
* @param borderColor border color
|
||||
* @param insideColor filling color
|
||||
*/
|
||||
public static void quadCoordBorder(Coord min, Coord max, double border, RGB borderColor, RGB insideColor)
|
||||
{
|
||||
quadCoordBorder(min.x, min.y, max.x, max.y, border, borderColor, insideColor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with coloured border
|
||||
*
|
||||
* @param minX min x
|
||||
* @param minY min y
|
||||
* @param maxX max x
|
||||
* @param maxY max y
|
||||
* @param border border width
|
||||
* @param borderColor border color
|
||||
* @param insideColor filling color
|
||||
*/
|
||||
public static void quadCoordBorder(double minX, double minY, double maxX, double maxY, double border, RGB borderColor, RGB insideColor)
|
||||
{
|
||||
double bdr = border;
|
||||
|
||||
RenderUtils.setColor(borderColor);
|
||||
|
||||
//@formatter:off
|
||||
RenderUtils.quadCoord(minX, minY, minX + bdr, maxY);
|
||||
RenderUtils.quadCoord(maxX - bdr, minY, maxX, maxY);
|
||||
RenderUtils.quadCoord(minX + bdr, maxY - bdr, maxX - bdr, maxY);
|
||||
RenderUtils.quadCoord(minX + bdr, minY, maxX - bdr, minY + bdr);
|
||||
//@formatter:on
|
||||
|
||||
if (insideColor != null) {
|
||||
RenderUtils.setColor(insideColor);
|
||||
RenderUtils.quadCoord(minX + bdr, minY + bdr, maxX - bdr, maxY - bdr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient horizontal
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @param colorLeft color left
|
||||
* @param colorRight color right
|
||||
*/
|
||||
public static void quadCoordGradH(Coord min, Coord max, RGB colorLeft, RGB colorRight)
|
||||
{
|
||||
quadCoordGradH(min.x, min.y, max.x, max.y, colorLeft, colorRight);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient horizontal
|
||||
*
|
||||
* @param minX units from left
|
||||
* @param minY units from bottom
|
||||
* @param maxX quad width
|
||||
* @param maxY quad height
|
||||
* @param colorLeft color left
|
||||
* @param colorRight color right
|
||||
*/
|
||||
public static void quadCoordGradH(double minX, double minY, double maxX, double maxY, RGB colorLeft, RGB colorRight)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
setColor(colorLeft);
|
||||
glVertex2d(minX, maxY);
|
||||
setColor(colorRight);
|
||||
glVertex2d(maxX, maxY);
|
||||
|
||||
setColor(colorRight);
|
||||
glVertex2d(maxX, minY);
|
||||
setColor(colorLeft);
|
||||
glVertex2d(minX, minY);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient horizontal
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @param colorOuter color outer
|
||||
* @param colorMiddle color inner
|
||||
*/
|
||||
public static void quadCoordGradHBilinear(Coord min, Coord max, RGB colorOuter, RGB colorMiddle)
|
||||
{
|
||||
quadCoordGradHBilinear(min.x, min.y, max.x, max.y, colorOuter, colorMiddle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient horizontal
|
||||
*
|
||||
* @param minX min x
|
||||
* @param minY min y
|
||||
* @param maxX max x
|
||||
* @param maxY max y
|
||||
* @param colorOuter color outer
|
||||
* @param colorMiddle color inner
|
||||
*/
|
||||
public static void quadCoordGradHBilinear(double minX, double minY, double maxX, double maxY, RGB colorOuter, RGB colorMiddle)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
setColor(colorOuter);
|
||||
glVertex2d(minX, maxY);
|
||||
setColor(colorMiddle);
|
||||
glVertex2d((minX + maxX) / 2, maxY);
|
||||
|
||||
setColor(colorMiddle);
|
||||
glVertex2d((minX + maxX) / 2, minY);
|
||||
setColor(colorOuter);
|
||||
glVertex2d(minX, minY);
|
||||
|
||||
setColor(colorMiddle);
|
||||
glVertex2d((minX + maxX) / 2, maxY);
|
||||
setColor(colorOuter);
|
||||
glVertex2d(maxX, maxY);
|
||||
|
||||
setColor(colorOuter);
|
||||
glVertex2d(maxX, minY);
|
||||
setColor(colorMiddle);
|
||||
glVertex2d((minX + maxX) / 2, minY);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient vertical
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @param colorTop top color
|
||||
* @param colorBottom bottom color
|
||||
*/
|
||||
public static void quadCoordGradV(Coord min, Coord max, RGB colorTop, RGB colorBottom)
|
||||
{
|
||||
quadCoordGradV(min.x, min.y, max.x, max.y, colorTop, colorBottom);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient vertical
|
||||
*
|
||||
* @param minX min X
|
||||
* @param minY min Y
|
||||
* @param maxX max X
|
||||
* @param maxY max Y
|
||||
* @param colorTop top color
|
||||
* @param colorBottom bottom color
|
||||
*/
|
||||
public static void quadCoordGradV(double minX, double minY, double maxX, double maxY, RGB colorTop, RGB colorBottom)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
setColor(colorTop);
|
||||
glVertex2d(minX, maxY);
|
||||
glVertex2d(maxX, maxY);
|
||||
|
||||
setColor(colorBottom);
|
||||
glVertex2d(maxX, minY);
|
||||
glVertex2d(minX, minY);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient vertical
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @param colorOuter outer color
|
||||
* @param colorMiddle middle color
|
||||
*/
|
||||
public static void quadCoordGradVBilinear(Coord min, Coord max, RGB colorOuter, RGB colorMiddle)
|
||||
{
|
||||
quadCoordGradVBilinear(min.x, min.y, max.x, max.y, colorOuter, colorMiddle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient vertical
|
||||
*
|
||||
* @param minX min X
|
||||
* @param minY min Y
|
||||
* @param maxX max X
|
||||
* @param maxY max Y
|
||||
* @param colorOuter outer color
|
||||
* @param colorMiddle middle color
|
||||
*/
|
||||
public static void quadCoordGradVBilinear(double minX, double minY, double maxX, double maxY, RGB colorOuter, RGB colorMiddle)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
setColor(colorOuter);
|
||||
glVertex2d(minX, maxY);
|
||||
glVertex2d(maxX, maxY);
|
||||
|
||||
setColor(colorMiddle);
|
||||
glVertex2d(maxX, (maxY + minY) / 2);
|
||||
glVertex2d(minX, (maxY + minY) / 2);
|
||||
|
||||
setColor(colorMiddle);
|
||||
glVertex2d(minX, (maxY + minY) / 2);
|
||||
glVertex2d(maxX, (maxY + minY) / 2);
|
||||
|
||||
setColor(colorOuter);
|
||||
glVertex2d(maxX, minY);
|
||||
glVertex2d(minX, minY);
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with coloured border with outset effect
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @param border border width
|
||||
* @param fill fill color
|
||||
* @param inset true for inset, false for outset
|
||||
*/
|
||||
public static void quadCoordOutset(Coord min, Coord max, double border, RGB fill, boolean inset)
|
||||
{
|
||||
quadCoordOutset(min.x, min.y, max.x, max.y, border, fill, inset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with coloured border with outset effect
|
||||
*
|
||||
* @param minX left X
|
||||
* @param minY bottom Y
|
||||
* @param maxX right X
|
||||
* @param maxY top Y
|
||||
* @param border border width
|
||||
* @param fill fill color
|
||||
* @param inset true for inset, false for outset
|
||||
*/
|
||||
public static void quadCoordOutset(double minX, double minY, double maxX, double maxY, double border, RGB fill, boolean inset)
|
||||
{
|
||||
HSV hsv = fill.toHSV();
|
||||
HSV h;
|
||||
|
||||
h = hsv.copy();
|
||||
h.s *= 0.6;
|
||||
RGB a = h.toRGB();
|
||||
|
||||
h = fill.toHSV();
|
||||
h.v *= 0.6;
|
||||
RGB b = h.toRGB();
|
||||
|
||||
RGB leftTopC, rightBottomC;
|
||||
|
||||
if (!inset) {
|
||||
leftTopC = a;
|
||||
rightBottomC = b;
|
||||
} else {
|
||||
leftTopC = b;
|
||||
rightBottomC = a;
|
||||
}
|
||||
|
||||
double bdr = border;
|
||||
|
||||
RenderUtils.setColor(leftTopC);
|
||||
|
||||
// left + top
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2d(minX, minY);
|
||||
glVertex2d(minX + bdr, minY + bdr);
|
||||
glVertex2d(minX + bdr, maxY - bdr);
|
||||
glVertex2d(minX, maxY);
|
||||
|
||||
glVertex2d(minX, maxY);
|
||||
glVertex2d(minX + bdr, maxY - bdr);
|
||||
glVertex2d(maxX - bdr, maxY - bdr);
|
||||
glVertex2d(maxX, maxY);
|
||||
glEnd();
|
||||
|
||||
RenderUtils.setColor(rightBottomC);
|
||||
|
||||
// right + bottom
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2d(maxX, minY);
|
||||
glVertex2d(maxX, maxY);
|
||||
glVertex2d(maxX - bdr, maxY - bdr);
|
||||
glVertex2d(maxX - bdr, minY + bdr);
|
||||
|
||||
glVertex2d(minX, minY);
|
||||
glVertex2d(maxX, minY);
|
||||
glVertex2d(maxX - bdr, minY + bdr);
|
||||
glVertex2d(minX + bdr, minY + bdr);
|
||||
glEnd();
|
||||
|
||||
RenderUtils.setColor(fill);
|
||||
RenderUtils.quadCoord(minX + bdr, minY + bdr, maxX - bdr, maxY - bdr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D
|
||||
*
|
||||
* @param rect rectangle
|
||||
*/
|
||||
public static void quadRect(Rect rect)
|
||||
{
|
||||
quadCoord(rect.getMin(), rect.getMax());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D
|
||||
*
|
||||
* @param rect rectangle
|
||||
* @param color draw color
|
||||
*/
|
||||
public static void quadRect(Rect rect, RGB color)
|
||||
{
|
||||
setColor(color);
|
||||
quadCoord(rect.getMin(), rect.getMax());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with coloured border
|
||||
*
|
||||
* @param rect rect
|
||||
* @param border border width
|
||||
* @param borderColor border color
|
||||
* @param insideColor filling color
|
||||
*/
|
||||
public static void quadBorder(Rect rect, double border, RGB borderColor, RGB insideColor)
|
||||
{
|
||||
quadCoordBorder(rect.getMin(), rect.getMax(), border, borderColor, insideColor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient horizontal
|
||||
*
|
||||
* @param rect rect
|
||||
* @param colorLeft color left
|
||||
* @param colorRight color right
|
||||
*/
|
||||
public static void quadGradH(Rect rect, RGB colorLeft, RGB colorRight)
|
||||
{
|
||||
quadCoordGradH(rect.getMin(), rect.getMax(), colorLeft, colorRight);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient horizontal
|
||||
*
|
||||
* @param rect rect
|
||||
* @param colorOuter color outer
|
||||
* @param colorMiddle color inner
|
||||
*/
|
||||
public static void quadGradHBilinear(Rect rect, RGB colorOuter, RGB colorMiddle)
|
||||
{
|
||||
quadCoordGradHBilinear(rect.getMin(), rect.getMax(), colorOuter, colorMiddle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient vertical
|
||||
*
|
||||
* @param rect rect
|
||||
* @param colorTop top color
|
||||
* @param colorBottom bottom color
|
||||
*/
|
||||
public static void quadGradV(Rect rect, RGB colorTop, RGB colorBottom)
|
||||
{
|
||||
quadCoordGradV(rect.getMin(), rect.getMax(), colorTop, colorBottom);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D with gradient vertical
|
||||
*
|
||||
* @param rect rect
|
||||
* @param colorOuter outer color
|
||||
* @param colorMiddle middle color
|
||||
*/
|
||||
public static void quadGradVBilinear(Rect rect, RGB colorOuter, RGB colorMiddle)
|
||||
{
|
||||
quadCoordGradVBilinear(rect.getMin(), rect.getMax(), colorOuter, colorMiddle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad with coloured border with outset effect
|
||||
*
|
||||
* @param rect rectangle
|
||||
* @param border border width
|
||||
* @param fill fill color
|
||||
* @param inset true for inset, false for outset
|
||||
*/
|
||||
public static void quadRectOutset(Rect rect, double border, RGB fill, boolean inset)
|
||||
{
|
||||
quadCoordOutset(rect.getMin(), rect.getMax(), border, fill, inset);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured rect (texture must be binded already)
|
||||
*
|
||||
* @param quad rectangle (px)
|
||||
* @param textureCoords texture coords (0-1)
|
||||
*/
|
||||
public static void quadTexturedAbs(Rect quad, Rect textureCoords)
|
||||
{
|
||||
double left = quad.x1();
|
||||
double bottom = quad.y2();
|
||||
double right = quad.x2();
|
||||
double top = quad.y1();
|
||||
|
||||
double tleft = textureCoords.x1();
|
||||
double tbottom = textureCoords.y1();
|
||||
double tright = textureCoords.x2();
|
||||
double ttop = textureCoords.y2();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2d(tleft, ttop);
|
||||
glVertex2d(left, top);
|
||||
glTexCoord2d(tright, ttop);
|
||||
glVertex2d(right, top);
|
||||
glTexCoord2d(tright, tbottom);
|
||||
glVertex2d(right, bottom);
|
||||
glTexCoord2d(tleft, tbottom);
|
||||
glVertex2d(left, bottom);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured rect
|
||||
*
|
||||
* @param quad rectangle (px)
|
||||
* @param txCoords texture coords rectangle (px)
|
||||
* @param texture texture instance
|
||||
*/
|
||||
public static void quadTextured(Rect quad, Rect txCoords, Texture texture)
|
||||
{
|
||||
quadTextured(quad, txCoords, texture, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured rect
|
||||
*
|
||||
* @param quad rectangle (px)
|
||||
* @param txCoords texture coords rectangle (px)
|
||||
* @param texture texture instance
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTextured(Rect quad, Rect txCoords, Texture texture, RGB tint)
|
||||
{
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
setColor(tint);
|
||||
TextureManager.bind(texture);
|
||||
|
||||
quadTexturedAbs(quad, txCoords.div(texture.getImageHeight()));
|
||||
|
||||
TextureManager.unbind();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured rect
|
||||
*
|
||||
* @param quad rectangle (px)
|
||||
* @param txquad texture quad
|
||||
*/
|
||||
public static void quadTextured(Rect quad, TxQuad txquad)
|
||||
{
|
||||
quadTextured(quad, txquad, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured rect
|
||||
*
|
||||
* @param quad rectangle (px)
|
||||
* @param txquad texture instance
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTextured(Rect quad, TxQuad txquad, RGB tint)
|
||||
{
|
||||
quadTextured(quad, txquad.uvs, txquad.tx, tint);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame with borders
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param yOffsetTimes offset count (move frame down n times)
|
||||
* @param texture the texture
|
||||
*/
|
||||
public static void quadTexturedFrame(Rect quadRect, Rect textureRect, int yOffsetTimes, Texture texture)
|
||||
{
|
||||
quadTexturedFrame(quadRect, textureRect, yOffsetTimes, texture, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame with borders
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param yOffsetTimes offset count (move frame down n times)
|
||||
* @param texture the texture
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTexturedFrame(Rect quadRect, Rect textureRect, int yOffsetTimes, Texture texture, RGB tint)
|
||||
{
|
||||
Texture tx = texture;
|
||||
|
||||
int frameHeight = (int) textureRect.getSize().y;
|
||||
|
||||
int yOffset = yOffsetTimes * frameHeight;
|
||||
|
||||
double x1 = quadRect.x1();
|
||||
double y1 = quadRect.y1();
|
||||
double x2 = quadRect.x2();
|
||||
double y2 = quadRect.y2();
|
||||
|
||||
double w = x2 - x1;
|
||||
double h = y2 - y1;
|
||||
|
||||
double tx1 = textureRect.x1();
|
||||
double ty1 = textureRect.y1();
|
||||
double tx2 = textureRect.x2();
|
||||
double ty2 = textureRect.y2();
|
||||
|
||||
double halfY = h / 2D;
|
||||
double halfX = w / 2D;
|
||||
|
||||
// lt, rt
|
||||
// lb, rb
|
||||
|
||||
Rect verts, uvs;
|
||||
|
||||
// lt
|
||||
verts = new Rect(x1, y2, x1 + halfX, y2 - halfY);
|
||||
uvs = new Rect(tx1, ty1, tx1 + halfX, ty1 + halfY);
|
||||
uvs.add_ip(0, yOffset);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// lb
|
||||
verts = new Rect(x1, y2 - halfY, x1 + halfX, y1);
|
||||
uvs = new Rect(tx1, ty2 - halfY, tx1 + halfX, ty2);
|
||||
uvs.add_ip(0, yOffset);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// rt
|
||||
verts = new Rect(x1 + halfX, y2, x2, y2 - halfY);
|
||||
uvs = new Rect(tx2 - halfX, ty1, tx2, ty1 + halfY);
|
||||
uvs.add_ip(0, yOffset);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// rb
|
||||
verts = new Rect(x1 + halfX, y2 - halfY, x2, y1);
|
||||
uvs = new Rect(tx2 - halfX, ty2 - halfY, tx2, ty2);
|
||||
uvs.add_ip(0, yOffset);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame with borders
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param texture the texture
|
||||
*/
|
||||
public static void quadTexturedFrame(Rect quadRect, Rect textureRect, Texture texture)
|
||||
{
|
||||
quadTexturedFrame(quadRect, textureRect, 0, texture, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame with borders
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
*/
|
||||
public static void quadTexturedFrame(Rect quadRect, TxQuad txquad)
|
||||
{
|
||||
quadTexturedFrame(quadRect, txquad, 0, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame with borders
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
* @param yOffsetTimes offset count (move frame down n times)
|
||||
*/
|
||||
public static void quadTexturedFrame(Rect quadRect, TxQuad txquad, int yOffsetTimes)
|
||||
{
|
||||
quadTexturedFrame(quadRect, txquad, yOffsetTimes, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame with borders
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
* @param yOffsetTimes offset count (move frame down n times)
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTexturedFrame(Rect quadRect, TxQuad txquad, int yOffsetTimes, RGB tint)
|
||||
{
|
||||
quadTexturedFrame(quadRect, txquad.uvs, yOffsetTimes, txquad.tx, tint);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching horizontally (rect height = texture rect
|
||||
* height)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
* @param texture the texture
|
||||
*/
|
||||
public static void quadTexturedStretchH(Rect quadRect, Rect textureRect, int borderSize, Texture texture)
|
||||
{
|
||||
quadTexturedStretchH(quadRect, textureRect, borderSize, texture, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching horizontally (rect height = texture rect
|
||||
* height)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
* @param texture the texture
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTexturedStretchH(Rect quadRect, Rect textureRect, int borderSize, Texture texture, RGB tint)
|
||||
{
|
||||
Texture tx = texture;
|
||||
|
||||
double x1 = quadRect.x1();
|
||||
double y1 = quadRect.y1();
|
||||
double x2 = quadRect.x2();
|
||||
double y2 = quadRect.y2();
|
||||
|
||||
double tx1 = textureRect.x1();
|
||||
double ty1 = textureRect.y1();
|
||||
double tx2 = textureRect.x2();
|
||||
double ty2 = textureRect.y2();
|
||||
|
||||
// lt, mi, rt
|
||||
|
||||
Rect verts, uvs;
|
||||
|
||||
// lt
|
||||
verts = new Rect(x1, y2, x1 + borderSize, y1);
|
||||
uvs = new Rect(tx1, ty1, tx1 + borderSize, ty2);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// // mi
|
||||
verts = new Rect(x1 + borderSize, y2, x2 - borderSize, y1);
|
||||
uvs = new Rect(tx1 + borderSize, ty1, tx2 - borderSize, ty2);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// rt
|
||||
verts = new Rect(x2 - borderSize, y2, x2, y1);
|
||||
uvs = new Rect(tx2 - borderSize, ty1, tx2, ty2);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching horizontally (rect height = texture rect
|
||||
* height)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
*/
|
||||
public static void quadTexturedStretchH(Rect quadRect, TxQuad txquad, int borderSize)
|
||||
{
|
||||
quadTexturedStretchH(quadRect, txquad, borderSize, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching horizontally (rect height = texture rect
|
||||
* height)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTexturedStretchH(Rect quadRect, TxQuad txquad, int borderSize, RGB tint)
|
||||
{
|
||||
quadTexturedStretchH(quadRect, txquad.uvs, borderSize, txquad.tx, tint);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching vertically (rect width = texture rect
|
||||
* width)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
* @param texture the texture
|
||||
*/
|
||||
public static void quadTexturedStretchV(Rect quadRect, Rect textureRect, int borderSize, Texture texture)
|
||||
{
|
||||
quadTexturedStretchV(quadRect, textureRect, borderSize, texture, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching vertically (rect width = texture rect
|
||||
* width)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param textureRect rectangle in texture with the basic frame (px)
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
* @param texture the texture
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTexturedStretchV(Rect quadRect, Rect textureRect, int borderSize, Texture texture, RGB tint)
|
||||
{
|
||||
Texture tx = texture;
|
||||
|
||||
double x1 = quadRect.x1();
|
||||
double y1 = quadRect.y1();
|
||||
double x2 = quadRect.x2();
|
||||
double y2 = quadRect.y2();
|
||||
|
||||
double tx1 = textureRect.x1();
|
||||
double ty1 = textureRect.y1();
|
||||
double tx2 = textureRect.x2();
|
||||
double ty2 = textureRect.y2();
|
||||
|
||||
// tp
|
||||
// mi
|
||||
// bn
|
||||
|
||||
Rect verts, uvs;
|
||||
|
||||
// tp
|
||||
verts = new Rect(x1, y2, x2, y2 - borderSize);
|
||||
uvs = new Rect(tx1, ty1, tx2, ty1 + borderSize);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// mi
|
||||
verts = new Rect(x1, y2 - borderSize, x2, y1 + borderSize);
|
||||
uvs = new Rect(tx1, ty1 + borderSize, tx2, ty2 - borderSize);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
|
||||
// rt
|
||||
verts = new Rect(x1, y1 + borderSize, x2, y1);
|
||||
uvs = new Rect(tx1, ty2 - borderSize, tx2, ty2);
|
||||
quadTextured(verts, uvs, tx, tint);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching vertically (rect width = texture rect
|
||||
* width)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
*/
|
||||
public static void quadTexturedStretchV(Rect quadRect, TxQuad txquad, int borderSize)
|
||||
{
|
||||
quadTexturedStretchV(quadRect, txquad, borderSize, RGB.WHITE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render textured frame stretching vertically (rect width = texture rect
|
||||
* width)
|
||||
*
|
||||
* @param quadRect drawn rectangle (px)
|
||||
* @param txquad texture quad
|
||||
* @param borderSize size of the unstretched horizontal border
|
||||
* @param tint color tint
|
||||
*/
|
||||
public static void quadTexturedStretchV(Rect quadRect, TxQuad txquad, int borderSize, RGB tint)
|
||||
{
|
||||
quadTexturedStretchV(quadRect, txquad.uvs, borderSize, txquad.tx, tint);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render quad 2D
|
||||
*
|
||||
* @param left units from left
|
||||
* @param bottom units from bottom
|
||||
* @param width quad width
|
||||
* @param height quad height
|
||||
*/
|
||||
public static void quadSize(double left, double bottom, double width, double height)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2d(left, bottom + height);
|
||||
glVertex2d(left + width, bottom + height);
|
||||
glVertex2d(left + width, bottom);
|
||||
glVertex2d(left, bottom);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind GL color
|
||||
*
|
||||
* @param color RGB color
|
||||
*/
|
||||
public static void setColor(RGB color)
|
||||
{
|
||||
if (color != null) glColor4d(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bind GL color
|
||||
*
|
||||
* @param color RGB color
|
||||
* @param alpha alpha multiplier
|
||||
*/
|
||||
public static void setColor(RGB color, double alpha)
|
||||
{
|
||||
if (color != null) glColor4d(color.r, color.g, color.b, color.a * alpha);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translate with coord
|
||||
*
|
||||
* @param coord coord
|
||||
*/
|
||||
public static void translate(Coord coord)
|
||||
{
|
||||
glTranslated(coord.x, coord.y, coord.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package mightypork.rogue.util;
|
||||
|
||||
|
||||
/**
|
||||
* Utils class
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Utils {
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.CheckedInputStream;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
public class FileTreeDiff {
|
||||
|
||||
private static final byte[] BUFFER = new byte[2048];
|
||||
private Checksum ck1 = new Adler32();
|
||||
private Checksum ck2 = new Adler32();
|
||||
|
||||
private boolean logging = true;
|
||||
|
||||
private List<Tuple<File>> compared = new ArrayList<Tuple<File>>();
|
||||
private Comparator<File> fileFirstSorter = new Comparator<File>() {
|
||||
|
||||
@Override
|
||||
public int compare(File o1, File o2)
|
||||
{
|
||||
if (!o1.isDirectory() && o2.isDirectory()) return -1;
|
||||
if (o1.isDirectory() && !o2.isDirectory()) return 1;
|
||||
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void enableLogging(boolean state)
|
||||
{
|
||||
logging = state;
|
||||
}
|
||||
|
||||
|
||||
public boolean areEqual(File dir1, File dir2)
|
||||
{
|
||||
if (logging) Log.f3("Comparing directory trees:\n 1. " + dir1 + "\n 2. " + dir2);
|
||||
|
||||
try {
|
||||
compared.clear();
|
||||
buildList(dir1, dir2);
|
||||
|
||||
calcChecksum();
|
||||
|
||||
if (logging) Log.f3("No difference found.");
|
||||
|
||||
return true;
|
||||
|
||||
} catch (NotEqualException e) {
|
||||
if (logging) Log.f3("Difference found:\n" + e.getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void calcChecksum() throws NotEqualException
|
||||
{
|
||||
FileInputStream in1, in2;
|
||||
CheckedInputStream cin1 = null, cin2 = null;
|
||||
|
||||
for (Tuple<File> pair : compared) {
|
||||
try {
|
||||
ck1.reset();
|
||||
ck2.reset();
|
||||
|
||||
in1 = new FileInputStream(pair.a);
|
||||
in2 = new FileInputStream(pair.b);
|
||||
|
||||
cin1 = new CheckedInputStream(in1, ck1);
|
||||
cin2 = new CheckedInputStream(in2, ck2);
|
||||
|
||||
while (true) {
|
||||
int read1 = cin1.read(BUFFER);
|
||||
int read2 = cin2.read(BUFFER);
|
||||
|
||||
if (read1 != read2 || ck1.getValue() != ck2.getValue()) {
|
||||
throw new NotEqualException("Bytes differ:\n" + pair.a + "\n" + pair.b);
|
||||
}
|
||||
|
||||
if (read1 == -1) break;
|
||||
}
|
||||
|
||||
} catch (IOException e) {} finally {
|
||||
try {
|
||||
if (cin1 != null) cin1.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if (cin1 != null) cin1.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void buildList(File f1, File f2) throws NotEqualException
|
||||
{
|
||||
if (f1.isDirectory() != f2.isDirectory()) throw new NotEqualException("isDirectory differs:\n" + f1 + "\n" + f2);
|
||||
|
||||
if (f1.isFile() && f2.isFile()) {
|
||||
if (f1.length() != f2.length()) throw new NotEqualException("Sizes differ:\n" + f1 + "\n" + f2);
|
||||
}
|
||||
|
||||
if (f1.isDirectory()) {
|
||||
File[] children1 = f1.listFiles();
|
||||
File[] children2 = f2.listFiles();
|
||||
|
||||
Arrays.sort(children1, fileFirstSorter);
|
||||
Arrays.sort(children2, fileFirstSorter);
|
||||
|
||||
if (children1.length != children2.length) throw new NotEqualException("Child counts differ:\n" + f1 + "\n" + f2);
|
||||
|
||||
for (int i = 0; i < children1.length; i++) {
|
||||
File ch1 = children1[i];
|
||||
File ch2 = children2[i];
|
||||
|
||||
if (!ch1.getName().equals(ch2.getName())) throw new NotEqualException("Filenames differ:\n" + ch1 + "\n" + ch2);
|
||||
|
||||
buildList(ch1, ch2);
|
||||
}
|
||||
|
||||
} else {
|
||||
compared.add(new Tuple<File>(f1, f2));
|
||||
}
|
||||
}
|
||||
|
||||
private class NotEqualException extends Exception {
|
||||
|
||||
public NotEqualException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Tuple<T> {
|
||||
|
||||
public T a;
|
||||
public T b;
|
||||
|
||||
|
||||
public Tuple(T a, T b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.string.StringUtils;
|
||||
import mightypork.utils.string.validation.StringFilter;
|
||||
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* Copy directory recursively.
|
||||
*
|
||||
* @param source source file
|
||||
* @param target target file
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyDirectory(File source, File target) throws IOException
|
||||
{
|
||||
copyDirectory(source, target, null, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy directory recursively - advanced variant.
|
||||
*
|
||||
* @param source source file
|
||||
* @param target target file
|
||||
* @param filter filter accepting only files and dirs to be copied
|
||||
* @param filesCopied list into which all the target files will be added
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyDirectory(File source, File target, FileFilter filter, List<File> filesCopied) throws IOException
|
||||
{
|
||||
if (!source.exists()) return;
|
||||
|
||||
if (source.isDirectory()) {
|
||||
if (!target.exists()) {
|
||||
target.mkdir();
|
||||
}
|
||||
|
||||
String[] children = source.list();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
copyDirectory(new File(source, children[i]), new File(target, children[i]), filter, filesCopied);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (filter != null && !filter.accept(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (filesCopied != null) filesCopied.add(target);
|
||||
copyFile(source, target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List directory recursively
|
||||
*
|
||||
* @param source source file
|
||||
* @param filter filter accepting only files and dirs to be copied (or null)
|
||||
* @param files list of the found files
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void listDirectoryRecursive(File source, StringFilter filter, List<File> files) throws IOException
|
||||
{
|
||||
if (source.isDirectory()) {
|
||||
String[] children = source.list();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
listDirectoryRecursive(new File(source, children[i]), filter, files);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (filter != null && !filter.accept(source.getAbsolutePath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
files.add(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy file using streams. Make sure target directory exists!
|
||||
*
|
||||
* @param source source file
|
||||
* @param target target file
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyFile(File source, File target) throws IOException
|
||||
{
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
in = new FileInputStream(source);
|
||||
out = new FileOutputStream(target);
|
||||
|
||||
copyStream(in, out);
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
if (out != null) out.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy bytes from input to output stream, leaving out stream open
|
||||
*
|
||||
* @param in input stream
|
||||
* @param out output stream
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void copyStream(InputStream in, OutputStream out) throws IOException
|
||||
{
|
||||
if (in == null) {
|
||||
throw new NullPointerException("Input stream is null");
|
||||
}
|
||||
|
||||
if (out == null) {
|
||||
throw new NullPointerException("Output stream is null");
|
||||
}
|
||||
|
||||
byte[] buf = new byte[2048];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Improved delete
|
||||
*
|
||||
* @param path deleted path
|
||||
* @param recursive recursive delete
|
||||
* @return success
|
||||
*/
|
||||
public static boolean delete(File path, boolean recursive)
|
||||
{
|
||||
if (!path.exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!recursive || !path.isDirectory()) return path.delete();
|
||||
|
||||
String[] list = path.list();
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (!delete(new File(path, list[i]), true)) return false;
|
||||
}
|
||||
|
||||
return path.delete();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read entire file to a string.
|
||||
*
|
||||
* @param file file
|
||||
* @return file contents
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String fileToString(File file) throws IOException
|
||||
{
|
||||
FileInputStream fin = new FileInputStream(file);
|
||||
|
||||
return streamToString(fin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get files in a folder (create folder if needed)
|
||||
*
|
||||
* @param dir folder
|
||||
* @return list of files
|
||||
*/
|
||||
public static List<File> listDirectory(File dir)
|
||||
{
|
||||
return FileUtils.listDirectory(dir, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get files in a folder (create folder if needed)
|
||||
*
|
||||
* @param dir folder
|
||||
* @param filter file filter
|
||||
* @return list of files
|
||||
*/
|
||||
public static List<File> listDirectory(File dir, FileFilter filter)
|
||||
{
|
||||
try {
|
||||
dir.mkdir();
|
||||
} catch (RuntimeException e) {
|
||||
Log.e("Error creating folder " + dir, e);
|
||||
}
|
||||
|
||||
List<File> list = new ArrayList<File>();
|
||||
|
||||
try {
|
||||
for (File f : dir.listFiles(filter)) {
|
||||
list.add(f);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("Error listing folder " + dir, e);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove extension.
|
||||
*
|
||||
* @param file file
|
||||
* @return filename without extension
|
||||
*/
|
||||
public static String[] getFilenameParts(File file)
|
||||
{
|
||||
return getFilenameParts(file.getName());
|
||||
}
|
||||
|
||||
|
||||
public static String getExtension(File file)
|
||||
{
|
||||
return getExtension(file.getName());
|
||||
}
|
||||
|
||||
|
||||
public static String getExtension(String file)
|
||||
{
|
||||
return StringUtils.fromLastChar(file, '.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove extension.
|
||||
*
|
||||
* @param filename
|
||||
* @return filename and extension
|
||||
*/
|
||||
public static String[] getFilenameParts(String filename)
|
||||
{
|
||||
String ext, name;
|
||||
|
||||
try {
|
||||
ext = StringUtils.fromLastDot(filename);
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
ext = "";
|
||||
}
|
||||
|
||||
try {
|
||||
name = StringUtils.toLastDot(filename);
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
name = "";
|
||||
Log.w("Error extracting extension from file " + filename);
|
||||
}
|
||||
|
||||
return new String[] { name, ext };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read entire input stream to a string, and close it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @return file contents
|
||||
*/
|
||||
public static String streamToString(InputStream in)
|
||||
{
|
||||
return streamToString(in, -1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read input stream to a string, and close it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @param lines max number of lines (-1 to disable limit)
|
||||
* @return file contents
|
||||
*/
|
||||
public static String streamToString(InputStream in, int lines)
|
||||
{
|
||||
if (in == null) {
|
||||
Log.e(new NullPointerException("Null stream to be converted to String."));
|
||||
return ""; // to avoid NPE's
|
||||
}
|
||||
|
||||
BufferedReader br = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
String line;
|
||||
try {
|
||||
int cnt = 0;
|
||||
br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
while ((line = br.readLine()) != null && (cnt < lines || lines <= 0)) {
|
||||
sb.append(line + "\n");
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (cnt == lines && lines > 0) {
|
||||
sb.append("--- end of preview ---\n");
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(e);
|
||||
} finally {
|
||||
try {
|
||||
if (br != null) br.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static InputStream stringToStream(String text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
|
||||
try {
|
||||
return new ByteArrayInputStream(text.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static InputStream getResource(String path)
|
||||
{
|
||||
return FileUtils.class.getResourceAsStream(path);
|
||||
}
|
||||
|
||||
|
||||
public static String getResourceAsString(String path)
|
||||
{
|
||||
return streamToString(FileUtils.class.getResourceAsStream(path));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save string to file
|
||||
*
|
||||
* @param file file
|
||||
* @param text string
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void stringToFile(File file, String text) throws IOException
|
||||
{
|
||||
PrintStream out = null;
|
||||
try {
|
||||
out = new PrintStream(new FileOutputStream(file), false, "UTF-8");
|
||||
|
||||
out.print(text);
|
||||
|
||||
out.flush();
|
||||
|
||||
} finally {
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void deleteEmptyDirs(File base)
|
||||
{
|
||||
for (File f : listDirectory(base)) {
|
||||
if (!f.isDirectory()) continue;
|
||||
|
||||
deleteEmptyDirs(f);
|
||||
|
||||
List<File> children = listDirectory(f);
|
||||
if (children.size() == 0) {
|
||||
f.delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace special characters with place holders in a filename.
|
||||
*
|
||||
* @param filestring filename string
|
||||
* @return escaped
|
||||
*/
|
||||
public static String escapeFileString(String filestring)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (char c : filestring.toCharArray()) {
|
||||
switch (c) {
|
||||
case '%':
|
||||
sb.append("%%");
|
||||
break;
|
||||
|
||||
case '.':
|
||||
sb.append("%d");
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unescape filename string obtained by escapeFileString().
|
||||
*
|
||||
* @param filestring escaped string
|
||||
* @return clean string
|
||||
*/
|
||||
public static String unescapeFileString(String filestring)
|
||||
{
|
||||
filestring = filestring.replace("%d", ".");
|
||||
filestring = filestring.replace("%%", "%");
|
||||
|
||||
return filestring;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape filename, keeping the same extension
|
||||
*
|
||||
* @param filename filename
|
||||
* @return escaped
|
||||
*/
|
||||
public static String escapeFilename(String filename)
|
||||
{
|
||||
String[] parts = getFilenameParts(filename);
|
||||
|
||||
return escapeFileString(parts[0]) + "." + parts[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unescape filename, keeping the same extension
|
||||
*
|
||||
* @param filename escaped filename
|
||||
* @return unesaped
|
||||
*/
|
||||
public static String unescapeFilename(String filename)
|
||||
{
|
||||
String[] parts = getFilenameParts(filename);
|
||||
|
||||
return unescapeFileString(parts[0]) + "." + parts[1];
|
||||
}
|
||||
|
||||
|
||||
public static String getBasename(String name)
|
||||
{
|
||||
return StringUtils.toLastChar(StringUtils.fromLastChar(name, '/'), '.');
|
||||
}
|
||||
|
||||
|
||||
public static String getFilename(String name)
|
||||
{
|
||||
return StringUtils.fromLastChar(name, '/');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy resource to file
|
||||
*
|
||||
* @param resname resource name
|
||||
* @param file out file
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void resourceToFile(String resname, File file) throws IOException
|
||||
{
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
in = FileUtils.getResource(resname);
|
||||
out = new FileOutputStream(file);
|
||||
|
||||
FileUtils.copyStream(in, out);
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) in.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if (out != null) out.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get resource as string, safely closing streams.
|
||||
*
|
||||
* @param resname resource name
|
||||
* @return resource as string, empty string on failure
|
||||
*/
|
||||
public static String resourceToString(String resname)
|
||||
{
|
||||
InputStream in = FileUtils.getResource(resname);
|
||||
return streamToString(in);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public class OsUtils {
|
||||
|
||||
public static enum EnumOS
|
||||
{
|
||||
linux, macos, solaris, unknown, windows;
|
||||
|
||||
public boolean isLinux()
|
||||
{
|
||||
return this == linux || this == solaris;
|
||||
}
|
||||
|
||||
|
||||
public boolean isMac()
|
||||
{
|
||||
return this == macos;
|
||||
}
|
||||
|
||||
|
||||
public boolean isWindows()
|
||||
{
|
||||
return this == windows;
|
||||
}
|
||||
}
|
||||
|
||||
private static EnumOS cachedOs;
|
||||
|
||||
|
||||
/**
|
||||
* Get App dir, ensure it exists
|
||||
*
|
||||
* @param dirname
|
||||
* @return app dir
|
||||
*/
|
||||
public static File getWorkDir(String dirname)
|
||||
{
|
||||
return getWorkDir(dirname, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get App sub-folder
|
||||
*
|
||||
* @param dirname
|
||||
* @param subfolderName
|
||||
* @param create
|
||||
* @return the folder
|
||||
*/
|
||||
public static File getWorkDir(String dirname, String subfolderName, boolean create)
|
||||
{
|
||||
File f = new File(getWorkDir(dirname), subfolderName);
|
||||
|
||||
if (!f.exists() && create) {
|
||||
if (!f.mkdirs()) {
|
||||
throw new RuntimeException("Could not create.");
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get App sub-folder, create
|
||||
*
|
||||
* @param dirname
|
||||
* @param subfolderName
|
||||
* @return the folder
|
||||
*/
|
||||
public static File getWorkDir(String dirname, String subfolderName)
|
||||
{
|
||||
return getWorkDir(dirname, subfolderName, true);
|
||||
}
|
||||
|
||||
|
||||
public static EnumOS getOs()
|
||||
{
|
||||
if (cachedOs != null) return cachedOs;
|
||||
|
||||
String s = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
if (s.contains("win")) {
|
||||
cachedOs = EnumOS.windows;
|
||||
|
||||
} else if (s.contains("mac")) {
|
||||
cachedOs = EnumOS.macos;
|
||||
|
||||
} else if (s.contains("linux") || s.contains("unix")) {
|
||||
cachedOs = EnumOS.linux;
|
||||
|
||||
} else if (s.contains("solaris") || s.contains("sunos")) {
|
||||
cachedOs = EnumOS.solaris;
|
||||
|
||||
} else {
|
||||
cachedOs = EnumOS.unknown;
|
||||
}
|
||||
|
||||
return cachedOs;
|
||||
}
|
||||
|
||||
|
||||
private static File getWorkDir(String dirname, boolean create)
|
||||
{
|
||||
String userhome = System.getProperty("user.home", ".");
|
||||
File file;
|
||||
|
||||
switch (getOs()) {
|
||||
case linux:
|
||||
case solaris:
|
||||
file = new File(userhome, "." + dirname + '/');
|
||||
break;
|
||||
|
||||
case windows:
|
||||
String appdata = System.getenv("APPDATA");
|
||||
|
||||
if (appdata != null) {
|
||||
file = new File(appdata, "." + dirname + '/');
|
||||
} else {
|
||||
file = new File(userhome, "." + dirname + '/');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case macos:
|
||||
file = new File(userhome, "Library/Application Support/" + dirname);
|
||||
break;
|
||||
|
||||
default:
|
||||
file = new File(userhome, dirname + "/");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!file.exists() || !file.isDirectory()) {
|
||||
if (create) {
|
||||
if (!file.mkdirs()) {
|
||||
throw new RuntimeException("Could not create working directory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,963 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Property manager with advanced formatting and value checking.<br>
|
||||
* Methods starting with put are for filling. Most of the others are shortcuts
|
||||
* to getters.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class PropertyManager {
|
||||
|
||||
/**
|
||||
* Properties stored in file, alphabetically sorted.<br>
|
||||
* Property file is much cleaner than the normal java.util.Properties,
|
||||
* newlines can be inserted to separate categories, and individual keys can
|
||||
* have their own inline comments.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
private static class SortedProperties extends Properties {
|
||||
|
||||
/** A table of hex digits */
|
||||
private static final char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
|
||||
/**
|
||||
* this is here because the original method is private.
|
||||
*
|
||||
* @param nibble
|
||||
* @return hex char.
|
||||
*/
|
||||
private static char toHex(int nibble)
|
||||
{
|
||||
return hexChars[(nibble & 0xF)];
|
||||
}
|
||||
|
||||
|
||||
private static void writeComments(BufferedWriter bw, String comm) throws IOException
|
||||
{
|
||||
String comments = comm.replace("\n\n", "\n \n");
|
||||
|
||||
int len = comments.length();
|
||||
int current = 0;
|
||||
int last = 0;
|
||||
char[] uu = new char[6];
|
||||
uu[0] = '\\';
|
||||
uu[1] = 'u';
|
||||
while (current < len) {
|
||||
char c = comments.charAt(current);
|
||||
if (c > '\u00ff' || c == '\n' || c == '\r') {
|
||||
if (last != current) {
|
||||
bw.write("# " + comments.substring(last, current));
|
||||
}
|
||||
|
||||
if (c > '\u00ff') {
|
||||
uu[2] = toHex((c >> 12) & 0xf);
|
||||
uu[3] = toHex((c >> 8) & 0xf);
|
||||
uu[4] = toHex((c >> 4) & 0xf);
|
||||
uu[5] = toHex(c & 0xf);
|
||||
bw.write(new String(uu));
|
||||
} else {
|
||||
bw.newLine();
|
||||
if (c == '\r' && current != len - 1 && comments.charAt(current + 1) == '\n') {
|
||||
current++;
|
||||
}
|
||||
}
|
||||
last = current + 1;
|
||||
}
|
||||
current++;
|
||||
}
|
||||
if (last != current) {
|
||||
bw.write("# " + comments.substring(last, current));
|
||||
}
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
}
|
||||
|
||||
/** Option: put empty line before each comment. */
|
||||
public boolean cfgEmptyLineBeforeComment = true;
|
||||
|
||||
/**
|
||||
* Option: Separate sections by newline<br>
|
||||
* Section = string before first dot in key.
|
||||
*/
|
||||
public boolean cfgSeparateSectionsByEmptyLine = true;
|
||||
|
||||
private boolean firstEntry = true;
|
||||
|
||||
/** Comments for individual keys */
|
||||
private Hashtable<String, String> keyComments = new Hashtable<String, String>();
|
||||
|
||||
private String lastSectionBeginning = "";
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public synchronized Enumeration keys()
|
||||
{
|
||||
Enumeration keysEnum = super.keys();
|
||||
Vector keyList = new Vector();
|
||||
while (keysEnum.hasMoreElements()) {
|
||||
keyList.add(keysEnum.nextElement());
|
||||
}
|
||||
Collections.sort(keyList);
|
||||
return keyList.elements();
|
||||
}
|
||||
|
||||
|
||||
private String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode)
|
||||
{
|
||||
int len = theString.length();
|
||||
int bufLen = len * 2;
|
||||
if (bufLen < 0) {
|
||||
bufLen = Integer.MAX_VALUE;
|
||||
}
|
||||
StringBuffer outBuffer = new StringBuffer(bufLen);
|
||||
|
||||
for (int x = 0; x < len; x++) {
|
||||
char aChar = theString.charAt(x);
|
||||
|
||||
// Handle common case first, selecting largest block that
|
||||
// avoids the specials below
|
||||
if ((aChar > 61) && (aChar < 127)) {
|
||||
if (aChar == '\\') {
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('\\');
|
||||
continue;
|
||||
}
|
||||
outBuffer.append(aChar);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (aChar) {
|
||||
case ' ':
|
||||
if (x == 0 || escapeSpace) {
|
||||
outBuffer.append('\\');
|
||||
}
|
||||
outBuffer.append(' ');
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('t');
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('n');
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('r');
|
||||
break;
|
||||
|
||||
case '\f':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('f');
|
||||
break;
|
||||
|
||||
case '=': // Fall through
|
||||
case ':': // Fall through
|
||||
case '#': // Fall through
|
||||
case '!':
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append(aChar);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('u');
|
||||
outBuffer.append(toHex((aChar >> 12) & 0xF));
|
||||
outBuffer.append(toHex((aChar >> 8) & 0xF));
|
||||
outBuffer.append(toHex((aChar >> 4) & 0xF));
|
||||
outBuffer.append(toHex(aChar & 0xF));
|
||||
} else {
|
||||
outBuffer.append(aChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outBuffer.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set additional comment to a key
|
||||
*
|
||||
* @param key key for comment
|
||||
* @param comment the comment
|
||||
*/
|
||||
public void setKeyComment(String key, String comment)
|
||||
{
|
||||
keyComments.put(key, comment);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void store(OutputStream out, String comments) throws IOException
|
||||
{
|
||||
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
|
||||
|
||||
boolean escUnicode = false;
|
||||
|
||||
if (comments != null) {
|
||||
writeComments(bw, comments);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
for (Enumeration e = keys(); e.hasMoreElements();) {
|
||||
boolean wasNewLine = false;
|
||||
|
||||
String key = (String) e.nextElement();
|
||||
String val = (String) get(key);
|
||||
key = saveConvert(key, true, escUnicode);
|
||||
val = saveConvert(val, false, escUnicode);
|
||||
|
||||
if (cfgSeparateSectionsByEmptyLine && !lastSectionBeginning.equals(key.split("[.]")[0])) {
|
||||
if (!firstEntry) {
|
||||
bw.newLine();
|
||||
bw.newLine();
|
||||
}
|
||||
|
||||
wasNewLine = true;
|
||||
lastSectionBeginning = key.split("[.]")[0];
|
||||
}
|
||||
|
||||
if (keyComments.containsKey(key)) {
|
||||
String cm = keyComments.get(key);
|
||||
cm = cm.replace("\r", "\n");
|
||||
cm = cm.replace("\r\n", "\n");
|
||||
cm = cm.replace("\n\n", "\n \n");
|
||||
|
||||
String[] cmlines = cm.split("\n");
|
||||
|
||||
if (!wasNewLine && !firstEntry && cfgEmptyLineBeforeComment) {
|
||||
bw.newLine();
|
||||
}
|
||||
for (String cmline : cmlines) {
|
||||
bw.write("# " + cmline);
|
||||
bw.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
bw.write(key + " = " + val);
|
||||
bw.newLine();
|
||||
|
||||
firstEntry = false;
|
||||
}
|
||||
}
|
||||
bw.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class which loads Properties from UTF-8 file (Properties use
|
||||
* "ISO-8859-1" by default)
|
||||
*
|
||||
* @author Itay Maman
|
||||
*/
|
||||
private static class PropertiesLoader {
|
||||
|
||||
private static String escapifyStr(String str)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int len = str.length();
|
||||
for (int x = 0; x < len; x++) {
|
||||
char ch = str.charAt(x);
|
||||
if (ch <= 0x007e) {
|
||||
result.append(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.append('\\');
|
||||
result.append('u');
|
||||
result.append(hexDigit(ch, 12));
|
||||
result.append(hexDigit(ch, 8));
|
||||
result.append(hexDigit(ch, 4));
|
||||
result.append(hexDigit(ch, 0));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
private static char hexDigit(char ch, int offset)
|
||||
{
|
||||
int val = (ch >> offset) & 0xF;
|
||||
if (val <= 9) {
|
||||
return (char) ('0' + val);
|
||||
}
|
||||
|
||||
return (char) ('A' + val - 10);
|
||||
}
|
||||
|
||||
|
||||
public static SortedProperties loadProperties(SortedProperties props, InputStream is) throws IOException
|
||||
{
|
||||
return loadProperties(props, is, "utf-8");
|
||||
}
|
||||
|
||||
|
||||
public static SortedProperties loadProperties(SortedProperties props, InputStream is, String encoding) throws IOException
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
InputStreamReader isr = new InputStreamReader(is, encoding);
|
||||
while (true) {
|
||||
int temp = isr.read();
|
||||
if (temp < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
char c = (char) temp;
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
String read = sb.toString();
|
||||
|
||||
String inputString = escapifyStr(read);
|
||||
byte[] bs = inputString.getBytes("ISO-8859-1");
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(bs);
|
||||
|
||||
SortedProperties ps = props;
|
||||
ps.load(bais);
|
||||
return ps;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property entry in Property manager.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
private class Property {
|
||||
|
||||
public String entryComment;
|
||||
|
||||
public String name;
|
||||
|
||||
public boolean bool = false;
|
||||
public boolean defbool = false;
|
||||
|
||||
public double num = -1;
|
||||
public double defnum = -1;
|
||||
|
||||
public String defstr = "";
|
||||
public String str = "";
|
||||
|
||||
public PropertyType type;
|
||||
|
||||
|
||||
/**
|
||||
* Property
|
||||
*
|
||||
* @param key key
|
||||
* @param default_value default value
|
||||
* @param entry_type type
|
||||
* @param entry_comment entry comment
|
||||
*/
|
||||
public Property(String key, boolean default_value, PropertyType entry_type, String entry_comment) {
|
||||
name = key;
|
||||
defbool = default_value;
|
||||
type = entry_type;
|
||||
entryComment = entry_comment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Property entry
|
||||
*
|
||||
* @param key property key
|
||||
* @param default_value default value
|
||||
* @param entry_type property type from enum
|
||||
* @param entry_comment property comment or null
|
||||
*/
|
||||
public Property(String key, double default_value, PropertyType entry_type, String entry_comment) {
|
||||
name = key;
|
||||
defnum = default_value;
|
||||
type = entry_type;
|
||||
entryComment = entry_comment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Property
|
||||
*
|
||||
* @param key key
|
||||
* @param default_value default value
|
||||
* @param entry_type type
|
||||
* @param entry_comment entry comment
|
||||
*/
|
||||
public Property(String key, String default_value, PropertyType entry_type, String entry_comment) {
|
||||
name = key;
|
||||
defstr = default_value;
|
||||
type = entry_type;
|
||||
entryComment = entry_comment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get boolean
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean getBoolean()
|
||||
{
|
||||
return bool;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number
|
||||
*
|
||||
* @return the number
|
||||
*/
|
||||
public int getInteger()
|
||||
{
|
||||
return (int) Math.round(num);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number as double
|
||||
*
|
||||
* @return the number
|
||||
*/
|
||||
public double getDouble()
|
||||
{
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get string
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String getString()
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this entry valid?
|
||||
*
|
||||
* @return is valid
|
||||
*/
|
||||
public boolean isValid()
|
||||
{
|
||||
switch (type) {
|
||||
case STRING:
|
||||
return str != null;
|
||||
|
||||
case BOOLEAN:
|
||||
case INT:
|
||||
case DOUBLE:
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load property value from a file
|
||||
*
|
||||
* @param string the string loaded
|
||||
* @return was OK
|
||||
*/
|
||||
public boolean parse(String string)
|
||||
{
|
||||
switch (type) {
|
||||
case INT:
|
||||
|
||||
if (string == null) {
|
||||
num = defnum;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
num = Integer.parseInt(string.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
num = defnum;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DOUBLE:
|
||||
|
||||
if (string == null) {
|
||||
num = defnum;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
num = Double.parseDouble(string.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
num = defnum;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
|
||||
if (string == null) {
|
||||
str = defstr;
|
||||
return false;
|
||||
}
|
||||
|
||||
str = string;
|
||||
break;
|
||||
|
||||
case BOOLEAN:
|
||||
|
||||
if (string == null) {
|
||||
bool = defbool;
|
||||
return false;
|
||||
}
|
||||
|
||||
String string2 = string.toLowerCase();
|
||||
bool = string2.equals("yes") || string2.equals("true") || string2.equals("on") || string2.equals("enabled") || string2.equals("enable");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* prepare the contents for insertion into Properties
|
||||
*
|
||||
* @return the string prepared, or null if type is invalid
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if (!isValid()) {
|
||||
if (type == PropertyType.INT || type == PropertyType.DOUBLE) {
|
||||
num = defnum;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case INT:
|
||||
return Integer.toString((int) num);
|
||||
|
||||
case DOUBLE:
|
||||
return Calc.floatToString((float) num);
|
||||
|
||||
case STRING:
|
||||
return str;
|
||||
|
||||
case BOOLEAN:
|
||||
return bool ? "True" : "False";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If this entry is not valid, change it to the dafault value.
|
||||
*/
|
||||
public void validate()
|
||||
{
|
||||
if (!isValid()) {
|
||||
if (type == PropertyType.STRING) {
|
||||
str = defstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property types
|
||||
*/
|
||||
private enum PropertyType
|
||||
{
|
||||
BOOLEAN, INT, STRING, DOUBLE;
|
||||
}
|
||||
|
||||
/** put newline before entry comments */
|
||||
private boolean cfgNewlineBeforeComments = true;
|
||||
/** Disable entry validation */
|
||||
private boolean cfgNoValidate = true;
|
||||
/** Put newline between sections. */
|
||||
private boolean cfgSeparateSections = true;
|
||||
/** Force save, even if nothing changed (used to save changed comments) */
|
||||
private boolean cfgForceSave;
|
||||
|
||||
private File file;
|
||||
private String fileComment = "";
|
||||
|
||||
private TreeMap<String, Property> entries;
|
||||
private TreeMap<String, String> keyRename;
|
||||
private TreeMap<String, String> setValues;
|
||||
private SortedProperties pr = new SortedProperties();
|
||||
|
||||
|
||||
/**
|
||||
* Create property manager from file path and an initial comment.
|
||||
*
|
||||
* @param file file with the props
|
||||
* @param comment the initial comment. Use \n in it if you want.
|
||||
*/
|
||||
public PropertyManager(File file, String comment) {
|
||||
this.file = file;
|
||||
this.entries = new TreeMap<String, Property>();
|
||||
this.setValues = new TreeMap<String, String>();
|
||||
this.keyRename = new TreeMap<String, String>();
|
||||
this.fileComment = comment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load, fix and write to file.
|
||||
*/
|
||||
public void apply()
|
||||
{
|
||||
boolean needsSave = false;
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
new File(file.getParent()).mkdirs();
|
||||
fis = new FileInputStream(file);
|
||||
pr = PropertiesLoader.loadProperties(pr, fis);
|
||||
|
||||
} catch (IOException e) {
|
||||
needsSave = true;
|
||||
pr = new SortedProperties();
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) fis.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
pr.cfgSeparateSectionsByEmptyLine = cfgSeparateSections;
|
||||
pr.cfgEmptyLineBeforeComment = cfgNewlineBeforeComments;
|
||||
|
||||
ArrayList<String> keyList = new ArrayList<String>();
|
||||
|
||||
// rename keys
|
||||
for (Entry<String, String> entry : keyRename.entrySet()) {
|
||||
if (pr.getProperty(entry.getKey()) == null) {
|
||||
continue;
|
||||
}
|
||||
pr.setProperty(entry.getValue(), pr.getProperty(entry.getKey()));
|
||||
pr.remove(entry.getKey());
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
// set the override values into the freshly loaded properties file
|
||||
for (Entry<String, String> entry : setValues.entrySet()) {
|
||||
pr.setProperty(entry.getKey(), entry.getValue());
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
// validate entries one by one, replace with default when needed
|
||||
for (Property entry : entries.values()) {
|
||||
keyList.add(entry.name);
|
||||
|
||||
String propOrig = pr.getProperty(entry.name);
|
||||
if (!entry.parse(propOrig)) needsSave = true;
|
||||
if (!cfgNoValidate) {
|
||||
entry.validate();
|
||||
}
|
||||
|
||||
if (entry.entryComment != null) {
|
||||
pr.setKeyComment(entry.name, entry.entryComment);
|
||||
}
|
||||
|
||||
if (propOrig == null || !entry.toString().equals(propOrig)) {
|
||||
pr.setProperty(entry.name, entry.toString());
|
||||
|
||||
needsSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
// removed unused props
|
||||
for (String propname : pr.keySet().toArray(new String[pr.size()])) {
|
||||
if (!keyList.contains(propname)) {
|
||||
pr.remove(propname);
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// save if needed
|
||||
if (needsSave || cfgForceSave) {
|
||||
try {
|
||||
pr.store(new FileOutputStream(file), fileComment);
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
setValues.clear();
|
||||
keyRename.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param newlineBeforeComments put newline before comments
|
||||
*/
|
||||
public void cfgNewlineBeforeComments(boolean newlineBeforeComments)
|
||||
{
|
||||
this.cfgNewlineBeforeComments = newlineBeforeComments;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param separateSections do separate sections by newline
|
||||
*/
|
||||
public void cfgSeparateSections(boolean separateSections)
|
||||
{
|
||||
this.cfgSeparateSections = separateSections;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param forceSave save even if unchanged.
|
||||
*/
|
||||
public void cfgForceSave(boolean forceSave)
|
||||
{
|
||||
this.cfgForceSave = forceSave;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param validate enable validation
|
||||
*/
|
||||
public void enableValidation(boolean validate)
|
||||
{
|
||||
this.cfgNoValidate = !validate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a property entry (rarely used)
|
||||
*
|
||||
* @param n key
|
||||
* @return the entry
|
||||
*/
|
||||
private Property get(String n)
|
||||
{
|
||||
try {
|
||||
return entries.get(n);
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @return the boolean found, or false
|
||||
*/
|
||||
public Boolean getBoolean(String n)
|
||||
{
|
||||
try {
|
||||
return entries.get(n).getBoolean();
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @return the int found, or null
|
||||
*/
|
||||
public Integer getInteger(String n)
|
||||
{
|
||||
try {
|
||||
return get(n).getInteger();
|
||||
} catch (Throwable t) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get numeric property as double
|
||||
*
|
||||
* @param n key
|
||||
* @return the double found, or null
|
||||
*/
|
||||
public Double getDouble(String n)
|
||||
{
|
||||
try {
|
||||
return get(n).getDouble();
|
||||
} catch (Throwable t) {
|
||||
return -1D;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get string property
|
||||
*
|
||||
* @param n key
|
||||
* @return the string found, or null
|
||||
*/
|
||||
public String getString(String n)
|
||||
{
|
||||
try {
|
||||
return get(n).getString();
|
||||
} catch (Throwable t) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putBoolean(String n, boolean d)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a boolean property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putBoolean(String n, boolean d, String comment)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a numeric property (double)
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putDouble(String n, int d)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.DOUBLE, null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a numeric property (double)
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putDouble(String n, int d, String comment)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.DOUBLE, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putInteger(String n, int d)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.INT, null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a numeric property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putInteger(String n, int d, String comment)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.INT, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a string property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
*/
|
||||
public void putString(String n, String d)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.STRING, null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a string property
|
||||
*
|
||||
* @param n key
|
||||
* @param d default value
|
||||
* @param comment the in-file comment
|
||||
*/
|
||||
public void putString(String n, String d, String comment)
|
||||
{
|
||||
entries.put(n, new Property(n, d, PropertyType.STRING, comment));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rename key before doing "apply"; value is preserved
|
||||
*
|
||||
* @param oldKey old key
|
||||
* @param newKey new key
|
||||
*/
|
||||
public void renameKey(String oldKey, String newKey)
|
||||
{
|
||||
keyRename.put(oldKey, newKey);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set value saved to certain key; use to save runtime-changed configuration
|
||||
* values.
|
||||
*
|
||||
* @param key key
|
||||
* @param value the saved value
|
||||
*/
|
||||
public void setValue(String key, Object value)
|
||||
{
|
||||
setValues.put(key, value.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Utility for parsing simple config files<br>
|
||||
* # and // mark a comment<br>
|
||||
* empty lines and lines without "=" are ignored<br>
|
||||
* lines with "=" must have "key = value" format, or a warning is logged.<br>
|
||||
* use "NULL" to create empty value.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class SimpleConfig {
|
||||
|
||||
/**
|
||||
* Load list from file
|
||||
*
|
||||
* @param file file
|
||||
* @return map of keys and values
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<String> listFromFile(File file) throws IOException
|
||||
{
|
||||
String fileText = FileUtils.fileToString(file);
|
||||
|
||||
return listFromString(fileText);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load map from file
|
||||
*
|
||||
* @param file file
|
||||
* @return map of keys and values
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Map<String, String> mapFromFile(File file) throws IOException
|
||||
{
|
||||
String fileText = FileUtils.fileToString(file);
|
||||
|
||||
return mapFromString(fileText);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load list from string
|
||||
*
|
||||
* @param text text of the file
|
||||
* @return map of keys and values
|
||||
*/
|
||||
public static List<String> listFromString(String text)
|
||||
{
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
String[] groupsLines = text.split("\n");
|
||||
|
||||
for (String s : groupsLines) {
|
||||
// ignore invalid lines
|
||||
if (s.length() == 0) continue;
|
||||
if (s.startsWith("#") || s.startsWith("//")) continue;
|
||||
|
||||
// NULL value
|
||||
if (s.equalsIgnoreCase("NULL")) s = null;
|
||||
|
||||
if (s != null) s = s.replace("\\n", "\n");
|
||||
|
||||
// save extracted key-value pair
|
||||
list.add(s);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load map from string
|
||||
*
|
||||
* @param text text of the file
|
||||
* @return map of keys and values
|
||||
*/
|
||||
public static Map<String, String> mapFromString(String text)
|
||||
{
|
||||
LinkedHashMap<String, String> pairs = new LinkedHashMap<String, String>();
|
||||
|
||||
String[] groupsLines = text.split("\n");
|
||||
|
||||
for (String s : groupsLines) {
|
||||
// ignore invalid lines
|
||||
if (s.length() == 0) continue;
|
||||
if (s.startsWith("#") || s.startsWith("//")) continue;
|
||||
if (!s.contains("=")) continue;
|
||||
|
||||
// split and trim
|
||||
String[] parts = s.split("=");
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
parts[i] = parts[i].trim();
|
||||
}
|
||||
|
||||
// check if both parts are valid
|
||||
if (parts.length == 0) {
|
||||
Log.w("Bad line in config file: " + s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parts.length == 1) {
|
||||
parts = new String[] { parts[0], "" };
|
||||
}
|
||||
|
||||
if (parts.length != 2) {
|
||||
Log.w("Bad line in config file: " + s);
|
||||
continue;
|
||||
}
|
||||
|
||||
// NULL value
|
||||
if (parts[0].equalsIgnoreCase("NULL")) parts[0] = null;
|
||||
if (parts[1].equalsIgnoreCase("NULL")) parts[1] = null;
|
||||
|
||||
if (parts[0] != null) parts[0] = parts[0].replace("\\n", "\n");
|
||||
if (parts[1] != null) parts[1] = parts[1].replace("\\n", "\n");
|
||||
|
||||
// save extracted key-value pair
|
||||
pairs.put(parts[0], parts[1]);
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save map to file
|
||||
*
|
||||
* @param target
|
||||
* @param data
|
||||
* @param allowNulls allow nulls.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void mapToFile(File target, Map<String, String> data, boolean allowNulls) throws IOException
|
||||
{
|
||||
List<String> lines = new ArrayList<String>();
|
||||
|
||||
for (Entry<String, String> e : data.entrySet()) {
|
||||
String key = e.getKey();
|
||||
String value = e.getValue();
|
||||
|
||||
if (!allowNulls && (key == null || value == null || key.length() == 0 || value.length() == 0)) continue;
|
||||
|
||||
if (key == null) key = "NULL";
|
||||
if (value == null) value = "NULL";
|
||||
|
||||
key = key.replace("\n", "\\n");
|
||||
value = value.replace("\n", "\\n");
|
||||
|
||||
lines.add(key + " = " + value);
|
||||
}
|
||||
|
||||
String text = ""; //# File written by SimpleConfig
|
||||
|
||||
for (String s : lines) {
|
||||
if (text.length() > 0) text += "\n";
|
||||
|
||||
text += s;
|
||||
}
|
||||
|
||||
FileUtils.stringToFile(target, text);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save list to file
|
||||
*
|
||||
* @param target
|
||||
* @param data
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void listToFile(File target, List<String> data) throws IOException
|
||||
{
|
||||
String text = ""; //# File written by SimpleConfig
|
||||
|
||||
for (String s : data) {
|
||||
if (text.length() > 0) text += "\n";
|
||||
|
||||
if (s == null) s = "NULL";
|
||||
|
||||
s = s.replace("\n", "\\n");
|
||||
|
||||
text += s;
|
||||
}
|
||||
|
||||
FileUtils.stringToFile(target, text);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
/**
|
||||
* Class for building a zip file
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ZipBuilder {
|
||||
|
||||
private ZipOutputStream out;
|
||||
private HashSet<String> included = new HashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* @param target target zip file
|
||||
* @throws FileNotFoundException if the file is directory or cannot be
|
||||
* created
|
||||
*/
|
||||
public ZipBuilder(File target) throws FileNotFoundException {
|
||||
target.getParentFile().mkdirs();
|
||||
|
||||
FileOutputStream dest = new FileOutputStream(target);
|
||||
out = new ZipOutputStream(new BufferedOutputStream(dest));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add stream to a path
|
||||
*
|
||||
* @param path path
|
||||
* @param in stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addStream(String path, InputStream in) throws IOException
|
||||
{
|
||||
path = preparePath(path);
|
||||
if (included.contains(path)) {
|
||||
Log.f3("Zip already contains file " + path + ", skipping.");
|
||||
return; // ignore
|
||||
}
|
||||
included.add(path);
|
||||
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add string as a file
|
||||
*
|
||||
* @param path path
|
||||
* @param text text to write
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addString(String path, String text) throws IOException
|
||||
{
|
||||
path = preparePath(path);
|
||||
if (included.contains(path)) return; // ignore
|
||||
included.add(path);
|
||||
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
|
||||
InputStream in = FileUtils.stringToStream(text);
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add resource obtained via FileUtils.getResource()
|
||||
*
|
||||
* @param path path
|
||||
* @param resPath resource path
|
||||
* @throws IOException
|
||||
*/
|
||||
public void addResource(String path, String resPath) throws IOException
|
||||
{
|
||||
path = preparePath(path);
|
||||
if (included.contains(path)) return; // ignore
|
||||
included.add(path);
|
||||
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
|
||||
InputStream in = FileUtils.getResource(resPath);
|
||||
FileUtils.copyStream(in, out);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalize path
|
||||
*
|
||||
* @param path original path
|
||||
* @return normalized path
|
||||
*/
|
||||
private String preparePath(String path)
|
||||
{
|
||||
path = path.replace("\\", "/");
|
||||
|
||||
if (path.charAt(0) == '/') path = path.substring(1);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close the zip stream
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package mightypork.utils.files;
|
||||
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import mightypork.utils.string.validation.StringFilter;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities for manipulating zip files
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ZipUtils {
|
||||
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
|
||||
|
||||
/**
|
||||
* Extract zip file to target directory
|
||||
*
|
||||
* @param file zip file
|
||||
* @param outputDir target directory
|
||||
* @param filter string filter (will be used to test entry names (paths))
|
||||
* @return list of entries extracted (paths)
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<String> extractZip(File file, File outputDir, StringFilter filter) throws IOException
|
||||
{
|
||||
ZipFile zip = null;
|
||||
try {
|
||||
zip = new ZipFile(file);
|
||||
|
||||
return extractZip(zip, outputDir, filter);
|
||||
|
||||
} finally {
|
||||
try {
|
||||
if (zip != null) zip.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract zip file to target directory
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @param outputDir target directory
|
||||
* @param filter string filter (will be used to test entry names (paths))
|
||||
* @return list of entries extracted (paths)
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<String> extractZip(ZipFile zip, File outputDir, StringFilter filter) throws IOException
|
||||
{
|
||||
ArrayList<String> files = new ArrayList<String>();
|
||||
|
||||
outputDir.mkdirs();
|
||||
|
||||
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
|
||||
|
||||
// process each entry
|
||||
while (zipFileEntries.hasMoreElements()) {
|
||||
ZipEntry entry = zipFileEntries.nextElement();
|
||||
|
||||
// parse filename and path
|
||||
String entryPath = entry.getName();
|
||||
File destFile = new File(outputDir, entryPath);
|
||||
File destinationParent = destFile.getParentFile();
|
||||
|
||||
if (entry.isDirectory() || (filter != null && !filter.accept(entryPath))) continue;
|
||||
|
||||
// make sure directories exist
|
||||
destinationParent.mkdirs();
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
extractZipEntry(zip, entry, destFile);
|
||||
files.add(entryPath);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read zip entries and add their paths to a list
|
||||
*
|
||||
* @param zipFile open zip file
|
||||
* @return list of entry names
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static List<String> listZip(File zipFile) throws IOException
|
||||
{
|
||||
ZipFile zip = null;
|
||||
try {
|
||||
zip = new ZipFile(zipFile);
|
||||
return listZip(zip);
|
||||
} finally {
|
||||
try {
|
||||
if (zip != null) zip.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read zip entries and add their paths to a list
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @return list of entry names
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static List<String> listZip(ZipFile zip) throws IOException
|
||||
{
|
||||
ArrayList<String> files = new ArrayList<String>();
|
||||
|
||||
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
|
||||
|
||||
// process each entry
|
||||
while (zipFileEntries.hasMoreElements()) {
|
||||
ZipEntry entry = zipFileEntries.nextElement();
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
files.add(entry.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract one zip entry to target file
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @param entry entry from the zip file
|
||||
* @param destFile destination file ((NOT directory!)
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static void extractZipEntry(ZipFile zip, ZipEntry entry, File destFile) throws IOException
|
||||
{
|
||||
destFile.getParentFile().mkdirs();
|
||||
|
||||
BufferedInputStream is = null;
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream dest = null;
|
||||
|
||||
try {
|
||||
is = new BufferedInputStream(zip.getInputStream(entry));
|
||||
fos = new FileOutputStream(destFile);
|
||||
dest = new BufferedOutputStream(fos, BUFFER_SIZE);
|
||||
|
||||
FileUtils.copyStream(is, dest);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if (dest != null) dest.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load zip entry to String
|
||||
*
|
||||
* @param zip open zip file
|
||||
* @param entry entry from the zip file
|
||||
* @return loaded string
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static String zipEntryToString(ZipFile zip, ZipEntry entry) throws IOException
|
||||
{
|
||||
BufferedInputStream is = null;
|
||||
try {
|
||||
is = new BufferedInputStream(zip.getInputStream(entry));
|
||||
String s = FileUtils.streamToString(is);
|
||||
return s;
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean entryExists(File selectedFile, String string)
|
||||
{
|
||||
ZipFile zf = null;
|
||||
|
||||
try {
|
||||
zf = new ZipFile(selectedFile);
|
||||
return zf.getEntry(string) != null;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (zf != null) zf.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable Arraylist
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractIonList<T> extends ArrayList<T> implements Ionizable {
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IonException
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
byte b = StreamUtils.readByte(in);
|
||||
|
||||
if (b == IonMarks.ENTRY) {
|
||||
T value = (T) Ion.readObject(in);
|
||||
add(value);
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new IonException("Unexpected mark in IonList: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
ionReadCustomData(in);
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Error reading ion list", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IonException
|
||||
{
|
||||
try {
|
||||
for (T entry : this) {
|
||||
if (entry instanceof IonizableOptional && !((IonizableOptional) entry).ionShouldSave()) continue;
|
||||
StreamUtils.writeByte(out, IonMarks.ENTRY);
|
||||
Ion.writeObject(out, entry);
|
||||
}
|
||||
StreamUtils.writeByte(out, IonMarks.END);
|
||||
ionWriteCustomData(out);
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Error reading ion map", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read custom data of this AbstractIonList implementation
|
||||
*
|
||||
* @param in input stream
|
||||
*/
|
||||
public void ionReadCustomData(InputStream in)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Write custom data of this AbstractIonList implementation
|
||||
*
|
||||
* @param out output stream
|
||||
*/
|
||||
public void ionWriteCustomData(OutputStream out)
|
||||
{}
|
||||
|
||||
|
||||
@Override
|
||||
public abstract byte ionMark();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable HashMap
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <V>
|
||||
*/
|
||||
public abstract class AbstractIonMap<V> extends LinkedHashMap<String, V> implements Ionizable {
|
||||
|
||||
@Override
|
||||
public V get(Object key)
|
||||
{
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public V put(String key, V value)
|
||||
{
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ionRead(InputStream in) throws IonException
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
byte b = StreamUtils.readByte(in);
|
||||
if (b == IonMarks.ENTRY) {
|
||||
String key = StreamUtils.readStringBytes(in);
|
||||
V value;
|
||||
try {
|
||||
value = (V) Ion.readObject(in);
|
||||
put(key, value);
|
||||
} catch (IonException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (b == IonMarks.END) {
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected mark in IonMap: " + Integer.toHexString(b));
|
||||
}
|
||||
}
|
||||
ionReadCustomData(in);
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Error reading ion map", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void ionWrite(OutputStream out) throws IonException
|
||||
{
|
||||
try {
|
||||
for (java.util.Map.Entry<String, V> entry : entrySet()) {
|
||||
StreamUtils.writeByte(out, IonMarks.ENTRY);
|
||||
StreamUtils.writeStringBytes(out, entry.getKey());
|
||||
Ion.writeObject(out, entry.getValue());
|
||||
}
|
||||
StreamUtils.writeByte(out, IonMarks.END);
|
||||
ionWriteCustomData(out);
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Error reading ion map", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read custom data of this AbstractIonMap implementation
|
||||
*
|
||||
* @param in input stream
|
||||
*/
|
||||
public void ionReadCustomData(InputStream in)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
* Write custom data of this AbstractIonMap implementation
|
||||
*
|
||||
* @param out output stream
|
||||
*/
|
||||
public void ionWriteCustomData(OutputStream out)
|
||||
{}
|
||||
|
||||
|
||||
@Override
|
||||
public byte ionMark()
|
||||
{
|
||||
return IonMarks.MAP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Universal data storage system
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Ion {
|
||||
|
||||
/** Ionizables<Mark, Class> */
|
||||
private static Map<Byte, Class<?>> customIonizables = new HashMap<Byte, Class<?>>();
|
||||
|
||||
// register default ionizables
|
||||
static {
|
||||
try {
|
||||
registerIonizable(IonMarks.MAP, IonMap.class);
|
||||
registerIonizable(IonMarks.LIST, IonList.class);
|
||||
} catch (IonException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register new Ionizable for direct reconstructing.
|
||||
*
|
||||
* @param mark byte mark to be used, see {@link IonMarks} for reference.
|
||||
* @param objClass class of the registered Ionizable
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void registerIonizable(byte mark, Class<?> objClass) throws IonException
|
||||
{
|
||||
if (customIonizables.containsKey(mark)) {
|
||||
throw new IonException("IonMark " + mark + " is already used.");
|
||||
}
|
||||
customIonizables.put(mark, objClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from file.
|
||||
*
|
||||
* @param file file path
|
||||
* @return the loaded object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static Object fromFile(String file) throws IonException
|
||||
{
|
||||
return fromFile(new File(file));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from file.
|
||||
*
|
||||
* @param file file
|
||||
* @return the loaded object
|
||||
* @throws IonException on failure
|
||||
*/
|
||||
public static Object fromFile(File file) throws IonException
|
||||
{
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(file);
|
||||
Object obj = fromStream(in);
|
||||
return obj;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Error loading ION file.", e);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Ion object from stream.
|
||||
*
|
||||
* @param in input stream
|
||||
* @return the loaded object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static Object fromStream(InputStream in) throws IonException
|
||||
{
|
||||
return readObject(in);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store Ion object to file.
|
||||
*
|
||||
* @param path file path
|
||||
* @param obj object to store
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void toFile(String path, Object obj) throws IonException
|
||||
{
|
||||
toFile(new File(path), obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store Ion object to file.
|
||||
*
|
||||
* @param path file path
|
||||
* @param obj object to store
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void toFile(File path, Object obj) throws IonException
|
||||
{
|
||||
OutputStream out = null;
|
||||
try {
|
||||
String f = path.toString();
|
||||
File dir = new File(f.substring(0, f.lastIndexOf(File.separator)));
|
||||
|
||||
dir.mkdirs();
|
||||
|
||||
out = new FileOutputStream(path);
|
||||
|
||||
toStream(out, obj);
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
throw new IonException("Error writing to ION file.", e);
|
||||
} finally {
|
||||
if (out != null) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store Ion object to output stream.
|
||||
*
|
||||
* @param out output stream *
|
||||
* @param obj object to store
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void toStream(OutputStream out, Object obj) throws IonException
|
||||
{
|
||||
writeObject(out, obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read single ionizable or primitive object from input stream
|
||||
*
|
||||
* @param in input stream
|
||||
* @return the loaded object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static Object readObject(InputStream in) throws IonException
|
||||
{
|
||||
try {
|
||||
int bi = in.read();
|
||||
if (bi == -1) throw new IonException("Unexpected end of stream.");
|
||||
byte b = (byte) bi;
|
||||
if (customIonizables.containsKey(b)) {
|
||||
Ionizable ion;
|
||||
try {
|
||||
ion = ((Ionizable) customIonizables.get(b).newInstance());
|
||||
} catch (InstantiationException e) {
|
||||
throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e);
|
||||
}
|
||||
ion.ionRead(in);
|
||||
return ion;
|
||||
}
|
||||
|
||||
switch (b) {
|
||||
case IonMarks.BOOLEAN:
|
||||
return StreamUtils.readBoolean(in);
|
||||
case IonMarks.BYTE:
|
||||
return StreamUtils.readByte(in);
|
||||
case IonMarks.CHAR:
|
||||
return StreamUtils.readChar(in);
|
||||
case IonMarks.SHORT:
|
||||
return StreamUtils.readShort(in);
|
||||
case IonMarks.INT:
|
||||
return StreamUtils.readInt(in);
|
||||
case IonMarks.LONG:
|
||||
return StreamUtils.readLong(in);
|
||||
case IonMarks.FLOAT:
|
||||
return StreamUtils.readFloat(in);
|
||||
case IonMarks.DOUBLE:
|
||||
return StreamUtils.readDouble(in);
|
||||
case IonMarks.STRING:
|
||||
String s = StreamUtils.readString(in);
|
||||
return s;
|
||||
default:
|
||||
throw new IonException("Invalid Ion mark " + Integer.toHexString(bi));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Error loading ION file: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write single ionizable or primitive object to output stream
|
||||
*
|
||||
* @param out output stream
|
||||
* @param obj stored object
|
||||
* @throws IonException
|
||||
*/
|
||||
public static void writeObject(OutputStream out, Object obj) throws IonException
|
||||
{
|
||||
try {
|
||||
if (obj instanceof Ionizable) {
|
||||
out.write(((Ionizable) obj).ionMark());
|
||||
((Ionizable) obj).ionWrite(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Boolean) {
|
||||
out.write(IonMarks.BOOLEAN);
|
||||
StreamUtils.writeBoolean(out, (Boolean) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Byte) {
|
||||
out.write(IonMarks.BYTE);
|
||||
StreamUtils.writeByte(out, (Byte) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Character) {
|
||||
out.write(IonMarks.CHAR);
|
||||
StreamUtils.writeChar(out, (Character) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Short) {
|
||||
out.write(IonMarks.SHORT);
|
||||
StreamUtils.writeShort(out, (Short) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Integer) {
|
||||
out.write(IonMarks.INT);
|
||||
StreamUtils.writeInt(out, (Integer) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Long) {
|
||||
out.write(IonMarks.LONG);
|
||||
StreamUtils.writeLong(out, (Long) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Float) {
|
||||
out.write(IonMarks.FLOAT);
|
||||
StreamUtils.writeFloat(out, (Float) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof Double) {
|
||||
out.write(IonMarks.DOUBLE);
|
||||
StreamUtils.writeDouble(out, (Double) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj instanceof String) {
|
||||
out.write(IonMarks.STRING);
|
||||
StreamUtils.writeString(out, (String) obj);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IonException(Calc.cname(obj) + " can't be stored in Ion storage.");
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new IonException("Could not store " + obj, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
public class IonException extends Exception {
|
||||
|
||||
public IonException() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public IonException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
public IonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public IonException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable Arraylist
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonList extends AbstractIonList<Object> implements Ionizable {
|
||||
|
||||
public Ionizable getIonizable(int index) throws IonException
|
||||
{
|
||||
return (Ionizable) getCheckType(index, Ionizable.class);
|
||||
}
|
||||
|
||||
|
||||
public boolean getBoolean(int index) throws IonException
|
||||
{
|
||||
return (Boolean) getCheckType(index, Boolean.class);
|
||||
}
|
||||
|
||||
|
||||
public byte getByte(int index) throws IonException
|
||||
{
|
||||
return (Byte) getCheckType(index, Byte.class);
|
||||
}
|
||||
|
||||
|
||||
public char getChar(int index) throws IonException
|
||||
{
|
||||
return (Character) getCheckType(index, Character.class);
|
||||
}
|
||||
|
||||
|
||||
public short getShort(int index) throws IonException
|
||||
{
|
||||
return (Short) getCheckType(index, Short.class);
|
||||
}
|
||||
|
||||
|
||||
public int getInt(int index) throws IonException
|
||||
{
|
||||
return (Integer) getCheckType(index, Integer.class);
|
||||
}
|
||||
|
||||
|
||||
public long getLong(int index) throws IonException
|
||||
{
|
||||
return (Long) getCheckType(index, Long.class);
|
||||
}
|
||||
|
||||
|
||||
public float getFloat(int index) throws IonException
|
||||
{
|
||||
return (Float) getCheckType(index, Float.class);
|
||||
}
|
||||
|
||||
|
||||
public double getDouble(int index) throws IonException
|
||||
{
|
||||
return (Double) getCheckType(index, Double.class);
|
||||
}
|
||||
|
||||
|
||||
public String getString(int index) throws IonException
|
||||
{
|
||||
return (String) getCheckType(index, String.class);
|
||||
}
|
||||
|
||||
|
||||
public void addIonizable(Ionizable o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addBoolean(boolean o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addByte(byte o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addChar(char o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addShort(short o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addInt(int o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addLong(long o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addFloat(float o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addDouble(double o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public void addString(String o) throws IonException
|
||||
{
|
||||
addCheckNull(o);
|
||||
}
|
||||
|
||||
|
||||
public Object getCheckType(int index, Class<?> type) throws IonException
|
||||
{
|
||||
try {
|
||||
Object o = super.get(index);
|
||||
if (o == null || !o.getClass().isAssignableFrom(type)) {
|
||||
throw new IonException("Incorrect object type");
|
||||
}
|
||||
return o;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new IonException("Out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addCheckNull(Object o) throws IonException
|
||||
{
|
||||
if (o == null) throw new IonException("Cannot store null");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte ionMark()
|
||||
{
|
||||
return IonMarks.LIST;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Ionizable HashMap
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonMap extends AbstractIonMap<Object> implements Ionizable {
|
||||
|
||||
public boolean getBoolean(String key)
|
||||
{
|
||||
return (Boolean) get(key);
|
||||
}
|
||||
|
||||
|
||||
public boolean getBool(String key)
|
||||
{
|
||||
return (Boolean) get(key);
|
||||
}
|
||||
|
||||
|
||||
public byte getByte(String key)
|
||||
{
|
||||
return (Byte) get(key);
|
||||
}
|
||||
|
||||
|
||||
public char getChar(String key)
|
||||
{
|
||||
return (Character) get(key);
|
||||
}
|
||||
|
||||
|
||||
public short getShort(String key)
|
||||
{
|
||||
return (Short) get(key);
|
||||
}
|
||||
|
||||
|
||||
public int getInt(String key)
|
||||
{
|
||||
return (Integer) get(key);
|
||||
}
|
||||
|
||||
|
||||
public long getLong(String key)
|
||||
{
|
||||
return (Long) get(key);
|
||||
}
|
||||
|
||||
|
||||
public float getFloat(String key)
|
||||
{
|
||||
return (Float) get(key);
|
||||
}
|
||||
|
||||
|
||||
public double getDouble(String key)
|
||||
{
|
||||
return (Double) get(key);
|
||||
}
|
||||
|
||||
|
||||
public String getString(String key)
|
||||
{
|
||||
return (String) get(key);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object get(Object arg0)
|
||||
{
|
||||
return super.get(arg0);
|
||||
}
|
||||
|
||||
|
||||
public void putBoolean(String key, boolean num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putBool(String key, boolean num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putByte(String key, int num)
|
||||
{
|
||||
put(key, (byte) num);
|
||||
}
|
||||
|
||||
|
||||
public void putChar(String key, char num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putCharacter(String key, char num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putShort(String key, int num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putInt(String key, int num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putInteger(String key, int num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putLong(String key, long num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putFloat(String key, double num)
|
||||
{
|
||||
put(key, (float) num);
|
||||
}
|
||||
|
||||
|
||||
public void putDouble(String key, double num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
public void putString(String key, String num)
|
||||
{
|
||||
put(key, num);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte ionMark()
|
||||
{
|
||||
return IonMarks.MAP;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Byte marks used to structure data in Ion files.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class IonMarks {
|
||||
|
||||
/** Null value */
|
||||
public static final byte NULL = 0;
|
||||
|
||||
/** Boolean value */
|
||||
public static final byte BOOLEAN = 1;
|
||||
|
||||
/** Byte value */
|
||||
public static final byte BYTE = 2;
|
||||
|
||||
/** Character value */
|
||||
public static final byte CHAR = 3;
|
||||
|
||||
/** Short value */
|
||||
public static final byte SHORT = 4;
|
||||
|
||||
/** Integer value */
|
||||
public static final byte INT = 5;
|
||||
|
||||
/** Long value */
|
||||
public static final byte LONG = 6;
|
||||
|
||||
/** Float value */
|
||||
public static final byte FLOAT = 7;
|
||||
|
||||
/** Double value */
|
||||
public static final byte DOUBLE = 8;
|
||||
|
||||
/** String value */
|
||||
public static final byte STRING = 9;
|
||||
|
||||
/** List value (begin) - contains entries, ends with END */
|
||||
public static final byte LIST = 10;
|
||||
|
||||
/** Map value (begin) - contains entries, ends with END */
|
||||
public static final byte MAP = 11;
|
||||
|
||||
/**
|
||||
* List / Map entry<br>
|
||||
* In list directly followed by entry value. In map followed by (string) key
|
||||
* and the entry value.
|
||||
*/
|
||||
public static final byte ENTRY = 12;
|
||||
|
||||
/** End of List / Map */
|
||||
public static final byte END = 13;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Object that can be saved to and loaded from Ion file.<br>
|
||||
* All classes implementing Ionizable must be registered to {@link Ion} using
|
||||
* Ion.registerIonizable(obj.class).
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface Ionizable {
|
||||
|
||||
/**
|
||||
* Load data from the input stream. Mark has already been read, begin
|
||||
* reading right after it.
|
||||
*
|
||||
* @param in input stream
|
||||
* @throws IonException
|
||||
*/
|
||||
public void ionRead(InputStream in) throws IonException;
|
||||
|
||||
|
||||
/**
|
||||
* Store data to output stream. mark has already been written, begin right
|
||||
* after it.
|
||||
*
|
||||
* @param out Output stream
|
||||
* @throws IonException
|
||||
*/
|
||||
public void ionWrite(OutputStream out) throws IonException;
|
||||
|
||||
|
||||
/**
|
||||
* Get Ion mark byte.
|
||||
*
|
||||
* @return Ion mark byte.
|
||||
*/
|
||||
public byte ionMark();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
/**
|
||||
* Optional ionizable
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface IonizableOptional extends Ionizable {
|
||||
|
||||
/**
|
||||
* Get if this ionizable should be saved to a list
|
||||
*
|
||||
* @return should save
|
||||
*/
|
||||
public boolean ionShouldSave();
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package mightypork.utils.files.ion;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* Utilities to store and load objects to streams.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class StreamUtils {
|
||||
|
||||
private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8);
|
||||
private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8);
|
||||
private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8);
|
||||
private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8);
|
||||
private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8);
|
||||
private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8);
|
||||
|
||||
private static byte[] ai = new byte[Integer.SIZE / 8];
|
||||
private static byte[] ad = new byte[Double.SIZE / 8];
|
||||
private static byte[] af = new byte[Float.SIZE / 8];
|
||||
private static byte[] ac = new byte[Character.SIZE / 8];
|
||||
private static byte[] al = new byte[Long.SIZE / 8];
|
||||
private static byte[] as = new byte[Short.SIZE / 8];
|
||||
|
||||
|
||||
// CONVERSIONS
|
||||
|
||||
private static byte[] convBool(boolean bool)
|
||||
{
|
||||
return new byte[] { (byte) (bool ? 1 : 0) };
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convByte(byte num)
|
||||
{
|
||||
return new byte[] { num };
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convChar(char num)
|
||||
{
|
||||
bc.clear();
|
||||
bc.putChar(num);
|
||||
return bc.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convShort(short num)
|
||||
{
|
||||
bs.clear();
|
||||
bs.putShort(num);
|
||||
return bs.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convInt(int num)
|
||||
{
|
||||
bi.clear();
|
||||
bi.putInt(num);
|
||||
return bi.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convLong(long num)
|
||||
{
|
||||
bl.clear();
|
||||
bl.putLong(num);
|
||||
return bl.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convFloat(float num)
|
||||
{
|
||||
bf.clear();
|
||||
bf.putFloat(num);
|
||||
return bf.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convDouble(double num)
|
||||
{
|
||||
bd.clear();
|
||||
bd.putDouble(num);
|
||||
return bd.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convString(String str)
|
||||
{
|
||||
char[] chars = str.toCharArray();
|
||||
|
||||
ByteBuffer bstr = ByteBuffer.allocate((Character.SIZE / 8) * chars.length + (Character.SIZE / 8));
|
||||
for (char c : chars) {
|
||||
bstr.putChar(c);
|
||||
}
|
||||
|
||||
bstr.putChar((char) 0);
|
||||
|
||||
return bstr.array();
|
||||
}
|
||||
|
||||
|
||||
private static byte[] convString_b(String str)
|
||||
{
|
||||
char[] chars = str.toCharArray();
|
||||
ByteBuffer bstr = ByteBuffer.allocate((Byte.SIZE / 8) * chars.length + 1);
|
||||
for (char c : chars) {
|
||||
bstr.put((byte) c);
|
||||
}
|
||||
bstr.put((byte) 0);
|
||||
|
||||
return bstr.array();
|
||||
}
|
||||
|
||||
|
||||
public static void writeBoolean(OutputStream out, boolean num) throws IOException
|
||||
{
|
||||
out.write(convBool(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeByte(OutputStream out, byte num) throws IOException
|
||||
{
|
||||
out.write(convByte(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeChar(OutputStream out, char num) throws IOException
|
||||
{
|
||||
out.write(convChar(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeShort(OutputStream out, short num) throws IOException
|
||||
{
|
||||
out.write(convShort(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeInt(OutputStream out, int num) throws IOException
|
||||
{
|
||||
out.write(convInt(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeLong(OutputStream out, long num) throws IOException
|
||||
{
|
||||
out.write(convLong(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeFloat(OutputStream out, float num) throws IOException
|
||||
{
|
||||
out.write(convFloat(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeDouble(OutputStream out, double num) throws IOException
|
||||
{
|
||||
out.write(convDouble(num));
|
||||
}
|
||||
|
||||
|
||||
public static void writeString(OutputStream out, String str) throws IOException
|
||||
{
|
||||
out.write(convString(str));
|
||||
}
|
||||
|
||||
|
||||
public static void writeStringBytes(OutputStream out, String str) throws IOException
|
||||
{
|
||||
out.write(convString_b(str));
|
||||
}
|
||||
|
||||
|
||||
// READING
|
||||
|
||||
public static boolean readBoolean(InputStream in) throws IOException
|
||||
{
|
||||
return in.read() > 0;
|
||||
}
|
||||
|
||||
|
||||
public static byte readByte(InputStream in) throws IOException
|
||||
{
|
||||
return (byte) in.read();
|
||||
}
|
||||
|
||||
|
||||
public static char readChar(InputStream in) throws IOException
|
||||
{
|
||||
in.read(ac, 0, ac.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(ac);
|
||||
return buf.getChar();
|
||||
}
|
||||
|
||||
|
||||
public static short readShort(InputStream in) throws IOException
|
||||
{
|
||||
in.read(as, 0, as.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(as);
|
||||
return buf.getShort();
|
||||
}
|
||||
|
||||
|
||||
public static long readLong(InputStream in) throws IOException
|
||||
{
|
||||
in.read(al, 0, al.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(al);
|
||||
return buf.getLong();
|
||||
}
|
||||
|
||||
|
||||
public static int readInt(InputStream in) throws IOException
|
||||
{
|
||||
in.read(ai, 0, ai.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(ai);
|
||||
return buf.getInt();
|
||||
}
|
||||
|
||||
|
||||
public static float readFloat(InputStream in) throws IOException
|
||||
{
|
||||
in.read(af, 0, af.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(af);
|
||||
return buf.getFloat();
|
||||
}
|
||||
|
||||
|
||||
public static double readDouble(InputStream in) throws IOException
|
||||
{
|
||||
in.read(ad, 0, ad.length);
|
||||
ByteBuffer buf = ByteBuffer.wrap(ad);
|
||||
return buf.getDouble();
|
||||
}
|
||||
|
||||
|
||||
public static String readString(InputStream in) throws IOException
|
||||
{
|
||||
String s = "";
|
||||
char c;
|
||||
while ((c = readChar(in)) > 0) {
|
||||
s += c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
public static String readStringBytes(InputStream in) throws IOException
|
||||
{
|
||||
String s = "";
|
||||
byte b;
|
||||
while ((b = readByte(in)) > 0) {
|
||||
s += (char) b;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package mightypork.utils.logging;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class Log {
|
||||
|
||||
/** enable static logging */
|
||||
private static boolean esl;
|
||||
|
||||
|
||||
/**
|
||||
* Log FINE message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public static void f1(String msg)
|
||||
{
|
||||
if (esl && main != null) main.f1(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log FINER message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public static void f2(String msg)
|
||||
{
|
||||
if (esl && main != null) main.f2(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log FINEST message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public static void f3(String msg)
|
||||
{
|
||||
if (esl && main != null) main.f3(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log INFO message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public static void i(String msg)
|
||||
{
|
||||
if (esl && main != null) main.i(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log WARNING message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public static void w(String msg)
|
||||
{
|
||||
if (esl && main != null) main.w(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log ERROR message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public static void e(String msg)
|
||||
{
|
||||
if (esl && main != null) main.e(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log EXCEPTION and ERROR message in main logger
|
||||
*
|
||||
* @param msg message
|
||||
* @param thrown thrown exception
|
||||
*/
|
||||
public static void e(String msg, Throwable thrown)
|
||||
{
|
||||
if (esl && main != null) main.e(msg, thrown);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log EXCEPTION in main logger
|
||||
*
|
||||
* @param thrown thrown exception
|
||||
*/
|
||||
public static void e(Throwable thrown)
|
||||
{
|
||||
if (esl && main != null) main.e(thrown);
|
||||
}
|
||||
|
||||
|
||||
public static void enable(boolean flag)
|
||||
{
|
||||
if (esl && main != null) main.enable(flag);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable / disable static log delegate methods
|
||||
*
|
||||
* @param flag enable
|
||||
*/
|
||||
public static void enableStaticLogging(boolean flag)
|
||||
{
|
||||
esl = flag;
|
||||
}
|
||||
|
||||
private static HashMap<String, LogInstance> logs = new HashMap<String, LogInstance>();
|
||||
private static LogInstance main = null;
|
||||
|
||||
|
||||
/**
|
||||
* Create a logger. If this is the first logger made, then it'll be made
|
||||
* available via the static methods.
|
||||
*
|
||||
* @param logName log name (used for filename, must be application-unique)
|
||||
* @param logsDir directory to store logs in
|
||||
* @param oldLogsCount number of old logs to keep, -1 for infinite, 0 for
|
||||
* none.
|
||||
* @return the created Log instance
|
||||
*/
|
||||
public static synchronized LogInstance create(String logName, File logsDir, int oldLogsCount)
|
||||
{
|
||||
if (logs.containsKey(logName)) return logs.get(logName);
|
||||
LogInstance log = new LogInstance(logName, logsDir, oldLogsCount);
|
||||
if (main == null) main = log;
|
||||
logs.put(logName, log);
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
|
||||
public int addMonitor(LogMonitor mon)
|
||||
{
|
||||
if (main == null) throw new IllegalStateException("Main logger not initialized.");
|
||||
|
||||
return main.addMonitor(mon);
|
||||
}
|
||||
|
||||
|
||||
public void removeMonitor(int id)
|
||||
{
|
||||
if (main == null) throw new IllegalStateException("Main logger not initialized.");
|
||||
|
||||
main.removeMonitor(id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
package mightypork.utils.logging;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import mightypork.utils.files.FileUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Static logger class.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2014
|
||||
*/
|
||||
public class LogInstance {
|
||||
|
||||
/** log file */
|
||||
private File file;
|
||||
|
||||
/** Log name */
|
||||
private String name;
|
||||
|
||||
/** Number of old logs to keep */
|
||||
private int logs_to_keep;
|
||||
|
||||
/** Logs dir */
|
||||
private File dir;
|
||||
|
||||
/** Logger instance. */
|
||||
private Logger logger;
|
||||
|
||||
/** Logging enabled */
|
||||
private boolean enabled = true;
|
||||
|
||||
private boolean sysout = true;
|
||||
|
||||
private int monitorId = 0;
|
||||
private HashMap<Integer, LogMonitor> monitors = new HashMap<Integer, LogMonitor>();
|
||||
|
||||
private LogToSysoutMonitor sysoutMonitor;
|
||||
|
||||
|
||||
public LogInstance(String name, File dir, int oldLogCount) {
|
||||
this.name = name;
|
||||
this.file = new File(dir, name + getSuffix());
|
||||
this.dir = dir;
|
||||
this.logs_to_keep = oldLogCount;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare logs for logging
|
||||
*/
|
||||
private void init()
|
||||
{
|
||||
logger = Logger.getLogger(name);
|
||||
|
||||
cleanup();
|
||||
|
||||
FileHandler handler = null;
|
||||
|
||||
try {
|
||||
handler = new FileHandler(file.getPath());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to init log", e);
|
||||
}
|
||||
|
||||
handler.setFormatter(new LogFormatter());
|
||||
logger.addHandler(handler);
|
||||
|
||||
enabled = true;
|
||||
|
||||
sysoutMonitor = new LogToSysoutMonitor();
|
||||
|
||||
addMonitor(sysoutMonitor);
|
||||
|
||||
logger.setUseParentHandlers(false);
|
||||
logger.setLevel(Level.ALL);
|
||||
logger.info("Main logger initialized.");
|
||||
logger.info((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()));
|
||||
}
|
||||
|
||||
|
||||
private void cleanup()
|
||||
{
|
||||
for (File f : FileUtils.listDirectory(file.getParentFile())) {
|
||||
if (!f.isFile()) continue;
|
||||
if (f.equals(file)) {
|
||||
Date d = new Date(f.lastModified());
|
||||
|
||||
String fname = name + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d) + getSuffix();
|
||||
|
||||
File f2 = new File(dir, fname);
|
||||
|
||||
f.renameTo(f2);
|
||||
}
|
||||
}
|
||||
|
||||
List<File> oldLogs = FileUtils.listDirectory(dir, new FileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(File f)
|
||||
{
|
||||
if (f.isDirectory()) return false;
|
||||
if (!f.getName().endsWith(getSuffix())) return false;
|
||||
if (!f.getName().startsWith(name)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Collections.sort(oldLogs, new Comparator<File>() {
|
||||
|
||||
@Override
|
||||
public int compare(File o1, File o2)
|
||||
{
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) {
|
||||
oldLogs.get(i).delete();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add log monitor
|
||||
*
|
||||
* @param mon monitor
|
||||
* @return assigned ID
|
||||
*/
|
||||
public synchronized int addMonitor(LogMonitor mon)
|
||||
{
|
||||
int id = monitorId;
|
||||
monitorId++;
|
||||
monitors.put(id, mon);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove a monitor by ID
|
||||
*
|
||||
* @param id monitor ID
|
||||
*/
|
||||
public synchronized void removeMonitor(int id)
|
||||
{
|
||||
monitors.remove(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable logging.
|
||||
*
|
||||
* @param flag do enable logging
|
||||
*/
|
||||
public void enable(boolean flag)
|
||||
{
|
||||
enabled = flag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable printing logs to sysout
|
||||
*
|
||||
* @param flag do enable logging
|
||||
*/
|
||||
public void enableSysout(boolean flag)
|
||||
{
|
||||
sysout = flag;
|
||||
sysoutMonitor.enable(sysout);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log FINE message
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public void f1(String msg)
|
||||
{
|
||||
if (enabled) logger.log(Level.FINE, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log FINER message
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public void f2(String msg)
|
||||
{
|
||||
if (enabled) logger.log(Level.FINER, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log FINEST message
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public void f3(String msg)
|
||||
{
|
||||
if (enabled) logger.log(Level.FINEST, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log INFO message
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public void i(String msg)
|
||||
{
|
||||
if (enabled) logger.log(Level.INFO, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log WARNING message (less severe than ERROR)
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public void w(String msg)
|
||||
{
|
||||
if (enabled) logger.log(Level.WARNING, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log ERROR message
|
||||
*
|
||||
* @param msg message
|
||||
*/
|
||||
public void e(String msg)
|
||||
{
|
||||
if (enabled) logger.log(Level.SEVERE, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log THROWING message
|
||||
*
|
||||
* @param msg message
|
||||
* @param thrown thrown exception
|
||||
*/
|
||||
public void e(String msg, Throwable thrown)
|
||||
{
|
||||
if (enabled) logger.log(Level.SEVERE, msg + "\n" + getStackTrace(thrown));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log exception thrown
|
||||
*
|
||||
* @param thrown thrown exception
|
||||
*/
|
||||
public void e(Throwable thrown)
|
||||
{
|
||||
if (enabled) logger.log(Level.SEVERE, getStackTrace(thrown));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get stack trace from throwable
|
||||
*
|
||||
* @param t
|
||||
* @return trace
|
||||
*/
|
||||
private String getStackTrace(Throwable t)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw, true);
|
||||
t.printStackTrace(pw);
|
||||
pw.flush();
|
||||
sw.flush();
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* PowerCraft Log file formatter.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
*/
|
||||
private class LogFormatter extends Formatter {
|
||||
|
||||
/** Newline string constant */
|
||||
private final String nl = System.getProperty("line.separator");
|
||||
|
||||
|
||||
@Override
|
||||
public String format(LogRecord record)
|
||||
{
|
||||
StringBuffer buf = new StringBuffer(180);
|
||||
|
||||
if (record.getMessage().equals("\n")) {
|
||||
return nl;
|
||||
}
|
||||
|
||||
if (record.getMessage().charAt(0) == '\n') {
|
||||
buf.append(nl);
|
||||
record.setMessage(record.getMessage().substring(1));
|
||||
}
|
||||
|
||||
Level level = record.getLevel();
|
||||
String trail = "[ ? ]";
|
||||
if (level == Level.FINE) {
|
||||
trail = "[ # ] ";
|
||||
}
|
||||
if (level == Level.FINER) {
|
||||
trail = "[ - ] ";
|
||||
}
|
||||
if (level == Level.FINEST) {
|
||||
trail = "[ ] ";
|
||||
}
|
||||
if (level == Level.INFO) {
|
||||
trail = "[ i ] ";
|
||||
}
|
||||
if (level == Level.SEVERE) {
|
||||
trail = "[!E!] ";
|
||||
}
|
||||
if (level == Level.WARNING) {
|
||||
trail = "[!W!] ";
|
||||
}
|
||||
|
||||
record.setMessage(record.getMessage().replaceAll("\n", nl + trail));
|
||||
|
||||
buf.append(trail);
|
||||
buf.append(formatMessage(record));
|
||||
|
||||
buf.append(nl);
|
||||
|
||||
Throwable throwable = record.getThrown();
|
||||
if (throwable != null) {
|
||||
buf.append("at ");
|
||||
buf.append(record.getSourceClassName());
|
||||
buf.append('.');
|
||||
buf.append(record.getSourceMethodName());
|
||||
buf.append(nl);
|
||||
|
||||
StringWriter sink = new StringWriter();
|
||||
throwable.printStackTrace(new PrintWriter(sink, true));
|
||||
buf.append(sink.toString());
|
||||
|
||||
buf.append(nl);
|
||||
}
|
||||
|
||||
String str = buf.toString();
|
||||
|
||||
for (LogMonitor mon : monitors.values()) {
|
||||
mon.log(level, str);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return log filename suffix (incl. dot)
|
||||
*/
|
||||
protected String getSuffix()
|
||||
{
|
||||
return ".log";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package mightypork.utils.logging;
|
||||
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public interface LogMonitor {
|
||||
|
||||
public void log(Level level, String message);
|
||||
|
||||
|
||||
public void enable(boolean enable);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package mightypork.utils.logging;
|
||||
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
||||
public class LogToSysoutMonitor implements LogMonitor {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message)
|
||||
{
|
||||
if (!enabled) return;
|
||||
|
||||
if (level == Level.FINE || level == Level.FINER || level == Level.FINEST || level == Level.INFO) {
|
||||
System.out.print(message);
|
||||
} else if (level == Level.SEVERE || level == Level.WARNING) {
|
||||
System.err.print(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void enable(boolean enable)
|
||||
{
|
||||
this.enabled = enable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,931 @@
|
||||
package mightypork.utils.math;
|
||||
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Vec;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Math helper
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Calc {
|
||||
|
||||
/** Square root of two */
|
||||
public static final double SQ2 = 1.41421356237;
|
||||
|
||||
|
||||
/**
|
||||
* Get distance from 2D line to 2D point [X,Y]
|
||||
*
|
||||
* @param lineDirVec line directional vector
|
||||
* @param linePoint point of line
|
||||
* @param point point coordinate
|
||||
* @return distance
|
||||
*/
|
||||
public static double linePointDist(Vec lineDirVec, Coord linePoint, Coord point)
|
||||
{
|
||||
// line point L[lx,ly]
|
||||
double lx = linePoint.x;
|
||||
double ly = linePoint.y;
|
||||
|
||||
// line equation ax+by+c=0
|
||||
double a = -lineDirVec.y;
|
||||
double b = lineDirVec.x;
|
||||
double c = -a * lx - b * ly;
|
||||
|
||||
// checked point P[x,y]
|
||||
double x = point.x;
|
||||
double y = point.y;
|
||||
|
||||
// distance
|
||||
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get distance from 2D line to 2D point [X,Z]
|
||||
*
|
||||
* @param lineDirVec line directional vector
|
||||
* @param linePoint point of line
|
||||
* @param point point coordinate
|
||||
* @return distance
|
||||
*/
|
||||
public static double linePointDistXZ(Vec lineDirVec, Coord linePoint, Coord point)
|
||||
{
|
||||
return linePointDist(new Vec(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get longest side of a right-angled triangle
|
||||
*
|
||||
* @param a side a (opposite)
|
||||
* @param b side b (adjacent)
|
||||
* @return longest side (hypotenuse)
|
||||
*/
|
||||
public static double pythC(double a, double b)
|
||||
{
|
||||
return Math.sqrt(square(a) + square(b));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get adjacent side of a right-angled triangle
|
||||
*
|
||||
* @param a side a (opposite)
|
||||
* @param c side c (hypotenuse)
|
||||
* @return side b (adjacent)
|
||||
*/
|
||||
public static double pythB(double a, double c)
|
||||
{
|
||||
return Math.sqrt(square(c) - square(a));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get opposite side of a right-angled triangle
|
||||
*
|
||||
* @param b side b (adjacent)
|
||||
* @param c side c (hypotenuse)
|
||||
* @return side a (opposite)
|
||||
*/
|
||||
public static double pythA(double b, double c)
|
||||
{
|
||||
return Math.sqrt(square(c) - square(b));
|
||||
}
|
||||
|
||||
private static class Angles {
|
||||
|
||||
public static double delta(double alpha, double beta, double a360)
|
||||
{
|
||||
while (Math.abs(alpha - beta) > a360 / 2D) {
|
||||
alpha = norm(alpha + a360 / 2D, a360);
|
||||
beta = norm(beta + a360 / 2D, a360);
|
||||
}
|
||||
|
||||
return beta - alpha;
|
||||
}
|
||||
|
||||
|
||||
public static double norm(double angle, double a360)
|
||||
{
|
||||
while (angle < 0)
|
||||
angle += a360;
|
||||
while (angle > a360)
|
||||
angle -= a360;
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calc subclass with buffer utils.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Buffers {
|
||||
|
||||
/**
|
||||
* Create java.nio.FloatBuffer of given floats, and flip it.
|
||||
*
|
||||
* @param obj floats or float array
|
||||
* @return float buffer
|
||||
*/
|
||||
public static FloatBuffer mkFillBuff(float... obj)
|
||||
{
|
||||
return (FloatBuffer) BufferUtils.createFloatBuffer(obj.length).put(obj).flip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill java.nio.FloatBuffer with floats or float array
|
||||
*
|
||||
* @param buff
|
||||
* @param obj
|
||||
*/
|
||||
public static void fill(FloatBuffer buff, float... obj)
|
||||
{
|
||||
buff.put(obj);
|
||||
buff.flip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create new java.nio.FloatBuffer of given length
|
||||
*
|
||||
* @param count elements
|
||||
* @return the new java.nio.FloatBuffer
|
||||
*/
|
||||
public static FloatBuffer alloc(int count)
|
||||
{
|
||||
return BufferUtils.createFloatBuffer(count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle calculations for degrees.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Deg {
|
||||
|
||||
/** 180° in degrees */
|
||||
public static final double a180 = 180;
|
||||
/** 270° in degrees */
|
||||
public static final double a270 = 270;
|
||||
/** 360° in degrees */
|
||||
public static final double a360 = 360;
|
||||
/** 45° in degrees */
|
||||
public static final double a45 = 45;
|
||||
/** 90° in degrees */
|
||||
public static final double a90 = 90;
|
||||
|
||||
|
||||
/**
|
||||
* Subtract two angles alpha - beta
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return (alpha - beta) in degrees
|
||||
*/
|
||||
public static double delta(double alpha, double beta)
|
||||
{
|
||||
return Angles.delta(alpha, beta, a360);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Difference of two angles (absolute value of delta)
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return difference in radians
|
||||
*/
|
||||
public static double diff(double alpha, double beta)
|
||||
{
|
||||
return Math.abs(Angles.delta(alpha, beta, a360));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cosinus in degrees
|
||||
*
|
||||
* @param deg angle in degrees
|
||||
* @return cosinus
|
||||
*/
|
||||
public static double cos(double deg)
|
||||
{
|
||||
return Math.cos(toRad(deg));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sinus in degrees
|
||||
*
|
||||
* @param deg angle in degrees
|
||||
* @return sinus
|
||||
*/
|
||||
public static double sin(double deg)
|
||||
{
|
||||
return Math.sin(toRad(deg));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tangents in degrees
|
||||
*
|
||||
* @param deg angle in degrees
|
||||
* @return tangents
|
||||
*/
|
||||
public static double tan(double deg)
|
||||
{
|
||||
return Math.tan(toRad(deg));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Angle normalized to 0-360 range
|
||||
*
|
||||
* @param angle angle to normalize
|
||||
* @return normalized angle
|
||||
*/
|
||||
public static double norm(double angle)
|
||||
{
|
||||
return Angles.norm(angle, a360);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to radians
|
||||
*
|
||||
* @param deg degrees
|
||||
* @return radians
|
||||
*/
|
||||
public static double toRad(double deg)
|
||||
{
|
||||
return Math.toRadians(deg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round angle to 0,45,90,135...
|
||||
*
|
||||
* @param deg angle in deg. to round
|
||||
* @param x rounding increment (45 - round to 0,45,90...)
|
||||
* @return rounded
|
||||
*/
|
||||
public static int roundX(double deg, double x)
|
||||
{
|
||||
double half = x / 2d;
|
||||
deg += half;
|
||||
deg = norm(deg);
|
||||
int times = (int) Math.floor(deg / x);
|
||||
double a = times * x;
|
||||
if (a == 360) a = 0;
|
||||
return (int) Math.round(a);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round angle to 0,45,90,135...
|
||||
*
|
||||
* @param deg angle in deg. to round
|
||||
* @return rounded
|
||||
*/
|
||||
public static int round45(double deg)
|
||||
{
|
||||
return roundX(deg, 45);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round angle to 0,90,180,270
|
||||
*
|
||||
* @param deg angle in deg. to round
|
||||
* @return rounded
|
||||
*/
|
||||
public static int round90(double deg)
|
||||
{
|
||||
return roundX(deg, 90);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round angle to 0,15,30,45,60,75,90...
|
||||
*
|
||||
* @param deg angle in deg to round
|
||||
* @return rounded
|
||||
*/
|
||||
public static int round15(double deg)
|
||||
{
|
||||
return roundX(deg, 15);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle calculations for radians.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class Rad {
|
||||
|
||||
/** 180° in radians */
|
||||
public static final double a180 = Math.PI;
|
||||
/** 270° in radians */
|
||||
public static final double a270 = Math.PI * 1.5D;
|
||||
/** 360° in radians */
|
||||
public static final double a360 = Math.PI * 2D;
|
||||
/** 45° in radians */
|
||||
public static final double a45 = Math.PI / 4D;
|
||||
/** 90° in radians */
|
||||
public static final double a90 = Math.PI / 2D;
|
||||
|
||||
|
||||
/**
|
||||
* Subtract two angles alpha - beta
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return (alpha - beta) in radians
|
||||
*/
|
||||
public static double delta(double alpha, double beta)
|
||||
{
|
||||
return Angles.delta(alpha, beta, a360);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Difference of two angles (absolute value of delta)
|
||||
*
|
||||
* @param alpha first angle
|
||||
* @param beta second angle
|
||||
* @return difference in radians
|
||||
*/
|
||||
public static double diff(double alpha, double beta)
|
||||
{
|
||||
return Math.abs(Angles.delta(alpha, beta, a360));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cos
|
||||
*
|
||||
* @param rad angle in rads
|
||||
* @return cos
|
||||
*/
|
||||
public static double cos(double rad)
|
||||
{
|
||||
return Math.cos(rad);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sin
|
||||
*
|
||||
* @param rad angle in rads
|
||||
* @return sin
|
||||
*/
|
||||
public static double sin(double rad)
|
||||
{
|
||||
return Math.sin(rad);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tan
|
||||
*
|
||||
* @param rad angle in rads
|
||||
* @return tan
|
||||
*/
|
||||
public static double tan(double rad)
|
||||
{
|
||||
return Math.tan(rad);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Angle normalized to 0-2*PI range
|
||||
*
|
||||
* @param angle angle to normalize
|
||||
* @return normalized angle
|
||||
*/
|
||||
public static double norm(double angle)
|
||||
{
|
||||
return Angles.norm(angle, a360);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to degrees
|
||||
*
|
||||
* @param rad radians
|
||||
* @return degrees
|
||||
*/
|
||||
public static double toDeg(double rad)
|
||||
{
|
||||
return Math.toDegrees(rad);
|
||||
}
|
||||
}
|
||||
|
||||
private static Random rand = new Random();
|
||||
|
||||
|
||||
/**
|
||||
* Get volume of a sphere
|
||||
*
|
||||
* @param radius sphere radius
|
||||
* @return volume in cubic units
|
||||
*/
|
||||
public static double sphereGetVolume(double radius)
|
||||
{
|
||||
return (4D / 3D) * Math.PI * cube(radius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get radius of a sphere
|
||||
*
|
||||
* @param volume sphere volume
|
||||
* @return radius in units
|
||||
*/
|
||||
public static double sphereGetRadius(double volume)
|
||||
{
|
||||
return Math.cbrt((3D * volume) / (4 * Math.PI));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get surface of a circle
|
||||
*
|
||||
* @param radius circle radius
|
||||
* @return volume in square units
|
||||
*/
|
||||
public static double circleGetSurface(double radius)
|
||||
{
|
||||
return Math.PI * square(radius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get radius of a circle
|
||||
*
|
||||
* @param surface circle volume
|
||||
* @return radius in units
|
||||
*/
|
||||
public static double circleGetRadius(double surface)
|
||||
{
|
||||
return Math.sqrt(surface / Math.PI);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if objects are equal (for equals function)
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @return are equal
|
||||
*/
|
||||
public static boolean areObjectsEqual(Object a, Object b)
|
||||
{
|
||||
return a == null ? b == null : a.equals(b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private clamping helper.
|
||||
*
|
||||
* @param number number to be clamped
|
||||
* @param min min value
|
||||
* @param max max value
|
||||
* @return clamped double
|
||||
*/
|
||||
private static double clamp_double(Number number, Number min, Number max)
|
||||
{
|
||||
double n = number.doubleValue();
|
||||
double mind = min.doubleValue();
|
||||
double maxd = max.doubleValue();
|
||||
if (n > maxd) n = maxd;
|
||||
if (n < mind) n = mind;
|
||||
if (Double.isNaN(n)) return mind;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private clamping helper.
|
||||
*
|
||||
* @param number number to be clamped
|
||||
* @param min min value
|
||||
* @return clamped double
|
||||
*/
|
||||
private static double clamp_double(Number number, Number min)
|
||||
{
|
||||
double n = number.doubleValue();
|
||||
double mind = min.doubleValue();
|
||||
if (n < mind) n = mind;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* DOUBLE version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @param max maximal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static double clampd(Number number, Number min, Number max)
|
||||
{
|
||||
return clamp_double(number, min, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* FLOAT version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @param max maximal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static float clampf(Number number, Number min, Number max)
|
||||
{
|
||||
return (float) clamp_double(number, min, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* INTEGER version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @param max maximal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static int clampi(Number number, Number min, Number max)
|
||||
{
|
||||
return (int) Math.round(clamp_double(number, min, max));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* INTEGER version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param range range
|
||||
* @return result
|
||||
*/
|
||||
public static int clampi(Number number, Range range)
|
||||
{
|
||||
return (int) Math.round(clamp_double(number, range.getMin(), range.getMax()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* DOUBLE version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param range range
|
||||
* @return result
|
||||
*/
|
||||
public static double clampd(Number number, Range range)
|
||||
{
|
||||
return clamp_double(number, range.getMin(), range.getMax());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and max bounds, inclusive.<br>
|
||||
* FLOAT version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param range range
|
||||
* @return result
|
||||
*/
|
||||
public static float clampf(Number number, Range range)
|
||||
{
|
||||
return (float) clamp_double(number, range.getMin(), range.getMax());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and infinite bounds, inclusive.<br>
|
||||
* DOUBLE version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static double clampd(Number number, Number min)
|
||||
{
|
||||
return clamp_double(number, min);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and infinite bounds, inclusive.<br>
|
||||
* FLOAT version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static float clampf(Number number, Number min)
|
||||
{
|
||||
return (float) clamp_double(number, min);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamp number to min and infinite bounds, inclusive.<br>
|
||||
* INTEGER version
|
||||
*
|
||||
* @param number clamped number
|
||||
* @param min minimal allowed value
|
||||
* @return result
|
||||
*/
|
||||
public static int clampi(Number number, Number min)
|
||||
{
|
||||
return (int) Math.round(clamp_double(number, min));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get class simple name
|
||||
*
|
||||
* @param obj object
|
||||
* @return simple name
|
||||
*/
|
||||
public static String cname(Object obj)
|
||||
{
|
||||
if (obj == null) return "NULL";
|
||||
return obj.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cube a double
|
||||
*
|
||||
* @param a squared double
|
||||
* @return square
|
||||
*/
|
||||
public static double cube(double a)
|
||||
{
|
||||
return a * a * a;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert double to string, remove the mess at the end.
|
||||
*
|
||||
* @param d double
|
||||
* @return string
|
||||
*/
|
||||
public static String doubleToString(double d)
|
||||
{
|
||||
String s = Double.toString(d);
|
||||
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
|
||||
s = s.replaceAll("0+$", "");
|
||||
s = s.replaceAll("\\.$", "");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert float to string, remove the mess at the end.
|
||||
*
|
||||
* @param f float
|
||||
* @return string
|
||||
*/
|
||||
public static String floatToString(float f)
|
||||
{
|
||||
String s = Float.toString(f);
|
||||
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
|
||||
s = s.replaceAll("0+$", "");
|
||||
s = s.replaceAll("\\.$", "");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if number is in range
|
||||
*
|
||||
* @param number checked
|
||||
* @param left lower end
|
||||
* @param right upper end
|
||||
* @return is in range
|
||||
*/
|
||||
public static boolean inRange(double number, double left, double right)
|
||||
{
|
||||
return number >= left && number <= right;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number from A to B at delta time (tween A to B)
|
||||
*
|
||||
* @param last last number
|
||||
* @param now new number
|
||||
* @param dtime delta time
|
||||
* @return current number to render
|
||||
*/
|
||||
public static double interpolate(double last, double now, double dtime)
|
||||
{
|
||||
return last + (now - last) * dtime;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get angle [degrees] from A to B at delta time (tween A to B)
|
||||
*
|
||||
* @param last last angle
|
||||
* @param now new angle
|
||||
* @param delta delta time
|
||||
* @return current angle to render
|
||||
*/
|
||||
public static double interpolateDeg(double last, double now, double delta)
|
||||
{
|
||||
return Deg.norm(last + Deg.delta(now, last) * delta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get highest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static double max(double... numbers)
|
||||
{
|
||||
double highest = numbers[0];
|
||||
for (double num : numbers) {
|
||||
if (num > highest) highest = num;
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get highest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static float max(float... numbers)
|
||||
{
|
||||
float highest = numbers[0];
|
||||
for (float num : numbers) {
|
||||
if (num > highest) highest = num;
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get highest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static int max(int... numbers)
|
||||
{
|
||||
int highest = numbers[0];
|
||||
for (int num : numbers) {
|
||||
if (num > highest) highest = num;
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get lowest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static double min(double... numbers)
|
||||
{
|
||||
double lowest = numbers[0];
|
||||
for (double num : numbers) {
|
||||
if (num < lowest) lowest = num;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get lowest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static float min(float... numbers)
|
||||
{
|
||||
float lowest = numbers[0];
|
||||
for (float num : numbers) {
|
||||
if (num < lowest) lowest = num;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get lowest number of a list
|
||||
*
|
||||
* @param numbers numbers
|
||||
* @return lowest
|
||||
*/
|
||||
public static int min(int... numbers)
|
||||
{
|
||||
int lowest = numbers[0];
|
||||
for (int num : numbers) {
|
||||
if (num < lowest) lowest = num;
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Split comma separated list of integers.
|
||||
*
|
||||
* @param list String containing the list.
|
||||
* @return array of integers or null.
|
||||
*/
|
||||
public static List<Integer> parseIntList(String list)
|
||||
{
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = list.split(",");
|
||||
|
||||
ArrayList<Integer> intList = new ArrayList<Integer>();
|
||||
|
||||
for (String part : parts) {
|
||||
try {
|
||||
intList.add(Integer.parseInt(part));
|
||||
} catch (NumberFormatException e) {}
|
||||
}
|
||||
|
||||
return intList;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pick random element from a given list.
|
||||
*
|
||||
* @param list list of choices
|
||||
* @return picked element
|
||||
*/
|
||||
public static Object pick(List<?> list)
|
||||
{
|
||||
if (list.size() == 0) return null;
|
||||
return list.get(rand.nextInt(list.size()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Square a double
|
||||
*
|
||||
* @param a squared double
|
||||
* @return square
|
||||
*/
|
||||
public static double square(double a)
|
||||
{
|
||||
return a * a;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Signum.
|
||||
*
|
||||
* @param number
|
||||
* @return sign, -1,0,1
|
||||
*/
|
||||
public static int sgn(double number)
|
||||
{
|
||||
return number > 0 ? 1 : number < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
public static double frag(double d)
|
||||
{
|
||||
return d - Math.floor(d);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package mightypork.utils.math;
|
||||
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Polar coordinate
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Polar {
|
||||
|
||||
/** angle in radians */
|
||||
public double angle = 0;
|
||||
/** distance in units */
|
||||
public double distance = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param angle angle in radians
|
||||
* @param distance distance from origin
|
||||
*/
|
||||
public Polar(double angle, double distance) {
|
||||
this.angle = angle;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coord
|
||||
*
|
||||
* @param coord coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoord(Coord coord)
|
||||
{
|
||||
return new Polar(Math.atan2(coord.y, coord.x), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoord(double x, double y)
|
||||
{
|
||||
return Polar.fromCoord(new Coord(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param z z coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoordXZ(double x, double z)
|
||||
{
|
||||
return Polar.fromCoordXZ(new Coord(x, 0, z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoord()
|
||||
{
|
||||
return new Coord(distance * Math.cos(angle), distance * Math.sin(angle));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get X,0,Y coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoordXZ()
|
||||
{
|
||||
return new Coord(distance * Math.cos(angle), 0, distance * Math.sin(angle));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Polar(theta=" + angle + ", r=" + distance + ")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build polar from X,Z instead of X,Y
|
||||
*
|
||||
* @param coord cpprd with X,Z
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoordXZ(Coord coord)
|
||||
{
|
||||
return fromCoord(coord.x, coord.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package mightypork.utils.math;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc.Deg;
|
||||
import mightypork.utils.math.Calc.Rad;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Polar coordinate in degrees
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class PolarDeg {
|
||||
|
||||
/** angle in degrees */
|
||||
public double angle = 0;
|
||||
/** distance in units */
|
||||
public double distance = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Polar coordinate in degrees
|
||||
*
|
||||
* @param angle angle in degrees
|
||||
* @param distance distance from origin
|
||||
*/
|
||||
public PolarDeg(double angle, double distance) {
|
||||
this.angle = angle;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coord
|
||||
*
|
||||
* @param coord coord
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoord(Coord coord)
|
||||
{
|
||||
return new PolarDeg(Rad.toDeg(Math.atan2(coord.y, coord.x)), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoord(double x, double y)
|
||||
{
|
||||
return PolarDeg.fromCoord(new Coord(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param z y coord
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoordXZ(double x, double z)
|
||||
{
|
||||
return PolarDeg.fromCoordXZ(new Coord(x, 0, z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoord()
|
||||
{
|
||||
return new Coord(distance * Math.cos(Deg.toRad(angle)), distance * Math.sin(Deg.toRad(angle)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get X,0,Y coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoordXZ()
|
||||
{
|
||||
return new Coord(distance * Math.cos(Deg.toRad(angle)), 0, distance * Math.sin(Deg.toRad(angle)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Polar(theta=" + angle + ", r=" + distance + ")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build polar from X,Z instead of X,Y
|
||||
*
|
||||
* @param coord cpprd with X,Z
|
||||
* @return polar
|
||||
*/
|
||||
public static PolarDeg fromCoordXZ(Coord coord)
|
||||
{
|
||||
return fromCoord(coord.x, coord.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package mightypork.utils.math;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
/**
|
||||
* Numeric range, able to generate random numbers and give min/max values.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Range {
|
||||
|
||||
private double min = 0;
|
||||
private double max = 1;
|
||||
|
||||
private static Random rand = new Random();
|
||||
|
||||
|
||||
/**
|
||||
* Implicit range constructor 0-1
|
||||
*/
|
||||
public Range() {}
|
||||
|
||||
|
||||
/**
|
||||
* Create new range
|
||||
*
|
||||
* @param min min number
|
||||
* @param max max number
|
||||
*/
|
||||
public Range(double min, double max) {
|
||||
if (min > max) {
|
||||
double t = min;
|
||||
min = max;
|
||||
max = t;
|
||||
}
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create new range
|
||||
*
|
||||
* @param minmax min = max number
|
||||
*/
|
||||
public Range(double minmax) {
|
||||
this.min = minmax;
|
||||
this.max = minmax;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get random integer from range
|
||||
*
|
||||
* @return random int
|
||||
*/
|
||||
public int randInt()
|
||||
{
|
||||
return (int) (Math.round(min) + rand.nextInt((int) (Math.round(max) - Math.round(min)) + 1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get random double from this range
|
||||
*
|
||||
* @return random double
|
||||
*/
|
||||
public double randDouble()
|
||||
{
|
||||
return min + rand.nextDouble() * (max - min);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get min
|
||||
*
|
||||
* @return min number
|
||||
*/
|
||||
public double getMin()
|
||||
{
|
||||
return min;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get max
|
||||
*
|
||||
* @return max number
|
||||
*/
|
||||
public double getMax()
|
||||
{
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get min
|
||||
*
|
||||
* @return min number
|
||||
*/
|
||||
public int getMinI()
|
||||
{
|
||||
return (int) min;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get max
|
||||
*
|
||||
* @return max number
|
||||
*/
|
||||
public int getMaxI()
|
||||
{
|
||||
return (int) max;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set min
|
||||
*
|
||||
* @param min min value
|
||||
*/
|
||||
public void setMin(double min)
|
||||
{
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set max
|
||||
*
|
||||
* @param max max value
|
||||
*/
|
||||
public void setMax(double max)
|
||||
{
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Range(" + min + ";" + max + ")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get identical copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public Range copy()
|
||||
{
|
||||
return new Range(min, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to value of other range
|
||||
*
|
||||
* @param other copied range
|
||||
*/
|
||||
public void setTo(Range other)
|
||||
{
|
||||
if (other == null) return;
|
||||
min = other.min;
|
||||
max = other.max;
|
||||
|
||||
if (min > max) {
|
||||
double t = min;
|
||||
min = max;
|
||||
max = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to min-max values
|
||||
*
|
||||
* @param min min value
|
||||
* @param max max value
|
||||
*/
|
||||
public void setTo(double min, double max)
|
||||
{
|
||||
if (min > max) {
|
||||
double t = min;
|
||||
min = max;
|
||||
max = t;
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package mightypork.utils.math.color;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* HSV color
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class HSV {
|
||||
|
||||
/** H */
|
||||
public double h;
|
||||
/** S */
|
||||
public double s;
|
||||
/** V */
|
||||
public double v;
|
||||
|
||||
|
||||
/**
|
||||
* Create black color 0,0,0
|
||||
*/
|
||||
public HSV() {}
|
||||
|
||||
|
||||
/**
|
||||
* Color from HSV 0-1
|
||||
*
|
||||
* @param h
|
||||
* @param s
|
||||
* @param v
|
||||
*/
|
||||
public HSV(Number h, Number s, Number v) {
|
||||
this.h = h.doubleValue();
|
||||
this.s = s.doubleValue();
|
||||
this.v = v.doubleValue();
|
||||
norm();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return hue 0-1
|
||||
*/
|
||||
public double h()
|
||||
{
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return saturation 0-1
|
||||
*/
|
||||
public double s()
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return value/brightness 0-1
|
||||
*/
|
||||
public double v()
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set color to other color
|
||||
*
|
||||
* @param copied copied color
|
||||
* @return this
|
||||
*/
|
||||
public HSV setTo(HSV copied)
|
||||
{
|
||||
h = copied.h;
|
||||
s = copied.s;
|
||||
v = copied.v;
|
||||
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to H,S,V 0-1
|
||||
*
|
||||
* @param h hue
|
||||
* @param s saturation
|
||||
* @param v value
|
||||
* @return this
|
||||
*/
|
||||
public HSV setTo(Number h, Number s, Number v)
|
||||
{
|
||||
this.h = h.doubleValue();
|
||||
this.s = s.doubleValue();
|
||||
this.v = v.doubleValue();
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fix numbers out of range 0-1
|
||||
*/
|
||||
public void norm()
|
||||
{
|
||||
h = Calc.clampd(h, 0, 1);
|
||||
s = Calc.clampd(s, 0, 1);
|
||||
v = Calc.clampd(v, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to RGB
|
||||
*
|
||||
* @return RGB representation
|
||||
*/
|
||||
public RGB toRGB()
|
||||
{
|
||||
norm();
|
||||
|
||||
int rgb = Color.HSBtoRGB((float) h, (float) s, (float) v);
|
||||
|
||||
return RGB.fromHex(rgb);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make from RGB
|
||||
*
|
||||
* @param color RGB
|
||||
* @return HSV
|
||||
*/
|
||||
public static HSV fromRGB(RGB color)
|
||||
{
|
||||
return color.toHSV();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "HSV[" + h + ";" + s + ";" + v + "]";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof HSV)) return false;
|
||||
return ((HSV) obj).h == h && ((HSV) obj).s == s && ((HSV) obj).v == v;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Double.valueOf(h).hashCode() ^ Double.valueOf(s).hashCode() ^ Double.valueOf(v).hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public HSV copy()
|
||||
{
|
||||
return new HSV().setTo(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package mightypork.utils.math.color;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* RGB color
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class RGB {
|
||||
|
||||
/** White */
|
||||
public static final RGB WHITE = new RGB(1, 1, 1);
|
||||
/** Black */
|
||||
public static final RGB BLACK = new RGB(0, 0, 0);
|
||||
/** Red */
|
||||
public static final RGB RED = new RGB(1, 0, 0);
|
||||
/** Lime green */
|
||||
public static final RGB GREEN = new RGB(0, 1, 0);
|
||||
/** Blue */
|
||||
public static final RGB BLUE = new RGB(0, 0, 1);
|
||||
/** Yellow */
|
||||
public static final RGB YELLOW = new RGB(1, 1, 0);
|
||||
/** Purple */
|
||||
public static final RGB PURPLE = new RGB(1, 0, 1);
|
||||
/** Cyan */
|
||||
public static final RGB CYAN = new RGB(0, 1, 1);
|
||||
/** orange */
|
||||
public static final RGB ORANGE = new RGB(1, 0.6, 0);
|
||||
/** no color (alpha=0) */
|
||||
public static final RGB TRANSPARENT = new RGB(0, 0, 0, 0);
|
||||
|
||||
/** R */
|
||||
public double r;
|
||||
/** G */
|
||||
public double g;
|
||||
/** B */
|
||||
public double b;
|
||||
/** ALPHA */
|
||||
public double a = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Create black color 0,0,0
|
||||
*/
|
||||
public RGB() {}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy with custom alpha
|
||||
*
|
||||
* @param alpha alpha to set
|
||||
* @return copy w/ alpha
|
||||
*/
|
||||
public RGB setAlpha(double alpha)
|
||||
{
|
||||
return copy().setAlpha_ip(alpha);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set alpha IP
|
||||
*
|
||||
* @param alpha alpha to set
|
||||
* @return this
|
||||
*/
|
||||
public RGB setAlpha_ip(double alpha)
|
||||
{
|
||||
a = alpha;
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy.
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public RGB copy()
|
||||
{
|
||||
return new RGB(r, g, b, a);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy with alpha multiplied by custom value
|
||||
*
|
||||
* @param alpha alpha to set
|
||||
* @return copy w/ alpha
|
||||
*/
|
||||
public RGB mulAlpha(double alpha)
|
||||
{
|
||||
return copy().mulAlpha_ip(alpha);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply alpha by given number
|
||||
*
|
||||
* @param alpha alpha multiplier
|
||||
* @return this
|
||||
*/
|
||||
public RGB mulAlpha_ip(double alpha)
|
||||
{
|
||||
a *= alpha;
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Color from RGB 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
*/
|
||||
public RGB(Number r, Number g, Number b) {
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
norm();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Color from RGB 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
* @param a alpha
|
||||
*/
|
||||
public RGB(Number r, Number g, Number b, Number a) {
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
this.a = a.doubleValue();
|
||||
norm();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Color from hex 0xRRGGBB
|
||||
*
|
||||
* @param hex hex integer
|
||||
*/
|
||||
public RGB(int hex) {
|
||||
setTo(RGB.fromHex(hex));
|
||||
norm();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Color from hex 0xRRGGBB
|
||||
*
|
||||
* @param hex hex integer
|
||||
* @param alpha alpha color
|
||||
*/
|
||||
public RGB(int hex, double alpha) {
|
||||
setTo(RGB.fromHex(hex));
|
||||
a = alpha;
|
||||
norm();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Color from other RGB and alpha channel
|
||||
*
|
||||
* @param color other RGB color
|
||||
* @param alpha new alpha channel
|
||||
*/
|
||||
public RGB(RGB color, double alpha) {
|
||||
setTo(color);
|
||||
setAlpha_ip(alpha);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return red channel 0-1
|
||||
*/
|
||||
public double r()
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return green channel 0-1
|
||||
*/
|
||||
public double g()
|
||||
{
|
||||
return g;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return blue channel 0-1
|
||||
*/
|
||||
public double b()
|
||||
{
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return alpha 0-1
|
||||
*/
|
||||
public double a()
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set color to other color
|
||||
*
|
||||
* @param copied copied color
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(RGB copied)
|
||||
{
|
||||
r = copied.r;
|
||||
g = copied.g;
|
||||
b = copied.b;
|
||||
a = copied.a;
|
||||
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to represent hex color
|
||||
*
|
||||
* @param hex hex integer RRGGBB
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(int hex)
|
||||
{
|
||||
setTo(RGB.fromHex(hex));
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to R,G,B 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
* @param a alpha
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(Number r, Number g, Number b, Number a)
|
||||
{
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
this.a = a.doubleValue();
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to R,G,B 0-1
|
||||
*
|
||||
* @param r red
|
||||
* @param g green
|
||||
* @param b blue
|
||||
* @return this
|
||||
*/
|
||||
public RGB setTo(Number r, Number g, Number b)
|
||||
{
|
||||
this.r = r.doubleValue();
|
||||
this.g = g.doubleValue();
|
||||
this.b = b.doubleValue();
|
||||
this.a = 1;
|
||||
norm();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fix numbers out of range 0-1
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public RGB norm()
|
||||
{
|
||||
r = Calc.clampd(r, 0, 1);
|
||||
g = Calc.clampd(g, 0, 1);
|
||||
b = Calc.clampd(b, 0, 1);
|
||||
a = Calc.clampd(a, 0, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get hex value 0xRRGGBB
|
||||
*
|
||||
* @return hex value RRGGBB
|
||||
*/
|
||||
public int getHex()
|
||||
{
|
||||
int ri = (int) Math.round(r * 255);
|
||||
int gi = (int) Math.round(g * 255);
|
||||
int bi = (int) Math.round(b * 255);
|
||||
return (ri << 16) | (gi << 8) | bi;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to HSV
|
||||
*
|
||||
* @return HSV representation
|
||||
*/
|
||||
public HSV toHSV()
|
||||
{
|
||||
float[] hsv = { 0, 0, 0 };
|
||||
Color.RGBtoHSB((int) (r * 255), (int) (g * 255), (int) (b * 255), hsv);
|
||||
return new HSV(hsv[0], hsv[1], hsv[2]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create color from hex 0xRRGGBB
|
||||
*
|
||||
* @param hex hex RRGGBB
|
||||
* @return the new color
|
||||
*/
|
||||
public static RGB fromHex(int hex)
|
||||
{
|
||||
int bi = hex & 0xff;
|
||||
int gi = (hex >> 8) & 0xff;
|
||||
int ri = (hex >> 16) & 0xff;
|
||||
return new RGB(ri / 255D, gi / 255D, bi / 255D);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make from HSV
|
||||
*
|
||||
* @param color HSV color
|
||||
* @return RGB
|
||||
*/
|
||||
public static RGB fromHSV(HSV color)
|
||||
{
|
||||
return color.toRGB();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "RGB[" + r + ";" + g + ";" + b + ";" + a + "]";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof RGB)) return false;
|
||||
return ((RGB) obj).r == r && ((RGB) obj).g == g && ((RGB) obj).b == b && ((RGB) obj).a == a;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Double.valueOf(r).hashCode() ^ Double.valueOf(g).hashCode() ^ Double.valueOf(b).hashCode() ^ Double.valueOf(a).hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,899 @@
|
||||
package mightypork.utils.math.coord;
|
||||
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
import mightypork.utils.time.Updateable;
|
||||
|
||||
|
||||
/**
|
||||
* Coordinate class, object with three or two double coordinates.<br>
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Coord implements Updateable {
|
||||
|
||||
/** Coord [1;1;1] */
|
||||
public static final Coord ONE = new Coord(1, 1, 1);
|
||||
|
||||
/** Zero Coord */
|
||||
public static final Coord ZERO = new Coord(0, 0);
|
||||
|
||||
/** RNG */
|
||||
protected static Random rand = new Random();
|
||||
|
||||
|
||||
/**
|
||||
* Get distance to other point
|
||||
*
|
||||
* @param a point a
|
||||
* @param b point b
|
||||
* @return distance in units
|
||||
*/
|
||||
public static double dist(Coord a, Coord b)
|
||||
{
|
||||
return a.distTo(b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate random coord (gaussian)
|
||||
*
|
||||
* @param max max distance from 0
|
||||
* @return new coord
|
||||
*/
|
||||
public static Coord random(double max)
|
||||
{
|
||||
return new Coord(Calc.clampd(rand.nextGaussian() * max, -max * 2, max * 2), Calc.clampd(rand.nextGaussian() * max, -max * 2, max * 2),
|
||||
Calc.clampd(rand.nextGaussian() * max, -max * 2, max * 2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate random coord (min-max)
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return new coord
|
||||
*/
|
||||
public static Coord random(double min, double max)
|
||||
{
|
||||
return new Coord((rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)), (rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)),
|
||||
(rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)));
|
||||
}
|
||||
|
||||
private double animTime = 0;
|
||||
|
||||
private Vec offs;
|
||||
|
||||
private Coord start;
|
||||
|
||||
private double time = 0;
|
||||
|
||||
/** X coordinate */
|
||||
public double x = 0;
|
||||
|
||||
/** Y coordinate */
|
||||
public double y = 0;
|
||||
|
||||
/** Z coordinate */
|
||||
public double z = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Create zero coord
|
||||
*/
|
||||
public Coord() {}
|
||||
|
||||
|
||||
/**
|
||||
* Create coord as a copy of another
|
||||
*
|
||||
* @param copied copied coord
|
||||
*/
|
||||
public Coord(Coord copied) {
|
||||
this.x = copied.x;
|
||||
this.y = copied.y;
|
||||
this.z = copied.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create 2D coord
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
*/
|
||||
public Coord(Number x, Number y) {
|
||||
setTo(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create 3D coord
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param z z coordinate
|
||||
*/
|
||||
public Coord(Number x, Number y, Number z) {
|
||||
setTo(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy offset by vector
|
||||
*
|
||||
* @param vec offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord add(Coord vec)
|
||||
{
|
||||
return copy().add_ip(vec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy offset by 2D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord add(Number x, Number y)
|
||||
{
|
||||
return copy().add_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy offset by 3D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord add(Number x, Number y, Number z)
|
||||
{
|
||||
return copy().add_ip(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset by vector in place
|
||||
*
|
||||
* @param vec offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord add_ip(Coord vec)
|
||||
{
|
||||
this.x += vec.x;
|
||||
this.y += vec.y;
|
||||
this.z += vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset by 2D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord add_ip(Number x, Number y)
|
||||
{
|
||||
this.x += x.doubleValue();
|
||||
this.y += y.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset by 3D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord add_ip(Number x, Number y, Number z)
|
||||
{
|
||||
this.x += x.doubleValue();
|
||||
this.y += y.doubleValue();
|
||||
this.z += z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start animation
|
||||
*
|
||||
* @param time anim length
|
||||
*/
|
||||
public void animate(double time)
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
this.time = time;
|
||||
animTime = 0;
|
||||
offs = start.vecTo(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return copy of this vector
|
||||
*/
|
||||
public Coord copy()
|
||||
{
|
||||
return new Coord(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get distance to other point
|
||||
*
|
||||
* @param point other point
|
||||
* @return distance in units
|
||||
*/
|
||||
public double distTo(Coord point)
|
||||
{
|
||||
return Math.sqrt((point.x - x) * (point.x - x) + (point.y - y) * (point.y - y) + (point.z - z) * (point.z - z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy divided by number
|
||||
*
|
||||
* @param d number to divide by
|
||||
* @return divided copy
|
||||
*/
|
||||
public Coord div(double d)
|
||||
{
|
||||
return copy().div_ip(d);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Divide by number in place
|
||||
*
|
||||
* @param d number to divide by
|
||||
* @return this
|
||||
*/
|
||||
public Coord div_ip(double d)
|
||||
{
|
||||
if (d == 0) return this;
|
||||
x /= d;
|
||||
y /= d;
|
||||
z /= d;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (!obj.getClass().isAssignableFrom(Coord.class)) return false;
|
||||
Coord other = (Coord) obj;
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get current value (animated)
|
||||
*
|
||||
* @return curent value
|
||||
*/
|
||||
public Coord getDelta()
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
if (isFinished()) return this;
|
||||
return new Coord(start.add(offs.scale(animTime / time)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Double.valueOf(x).hashCode() ^ Double.valueOf(y).hashCode() ^ Double.valueOf(z).hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get if animation is finished
|
||||
*
|
||||
* @return is finished
|
||||
*/
|
||||
public boolean isFinished()
|
||||
{
|
||||
return animTime >= time;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this rectangle in inside a rectangular zone
|
||||
*
|
||||
* @param min min coord
|
||||
* @param max max coord
|
||||
* @return is inside
|
||||
*/
|
||||
public boolean isInRect(Coord min, Coord max)
|
||||
{
|
||||
return (x >= min.x && x <= max.x) && (y >= min.y && y <= max.y) && (z >= min.z && z <= max.z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this rectangle in inside a rectangular zone
|
||||
*
|
||||
* @param rect checked rect.
|
||||
* @return is inside
|
||||
*/
|
||||
public boolean isInRect(Rect rect)
|
||||
{
|
||||
return isInRect(rect.min, rect.max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get middle of line to other point
|
||||
*
|
||||
* @param other other point
|
||||
* @return middle
|
||||
*/
|
||||
public Coord midTo(Coord other)
|
||||
{
|
||||
return add(vecTo(other).scale(0.5));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by number
|
||||
*
|
||||
* @param d number
|
||||
* @return multiplied copy
|
||||
*/
|
||||
public Coord mul(double d)
|
||||
{
|
||||
return copy().mul_ip(d);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply coords by number
|
||||
*
|
||||
* @param xd x multiplier
|
||||
* @param yd y multiplier
|
||||
* @param zd z multiplier
|
||||
* @return multiplied copy
|
||||
*/
|
||||
public Coord mul(double xd, double yd, double zd)
|
||||
{
|
||||
return copy().mul_ip(xd, yd, zd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by number in place
|
||||
*
|
||||
* @param d multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Coord mul_ip(double d)
|
||||
{
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply coords by number in place
|
||||
*
|
||||
* @param xd x multiplier
|
||||
* @param yd y multiplier
|
||||
* @param zd z multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Coord mul_ip(double xd, double yd, double zd)
|
||||
{
|
||||
x *= xd;
|
||||
y *= yd;
|
||||
z *= zd;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return offset coord
|
||||
*/
|
||||
public Coord random_offset(double max)
|
||||
{
|
||||
Coord r = random(1);
|
||||
Vec v = new Vec(r);
|
||||
v.norm_ip(0.00001 + rand.nextDouble() * max);
|
||||
return copy().add_ip(v);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return offset coord
|
||||
*/
|
||||
public Coord random_offset(double min, double max)
|
||||
{
|
||||
return copy().add_ip(random(min, max));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord random_offset_ip(double max)
|
||||
{
|
||||
return add(random(max));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord random_offset_ip(double min, double max)
|
||||
{
|
||||
add(random(min, max));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remember position (other changes will be for animation)
|
||||
*/
|
||||
public void remember()
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
start.setTo(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy with rounded coords
|
||||
*
|
||||
* @return rounded copy
|
||||
*/
|
||||
public Coord round()
|
||||
{
|
||||
return copy().round_ip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round in place
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Coord round_ip()
|
||||
{
|
||||
x = Math.round(x);
|
||||
y = Math.round(y);
|
||||
z = Math.round(z);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to max values of this and other coord
|
||||
*
|
||||
* @param other other coord
|
||||
*/
|
||||
public void setMax(Coord other)
|
||||
{
|
||||
x = Math.max(x, other.x);
|
||||
y = Math.max(y, other.y);
|
||||
z = Math.max(z, other.z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to min values of this and other coord
|
||||
*
|
||||
* @param other other coord
|
||||
*/
|
||||
public void setMin(Coord other)
|
||||
{
|
||||
x = Math.min(x, other.x);
|
||||
y = Math.min(y, other.y);
|
||||
z = Math.min(z, other.z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set coordinates to match other coord
|
||||
*
|
||||
* @param copied coord whose coordinates are used
|
||||
* @return this
|
||||
*/
|
||||
public Coord setTo(Coord copied)
|
||||
{
|
||||
setTo(copied.x, copied.y, copied.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set 2D coordinates to
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setTo(Number x, Number y)
|
||||
{
|
||||
setTo(x, y, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set 3D coordinates to
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param z z coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setTo(Number x, Number y, Number z)
|
||||
{
|
||||
this.x = x.doubleValue();
|
||||
this.y = y.doubleValue();
|
||||
this.z = z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set X coordinate in a copy
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @return copy with set coordinate
|
||||
*/
|
||||
public Coord setX(Number x)
|
||||
{
|
||||
return copy().setX_ip(x);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set X coordinate in place
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setX_ip(Number x)
|
||||
{
|
||||
this.x = x.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set Y coordinate in a copy
|
||||
*
|
||||
* @param y y coordinate
|
||||
* @return copy with set coordinate
|
||||
*/
|
||||
public Coord setY(Number y)
|
||||
{
|
||||
return copy().setY_ip(y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set Y coordinate in place
|
||||
*
|
||||
* @param y y coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setY_ip(Number y)
|
||||
{
|
||||
this.y = y.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set Z coordinate in a copy
|
||||
*
|
||||
* @param z z coordinate
|
||||
* @return copy with set coordinate
|
||||
*/
|
||||
public Coord setZ(Number z)
|
||||
{
|
||||
return copy().setZ_ip(z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set Z coordinate in place
|
||||
*
|
||||
* @param z z coordinate
|
||||
* @return this
|
||||
*/
|
||||
public Coord setZ_ip(Number z)
|
||||
{
|
||||
this.z = z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get size
|
||||
*
|
||||
* @return size
|
||||
*/
|
||||
public double size()
|
||||
{
|
||||
return new Vec(this).size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by vector
|
||||
*
|
||||
* @param vec offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord sub(Coord vec)
|
||||
{
|
||||
return copy().sub_ip(vec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by 2D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord sub(Number x, Number y)
|
||||
{
|
||||
return copy().sub_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by 3D coordinate
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return the offset copy
|
||||
*/
|
||||
public Coord sub(Number x, Number y, Number z)
|
||||
{
|
||||
return copy().sub_ip(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset by vector in place
|
||||
*
|
||||
* @param vec offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord sub_ip(Coord vec)
|
||||
{
|
||||
this.x -= vec.x;
|
||||
this.y -= vec.y;
|
||||
this.z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset by 2D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord sub_ip(Number x, Number y)
|
||||
{
|
||||
this.x -= x.doubleValue();
|
||||
this.y -= y.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset by 3D coordinate in place
|
||||
*
|
||||
* @param x x offset
|
||||
* @param y y offset
|
||||
* @param z z offset
|
||||
* @return this
|
||||
*/
|
||||
public Coord sub_ip(Number x, Number y, Number z)
|
||||
{
|
||||
this.x -= x.doubleValue();
|
||||
this.y -= y.doubleValue();
|
||||
this.z -= z.doubleValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert X and Y coordinates of this coord to a new CoordI.
|
||||
*
|
||||
* @return the new CoordI
|
||||
*/
|
||||
public CoordI toCoordI()
|
||||
{
|
||||
return new CoordI((int) Math.round(x), (int) Math.round(y));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[ " + x + " ; " + y + " ; " + z + " ]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update delta timing
|
||||
*
|
||||
* @param delta delta time to add
|
||||
*/
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
animTime = Calc.clampd(animTime + delta, 0, time);
|
||||
if (isFinished()) {
|
||||
time = 0;
|
||||
animTime = 0;
|
||||
start.setTo(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create vector from this point to other point
|
||||
*
|
||||
* @param point second point
|
||||
* @return vector
|
||||
*/
|
||||
public Vec vecTo(Coord point)
|
||||
{
|
||||
return (Vec) (new Vec(point)).add(new Vec(this).neg());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return X as double
|
||||
*/
|
||||
public double x()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return X as double
|
||||
*/
|
||||
public double xd()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return X as float
|
||||
*/
|
||||
public float xf()
|
||||
{
|
||||
return (float) x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return X as int
|
||||
*/
|
||||
public int xi()
|
||||
{
|
||||
return (int) Math.round(x);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Y as double
|
||||
*/
|
||||
public double y()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Y as double
|
||||
*/
|
||||
public double yd()
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Y as float
|
||||
*/
|
||||
public float yf()
|
||||
{
|
||||
return (float) y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Y as int
|
||||
*/
|
||||
public int yi()
|
||||
{
|
||||
return (int) Math.round(y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Z as double
|
||||
*/
|
||||
public double z()
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Z as double
|
||||
*/
|
||||
public double zd()
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Z as float
|
||||
*/
|
||||
public float zf()
|
||||
{
|
||||
return (float) z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Z as int
|
||||
*/
|
||||
public int zi()
|
||||
{
|
||||
return (int) Math.round(z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
package mightypork.utils.math.coord;
|
||||
|
||||
|
||||
/**
|
||||
* Simple integer coordinate class<br>
|
||||
* Unlike Coord, this is suitable for using in array indices etc.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class CoordI {
|
||||
|
||||
/** X coordinate */
|
||||
public int x = 0;
|
||||
/** Y coordinate */
|
||||
public int y = 0;
|
||||
/** Z coordinate */
|
||||
public int z = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Create CoordI as copy of other
|
||||
*
|
||||
* @param other coord to copy
|
||||
*/
|
||||
public CoordI(CoordI other) {
|
||||
setTo(other);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Integer 2D Coord
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
*/
|
||||
public CoordI(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Integer 3D Coord
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
*/
|
||||
public CoordI(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Empty cobnstructor 0,0,0
|
||||
*/
|
||||
public CoordI() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add other coordI coordinates in a copy
|
||||
*
|
||||
* @param other coordI to add
|
||||
* @return copy modified
|
||||
*/
|
||||
public CoordI add(CoordI other)
|
||||
{
|
||||
return copy().add_ip(other);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add coords in copy
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return the copy
|
||||
*/
|
||||
public CoordI add(int x, int y)
|
||||
{
|
||||
return copy().add_ip(x, y, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add coords in copy
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @return the copy
|
||||
*/
|
||||
public CoordI add(int x, int y, int z)
|
||||
{
|
||||
return copy().add_ip(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add other coordI coordinates in place
|
||||
*
|
||||
* @param move coordI to add
|
||||
* @return this
|
||||
*/
|
||||
public CoordI add_ip(CoordI move)
|
||||
{
|
||||
x += move.x;
|
||||
y += move.y;
|
||||
z += move.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add coords in place
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return this
|
||||
*/
|
||||
public CoordI add_ip(int x, int y)
|
||||
{
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add coords in place
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @return this
|
||||
*/
|
||||
public CoordI add_ip(int x, int y, int z)
|
||||
{
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public CoordI copy()
|
||||
{
|
||||
return new CoordI(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (obj instanceof CoordI) return ((CoordI) obj).x == x && ((CoordI) obj).y == y && ((CoordI) obj).z == z;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Middle of this and other coordinate, rounded to CoordI - integers
|
||||
*
|
||||
* @param other other coordI
|
||||
* @return middle CoordI
|
||||
*/
|
||||
public CoordI midTo(CoordI other)
|
||||
{
|
||||
return new CoordI((x + other.x) / 2, (y + other.y) / 2, (z + other.z) / 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply in copy 2D
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return the copy
|
||||
*/
|
||||
public CoordI mul(double x, double y)
|
||||
{
|
||||
return copy().mul_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply in copy
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @return the copy
|
||||
*/
|
||||
public CoordI mul(double x, double y, double z)
|
||||
{
|
||||
return copy().mul_ip(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply in place 2D
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return this
|
||||
*/
|
||||
public CoordI mul_ip(double x, double y)
|
||||
{
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply in place
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @return this
|
||||
*/
|
||||
public CoordI mul_ip(double x, double y, double z)
|
||||
{
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
this.z *= z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to coords from other coord
|
||||
*
|
||||
* @param other source coord
|
||||
*/
|
||||
public void setTo(CoordI other)
|
||||
{
|
||||
if (other == null) {
|
||||
setTo(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
this.x = other.x;
|
||||
this.y = other.y;
|
||||
this.z = other.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set coords to
|
||||
*
|
||||
* @param x x coord to set
|
||||
* @param y y coord to set
|
||||
*/
|
||||
public void setTo(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set coords to
|
||||
*
|
||||
* @param x x coord to set
|
||||
* @param y y coord to set
|
||||
* @param z z coord to set
|
||||
*/
|
||||
public void setTo(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract other coordI coordinates in a copy
|
||||
*
|
||||
* @param other coordI to subtract
|
||||
* @return copy subtracted
|
||||
*/
|
||||
public CoordI sub(CoordI other)
|
||||
{
|
||||
return copy().sub_ip(other);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract x,y in a copy
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @return copy subtracted
|
||||
*/
|
||||
public CoordI sub(int x, int y)
|
||||
{
|
||||
return copy().sub_ip(new CoordI(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract x,y,z in a copy
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @param z z to subtract
|
||||
* @return copy subtracted
|
||||
*/
|
||||
public CoordI sub(int x, int y, int z)
|
||||
{
|
||||
return copy().sub_ip(new CoordI(x, y, z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract other coordI coordinates in place
|
||||
*
|
||||
* @param move coordI to subtract
|
||||
* @return this
|
||||
*/
|
||||
public CoordI sub_ip(CoordI move)
|
||||
{
|
||||
x -= move.x;
|
||||
y -= move.y;
|
||||
z -= move.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sub coords in place
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @return this
|
||||
*/
|
||||
public CoordI sub_ip(int x, int y)
|
||||
{
|
||||
this.x -= x;
|
||||
this.y -= y;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sub coords in place
|
||||
*
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @return this
|
||||
*/
|
||||
public CoordI sub_ip(int x, int y, int z)
|
||||
{
|
||||
this.x -= x;
|
||||
this.y -= y;
|
||||
this.z -= z;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert to double Coord
|
||||
*
|
||||
* @return coord with X and y from this CoordI
|
||||
*/
|
||||
public Coord toCoord()
|
||||
{
|
||||
return new Coord(x, y);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[ " + x + " ; " + y + " ; " + z + " ]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,805 @@
|
||||
package mightypork.utils.math.coord;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Rectangle determined by two coordinates - min and max.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Rect {
|
||||
|
||||
/** Rect [0, 0, 1, 1] */
|
||||
public static final Rect ONE = new Rect(0, 0, 1, 1);
|
||||
/** Rect all zeros */
|
||||
public static final Rect ZERO = new Rect(0, 0, 0, 0);
|
||||
|
||||
|
||||
/**
|
||||
* Rectangle from size
|
||||
*
|
||||
* @param min min coord
|
||||
* @param size rect size
|
||||
* @return the rect
|
||||
*/
|
||||
public static Rect fromSize(Coord min, Coord size)
|
||||
{
|
||||
return fromSize(min, size.xi(), size.yi());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make rect from min coord and size
|
||||
*
|
||||
* @param min min coord
|
||||
* @param width size x
|
||||
* @param height size y
|
||||
* @return the new rect
|
||||
*/
|
||||
public static Rect fromSize(Coord min, int width, int height)
|
||||
{
|
||||
return new Rect(min, min.add(width, height));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rectangle from size
|
||||
*
|
||||
* @param i min X
|
||||
* @param j min Y
|
||||
* @param size rect size
|
||||
* @return the rect
|
||||
*/
|
||||
public static Rect fromSize(int i, int j, CoordI size)
|
||||
{
|
||||
return fromSize(i, j, size.x, size.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make rect from min coord and size
|
||||
*
|
||||
* @param x1 min x
|
||||
* @param y1 min y
|
||||
* @param width size x
|
||||
* @param height size y
|
||||
* @return the new rect
|
||||
*/
|
||||
public static Rect fromSize(int x1, int y1, int width, int height)
|
||||
{
|
||||
return new Rect(x1, y1, x1 + width, y1 + height);
|
||||
}
|
||||
|
||||
/** Highest coordinates xy */
|
||||
protected Coord max = new Coord();
|
||||
|
||||
/** Lowest coordinates xy */
|
||||
protected Coord min = new Coord();
|
||||
|
||||
|
||||
/**
|
||||
* New Rect [0, 0, 0, 0]
|
||||
*/
|
||||
public Rect() {
|
||||
this(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rect [0, 0, size.x, size.y]
|
||||
*
|
||||
* @param size size coord
|
||||
*/
|
||||
public Rect(Coord size) {
|
||||
this(0, 0, size.x, size.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New rect of two coords
|
||||
*
|
||||
* @param c1 coord 1
|
||||
* @param c2 coord 2
|
||||
*/
|
||||
public Rect(Coord c1, Coord c2) {
|
||||
this(c1.x, c1.y, c2.x, c2.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New Rect
|
||||
*
|
||||
* @param x1 lower x
|
||||
* @param y1 lower y
|
||||
* @param x2 upper x
|
||||
* @param y2 upper y
|
||||
*/
|
||||
public Rect(double x1, double y1, double x2, double y2) {
|
||||
setTo(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rect [0, 0, x, y]
|
||||
*
|
||||
* @param x width
|
||||
* @param y height
|
||||
*/
|
||||
public Rect(int x, int y) {
|
||||
this(0, 0, x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New rect as a copy of other rect
|
||||
*
|
||||
* @param r other rect
|
||||
*/
|
||||
public Rect(Rect r) {
|
||||
this(r.min.x, r.min.y, r.max.x, r.max.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get offset copy (add)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect add(Coord move)
|
||||
{
|
||||
return copy().add_ip(move);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add X and Y to all coordinates in a copy
|
||||
*
|
||||
* @param x x to add
|
||||
* @param y y to add
|
||||
* @return copy changed
|
||||
*/
|
||||
public Rect add(double x, double y)
|
||||
{
|
||||
return add(new Vec(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset in place (add)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return this
|
||||
*/
|
||||
public Rect add_ip(Coord move)
|
||||
{
|
||||
min.add_ip(move);
|
||||
max.add_ip(move);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add X and Y to all coordinates in place
|
||||
*
|
||||
* @param x x to add
|
||||
* @param y y to add
|
||||
* @return this
|
||||
*/
|
||||
public Rect add_ip(double x, double y)
|
||||
{
|
||||
return add_ip(new Vec(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public Rect copy()
|
||||
{
|
||||
return new Rect(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Divide in copy
|
||||
*
|
||||
* @param factor divisor
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect div(double factor)
|
||||
{
|
||||
return copy().div_ip(factor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Divide coord in place
|
||||
*
|
||||
* @param factor divisor
|
||||
* @return this
|
||||
*/
|
||||
public Rect div_ip(double factor)
|
||||
{
|
||||
min.div_ip(factor);
|
||||
max.div_ip(factor);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy with the same center and height=0
|
||||
*
|
||||
* @return line
|
||||
*/
|
||||
public Rect getAxisH()
|
||||
{
|
||||
return new Rect(getCenterLeft(), getCenterRight());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy with the same center and width=0
|
||||
*
|
||||
* @return line
|
||||
*/
|
||||
public Rect getAxisV()
|
||||
{
|
||||
return new Rect(getCenterDown(), getCenterTop());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get rect center
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenter()
|
||||
{
|
||||
return min.midTo(max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get center of the lower edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterDown()
|
||||
{
|
||||
return new Coord((max.x + min.x) / 2, min.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get center of the left edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterLeft()
|
||||
{
|
||||
return new Coord(min.x, (max.y + min.y) / 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get center of the right edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterRight()
|
||||
{
|
||||
return new Coord(max.x, (max.y + min.y) / 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get center of the top edge.
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getCenterTop()
|
||||
{
|
||||
return new Coord((max.x + min.x) / 2, max.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get bottom edge rect
|
||||
*
|
||||
* @return line
|
||||
*/
|
||||
public Rect getEdgeBottom()
|
||||
{
|
||||
return new Rect(getLeftBottom(), getRightBottom());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get left edge rect
|
||||
*
|
||||
* @return line
|
||||
*/
|
||||
public Rect getEdgeLeft()
|
||||
{
|
||||
return new Rect(getLeftBottom(), getLeftTop());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get right edge rect
|
||||
*
|
||||
* @return line
|
||||
*/
|
||||
public Rect getEdgeRight()
|
||||
{
|
||||
return new Rect(getRightBottom(), getRightTop());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get top edge rect
|
||||
*
|
||||
* @return line
|
||||
*/
|
||||
public Rect getEdgeTop()
|
||||
{
|
||||
return new Rect(getLeftTop(), getRightTop());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get left bottom
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getLeftBottom()
|
||||
{
|
||||
return new Coord(min.x, min.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get left top
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getLeftTop()
|
||||
{
|
||||
return new Coord(min.x, max.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return highjest coordinates xy
|
||||
*/
|
||||
public Coord getMax()
|
||||
{
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return lowest coordinates xy
|
||||
*/
|
||||
public Coord getMin()
|
||||
{
|
||||
return min;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get right bottom
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getRightBottom()
|
||||
{
|
||||
return new Coord(max.x, min.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get right top
|
||||
*
|
||||
* @return center
|
||||
*/
|
||||
public Coord getRightTop()
|
||||
{
|
||||
return new Coord(max.x, max.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get size (width, height) as (x,y)
|
||||
*
|
||||
* @return coord of width,height
|
||||
*/
|
||||
public Coord getSize()
|
||||
{
|
||||
return new Coord(Math.abs(min.x - max.x), Math.abs(min.y - max.y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to sides in copy
|
||||
*
|
||||
* @param grow grow size (added to each side)
|
||||
* @return grown copy
|
||||
*/
|
||||
public Rect grow(Coord grow)
|
||||
{
|
||||
return copy().grow_ip(grow);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to sides in copy
|
||||
*
|
||||
* @param x x to add
|
||||
* @param y y to add
|
||||
* @return grown copy
|
||||
*/
|
||||
public Rect grow(double x, double y)
|
||||
{
|
||||
return copy().grow_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to sides in place
|
||||
*
|
||||
* @param grow grow size (added to each side)
|
||||
* @return this
|
||||
*/
|
||||
public Rect grow_ip(Coord grow)
|
||||
{
|
||||
min.sub_ip(grow);
|
||||
max.add_ip(grow);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to sides in place
|
||||
*
|
||||
* @param x x to add
|
||||
* @param y y to add
|
||||
* @return this
|
||||
*/
|
||||
public Rect grow_ip(double x, double y)
|
||||
{
|
||||
min.sub_ip(x, y);
|
||||
max.add_ip(x, y);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow down in copy
|
||||
*
|
||||
* @param down added pixels
|
||||
* @return grown copy
|
||||
*/
|
||||
public Rect growDown(double down)
|
||||
{
|
||||
return copy().growDown_ip(down);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow down in place
|
||||
*
|
||||
* @param down added pixels
|
||||
* @return this
|
||||
*/
|
||||
public Rect growDown_ip(double down)
|
||||
{
|
||||
min.sub_ip(0, down);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to left in copy
|
||||
*
|
||||
* @param left added pixels
|
||||
* @return grown copy
|
||||
*/
|
||||
public Rect growLeft(double left)
|
||||
{
|
||||
return copy().growLeft_ip(left);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to left in place
|
||||
*
|
||||
* @param left added pixels
|
||||
* @return this
|
||||
*/
|
||||
public Rect growLeft_ip(double left)
|
||||
{
|
||||
min.sub_ip(left, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to right in copy
|
||||
*
|
||||
* @param right added pixels
|
||||
* @return grown copy
|
||||
*/
|
||||
public Rect growRight(double right)
|
||||
{
|
||||
return copy().growRight_ip(right);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow to right in place
|
||||
*
|
||||
* @param right added pixels
|
||||
* @return this
|
||||
*/
|
||||
public Rect growRight_ip(double right)
|
||||
{
|
||||
max.add_ip(right, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow up in copy
|
||||
*
|
||||
* @param add added pixels
|
||||
* @return grown copy
|
||||
*/
|
||||
public Rect growUp(double add)
|
||||
{
|
||||
return copy().growUp_ip(add);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grow up in place
|
||||
*
|
||||
* @param add added pixels
|
||||
* @return this
|
||||
*/
|
||||
public Rect growUp_ip(double add)
|
||||
{
|
||||
max.add_ip(0, add);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if point is inside this rectangle
|
||||
*
|
||||
* @param point point to test
|
||||
* @return is inside
|
||||
*/
|
||||
public boolean isInside(Coord point)
|
||||
{
|
||||
return Calc.inRange(point.x, min.x, max.x) && Calc.inRange(point.y, min.y, max.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply in copy
|
||||
*
|
||||
* @param factor multiplier
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect mul(double factor)
|
||||
{
|
||||
return copy().mul_ip(factor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by number (useful for centered rects)
|
||||
*
|
||||
* @param x x multiplier
|
||||
* @param y y multiplier
|
||||
* @return copy multiplied
|
||||
*/
|
||||
public Rect mul(double x, double y)
|
||||
{
|
||||
return copy().mul_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply coord in place
|
||||
*
|
||||
* @param factor multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Rect mul_ip(double factor)
|
||||
{
|
||||
min.mul_ip(factor);
|
||||
max.mul_ip(factor);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply coord in place
|
||||
*
|
||||
* @param x multiplier x
|
||||
* @param y multiplier y
|
||||
* @return this
|
||||
*/
|
||||
public Rect mul_ip(double x, double y)
|
||||
{
|
||||
min.mul_ip(x, y, 1);
|
||||
max.mul_ip(x, y, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round coords in copy
|
||||
*
|
||||
* @return copy, rounded
|
||||
*/
|
||||
public Rect round()
|
||||
{
|
||||
return new Rect(min.round(), max.round());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Round this in place
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Rect round_ip()
|
||||
{
|
||||
min.round_ip();
|
||||
max.round_ip();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to [0,0,coord.x,coord.y]
|
||||
*
|
||||
* @param coord size coord
|
||||
*/
|
||||
public void setTo(Coord coord)
|
||||
{
|
||||
setTo(0, 0, coord.x, coord.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to coordinates
|
||||
*
|
||||
* @param x1 lower x
|
||||
* @param y1 lower y
|
||||
* @param x2 upper x
|
||||
* @param y2 upper y
|
||||
*/
|
||||
public void setTo(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
min.x = Calc.min(x1, x2);
|
||||
min.y = Calc.min(y1, y2);
|
||||
max.x = Calc.max(x1, x2);
|
||||
max.y = Calc.max(y1, y2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to other rect's coordinates
|
||||
*
|
||||
* @param r other rect
|
||||
*/
|
||||
public void setTo(Rect r)
|
||||
{
|
||||
min.setTo(r.min);
|
||||
max.setTo(r.max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract X and Y from all coordinates in a copy
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @return copy changed
|
||||
*/
|
||||
public Rect sub(double x, double y)
|
||||
{
|
||||
return sub(new Vec(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get offset copy (subtract)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect sub(Vec move)
|
||||
{
|
||||
return copy().sub_ip(move);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract X and Y from all coordinates in place
|
||||
*
|
||||
* @param x x to subtract
|
||||
* @param y y to subtract
|
||||
* @return this
|
||||
*/
|
||||
public Rect sub_ip(double x, double y)
|
||||
{
|
||||
return sub_ip(new Vec(x, y));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offset in place (subtract)
|
||||
*
|
||||
* @param move offset vector
|
||||
* @return this
|
||||
*/
|
||||
public Rect sub_ip(Vec move)
|
||||
{
|
||||
min.sub_ip(move);
|
||||
max.sub_ip(move);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "rect{ " + min + " - " + max + " }";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return lower x
|
||||
*/
|
||||
public double x1()
|
||||
{
|
||||
return min.x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return upper x
|
||||
*/
|
||||
public double x2()
|
||||
{
|
||||
return max.x;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return lower y
|
||||
*/
|
||||
public double y1()
|
||||
{
|
||||
return min.y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return upper y
|
||||
*/
|
||||
public double y2()
|
||||
{
|
||||
return max.y;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
package mightypork.utils.math.coord;
|
||||
|
||||
|
||||
/**
|
||||
* Vector in 2D/3D space.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Vec extends Coord {
|
||||
|
||||
/** Vec [1;1;1] */
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Vec ONE = new Vec(1, 1, 1);
|
||||
/** Zero vector */
|
||||
@SuppressWarnings("hiding")
|
||||
public static final Vec ZERO = new Vec(0, 0, 0);
|
||||
|
||||
|
||||
/**
|
||||
* Get cross product of two vectors
|
||||
*
|
||||
* @param a 1st vector
|
||||
* @param b 2nd vector
|
||||
* @return cross product
|
||||
*/
|
||||
public static Vec cross(Vec a, Vec b)
|
||||
{
|
||||
return a.cross(b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get dot product of two vectors
|
||||
*
|
||||
* @param a 1st vector
|
||||
* @param b 2nd vector
|
||||
* @return dot product
|
||||
*/
|
||||
public static double dot(Vec a, Vec b)
|
||||
{
|
||||
return a.dot(b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate random coord (gaussian)
|
||||
*
|
||||
* @param max max distance from 0
|
||||
* @return new coord
|
||||
*/
|
||||
public static Vec random(double max)
|
||||
{
|
||||
return new Vec(Coord.random(max));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate random coord (min-max)
|
||||
*
|
||||
* @param max max distance from 0
|
||||
* @return new coord
|
||||
*/
|
||||
public static Vec random(double min, double max)
|
||||
{
|
||||
return new Vec(Coord.random(min, max));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scale vector
|
||||
*
|
||||
* @param a vector
|
||||
* @param scale
|
||||
* @return scaled copy
|
||||
*/
|
||||
public static Vec scale(Vec a, double scale)
|
||||
{
|
||||
return a.scale(scale);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get vector size
|
||||
*
|
||||
* @param vec vector to get size of
|
||||
* @return size in units
|
||||
*/
|
||||
public static double size(Vec vec)
|
||||
{
|
||||
return vec.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create zero vector
|
||||
*/
|
||||
public Vec() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create vector as a copy of another
|
||||
*
|
||||
* @param copied copied vector
|
||||
*/
|
||||
public Vec(Coord copied) {
|
||||
super(copied);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create 2D vector
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
*/
|
||||
public Vec(Number x, Number y) {
|
||||
super(x, y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create 3D vector
|
||||
*
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param z z coordinate
|
||||
*/
|
||||
public Vec(Number x, Number y, Number z) {
|
||||
super(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Vec copy()
|
||||
{
|
||||
return new Vec(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by other vector, vector multiplication
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return copy multiplied
|
||||
*/
|
||||
public Vec cross(Vec vec)
|
||||
{
|
||||
return copy().cross_ip(vec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by other vector, vector multiplication; in place
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return this
|
||||
*/
|
||||
public Vec cross_ip(Vec vec)
|
||||
{
|
||||
setTo(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get dot product
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return dot product
|
||||
*/
|
||||
public double dot(Vec vec)
|
||||
{
|
||||
return x * vec.x + y * vec.y + z * vec.z;
|
||||
}
|
||||
|
||||
|
||||
// STATIC
|
||||
|
||||
/**
|
||||
* Negate all coordinates (* -1)
|
||||
*
|
||||
* @return negated coordinate
|
||||
*/
|
||||
public Vec neg()
|
||||
{
|
||||
return copy().neg_ip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Negate all coordinates (* -1), in place
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Vec neg_ip()
|
||||
{
|
||||
scale_ip(-1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scale vector to given size
|
||||
*
|
||||
* @param size size we need
|
||||
* @return scaled vector
|
||||
*/
|
||||
public Vec norm(double size)
|
||||
{
|
||||
return copy().norm_ip(size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scale vector to given size, in place
|
||||
*
|
||||
* @param size size we need
|
||||
* @return scaled vector
|
||||
*/
|
||||
public Vec norm_ip(double size)
|
||||
{
|
||||
if (size() == 0) {
|
||||
z = -1;
|
||||
}
|
||||
if (size == 0) {
|
||||
setTo(0, 0, 0);
|
||||
return this;
|
||||
}
|
||||
double k = size / size();
|
||||
scale_ip(k);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return offset coord
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset(double max)
|
||||
{
|
||||
return (Vec) super.random_offset(max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return offset coord
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset(double min, double max)
|
||||
{
|
||||
return (Vec) super.random_offset(min, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param max max +- offset
|
||||
* @return this
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset_ip(double max)
|
||||
{
|
||||
return (Vec) super.random_offset_ip(max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* offset randomly in place
|
||||
*
|
||||
* @param min min offset
|
||||
* @param max max offset
|
||||
* @return this
|
||||
*/
|
||||
@Override
|
||||
public Vec random_offset_ip(double min, double max)
|
||||
{
|
||||
return (Vec) super.random_offset_ip(min, max);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply all coordinates by factor; scalar multiplication
|
||||
*
|
||||
* @param factor multiplier
|
||||
* @return copy multiplied
|
||||
*/
|
||||
public Vec scale(double factor)
|
||||
{
|
||||
return copy().scale_ip(factor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply all coordinates by factor, in place
|
||||
*
|
||||
* @param factor multiplier
|
||||
* @return this
|
||||
*/
|
||||
public Vec scale_ip(double factor)
|
||||
{
|
||||
return (Vec) mul_ip(factor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get vector size
|
||||
*
|
||||
* @return vector size in units
|
||||
*/
|
||||
@Override
|
||||
public double size()
|
||||
{
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
package mightypork.utils.objects;
|
||||
|
||||
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.Range;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.CoordI;
|
||||
|
||||
|
||||
/**
|
||||
* Utility for converting Object to data types; Can also convert strings to data
|
||||
* types.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Convertor {
|
||||
|
||||
/**
|
||||
* Get INTEGER
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return integer
|
||||
*/
|
||||
public static int getInteger(Object o, Integer def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
if (o instanceof String) return (int) Math.round(Double.parseDouble((String) o));
|
||||
if (o instanceof Number) return ((Number) o).intValue();
|
||||
if (o instanceof Range) return ((Range) o).randInt();
|
||||
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
||||
} catch (NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Integer.");
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get DOUBLE
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return double
|
||||
*/
|
||||
public static double getDouble(Object o, Double def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
if (o instanceof String) return Double.parseDouble((String) o);
|
||||
if (o instanceof Number) return ((Number) o).doubleValue();
|
||||
if (o instanceof Range) return ((Range) o).randDouble();
|
||||
if (o instanceof Boolean) return ((Boolean) o) ? 1 : 0;
|
||||
} catch (NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Double.");
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get FLOAT
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return float
|
||||
*/
|
||||
public static double getFloat(Object o, Float def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
if (o instanceof Number) return ((Number) o).floatValue();
|
||||
} catch (NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Float.");
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get BOOLEAN
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean getBoolean(Object o, Boolean def)
|
||||
{
|
||||
if (o == null) return def;
|
||||
|
||||
if (o instanceof String) {
|
||||
String s = ((String) o).trim().toLowerCase();
|
||||
if (s.equals("0")) return false;
|
||||
if (s.equals("1")) return true;
|
||||
try {
|
||||
double n = Double.parseDouble(s);
|
||||
return n != 0;
|
||||
} catch (NumberFormatException e) {}
|
||||
|
||||
if (s.equals("true")) return true;
|
||||
if (s.equals("yes")) return true;
|
||||
if (s.equals("y")) return true;
|
||||
if (s.equals("enabled")) return true;
|
||||
|
||||
if (s.equals("false")) return false;
|
||||
if (s.equals("no")) return false;
|
||||
if (s.equals("n")) return false;
|
||||
if (s.equals("disabled")) return true;
|
||||
}
|
||||
|
||||
if (o instanceof Boolean) return ((Boolean) o).booleanValue();
|
||||
if (o instanceof Number) return ((Number) o).intValue() != 0;
|
||||
Log.w("Cannot convert " + o + " to Boolean.");
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get STRING
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return String
|
||||
*/
|
||||
public static String getString(Object o, String def)
|
||||
{
|
||||
if (o == null) return def;
|
||||
if (o instanceof String) return ((String) o);
|
||||
Log.w("Cannot convert " + o + " to String.");
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get AI_COORD<br>
|
||||
* Converts special constants to magic coordinate instances.
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return AiCoord
|
||||
*/
|
||||
public static Coord getCoord(Object o, Coord def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
if (o instanceof String) {
|
||||
String s = ((String) o).trim().toUpperCase();
|
||||
|
||||
// colon to semicolon
|
||||
s = s.replace(':', ';');
|
||||
// comma to semicolon
|
||||
s = s.replace(',', ';');
|
||||
// remove brackets if any
|
||||
s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", "");
|
||||
String[] parts = s.split("[;]");
|
||||
return new Coord(Double.parseDouble(parts[0].trim()), Double.parseDouble(parts[1].trim()));
|
||||
}
|
||||
if (o instanceof Coord) return new Coord((Coord) o);
|
||||
if (o instanceof CoordI) return ((CoordI) o).toCoord();
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
Log.w("Cannot convert " + o + " to Coord.");
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get RANGE
|
||||
*
|
||||
* @param o object
|
||||
* @param def default value
|
||||
* @return AiCoord
|
||||
*/
|
||||
public static Range getRange(Object o, Range def)
|
||||
{
|
||||
try {
|
||||
if (o == null) return def;
|
||||
if (o instanceof Number) return new Range(((Number) o).doubleValue(), ((Number) o).doubleValue());
|
||||
if (o instanceof String) {
|
||||
String s = ((String) o).trim();
|
||||
|
||||
// colon to semicolon
|
||||
s = s.replace(',', ':');
|
||||
// comma to dot.
|
||||
s = s.replace(';', ':');
|
||||
// dash
|
||||
s = s.replaceAll("([0-9])\\s?[\\-]", "$1:");
|
||||
// remove brackets if any
|
||||
s = s.replaceAll("[\\(\\[\\{\\)\\]\\}]", "");
|
||||
String[] parts = s.split("[:]");
|
||||
if (parts.length == 2) return new Range(Double.parseDouble(parts[0].trim()), Double.parseDouble(parts[1].trim()));
|
||||
return new Range(Double.parseDouble(parts[0].trim()), Double.parseDouble(parts[0].trim()));
|
||||
|
||||
}
|
||||
if (o instanceof Range) return (Range) o;
|
||||
} catch (NumberFormatException e) {}
|
||||
Log.w("Cannot convert " + o + " to Range.");
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get INTEGER
|
||||
*
|
||||
* @param o object
|
||||
* @return integer
|
||||
*/
|
||||
public static int getInteger(Object o)
|
||||
{
|
||||
return getInteger(o, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get DOUBLE
|
||||
*
|
||||
* @param o object
|
||||
* @return double
|
||||
*/
|
||||
public static double getDouble(Object o)
|
||||
{
|
||||
return getDouble(o, 0d);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get FLOAT
|
||||
*
|
||||
* @param o object
|
||||
* @return float
|
||||
*/
|
||||
public static double getFloat(Object o)
|
||||
{
|
||||
return getFloat(o, 0f);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get BOOLEAN
|
||||
*
|
||||
* @param o object
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean getBoolean(Object o)
|
||||
{
|
||||
return getBoolean(o, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get STRING
|
||||
*
|
||||
* @param o object
|
||||
* @return String
|
||||
*/
|
||||
public static String getString(Object o)
|
||||
{
|
||||
return getString(o, "");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get AI_COORD (if special string constant is present instead, build coord
|
||||
* of it)
|
||||
*
|
||||
* @param o object
|
||||
* @return AiCoord
|
||||
*/
|
||||
public static Coord getCoord(Object o)
|
||||
{
|
||||
return getCoord(o, Coord.ZERO.copy());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get RANGE
|
||||
*
|
||||
* @param o object
|
||||
* @return AiCoord
|
||||
*/
|
||||
public static Range getRange(Object o)
|
||||
{
|
||||
return getRange(o, new Range());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package mightypork.utils.objects;
|
||||
|
||||
|
||||
/**
|
||||
* Mutable object
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <T> type
|
||||
*/
|
||||
public class Mutable<T> {
|
||||
|
||||
/** The wrapped value */
|
||||
public T o = null;
|
||||
|
||||
|
||||
/**
|
||||
* Implicint constructor
|
||||
*/
|
||||
public Mutable() {}
|
||||
|
||||
|
||||
/**
|
||||
* new mutable object
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
public Mutable(T o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the wrapped value
|
||||
*
|
||||
* @return value
|
||||
*/
|
||||
public T get()
|
||||
{
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set value
|
||||
*
|
||||
* @param o new value to set
|
||||
*/
|
||||
public void set(T o)
|
||||
{
|
||||
this.o = o;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package mightypork.utils.objects;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* Object utils class
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class ObjectUtils {
|
||||
|
||||
public static Object fallback(Object... options)
|
||||
{
|
||||
for (Object o : options) {
|
||||
if (o != null) return o;
|
||||
}
|
||||
return null; // error
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort a map by keys, maintaining key-value pairs.
|
||||
*
|
||||
* @param map map to be sorted
|
||||
* @param comparator a comparator, or null for natural ordering
|
||||
* @return linked hash map with sorted entries
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static <K extends Comparable, V extends Comparable> Map<K, V> sortByKeys(Map<K, V> map, final Comparator<K> comparator)
|
||||
{
|
||||
List<K> keys = new LinkedList<K>(map.keySet());
|
||||
|
||||
if (comparator == null) {
|
||||
Collections.sort(keys);
|
||||
} else {
|
||||
Collections.sort(keys, comparator);
|
||||
}
|
||||
|
||||
// LinkedHashMap will keep the keys in the order they are inserted
|
||||
// which is currently sorted on natural ordering
|
||||
Map<K, V> sortedMap = new LinkedHashMap<K, V>();
|
||||
for (K key : keys) {
|
||||
sortedMap.put(key, map.get(key));
|
||||
}
|
||||
|
||||
return sortedMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort a map by values, maintaining key-value pairs.
|
||||
*
|
||||
* @param map map to be sorted
|
||||
* @param comparator a comparator, or null for natural ordering
|
||||
* @return linked hash map with sorted entries
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static <K extends Comparable, V extends Comparable> Map<K, V> sortByValues(Map<K, V> map, final Comparator<V> comparator)
|
||||
{
|
||||
List<Map.Entry<K, V>> entries = new LinkedList<Map.Entry<K, V>>(map.entrySet());
|
||||
|
||||
Collections.sort(entries, new Comparator<Map.Entry<K, V>>() {
|
||||
|
||||
@Override
|
||||
public int compare(Entry<K, V> o1, Entry<K, V> o2)
|
||||
{
|
||||
if (comparator == null) return o1.getValue().compareTo(o2.getValue());
|
||||
return comparator.compare(o1.getValue(), o2.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
// LinkedHashMap will keep the keys in the order they are inserted
|
||||
// which is currently sorted on natural ordering
|
||||
Map<K, V> sortedMap = new LinkedHashMap<K, V>();
|
||||
|
||||
for (Map.Entry<K, V> entry : entries) {
|
||||
sortedMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return sortedMap;
|
||||
}
|
||||
|
||||
|
||||
public static String arrayToString(Object[] arr)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append('[');
|
||||
boolean first = true;
|
||||
for (Object o : arr) {
|
||||
if (!first) sb.append(',');
|
||||
sb.append(o.toString());
|
||||
}
|
||||
sb.append(']');
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static <T extends Object> List<T> arrayToList(T[] objs)
|
||||
{
|
||||
ArrayList<T> list = new ArrayList<T>();
|
||||
for (T o : objs) {
|
||||
list.add(o);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package mightypork.utils.objects;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 2 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
*/
|
||||
public class Pair<T1, T2> {
|
||||
|
||||
/**
|
||||
* 1st object
|
||||
*/
|
||||
public T1 first;
|
||||
|
||||
/**
|
||||
* 2nd object
|
||||
*/
|
||||
public T2 second;
|
||||
|
||||
|
||||
/**
|
||||
* Make structure of 2 objects
|
||||
*
|
||||
* @param first 1st object
|
||||
* @param second 2nd object
|
||||
*/
|
||||
public Pair(T1 first, T2 second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 1st object
|
||||
*/
|
||||
public T1 getFirst()
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 2nd object
|
||||
*/
|
||||
public T2 getSecond()
|
||||
{
|
||||
return second;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.getClass().equals(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Pair<?, ?> t = (Pair<?, ?>) obj;
|
||||
|
||||
return Calc.areObjectsEqual(first, t.first) && Calc.areObjectsEqual(second, t.second);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 13;
|
||||
hash += (first == null ? 0 : first.hashCode());
|
||||
hash += (second == null ? 0 : second.hashCode());
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "PAIR{" + first + "," + second + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package mightypork.utils.objects;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Structure of 3 objects.
|
||||
*
|
||||
* @author MightyPork
|
||||
* @copy (c) 2012
|
||||
* @param <T1> 1st object class
|
||||
* @param <T2> 2nd object class
|
||||
* @param <T3> 3rd object class
|
||||
*/
|
||||
public class Triad<T1, T2, T3> extends Pair<T1, T2> {
|
||||
|
||||
/**
|
||||
* 3rd object
|
||||
*/
|
||||
public T3 third;
|
||||
|
||||
|
||||
/**
|
||||
* Make structure of 3 objects
|
||||
*
|
||||
* @param objA 1st object
|
||||
* @param objB 2nd object
|
||||
* @param objC 3rd object
|
||||
*/
|
||||
public Triad(T1 objA, T2 objB, T3 objC) {
|
||||
super(objA, objB);
|
||||
third = objC;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 3rd object
|
||||
*/
|
||||
public T3 getThird()
|
||||
{
|
||||
return third;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set 1st object
|
||||
*
|
||||
* @param obj 1st object
|
||||
*/
|
||||
public void setThird(T3 obj)
|
||||
{
|
||||
third = obj;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (!super.equals(obj)) return false;
|
||||
|
||||
return Calc.areObjectsEqual(third, ((Triad<?, ?, ?>) obj).third);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return super.hashCode() + (third == null ? 0 : third.hashCode());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "TRIAD{" + first + "," + second + "," + third + "}";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package mightypork.utils.objects;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Varargs parser<br>
|
||||
* Converts an array of repeated "key, value" pairs to a LinkedHashMap.<br>
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Object[] array = { "one", 1, "two", 4, "three", 9, "four", 16 };
|
||||
* Map<String, Integer> args = new VarargsParser<String, Integer>().parse(array);
|
||||
* </pre>
|
||||
*
|
||||
* @author MightyPork
|
||||
* @param <K> Type for Map keys
|
||||
* @param <V> Type for Map values
|
||||
*/
|
||||
public class VarargsParser<K, V> {
|
||||
|
||||
/**
|
||||
* Parse array of vararg key, value pairs to a LinkedHashMap.
|
||||
*
|
||||
* @param args varargs
|
||||
* @return LinkedHashMap
|
||||
* @throws ClassCastException in case of incompatible type in the array
|
||||
* @throws IllegalArgumentException in case of invalid array length (odd)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<K, V> parse(Object... args) throws ClassCastException, IllegalArgumentException
|
||||
{
|
||||
LinkedHashMap<K, V> attrs = new LinkedHashMap<K, V>();
|
||||
|
||||
if (args.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Odd number of elements in varargs map!");
|
||||
}
|
||||
|
||||
K key = null;
|
||||
for (Object o : args) {
|
||||
if (key == null) {
|
||||
if (o == null) throw new RuntimeException("Key cannot be NULL in varargs map.");
|
||||
key = (K) o;
|
||||
} else {
|
||||
attrs.put(key, (V) o);
|
||||
key = null;
|
||||
}
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package mightypork.utils.string;
|
||||
|
||||
|
||||
/**
|
||||
* General purpose string utilities
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
/**
|
||||
* Get if string is in array
|
||||
*
|
||||
* @param needle checked string
|
||||
* @param case_sensitive case sensitive comparision
|
||||
* @param haystack array of possible values
|
||||
* @return is in array
|
||||
*/
|
||||
public static boolean isInArray(String needle, boolean case_sensitive, String... haystack)
|
||||
{
|
||||
if (case_sensitive) {
|
||||
for (String s : haystack) {
|
||||
if (needle.equals(s)) return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (String s : haystack) {
|
||||
if (needle.equalsIgnoreCase(s)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String fromLastDot(String s)
|
||||
{
|
||||
return fromLastChar(s, '.');
|
||||
}
|
||||
|
||||
|
||||
public static String toLastDot(String s)
|
||||
{
|
||||
return toLastChar(s, '.');
|
||||
}
|
||||
|
||||
|
||||
public static String fromLastChar(String s, char c)
|
||||
{
|
||||
if (s == null) return null;
|
||||
return s.substring(s.lastIndexOf(c) + 1, s.length());
|
||||
}
|
||||
|
||||
|
||||
public static String toLastChar(String s, char c)
|
||||
{
|
||||
if (s == null) return null;
|
||||
return s.substring(0, s.lastIndexOf(c));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Repeat a string
|
||||
*
|
||||
* @param repeated string
|
||||
* @param count
|
||||
* @return output
|
||||
*/
|
||||
public static String repeat(String repeated, int count)
|
||||
{
|
||||
String s = "";
|
||||
for (int i = 0; i < count; i++)
|
||||
s += repeated;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convert string to a same-length sequence of # marks
|
||||
*
|
||||
* @param password password
|
||||
* @return encoded
|
||||
*/
|
||||
public static String passwordify(String password)
|
||||
{
|
||||
return passwordify(password, "*");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convert string to a same-length sequence of chars
|
||||
*
|
||||
* @param password password
|
||||
* @param replacing character used in output
|
||||
* @return encoded
|
||||
*/
|
||||
public static String passwordify(String password, String replacing)
|
||||
{
|
||||
return repeat(replacing, password.length());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get ordinal version of numbers (1 = 1st, 5 = 5th etc.)
|
||||
*
|
||||
* @param number number
|
||||
* @return ordinal, string
|
||||
*/
|
||||
public static String numberToOrdinal(int number)
|
||||
{
|
||||
if (number % 100 < 4 || number % 100 > 13) {
|
||||
if (number % 10 == 1) return number + "st";
|
||||
if (number % 10 == 2) return number + "nd";
|
||||
if (number % 10 == 3) return number + "rd";
|
||||
}
|
||||
return number + "th";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format number with thousands separated by a dot.
|
||||
*
|
||||
* @param number number
|
||||
* @return string 12.004.225
|
||||
*/
|
||||
public static String formatInt(long number)
|
||||
{
|
||||
String num = number + "";
|
||||
String out = "";
|
||||
String dot = ".";
|
||||
int cnt = 1;
|
||||
for (int i = num.length() - 1; i >= 0; i--) {
|
||||
out = num.charAt(i) + out;
|
||||
if (cnt % 3 == 0 && i > 0) out = dot + out;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidFilenameChar(char ch)
|
||||
{
|
||||
return isValidFilenameString(Character.toString(ch));
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidFilenameString(String filename)
|
||||
{
|
||||
return filename.matches("[a-zA-Z0-9 +\\-.,_%@#!]+");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidIdentifierChar(char ch)
|
||||
{
|
||||
return isValidIdentifierString(Character.toString(ch));
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidIdentifierString(String filename)
|
||||
{
|
||||
return filename.matches("[a-zA-Z0-9._]+");
|
||||
}
|
||||
|
||||
|
||||
public static String ellipsisStart(String orig, int length)
|
||||
{
|
||||
if (orig.length() > length) {
|
||||
orig = "\u2026" + orig.substring(length, orig.length());
|
||||
}
|
||||
return orig;
|
||||
}
|
||||
|
||||
|
||||
public static String ellipsisEnd(String orig, int length)
|
||||
{
|
||||
if (orig.length() > length) {
|
||||
orig = orig.substring(0, length - 1) + "\u2026";
|
||||
}
|
||||
return orig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package mightypork.utils.string.validation;
|
||||
|
||||
|
||||
public interface CharValidator {
|
||||
|
||||
public boolean isValid(char c);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package mightypork.utils.string.validation;
|
||||
|
||||
|
||||
public class CharValidatorRegex implements CharValidator {
|
||||
|
||||
private String formula;
|
||||
|
||||
|
||||
public CharValidatorRegex(String regex) {
|
||||
this.formula = regex;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValid(char c)
|
||||
{
|
||||
return Character.toString(c).matches(formula);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package mightypork.utils.string.validation;
|
||||
|
||||
|
||||
public class CharValidatorWhitelist implements CharValidator {
|
||||
|
||||
private String whitelist;
|
||||
|
||||
|
||||
public CharValidatorWhitelist(String allowed) {
|
||||
this.whitelist = allowed;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValid(char c)
|
||||
{
|
||||
return whitelist.contains(Character.toString(c));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package mightypork.utils.string.validation;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
|
||||
/**
|
||||
* File filter for certain suffixes
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class FileSuffixFilter implements FileFilter {
|
||||
|
||||
/** Array of allowed suffixes */
|
||||
private String[] suffixes = null;
|
||||
|
||||
|
||||
/**
|
||||
* Suffix filter
|
||||
*
|
||||
* @param suffixes var-args allowed suffixes, case insensitive
|
||||
*/
|
||||
public FileSuffixFilter(String... suffixes) {
|
||||
this.suffixes = suffixes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname)
|
||||
{
|
||||
if (!pathname.isFile()) return false;
|
||||
|
||||
String fname = pathname.getName().toLowerCase().trim();
|
||||
|
||||
for (String suffix : suffixes) {
|
||||
if (fname.endsWith(suffix.toLowerCase().trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package mightypork.utils.string.validation;
|
||||
|
||||
|
||||
public class FilenameCharValidator extends CharValidatorRegex {
|
||||
|
||||
public FilenameCharValidator() {
|
||||
super("[a-zA-Z0-9 +\\-.,_%@#$!'\"]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package mightypork.utils.string.validation;
|
||||
|
||||
|
||||
/**
|
||||
* Utility interface for string filters (accepting filepaths and similar)
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface StringFilter {
|
||||
|
||||
public boolean accept(String entry);
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Double which supports delta timing
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class AnimDouble implements Updateable, Pauseable {
|
||||
|
||||
/** target double */
|
||||
protected double endValue = 0;
|
||||
|
||||
/** last tick double */
|
||||
protected double startValue = 0;
|
||||
|
||||
/** how long the transition should last */
|
||||
protected double duration = 0;
|
||||
|
||||
/** current anim time */
|
||||
protected double elapsedTime = 0;
|
||||
|
||||
/** True if this animator is paused */
|
||||
protected boolean paused = false;
|
||||
|
||||
|
||||
/**
|
||||
* @param value value
|
||||
*/
|
||||
public AnimDouble(double value) {
|
||||
setTo(value);
|
||||
}
|
||||
|
||||
|
||||
public AnimDouble(AnimDouble other) {
|
||||
setTo(other);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get start value
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public double getStartValue()
|
||||
{
|
||||
return startValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get value at delta time
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public double getCurrentValue()
|
||||
{
|
||||
if (duration == 0) return endValue;
|
||||
return Calc.interpolate(startValue, endValue, elapsedTime / duration);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get end value
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public double getEndValue()
|
||||
{
|
||||
return endValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get how much of the animation is already finished
|
||||
*
|
||||
* @return completion ratio (0 to 1)
|
||||
*/
|
||||
public double getProgress()
|
||||
{
|
||||
if (duration == 0) return 1;
|
||||
return elapsedTime / duration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
elapsedTime = Calc.clampd(elapsedTime + delta, 0, duration);
|
||||
if (isFinished()) {
|
||||
duration = 0;
|
||||
elapsedTime = 0;
|
||||
startValue = endValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get if animation is finished
|
||||
*
|
||||
* @return is finished
|
||||
*/
|
||||
public boolean isFinished()
|
||||
{
|
||||
return duration == 0 || elapsedTime >= duration;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to a value (without animation)
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void setTo(double value)
|
||||
{
|
||||
startValue = endValue = value;
|
||||
elapsedTime = 0;
|
||||
duration = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy other
|
||||
*
|
||||
* @param other
|
||||
*/
|
||||
public void setTo(AnimDouble other)
|
||||
{
|
||||
this.startValue = other.startValue;
|
||||
this.endValue = other.endValue;
|
||||
this.duration = other.duration;
|
||||
this.elapsedTime = other.elapsedTime;
|
||||
this.paused = other.paused;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animate between two states, discard current state
|
||||
*
|
||||
* @param from initial state
|
||||
* @param to target state
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void animate(double from, double to, double time)
|
||||
{
|
||||
startValue = from;
|
||||
endValue = to;
|
||||
duration = time;
|
||||
elapsedTime = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animate between two states, start from current value (if it's in between)
|
||||
*
|
||||
* @param from start value
|
||||
* @param to target state
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void fadeTo(double from, double to, double time)
|
||||
{
|
||||
double current = getCurrentValue();
|
||||
|
||||
startValue = from;
|
||||
endValue = to;
|
||||
duration = time;
|
||||
elapsedTime = 0;
|
||||
|
||||
// if in between, pick up from where it is
|
||||
if (current >= from && current <= to) { // up
|
||||
elapsedTime = ((current - from) / (to - from)) * time;
|
||||
} else if (current >= to && current <= from) { // down
|
||||
elapsedTime = ((from - current) / (from - to)) * time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animate 0 to 1
|
||||
*
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void fadeIn(double time)
|
||||
{
|
||||
fadeTo(0, 1, time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animate 1 to 0
|
||||
*
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void fadeOut(double time)
|
||||
{
|
||||
fadeTo(1, 0, time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a copy
|
||||
*
|
||||
* @return copy
|
||||
*/
|
||||
public Pauseable getCopy()
|
||||
{
|
||||
return new AnimDouble(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Animation(" + startValue + " -> " + endValue + ", t=" + duration + "s, elapsed=" + elapsedTime + "s)";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set to zero and stop animation
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
startValue = endValue = 0;
|
||||
elapsedTime = 0;
|
||||
duration = 0;
|
||||
paused = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stop animation, keep current value
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
startValue = endValue = getCurrentValue();
|
||||
elapsedTime = 0;
|
||||
duration = 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void pause()
|
||||
{
|
||||
paused = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resume()
|
||||
{
|
||||
paused = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPaused()
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
/**
|
||||
* Double which supports delta timing
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class AnimDoubleDeg extends AnimDouble {
|
||||
|
||||
/**
|
||||
* new AnimDoubleDeg
|
||||
*
|
||||
* @param d value
|
||||
*/
|
||||
public AnimDoubleDeg(double d) {
|
||||
super(d);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get value at delta time
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
@Override
|
||||
public double getCurrentValue()
|
||||
{
|
||||
return Calc.interpolateDeg(startValue, endValue, elapsedTime / duration);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void fadeTo(double from, double to, double time)
|
||||
{
|
||||
throw new UnsupportedOperationException("Cannot fadeTo in AnimDoubleDeg. Use animate() instead.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
/**
|
||||
* Class for counting FPS in games.<br>
|
||||
* This class can be used also as a simple frequency meter - output is in Hz.
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class FpsMeter {
|
||||
|
||||
private long frames = 0;
|
||||
private long drops = 0;
|
||||
private long lastTimeMillis = System.currentTimeMillis();
|
||||
private long lastSecFPS = 0;
|
||||
private long lastSecDrop = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @return current second's FPS
|
||||
*/
|
||||
public long getFPS()
|
||||
{
|
||||
return lastSecFPS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notification that frame was rendered
|
||||
*/
|
||||
public void frame()
|
||||
{
|
||||
if (System.currentTimeMillis() - lastTimeMillis > 1000) {
|
||||
lastSecFPS = frames;
|
||||
lastSecDrop = drops;
|
||||
frames = 0;
|
||||
drops = 0;
|
||||
lastTimeMillis = System.currentTimeMillis();
|
||||
}
|
||||
frames++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notification that some frames have been dropped
|
||||
*
|
||||
* @param dropped dropped frames
|
||||
*/
|
||||
public void drop(int dropped)
|
||||
{
|
||||
drops += dropped;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return current second's dropped frames
|
||||
*/
|
||||
public long getDropped()
|
||||
{
|
||||
return lastSecDrop;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
public interface Pauseable {
|
||||
|
||||
/**
|
||||
* Pause operation
|
||||
*/
|
||||
public void pause();
|
||||
|
||||
|
||||
/**
|
||||
* Resume operation
|
||||
*/
|
||||
public void resume();
|
||||
|
||||
|
||||
/**
|
||||
* @return paused state
|
||||
*/
|
||||
public boolean isPaused();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
/**
|
||||
* Timer for delta timing
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class TimerDelta {
|
||||
|
||||
private long lastFrame;
|
||||
|
||||
private static final long SECOND = 1000000000; // a million nanoseconds
|
||||
|
||||
|
||||
/**
|
||||
* New delta timer
|
||||
*/
|
||||
public TimerDelta() {
|
||||
lastFrame = System.nanoTime();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get current time in NS
|
||||
*
|
||||
* @return current time NS
|
||||
*/
|
||||
public long getTime()
|
||||
{
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get time since the last "getDelta()" call.
|
||||
*
|
||||
* @return delta time (seconds)
|
||||
*/
|
||||
public double getDelta()
|
||||
{
|
||||
long time = getTime();
|
||||
double delta = (time - lastFrame) / (double) SECOND;
|
||||
lastFrame = time;
|
||||
return delta;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
/**
|
||||
* Timer for interpolated timing
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class TimerInterpolating {
|
||||
|
||||
private long lastFrame = 0;
|
||||
private long nextFrame = 0;
|
||||
private long skipped = 0;
|
||||
private long lastSkipped = 0;
|
||||
|
||||
private static final long SECOND = 1000000000; // a million nanoseconds
|
||||
private long FRAME; // a time of one frame in nanoseconds
|
||||
|
||||
|
||||
/**
|
||||
* New interpolated timer
|
||||
*
|
||||
* @param fps target FPS
|
||||
*/
|
||||
public TimerInterpolating(long fps) {
|
||||
FRAME = Math.round(SECOND / fps);
|
||||
|
||||
lastFrame = System.nanoTime();
|
||||
nextFrame = System.nanoTime() + FRAME;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sync and calculate dropped frames etc.
|
||||
*/
|
||||
public void sync()
|
||||
{
|
||||
long time = getTime();
|
||||
if (time >= nextFrame) {
|
||||
long skippedNow = (long) Math.floor((time - nextFrame) / (double) FRAME) + 1;
|
||||
//System.out.println("Skipping: "+skippedNow);
|
||||
skipped += skippedNow;
|
||||
lastFrame = nextFrame + (1 - skippedNow) * FRAME;
|
||||
nextFrame += skippedNow * FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get nanotime
|
||||
*
|
||||
* @return nanotime
|
||||
*/
|
||||
public long getTime()
|
||||
{
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get fraction of next frame
|
||||
*
|
||||
* @return fraction
|
||||
*/
|
||||
public double getFraction()
|
||||
{
|
||||
if (getSkipped() >= 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
long time = getTime();
|
||||
|
||||
if (time <= nextFrame) {
|
||||
return (double) (time - lastFrame) / (double) FRAME;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get number of elapsed ticks
|
||||
*
|
||||
* @return ticks
|
||||
*/
|
||||
public int getSkipped()
|
||||
{
|
||||
long change = skipped - lastSkipped;
|
||||
lastSkipped = skipped;
|
||||
return (int) change;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear timer and start counting new tick.
|
||||
*/
|
||||
public void startNewFrame()
|
||||
{
|
||||
//System.out.println("! start new frame !");
|
||||
long time = getTime();
|
||||
lastFrame = time;
|
||||
nextFrame = time + FRAME;
|
||||
lastSkipped = skipped;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package mightypork.utils.time;
|
||||
|
||||
|
||||
/**
|
||||
* object supporting delta timing
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public interface Updateable {
|
||||
|
||||
/**
|
||||
* Update item state based on elapsed time
|
||||
*
|
||||
* @param delta time elapsed since last update, in seconds
|
||||
*/
|
||||
public void update(double delta);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package net.mightypork.rogue;
|
||||
|
||||
import org.mini2Dx.core.game.BasicGame;
|
||||
import org.mini2Dx.core.game.Mini2DxGame;
|
||||
import org.mini2Dx.core.graphics.Graphics;
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
|
||||
|
||||
public class Game extends BasicGame {
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate(float arg0) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics arg0) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float arg0) {
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public static void main(String [] args) {
|
||||
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
|
||||
cfg.title = "A basic game";
|
||||
cfg.useGL20 = true;
|
||||
cfg.width = 800;
|
||||
cfg.height = 600;
|
||||
cfg.useCPUSynch = false;
|
||||
cfg.vSyncEnabled = true;
|
||||
new LwjglApplication(new Mini2DxGame(new Game()), cfg);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user