Coord and rect views and freezing; Fixed font glitches.

v5stable
Ondřej Hruška 10 years ago
parent ebac276ed2
commit e940c53dd6
  1. 8
      src/mightypork/gamecore/audio/DeferredAudio.java
  2. 4
      src/mightypork/gamecore/audio/SoundSystem.java
  3. 4
      src/mightypork/gamecore/control/GameLoop.java
  4. 8
      src/mightypork/gamecore/control/bus/events/ResourceLoadRequest.java
  5. 110
      src/mightypork/gamecore/gui/components/painters/TextPainter.java
  6. 28
      src/mightypork/gamecore/gui/constraints/Constraints.java
  7. 3
      src/mightypork/gamecore/gui/screens/LayeredScreen.java
  8. 8
      src/mightypork/gamecore/input/InputSystem.java
  9. 12
      src/mightypork/gamecore/loading/AsyncResourceLoader.java
  10. 144
      src/mightypork/gamecore/loading/BaseDeferredResource.java
  11. 23
      src/mightypork/gamecore/loading/Deferred.java
  12. 131
      src/mightypork/gamecore/loading/DeferredResource.java
  13. 11
      src/mightypork/gamecore/render/DisplaySystem.java
  14. 122
      src/mightypork/gamecore/render/Render.java
  15. 2
      src/mightypork/gamecore/render/fonts/FontBank.java
  16. 26
      src/mightypork/gamecore/render/fonts/FontRenderer.java
  17. 23
      src/mightypork/gamecore/render/fonts/GLFont.java
  18. 134
      src/mightypork/gamecore/render/fonts/SlickFont.java
  19. 430
      src/mightypork/gamecore/render/fonts/impl/CachedFont.java
  20. 107
      src/mightypork/gamecore/render/fonts/impl/DeferredFont.java
  21. 32
      src/mightypork/gamecore/render/fonts/impl/DeferredFontNative.java
  22. 20
      src/mightypork/gamecore/render/fonts/impl/NullFont.java
  23. 7
      src/mightypork/gamecore/render/textures/DeferredTexture.java
  24. 26
      src/mightypork/gamecore/render/textures/TextureBank.java
  25. 6
      src/mightypork/gamecore/render/textures/TxQuad.java
  26. 2
      src/mightypork/rogue/App.java
  27. 31
      src/mightypork/rogue/Res.java
  28. 6
      src/mightypork/rogue/screens/LayerFps.java
  29. 24
      src/mightypork/rogue/screens/test_cat_sound/LayerFlyingCat.java
  30. 7
      src/mightypork/rogue/screens/test_cat_sound/ScreenTestCat.java
  31. 25
      src/mightypork/utils/logging/Log.java
  32. 26
      src/mightypork/utils/math/Calc.java
  33. 8
      src/mightypork/utils/math/Polar.java
  34. 11
      src/mightypork/utils/math/color/RGB.java
  35. 42
      src/mightypork/utils/math/coord/ConstraintCoordView.java
  36. 402
      src/mightypork/utils/math/coord/Coord.java
  37. 31
      src/mightypork/utils/math/coord/CoordValue.java
  38. 62
      src/mightypork/utils/math/coord/CoordView.java
  39. 251
      src/mightypork/utils/math/coord/Rect.java
  40. 37
      src/mightypork/utils/math/coord/RectView.java
  41. 2
      src/mightypork/utils/objects/Convert.java

@ -3,7 +3,7 @@ package mightypork.gamecore.audio;
import java.io.IOException; import java.io.IOException;
import mightypork.gamecore.loading.BaseDeferredResource; import mightypork.gamecore.loading.DeferredResource;
import mightypork.utils.files.FileUtils; import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.LoggedName; import mightypork.utils.logging.LoggedName;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
@ -18,7 +18,7 @@ import org.newdawn.slick.openal.SoundStore;
* @author MightyPork * @author MightyPork
*/ */
@LoggedName(name = "Audio") @LoggedName(name = "Audio")
public class DeferredAudio extends BaseDeferredResource { public class DeferredAudio extends DeferredResource {
private enum PlayMode private enum PlayMode
{ {
@ -168,7 +168,7 @@ public class DeferredAudio extends BaseDeferredResource {
*/ */
public int playAsEffect(double pitch, double gain, boolean loop, double x, double y) public int playAsEffect(double pitch, double gain, boolean loop, double x, double y)
{ {
return playAsEffect(pitch, gain, loop, x, y, SoundSystem.getListener().z); return playAsEffect(pitch, gain, loop, x, y, SoundSystem.getListener().z());
} }
@ -208,7 +208,7 @@ public class DeferredAudio extends BaseDeferredResource {
{ {
if (!ensureLoaded()) return -1; if (!ensureLoaded()) return -1;
return playAsEffect(pitch, gain, loop, pos.x, pos.y, pos.z); return playAsEffect(pitch, gain, loop, pos.x(), pos.y(), pos.z());
} }

@ -26,7 +26,7 @@ import org.newdawn.slick.openal.SoundStore;
*/ */
public class SoundSystem extends RootBusNode implements Updateable { public class SoundSystem extends RootBusNode implements Updateable {
private static final Coord INITIAL_LISTENER_POS = new Coord(0, 0, 0); private static final Coord INITIAL_LISTENER_POS = Coord.ZERO;
private static final int MAX_SOURCES = 256; private static final int MAX_SOURCES = 256;
private static Coord listener = new Coord(); private static Coord listener = new Coord();
@ -45,7 +45,7 @@ public class SoundSystem extends RootBusNode implements Updateable {
FloatBuffer buf3 = Buffers.alloc(3); FloatBuffer buf3 = Buffers.alloc(3);
FloatBuffer buf6 = Buffers.alloc(6); FloatBuffer buf6 = Buffers.alloc(6);
buf3.clear(); buf3.clear();
Buffers.fill(buf3, (float) pos.x, (float) pos.y, (float) pos.z); Buffers.fill(buf3, pos.xf(), pos.yf(), pos.zf());
AL10.alListener(AL10.AL_POSITION, buf3); AL10.alListener(AL10.AL_POSITION, buf3);
buf3.clear(); buf3.clear();
Buffers.fill(buf3, 0, 0, 0); Buffers.fill(buf3, 0, 0, 0);

@ -64,7 +64,9 @@ public abstract class GameLoop extends AppModule implements MainLoopTaskRequest.
beforeRender(); beforeRender();
if (rootRenderable != null) rootRenderable.render(); if (rootRenderable != null) {
rootRenderable.render();
}
afterRender(); afterRender();

@ -2,7 +2,7 @@ package mightypork.gamecore.control.bus.events;
import mightypork.gamecore.control.bus.events.types.SingleReceiverEvent; import mightypork.gamecore.control.bus.events.types.SingleReceiverEvent;
import mightypork.gamecore.loading.DeferredResource; import mightypork.gamecore.loading.Deferred;
/** /**
@ -13,13 +13,13 @@ import mightypork.gamecore.loading.DeferredResource;
@SingleReceiverEvent @SingleReceiverEvent
public class ResourceLoadRequest implements Event<ResourceLoadRequest.Listener> { public class ResourceLoadRequest implements Event<ResourceLoadRequest.Listener> {
private final DeferredResource resource; private final Deferred resource;
/** /**
* @param resource resource to load * @param resource resource to load
*/ */
public ResourceLoadRequest(DeferredResource resource) { public ResourceLoadRequest(Deferred resource) {
this.resource = resource; this.resource = resource;
} }
@ -42,6 +42,6 @@ public class ResourceLoadRequest implements Event<ResourceLoadRequest.Listener>
* *
* @param resource * @param resource
*/ */
void loadResource(DeferredResource resource); void loadResource(Deferred resource);
} }
} }

@ -6,12 +6,16 @@ import mightypork.gamecore.render.fonts.FontRenderer;
import mightypork.gamecore.render.fonts.FontRenderer.Align; import mightypork.gamecore.render.fonts.FontRenderer.Align;
import mightypork.gamecore.render.fonts.GLFont; import mightypork.gamecore.render.fonts.GLFont;
import mightypork.utils.math.color.RGB; import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect;
import mightypork.utils.string.StringProvider; import mightypork.utils.string.StringProvider;
import mightypork.utils.string.StringProvider.StringWrapper; import mightypork.utils.string.StringProvider.StringWrapper;
/** /**
* Text painting component * Text painting component.<br>
* Drawing values are obtained through getters, so overriding getters can be
* used to change parameters dynamically.
* *
* @author MightyPork * @author MightyPork
*/ */
@ -21,6 +25,10 @@ public class TextPainter extends PluggableRenderer {
private RGB color; private RGB color;
private Align align; private Align align;
private StringProvider text; private StringProvider text;
private boolean shadow;
private RGB shadowColor = RGB.BLACK;
private Coord shadowOffset = Coord.one();
/** /**
@ -70,110 +78,68 @@ public class TextPainter extends PluggableRenderer {
} }
/** @Override
* Use size specified during font init instead of size provided by public void render()
* {@link GLFont} instance (measured from tile heights.<br>
* This is better when the font is drawn in original size, but can cause
* weird artifacts if the font is scaled up.
*
* @param enable use it
*/
public void usePtSize(boolean enable)
{ {
font.usePtSize(enable); if (text == null) return;
final String str = text.getString();
final Rect rect = getRect();
if (shadow) {
font.draw(str, rect.add(shadowOffset), align, shadowColor);
}
font.draw(str, rect, align, color);
} }
@Override public void setShadow(RGB color, Coord offset)
public void render()
{ {
if (getText() == null) return; setShadow(true);
setShadowColor(color);
font.draw(getText(), getRect(), getAlign(), getColor()); setShadowOffset(offset);
} }
/** public void setShadow(boolean shadow)
* Assign paint color
*
* @param color paint color
*/
public void setColor(RGB color)
{ {
this.color = color; this.shadow = shadow;
} }
/** public void setShadowColor(RGB shadowColor)
* Set text align
*
* @param align text align
*/
public void setAlign(Align align)
{ {
this.align = align; this.shadowColor = shadowColor;
} }
/** public void setShadowOffset(Coord shadowOffset)
* Set drawn text
*
* @param text text
*/
public void setText(String text)
{ {
this.text = new StringWrapper(text); this.shadowOffset = shadowOffset;
} }
/** public void setColor(RGB color)
* Set drawn text provider
*
* @param text text provider
*/
public void setText(StringProvider text)
{ {
this.text = text; this.color = color;
} }
/** public void setAlign(Align align)
* Get draw color.<br>
* <i>This getter is used for getting drawing color; so if it's overriden,
* the draw color can be adjusted in real time.</i>
*
* @return drawing color
*/
public RGB getColor()
{ {
return color; this.align = align;
} }
/** public void setText(String text)
* Get text align.<br>
* <i>This getter is used for getting align; so if it's overidden, the align
* can be adjusted in real time.</i>
*
* @return text align
*/
public Align getAlign()
{ {
return align; this.text = new StringWrapper(text);
} }
/** public void setText(StringProvider text)
* Get text to draw.<br>
* <i>This getter is used for getting text to draw; so if it's overidden,
* the text can be adjusted in real time. (alternative to using
* StringProvider)</i>
*
* @return text align
*/
public String getText()
{ {
return text.getString(); this.text = text;
} }
} }

@ -23,7 +23,7 @@ public class Constraints {
@Override @Override
public double getValue() public double getValue()
{ {
return InputSystem.getMousePos().x; return InputSystem.getMousePos().x();
} }
}; };
@ -32,7 +32,7 @@ public class Constraints {
@Override @Override
public double getValue() public double getValue()
{ {
return InputSystem.getMousePos().y; return InputSystem.getMousePos().y();
} }
}; };
@ -246,7 +246,7 @@ public class Constraints {
@Override @Override
public double getValue() public double getValue()
{ {
return r.getRect().getSize().x; return r.getRect().getSize().x();
} }
}; };
} }
@ -259,7 +259,7 @@ public class Constraints {
@Override @Override
public double getValue() public double getValue()
{ {
return r.getRect().getSize().y; return r.getRect().getSize().y();
} }
}; };
} }
@ -274,7 +274,7 @@ public class Constraints {
@Override @Override
public Rect getRect() public Rect getRect()
{ {
final double height = r.getRect().getSize().y; final double height = r.getRect().getSize().y();
final double perRow = height / rows; final double perRow = height / rows;
final Coord origin = r.getRect().getOrigin().add(0, perRow * index); final Coord origin = r.getRect().getOrigin().add(0, perRow * index);
@ -293,7 +293,7 @@ public class Constraints {
@Override @Override
public Rect getRect() public Rect getRect()
{ {
final double width = r.getRect().getSize().x; final double width = r.getRect().getSize().x();
final double perCol = width / columns; final double perCol = width / columns;
final Coord origin = r.getRect().getOrigin().add(perCol * index, 0); final Coord origin = r.getRect().getOrigin().add(perCol * index, 0);
@ -312,8 +312,8 @@ public class Constraints {
@Override @Override
public Rect getRect() public Rect getRect()
{ {
final double height = r.getRect().getSize().y; final double height = r.getRect().getSize().y();
final double width = r.getRect().getSize().y; final double width = r.getRect().getSize().y();
final double perRow = height / rows; final double perRow = height / rows;
final double perCol = width / cols; final double perCol = width / cols;
@ -522,8 +522,8 @@ public class Constraints {
//@formatter:off //@formatter:off
return Rect.fromSize( return Rect.fromSize(
origin.x, origin.x(),
origin.y, origin.y(),
_nv(width), _nv(width),
_nv(height) _nv(height)
); );
@ -544,8 +544,8 @@ public class Constraints {
//@formatter:off //@formatter:off
return Rect.fromSize( return Rect.fromSize(
origin.x + _nv(x), origin.x() + _nv(x),
origin.y + _nv(y), origin.y() + _nv(y),
_nv(width), _nv(width),
_nv(height) _nv(height)
); );
@ -582,7 +582,7 @@ public class Constraints {
final Coord size = r.getRect().getSize(); final Coord size = r.getRect().getSize();
final Coord center = centerTo.getRect().getCenter(); final Coord center = centerTo.getRect().getCenter();
return Rect.fromSize(center.x - size.x / 2D, center.y - size.y / 2D, size.x, size.y); return Rect.fromSize(center.x() - size.x() / 2D, center.y() - size.y() / 2D, size.x(), size.y());
} }
}; };
} }
@ -605,7 +605,7 @@ public class Constraints {
{ {
final Coord size = r.getRect().getSize(); final Coord size = r.getRect().getSize();
return Rect.fromSize(_nv(x) - size.x / 2D, _nv(y) - size.y / 2D, size.x, size.y); return Rect.fromSize(_nv(x) - size.x() / 2D, _nv(y) - size.y() / 2D, size.x(), size.y());
} }
}; };
} }

@ -5,7 +5,6 @@ import java.util.Collection;
import java.util.TreeSet; import java.util.TreeSet;
import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.render.Render;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
@ -31,9 +30,7 @@ public abstract class LayeredScreen extends Screen {
protected void renderScreen() protected void renderScreen()
{ {
for (final ScreenLayer layer : layers) { for (final ScreenLayer layer : layers) {
Render.pushState();
layer.render(); layer.render();
Render.popState();
} }
} }

@ -129,10 +129,10 @@ public class InputSystem extends RootBusNode implements Updateable, KeyBinder {
} }
if (button != -1 || wheeld != 0) { if (button != -1 || wheeld != 0) {
getEventBus().send(new MouseButtonEvent(pos, button, down, wheeld)); getEventBus().send(new MouseButtonEvent(pos.freeze(), button, down, wheeld));
} }
moveSum.add_ip(move); moveSum.add_ip(move.freeze());
lastPos.setTo(pos); lastPos.setTo(pos);
} }
@ -149,7 +149,7 @@ public class InputSystem extends RootBusNode implements Updateable, KeyBinder {
private static void flipScrY(Coord c) private static void flipScrY(Coord c)
{ {
if (DisplaySystem.yAxisDown) c.setY_ip(DisplaySystem.getSize().y - c.y); if (DisplaySystem.yAxisDown) c.setY_ip(DisplaySystem.getSize().y() - c.y());
} }
@ -162,7 +162,7 @@ public class InputSystem extends RootBusNode implements Updateable, KeyBinder {
{ {
final Coord pos = new Coord(Mouse.getX(), Mouse.getY()); final Coord pos = new Coord(Mouse.getX(), Mouse.getY());
flipScrY(pos); flipScrY(pos);
return pos; return pos.freeze();
} }

@ -6,7 +6,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import mightypork.gamecore.control.bus.BusAccess; import mightypork.gamecore.control.bus.BusAccess;
import mightypork.gamecore.control.bus.events.MainLoopTaskRequest;
import mightypork.gamecore.control.bus.events.ResourceLoadRequest; import mightypork.gamecore.control.bus.events.ResourceLoadRequest;
import mightypork.gamecore.control.interf.Destroyable; import mightypork.gamecore.control.interf.Destroyable;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
@ -33,8 +32,9 @@ public class AsyncResourceLoader extends Thread implements ResourceLoadRequest.L
private final ExecutorService exs = Executors.newCachedThreadPool(); private final ExecutorService exs = Executors.newCachedThreadPool();
private final LinkedBlockingQueue<DeferredResource> toLoad = new LinkedBlockingQueue<>(); private final LinkedBlockingQueue<Deferred> toLoad = new LinkedBlockingQueue<>();
private volatile boolean stopped; private volatile boolean stopped;
@SuppressWarnings("unused")
private final BusAccess app; private final BusAccess app;
@ -49,7 +49,7 @@ public class AsyncResourceLoader extends Thread implements ResourceLoadRequest.L
@Override @Override
public void loadResource(final DeferredResource resource) public void loadResource(final Deferred resource)
{ {
if (resource.isLoaded()) return; if (resource.isLoaded()) return;
if (resource instanceof NullResource) return; if (resource instanceof NullResource) return;
@ -57,7 +57,7 @@ public class AsyncResourceLoader extends Thread implements ResourceLoadRequest.L
// textures & fonts needs to be loaded in main thread // textures & fonts needs to be loaded in main thread
if (resource.getClass().isAnnotationPresent(MustLoadInMainThread.class)) { if (resource.getClass().isAnnotationPresent(MustLoadInMainThread.class)) {
// just ignore Log.f3("<LOADER> Cannot load async: " + Log.str(resource));
// Log.f3("<LOADER> Delegating to main thread:\n " + Log.str(resource)); // Log.f3("<LOADER> Delegating to main thread:\n " + Log.str(resource));
// //
@ -85,12 +85,12 @@ public class AsyncResourceLoader extends Thread implements ResourceLoadRequest.L
while (!stopped) { while (!stopped) {
try { try {
final DeferredResource def = toLoad.take(); final Deferred def = toLoad.take();
if (def == null) continue; if (def == null) continue;
if (!def.isLoaded()) { if (!def.isLoaded()) {
Log.f3("<LOADER> Loading async:\n " + Log.str(def)); Log.f3("<LOADER> Loading: " + Log.str(def));
exs.submit(new Runnable() { exs.submit(new Runnable() {

@ -1,144 +0,0 @@
package mightypork.gamecore.loading;
import mightypork.gamecore.control.interf.Destroyable;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LoggedName;
/**
* Deferred resource abstraction.<br>
* Resources implementing {@link NullResource} will be treated as fake and not
* attempted to load.
*
* @author MightyPork
*/
@LoggedName(name = "Resource")
public abstract class BaseDeferredResource implements DeferredResource, Destroyable {
private final String resource;
private volatile boolean loadFailed = false;
private volatile boolean loadAttempted = false;
/**
* @param resource resource path / name; this string is later used in
* loadResource()
*/
public BaseDeferredResource(String resource) {
this.resource = resource;
}
@Override
public synchronized final void load()
{
if(loadFailed) return;
if (loadAttempted) {
Log.w("<RES> Already loaded @ load():\n " + this);
(new IllegalStateException()).printStackTrace();
return;
}
loadAttempted = true;
loadFailed = false;
if (isNull()) return;
try {
if (resource == null) {
throw new NullPointerException("Resource string cannot be null for non-null resource.");
}
Log.f3("<RES> Loading:\n " + this);
loadResource(resource);
Log.f3("<RES> Loaded:\n " + this);
} catch (final Exception e) {
loadFailed = true;
Log.e("<RES> Failed to load:\n " + this, e);
}
}
@Override
public synchronized final boolean isLoaded()
{
if (isNull()) return false;
return loadAttempted && !loadFailed;
}
/**
* Check if the resource is loaded; if not, try to do so.
*
* @return true if it's loaded now.
*/
public synchronized final boolean ensureLoaded()
{
if (isNull()) return false;
if (isLoaded()) {
return true;
} else {
if(loadFailed) return false;
Log.f3("<RES> (!) First use, not loaded yet - loading directly\n " + this);
load();
}
return isLoaded();
}
/**
* Load the resource. Called from load() - once only.
*
* @param resource the path / name of a resource
* @throws Exception when some problem prevented the resource from being
* loaded.
*/
protected abstract void loadResource(String resource) throws Exception;
@Override
public abstract void destroy();
@Override
public String toString()
{
return Log.str(getClass()) + "(\"" + resource + "\")";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((resource == null) ? 0 : resource.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof BaseDeferredResource)) return false;
final BaseDeferredResource other = (BaseDeferredResource) obj;
if (resource == null) {
if (other.resource != null) return false;
} else if (!resource.equals(other.resource)) return false;
return true;
}
private boolean isNull()
{
return this instanceof NullResource;
}
}

@ -0,0 +1,23 @@
package mightypork.gamecore.loading;
/**
* Deferred resource
*
* @author MightyPork
*/
public interface Deferred {
/**
* Load the actual resource, if not loaded yet.
*/
void load();
/**
* Check if resource was successfully loaded.
*
* @return true if already loaded
*/
boolean isLoaded();
}

@ -1,23 +1,140 @@
package mightypork.gamecore.loading; package mightypork.gamecore.loading;
import mightypork.gamecore.control.interf.Destroyable;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LoggedName;
/** /**
* Deferred resource * Deferred resource abstraction.<br>
* Resources implementing {@link NullResource} will be treated as fake and not
* attempted to load.
* *
* @author MightyPork * @author MightyPork
*/ */
public interface DeferredResource { @LoggedName(name = "Resource")
public abstract class DeferredResource implements Deferred, Destroyable {
private final String resource;
private volatile boolean loadFailed = false;
private volatile boolean loadAttempted = false;
/** /**
* Load the actual resource, if not loaded yet. * @param resource resource path / name; this string is later used in
* loadResource()
*/ */
void load(); public DeferredResource(String resource) {
this.resource = resource;
}
@Override
public synchronized final void load()
{
if (loadFailed) return;
if (loadAttempted) return;
loadAttempted = true;
loadFailed = false;
if (isNull()) return;
try {
if (resource == null) {
throw new NullPointerException("Resource string cannot be null for non-null resource.");
}
Log.f3("<RES> Loading: " + this);
loadResource(resource);
Log.f3("<RES> Loaded: " + this);
} catch (final Exception e) {
loadFailed = true;
Log.e("<RES> Failed to load: " + this, e);
}
}
@Override
public synchronized final boolean isLoaded()
{
if (isNull()) return false;
return loadAttempted && !loadFailed;
}
/** /**
* Check if resource was successfully loaded. * Check if the resource is loaded; if not, try to do so.
* *
* @return true if already loaded * @return true if it's loaded now.
*/ */
boolean isLoaded(); public synchronized final boolean ensureLoaded()
{
if (isNull()) return false;
if (isLoaded()) {
return true;
} else {
if (loadFailed) return false;
Log.f3("<RES> (!) Loading on access: " + this);
load();
}
return isLoaded();
}
/**
* Load the resource. Called from load() - once only.
*
* @param resource the path / name of a resource
* @throws Exception when some problem prevented the resource from being
* loaded.
*/
protected abstract void loadResource(String resource) throws Exception;
@Override
public abstract void destroy();
@Override
public String toString()
{
return Log.str(getClass()) + "(\"" + resource + "\")";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((resource == null) ? 0 : resource.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof DeferredResource)) return false;
final DeferredResource other = (DeferredResource) obj;
if (resource == null) {
if (other.resource != null) return false;
} else if (!resource.equals(other.resource)) return false;
return true;
}
private boolean isNull()
{
return this instanceof NullResource;
}
} }

@ -12,6 +12,7 @@ import mightypork.gamecore.control.timing.FpsMeter;
import mightypork.gamecore.gui.constraints.NumberConstraint; import mightypork.gamecore.gui.constraints.NumberConstraint;
import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import mightypork.utils.math.coord.ConstraintCoordView;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect; import mightypork.utils.math.coord.Rect;
@ -176,7 +177,7 @@ public class DisplaySystem extends AppModule implements RectConstraint {
*/ */
public static Coord getSize() public static Coord getSize()
{ {
return new Coord(getWidth(), getHeight()); return size;
} }
@ -227,7 +228,7 @@ public class DisplaySystem extends AppModule implements RectConstraint {
@Override @Override
public Rect getRect() public Rect getRect()
{ {
return new Rect(getSize()); return new Rect(Coord.ZERO, getSize());
} }
@ -240,7 +241,7 @@ public class DisplaySystem extends AppModule implements RectConstraint {
} }
/** Screen width constraint */ /** Screen width constraint */
public final NumberConstraint width = new NumberConstraint() { public static final NumberConstraint width = new NumberConstraint() {
@Override @Override
public double getValue() public double getValue()
@ -250,7 +251,7 @@ public class DisplaySystem extends AppModule implements RectConstraint {
}; };
/** Screen height constaint */ /** Screen height constaint */
public final NumberConstraint height = new NumberConstraint() { public static final NumberConstraint height = new NumberConstraint() {
@Override @Override
public double getValue() public double getValue()
@ -258,4 +259,6 @@ public class DisplaySystem extends AppModule implements RectConstraint {
return getHeight(); return getHeight();
} }
}; };
public static final ConstraintCoordView size = new ConstraintCoordView(width, height, null);
} }

@ -26,9 +26,9 @@ import org.newdawn.slick.util.ResourceLoader;
*/ */
public class Render { public class Render {
private static final Coord AXIS_X = new Coord(1, 0, 0); public static final Coord AXIS_X = new Coord(1, 0, 0).freeze();
private static final Coord AXIS_Y = new Coord(0, 1, 0); public static final Coord AXIS_Y = new Coord(0, 1, 0).freeze();
private static final Coord AXIS_Z = new Coord(0, 0, 1); public static final Coord AXIS_Z = new Coord(0, 0, 1).freeze();
/** /**
@ -54,6 +54,31 @@ public class Render {
} }
/**
* Translate
*
* @param x
* @param y
*/
public static void translate(double x, double y)
{
glTranslated(x, y, 0);
}
/**
* Translate
*
* @param x
* @param y
* @param z
*/
public static void translate(double x, double y, double z)
{
glTranslated(x, y, z);
}
/** /**
* Translate with coord * Translate with coord
* *
@ -61,7 +86,32 @@ public class Render {
*/ */
public static void translate(Coord coord) public static void translate(Coord coord)
{ {
glTranslated(coord.x, coord.y, coord.z); glTranslated(coord.x(), coord.y(), coord.z());
}
/**
* Scale
*
* @param x
* @param y
*/
public static void scale(double x, double y)
{
glScaled(x, y, 0);
}
/**
* Scale
*
* @param x
* @param y
* @param z
*/
public static void scale(double x, double y, double z)
{
glScaled(x, y, z);
} }
@ -72,7 +122,7 @@ public class Render {
*/ */
public static void scale(Coord factor) public static void scale(Coord factor)
{ {
glScaled(factor.x, factor.y, factor.z); glScaled(factor.x(), factor.y(), factor.z());
} }
@ -162,7 +212,7 @@ public class Render {
public static void rotate(double angle, Coord axis) public static void rotate(double angle, Coord axis)
{ {
final Coord vec = axis.norm(1); final Coord vec = axis.norm(1);
glRotated(angle, vec.x, vec.y, vec.z); glRotated(angle, vec.x(), vec.y(), vec.z());
} }
private static int pushed = 0; private static int pushed = 0;
@ -179,8 +229,6 @@ public class Render {
Log.w("Suspicious number of state pushes: " + pushed); Log.w("Suspicious number of state pushes: " + pushed);
} }
// Log.f3("push : "+pushed);
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS); GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS);
GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glMatrixMode(GL11.GL_MODELVIEW);
@ -188,10 +236,6 @@ public class Render {
GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix(); GL11.glPushMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glMatrixMode(GL11.GL_MODELVIEW);
// GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
// GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS);
// GL11.glPushMatrix();
} }
@ -206,18 +250,30 @@ public class Render {
pushed--; pushed--;
// Log.f3("pop : "+pushed);
GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix(); GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW); GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPopMatrix(); GL11.glPopMatrix();
GL11.glPopClientAttrib(); GL11.glPopClientAttrib();
GL11.glPopAttrib(); GL11.glPopAttrib();
}
// GL11.glPopMatrix();
// GL11.glPopClientAttrib();
// GL11.glPopAttrib(); /**
* Store matrix
*/
public static void pushMatrix()
{
GL11.glPushMatrix();
}
/**
* Restore Gl state
*/
public static void popMatrix()
{
GL11.glPopMatrix();
} }
@ -254,24 +310,11 @@ public class Render {
* Bind texture * Bind texture
* *
* @param texture the texture * @param texture the texture
* @param linear use linear interpolation for scaling
* @throws RuntimeException if not loaded yet
*/
private static void bindTexture(Texture texture, boolean linear) throws RuntimeException
{
texture.bind();
}
/**
* Bind texture with linear interpolation
*
* @param texture the texture
* @throws RuntimeException if not loaded yet * @throws RuntimeException if not loaded yet
*/ */
private static void bindTexture(Texture texture) throws RuntimeException private static void bindTexture(Texture texture) throws RuntimeException
{ {
bindTexture(texture, false); texture.bind();
} }
@ -280,9 +323,9 @@ public class Render {
*/ */
private static void unbindTexture() private static void unbindTexture()
{ {
if (TextureImpl.getLastBind() != null) { //if (TextureImpl.getLastBind() != null) {
TextureImpl.bindNone(); TextureImpl.bindNone();
} //}
} }
@ -437,11 +480,10 @@ public class Render {
*/ */
public static void quadTextured(Rect quad, Rect uvs, Texture texture, RGB tint) public static void quadTextured(Rect quad, Rect uvs, Texture texture, RGB tint)
{ {
pushState();
bindTexture(texture); bindTexture(texture);
setColor(tint); setColor(tint);
quadUV(quad, uvs); quadUV(quad, uvs);
popState(); unbindTexture();
} }
@ -466,7 +508,7 @@ public class Render {
*/ */
public static void quadTextured(Rect quad, Texture texture) public static void quadTextured(Rect quad, Texture texture)
{ {
quadTextured(quad, Rect.one(), texture, RGB.WHITE); quadTextured(quad, Rect.ONE, texture, RGB.WHITE);
} }
@ -505,7 +547,7 @@ public class Render {
glLoadIdentity(); glLoadIdentity();
final Coord s = DisplaySystem.getSize(); final Coord s = DisplaySystem.getSize();
glViewport(0, 0, s.xi(), s.yi()); glViewport(0, 0, s.xi(), s.yi());
glOrtho(0, s.x, (DisplaySystem.yAxisDown ? 1 : -1) * s.y, 0, -1000, 1000); glOrtho(0, s.x(), (DisplaySystem.yAxisDown ? 1 : -1) * s.y(), 0, -1000, 1000);
// back to modelview // back to modelview
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
@ -523,7 +565,7 @@ public class Render {
glShadeModel(GL_SMOOTH); glShadeModel(GL_SMOOTH);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} }
} }

@ -6,6 +6,8 @@ import java.util.HashMap;
import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.AppAdapter; import mightypork.gamecore.control.AppAdapter;
import mightypork.gamecore.control.bus.events.ResourceLoadRequest; import mightypork.gamecore.control.bus.events.ResourceLoadRequest;
import mightypork.gamecore.render.fonts.impl.DeferredFont;
import mightypork.gamecore.render.fonts.impl.NullFont;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import org.newdawn.slick.opengl.Texture; import org.newdawn.slick.opengl.Texture;

@ -6,8 +6,6 @@ import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect; import mightypork.utils.math.coord.Rect;
import org.lwjgl.opengl.GL11;
/** /**
* Font renderer * Font renderer
@ -17,7 +15,6 @@ import org.lwjgl.opengl.GL11;
public class FontRenderer { public class FontRenderer {
private GLFont font; private GLFont font;
private boolean nativeRes = false;
public static enum Align public static enum Align
{ {
@ -45,20 +42,6 @@ public class FontRenderer {
} }
/**
* Use size specified during font init instead of size provided by
* {@link GLFont} instance (measured from tile heights.<br>
* This is better when the font is drawn in original size, but can cause
* weird artifacts if the font is scaled up.
*
* @param use use it
*/
public void usePtSize(boolean use)
{
nativeRes = use;
}
/** /**
* Get region needed to draw text at size * Get region needed to draw text at size
* *
@ -81,13 +64,13 @@ public class FontRenderer {
*/ */
public double getWidth(String text, double height) public double getWidth(String text, double height)
{ {
return getNeededSpace(text, height).x; return getNeededSpace(text, height).x();
} }
private double getScale(double height) private double getScale(double height)
{ {
return height / (nativeRes ? font.getSize() : font.getGlyphHeight()); return height / font.getHeight();
} }
@ -123,15 +106,14 @@ public class FontRenderer {
*/ */
public void draw(String text, Coord pos, double height, RGB color) public void draw(String text, Coord pos, double height, RGB color)
{ {
//Render.pushState(); Render.pushMatrix();
//GL11.glEnable(GL11.GL_TEXTURE_2D);
Render.translate(pos.round()); Render.translate(pos.round());
Render.scaleXY(getScale(height)); Render.scaleXY(getScale(height));
font.draw(text, color); font.draw(text, color);
//Render.popState(); Render.popMatrix();
} }

@ -1,7 +1,6 @@
package mightypork.gamecore.render.fonts; package mightypork.gamecore.render.fonts;
import mightypork.gamecore.render.textures.FilterMode;
import mightypork.utils.math.color.RGB; import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
@ -14,9 +13,9 @@ import mightypork.utils.math.coord.Coord;
public interface GLFont { public interface GLFont {
/** /**
* Draw string at position * Draw without scaling at (0, 0) in given color.
* *
* @param text string to draw * @param text text to draw
* @param color draw color * @param color draw color
*/ */
void draw(String text, RGB color); void draw(String text, RGB color);
@ -34,7 +33,7 @@ public interface GLFont {
/** /**
* @return font height * @return font height
*/ */
int getGlyphHeight(); int getHeight();
/** /**
@ -48,20 +47,4 @@ public interface GLFont {
* @return specified font size * @return specified font size
*/ */
int getSize(); int getSize();
/**
* Set used filtering
*
* @param filter font filtering mode
*/
void setFiltering(FilterMode filter);
/**
* Get used filter mode
*
* @return filter mode
*/
FilterMode getFiltering();
} }

@ -1,134 +0,0 @@
package mightypork.gamecore.render.fonts;
import static org.lwjgl.opengl.GL11.*;
import java.awt.Font;
import mightypork.gamecore.render.Render;
import mightypork.gamecore.render.textures.FilterMode;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import org.newdawn.slick.Color;
import org.newdawn.slick.TrueTypeFont;
/**
* Wrapper for slick font
*
* @author MightyPork
*/
public class SlickFont implements GLFont {
private final TrueTypeFont ttf;
private FilterMode filter;
private final int fsize;
/**
* A font with ASCII and extra chars
*
* @param font font to load
* @param filtering filtering mode
* @param extraChars extra chars to load
*/
public SlickFont(Font font, FilterMode filtering, String extraChars) {
this.filter = filtering;
this.fsize = font.getSize();
ttf = new TrueTypeFont(font, true, stripASCII(extraChars));
}
@Override
public void setFiltering(FilterMode filter)
{
this.filter = filter;
}
private static char[] stripASCII(String chars)
{
if (chars == null) return null;
final StringBuilder sb = new StringBuilder();
for (final char c : chars.toCharArray()) {
if (c <= 255) continue; // already included in default set
sb.append(c);
}
return sb.toString().toCharArray();
}
private void prepareForRender()
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter.num);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter.num);
}
/**
* Draw in color
*
* @param str string to draw
* @param color text color
*/
@Override
public void draw(String str, RGB color)
{
Render.pushState();
prepareForRender();
ttf.drawString(0, 0, str, rgbToSlickColor(color));
Render.popState();
}
private static Color rgbToSlickColor(RGB rgb)
{
return new Color((float) rgb.r, (float) rgb.g, (float) rgb.b, (float) rgb.a);
}
@Override
public Coord getNeededSpace(String text)
{
return new Coord(getWidth(text), getGlyphHeight());
}
@Override
public int getGlyphHeight()
{
return ttf.getHeight();
}
@Override
public int getWidth(String text)
{
return ttf.getWidth(text);
}
@Override
public int getSize()
{
return fsize;
}
@Override
public FilterMode getFiltering()
{
return filter;
}
}

@ -0,0 +1,430 @@
package mightypork.gamecore.render.fonts.impl;
import static org.lwjgl.opengl.GL11.*;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
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.gamecore.render.fonts.GLFont;
import mightypork.gamecore.render.textures.FilterMode;
import mightypork.utils.logging.Log;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.glu.GLU;
import org.newdawn.slick.opengl.GLUtils;
/**
* A TrueType font renderer with backing texture.
*
* @author James Chambers (Jimmy)
* @author Jeremy Adams (elias4444)
* @author Kevin Glass (kevglass)
* @author Peter Korzuszek (genail)
* @author David Aaron Muhar (bobjob)
* @author MightyPork
*/
public class CachedFont implements GLFont {
private class CharTile {
public int width;
public int height;
public int texPosX;
public int texPosY;
}
/* char bank */
private final Map<Character, CharTile> chars = new HashMap<>(255);
/* use antialiasing for rendering */
private final boolean antiAlias;
/* loaded font size (requested) */
private final int fontSize;
/* actual height of drawn glyphs */
private int fontHeight;
/* texture id */
private int textureID;
/* texture width */
private int textureWidth;
/* texture height */
private int textureHeight;
/* AWT font source */
private final java.awt.Font font;
private final FilterMode filter;
/**
* Make a font
*
* @param font original awt font to load
* @param antialias use antialiasing when rendering to cache texture
* @param filter used Gl filter
* @param chars chars to load
*/
public CachedFont(java.awt.Font font, boolean antialias, FilterMode filter, String chars) {
this(font, antialias, filter, chars.toCharArray());
}
/**
* Make a font
*
* @param font original awt font to load
* @param antialias use antialiasing when rendering to cache texture
* @param filter used Gl filter
* @param chars chars to load
*/
public CachedFont(java.awt.Font font, boolean antialias, FilterMode filter, char[] chars) {
GLUtils.checkGLContext();
this.font = font;
this.filter = filter;
this.fontSize = font.getSize();
this.antiAlias = antialias;
createSet(chars);
}
/**
* Create a BufferedImage of the given character
*
* @param ch the character
* @return BufferedImage containing the drawn character
*/
private BufferedImage getFontImage(char ch)
{
FontMetrics metrics;
BufferedImage img;
Graphics2D g;
// Create a temporary image to extract the character's size
img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
g = (Graphics2D) img.getGraphics();
if (antiAlias == true) g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(font);
metrics = g.getFontMetrics();
final int charwidth = Math.max(1, metrics.charWidth(ch));
final int charheight = Math.max(fontSize, metrics.getHeight());
// Create another image holding the character we are creating
final BufferedImage fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB);
g = (Graphics2D) fontImage.getGraphics();
if (antiAlias == true) g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(font);
g.setColor(Color.WHITE);
g.drawString(String.valueOf(ch), 0, metrics.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();
}
}
final List<LoadedGlyph> glyphs = new ArrayList<>();
final List<Character> loaded = new ArrayList<>();
for (final char ch : charsToLoad) {
if (!loaded.contains(ch)) {
glyphs.add(new LoadedGlyph(ch, getFontImage(ch)));
loaded.add(ch);
}
}
int lineHeight = 0;
int beginX = 0, beginY = 0;
int canvasW = 128, canvasH = 128;
boolean needsLarger = false;
// find smallest 2^x size for texture
while (true) {
needsLarger = false;
for (final LoadedGlyph glyph : glyphs) {
if (beginX + glyph.width > canvasW) {
beginY += lineHeight;
lineHeight = 0;
beginX = 0;
}
if (lineHeight < glyph.height) {
lineHeight = glyph.height;
}
if (beginY + lineHeight > canvasH) {
needsLarger = true;
break;
}
// draw.
beginX += glyph.width;
}
if (needsLarger) {
canvasW *= 2;
canvasH *= 2;
beginX = 0;
beginY = 0;
lineHeight = 0;
} else {
Log.f3(String.format("Generating font texture: %d×%d", canvasW, canvasH));
break;
}
}
textureWidth = canvasW;
textureHeight = canvasH;
BufferedImage imag = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g = (Graphics2D) imag.getGraphics();
g.setColor(new Color(0, 0, 0, 1));
g.fillRect(0, 0, textureWidth, textureHeight);
int rowHeight = 0, posX = 0, posY = 0;
for (final LoadedGlyph glyph : glyphs) {
final CharTile cht = new CharTile();
cht.width = glyph.width;
cht.height = glyph.height;
if (posX + cht.width >= textureWidth) {
posX = 0;
posY += rowHeight;
rowHeight = 0;
}
cht.texPosX = posX;
cht.texPosY = posY;
if (cht.height > fontHeight) {
fontHeight = cht.height;
}
if (cht.height > rowHeight) {
rowHeight = cht.height;
}
// Draw it here
g.drawImage(glyph.image, posX, posY, null);
posX += cht.width;
chars.put(glyph.c, cht);
}
textureID = loadImage(imag);
imag = null;
} catch (final Exception e) {
Log.e("Failed to load font.", e);
}
}
private int loadImage(BufferedImage bufferedImage)
{
try {
final short width = (short) bufferedImage.getWidth();
final short height = (short) bufferedImage.getHeight();
final int bpp = (byte) bufferedImage.getColorModel().getPixelSize();
ByteBuffer byteBuffer;
final DataBuffer db = bufferedImage.getData().getDataBuffer();
if (db instanceof DataBufferInt) {
final int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData();
final byte newI[] = new byte[intI.length * 4];
for (int i = 0; i < intI.length; i++) {
final byte b[] = intToByteArray(intI[i]);
final 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();
final int internalFormat = GL_RGBA8, format = GL_RGBA;
final IntBuffer textureId = BufferUtils.createIntBuffer(1);
glGenTextures(textureId);
glBindTexture(GL_TEXTURE_2D, textureId.get(0));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter.num);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GLU.gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, width, height, format, GL_UNSIGNED_BYTE, byteBuffer);
return textureId.get(0);
} catch (final Exception e) {
Log.e("Failed to load font.", e);
}
return -1;
}
private static byte[] intToByteArray(int value)
{
return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
}
private void drawQuad(float xmin, float ymin, float xmax, float ymax, float txmin, float tymin, float txmax, float tymax)
{
final float draw_h = xmax - xmin;
final float draw_w = ymax - ymin;
final float txmin01 = txmin / textureWidth;
final float tymin01 = tymin / textureHeight;
final float twidth01 = ((txmax - txmin) / textureWidth);
final float theight01 = ((tymax - tymin) / textureHeight);
glTexCoord2f(txmin01, tymin01);
glVertex2f(xmin, ymin);
glTexCoord2f(txmin01, tymin01 + theight01);
glVertex2f(xmin, ymin + draw_w);
glTexCoord2f(txmin01 + twidth01, tymin01 + theight01);
glVertex2f(xmin + draw_h, ymin + draw_w);
glTexCoord2f(txmin01 + twidth01, tymin01);
glVertex2f(xmin + draw_h, ymin);
}
/**
* Get size needed to draw given string
*
* @param text drawn text
* @return needed width
*/
@Override
public int getWidth(String text)
{
int totalwidth = 0;
CharTile ch = null;
for (int i = 0; i < text.length(); i++) {
ch = chars.get(text.charAt(i));
if (ch != null) totalwidth += ch.width;
}
return totalwidth;
}
@Override
public int getHeight()
{
return fontHeight;
}
@Override
public int getSize()
{
return fontSize;
}
@Override
public void draw(String text, RGB color)
{
GLUtils.checkGLContext();
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureID);
glColor4d(color.r, color.g, color.b, color.a);
glBegin(GL_QUADS);
CharTile chtx = null;
char charCurrent;
glBegin(GL_QUADS);
int totalwidth = 0;
for (int i = 0; i < text.length(); i++) {
charCurrent = text.charAt(i);
chtx = chars.get(charCurrent);
if (chtx != null) {
drawQuad((totalwidth), 0, (totalwidth + chtx.width), (chtx.height), chtx.texPosX, chtx.texPosY, chtx.texPosX + chtx.width, chtx.texPosY + chtx.height);
totalwidth += chtx.width;
}
}
glEnd();
glPopAttrib();
}
@Override
public Coord getNeededSpace(String text)
{
return new Coord(getWidth(text), getHeight());
}
}

@ -1,15 +1,14 @@
package mightypork.gamecore.render.fonts; package mightypork.gamecore.render.fonts.impl;
import static org.lwjgl.opengl.GL11.*;
import java.awt.Font; import java.awt.Font;
import java.awt.FontFormatException; import java.awt.FontFormatException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import mightypork.gamecore.loading.BaseDeferredResource; import mightypork.gamecore.loading.DeferredResource;
import mightypork.gamecore.loading.MustLoadInMainThread; import mightypork.gamecore.loading.MustLoadInMainThread;
import mightypork.gamecore.render.fonts.GLFont;
import mightypork.gamecore.render.textures.FilterMode; import mightypork.gamecore.render.textures.FilterMode;
import mightypork.utils.files.FileUtils; import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.LoggedName; import mightypork.utils.logging.LoggedName;
@ -24,7 +23,7 @@ import mightypork.utils.math.coord.Coord;
*/ */
@MustLoadInMainThread @MustLoadInMainThread
@LoggedName(name = "Font") @LoggedName(name = "Font")
public class DeferredFont extends BaseDeferredResource implements GLFont { public class DeferredFont extends DeferredResource implements GLFont {
public static enum FontStyle public static enum FontStyle
{ {
@ -38,22 +37,24 @@ public class DeferredFont extends BaseDeferredResource implements GLFont {
} }
} }
private SlickFont font = null; private GLFont font = null;
private final double size; private double size;
private final FontStyle style; private FontStyle style;
private final String extraChars; private String chars;
private FilterMode filter; private FilterMode filter;
private boolean antialias;
/** /**
* A font from resource * A font from resource; setters shall be used to specify parameters in
* greater detail.
* *
* @param resourcePath resource to load * @param resourcePath resource to load
* @param extraChars extra chars (0-255 loaded by default) * @param chars chars to load; null to load basic chars only
* @param size size (px) * @param size size (px)
*/ */
public DeferredFont(String resourcePath, String extraChars, double size) { public DeferredFont(String resourcePath, String chars, double size) {
this(resourcePath, extraChars, size, FontStyle.PLAIN, FilterMode.NEAREST); this(resourcePath, chars, size, FontStyle.PLAIN, true, FilterMode.LINEAR);
} }
@ -61,39 +62,64 @@ public class DeferredFont extends BaseDeferredResource implements GLFont {
* A font from resource * A font from resource
* *
* @param resourcePath resource to load * @param resourcePath resource to load
* @param extraChars extra chars (0-255 loaded by default) * @param chars chars to load; null to load basic chars only
* @param size size (pt) * @param size size (px)
* @param style font style * @param style font style
* @param antialias use antialiasing for caching texture
* @param filter gl filtering mode
*/ */
public DeferredFont(String resourcePath, String extraChars, double size, FontStyle style) { public DeferredFont(String resourcePath, String chars, double size, FontStyle style, boolean antialias, FilterMode filter) {
this(resourcePath, extraChars, size, style, FilterMode.NEAREST); super(resourcePath);
this.size = size;
this.style = style;
this.chars = chars;
this.filter = filter;
this.antialias = antialias;
} }
/** public void setFont(GLFont font)
* A font from resource {
* this.font = font;
* @param resourcePath resource to load }
* @param extraChars extra chars (0-255 loaded by default)
* @param size size (pt)
* @param style font style public void setSize(double size)
* @param filter gl filtering mode {
*/
public DeferredFont(String resourcePath, String extraChars, double size, FontStyle style, FilterMode filter) {
super(resourcePath);
this.size = size; this.size = size;
}
public void setStyle(FontStyle style)
{
this.style = style; this.style = style;
this.extraChars = extraChars; }
public void setChars(String chars)
{
this.chars = chars;
}
public void setFilter(FilterMode filter)
{
this.filter = filter; this.filter = filter;
} }
public void setAntialias(boolean antialias)
{
this.antialias = antialias;
}
@Override @Override
protected synchronized final void loadResource(String path) throws FontFormatException, IOException protected synchronized final void loadResource(String path) throws FontFormatException, IOException
{ {
final Font awtFont = getAwtFont(path, (float) size, style.numval); final Font awtFont = getAwtFont(path, (float) size, style.numval);
font = new SlickFont(awtFont, filter, extraChars); font = new CachedFont(awtFont, antialias, filter, chars);
} }
@ -109,7 +135,6 @@ public class DeferredFont extends BaseDeferredResource implements GLFont {
*/ */
protected Font getAwtFont(String resource, float size, int style) throws FontFormatException, IOException protected Font getAwtFont(String resource, float size, int style) throws FontFormatException, IOException
{ {
try (InputStream in = FileUtils.getResource(resource)) { try (InputStream in = FileUtils.getResource(resource)) {
Font awtFont = Font.createFont(Font.TRUETYPE_FONT, in); Font awtFont = Font.createFont(Font.TRUETYPE_FONT, in);
@ -119,7 +144,6 @@ public class DeferredFont extends BaseDeferredResource implements GLFont {
return awtFont; return awtFont;
} }
} }
@ -157,11 +181,11 @@ public class DeferredFont extends BaseDeferredResource implements GLFont {
* @return font height * @return font height
*/ */
@Override @Override
public int getGlyphHeight() public int getHeight()
{ {
if (!ensureLoaded()) return 0; if (!ensureLoaded()) return 0;
return font.getGlyphHeight(); return font.getHeight();
} }
@ -187,20 +211,11 @@ public class DeferredFont extends BaseDeferredResource implements GLFont {
// this will have to suffice // this will have to suffice
font = null; font = null;
} }
@Override
public void setFiltering(FilterMode filter) public void setFiltering(FilterMode filter)
{ {
this.filter = filter; this.filter = filter;
if(isLoaded()) font.setFiltering(filter);
}
@Override
public FilterMode getFiltering()
{
return filter;
} }
} }

@ -1,4 +1,4 @@
package mightypork.gamecore.render.fonts; package mightypork.gamecore.render.fonts.impl;
import java.awt.Font; import java.awt.Font;
@ -17,31 +17,6 @@ import mightypork.utils.logging.LoggedName;
@LoggedName(name = "FontNative") @LoggedName(name = "FontNative")
public class DeferredFontNative extends DeferredFont { public class DeferredFontNative extends DeferredFont {
/**
* A font from OS, found by name
*
* @param fontName font family name
* @param extraChars extra chars (0-255 loaded by default)
* @param size size (pt)
*/
public DeferredFontNative(String fontName, String extraChars, double size) {
super(fontName, extraChars, size);
}
/**
* A font from OS, found by name
*
* @param fontName font family name
* @param extraChars extra chars (0-255 loaded by default)
* @param size size (pt)
* @param style font style
*/
public DeferredFontNative(String fontName, String extraChars, double size, FontStyle style) {
super(fontName, extraChars, size, style);
}
/** /**
* A font from OS, found by name * A font from OS, found by name
* *
@ -49,10 +24,11 @@ public class DeferredFontNative extends DeferredFont {
* @param extraChars extra chars (0-255 loaded by default) * @param extraChars extra chars (0-255 loaded by default)
* @param size size (pt) * @param size size (pt)
* @param style font style * @param style font style
* @param antialias use antialiasing when drawn on the cache texture
* @param filter GL filtering mode * @param filter GL filtering mode
*/ */
public DeferredFontNative(String fontName, String extraChars, double size, FontStyle style, FilterMode filter) { public DeferredFontNative(String fontName, String extraChars, double size, FontStyle style, boolean antialias, FilterMode filter) {
super(fontName, extraChars, size, style, filter); super(fontName, extraChars, size, style, antialias, filter);
} }

@ -1,7 +1,7 @@
package mightypork.gamecore.render.fonts; package mightypork.gamecore.render.fonts.impl;
import mightypork.gamecore.render.textures.FilterMode; import mightypork.gamecore.render.fonts.GLFont;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import mightypork.utils.math.color.RGB; import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
@ -29,7 +29,7 @@ public class NullFont implements GLFont {
@Override @Override
public int getGlyphHeight() public int getHeight()
{ {
return 0; return 0;
} }
@ -47,19 +47,5 @@ public class NullFont implements GLFont {
{ {
return 0; return 0;
} }
@Override
public void setFiltering(FilterMode filter)
{
// nope
}
@Override
public FilterMode getFiltering()
{
return null;
}
} }

@ -1,7 +1,7 @@
package mightypork.gamecore.render.textures; package mightypork.gamecore.render.textures;
import mightypork.gamecore.loading.BaseDeferredResource; import mightypork.gamecore.loading.DeferredResource;
import mightypork.gamecore.loading.MustLoadInMainThread; import mightypork.gamecore.loading.MustLoadInMainThread;
import mightypork.gamecore.render.Render; import mightypork.gamecore.render.Render;
import mightypork.utils.logging.LoggedName; import mightypork.utils.logging.LoggedName;
@ -18,10 +18,10 @@ import org.newdawn.slick.opengl.Texture;
*/ */
@MustLoadInMainThread @MustLoadInMainThread
@LoggedName(name = "Texture") @LoggedName(name = "Texture")
public class DeferredTexture extends BaseDeferredResource implements FilteredTexture { public class DeferredTexture extends DeferredResource implements FilteredTexture {
private Texture backingTexture; private Texture backingTexture;
private FilterMode filter_min = FilterMode.NEAREST; private FilterMode filter_min = FilterMode.LINEAR;
private FilterMode filter_mag = FilterMode.NEAREST; private FilterMode filter_mag = FilterMode.NEAREST;
private WrapMode wrap = WrapMode.CLAMP; private WrapMode wrap = WrapMode.CLAMP;
@ -90,7 +90,6 @@ public class DeferredTexture extends BaseDeferredResource implements FilteredTex
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter_mag.num); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter_mag.num);
bindRaw(); bindRaw();
} }

@ -33,14 +33,19 @@ public class TextureBank extends AppAdapter {
/** /**
* Load a {@link Texture} from resource, with filters LINEAR and wrap CLAMP * Load a {@link Texture}
* *
* @param key texture key * @param key texture key
* @param resourcePath texture resource path * @param texture texture to load
*/ */
public void loadTexture(String key, String resourcePath) public void loadTexture(String key, DeferredTexture texture)
{ {
loadTexture(key, resourcePath, FilterMode.LINEAR, FilterMode.NEAREST, WrapMode.CLAMP); getEventBus().send(new ResourceLoadRequest(texture));
textures.put(key, texture);
lastTx = texture;
makeQuad(key, Rect.ONE);
} }
@ -55,16 +60,11 @@ public class TextureBank extends AppAdapter {
*/ */
public void loadTexture(String key, String resourcePath, FilterMode filter_min, FilterMode filter_mag, WrapMode wrap) public void loadTexture(String key, String resourcePath, FilterMode filter_min, FilterMode filter_mag, WrapMode wrap)
{ {
final DeferredTexture tx = new DeferredTexture(resourcePath); final DeferredTexture texture = new DeferredTexture(resourcePath);
tx.setFilter(filter_min, filter_mag); texture.setFilter(filter_min, filter_mag);
tx.setWrap(wrap); texture.setWrap(wrap);
getEventBus().send(new ResourceLoadRequest(tx));
textures.put(key, tx);
lastTx = tx;
makeQuad(key, Rect.one()); loadTexture(key, texture);
} }

@ -70,11 +70,11 @@ public class TxQuad {
/** /**
* @param tx Texture * @param tx Texture
* @param uvs Rect of texture UVs (0-1) * @param uvs Rect of texture UVs (0-1); will be stored as is.
*/ */
public TxQuad(Texture tx, Rect uvs) { public TxQuad(Texture tx, Rect uvs) {
this.tx = tx; this.tx = tx;
this.uvs = uvs.copy(); this.uvs = uvs.view();
} }
@ -85,7 +85,7 @@ public class TxQuad {
*/ */
public TxQuad(TxQuad txQuad) { public TxQuad(TxQuad txQuad) {
this.tx = txQuad.tx; this.tx = txQuad.tx;
this.uvs = txQuad.uvs.copy(); this.uvs = txQuad.uvs.view();
} }

@ -64,7 +64,7 @@ public class App extends BaseApp {
screens.add(new ScreenTestFont(this)); screens.add(new ScreenTestFont(this));
screens.add(new ScreenTestRender(this)); screens.add(new ScreenTestRender(this));
screens.showScreen("test.render"); screens.showScreen("test.cat");
} }

@ -6,10 +6,11 @@ import mightypork.gamecore.audio.players.EffectPlayer;
import mightypork.gamecore.audio.players.LoopPlayer; import mightypork.gamecore.audio.players.LoopPlayer;
import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.BaseApp; import mightypork.gamecore.control.BaseApp;
import mightypork.gamecore.render.fonts.DeferredFont;
import mightypork.gamecore.render.fonts.DeferredFont.FontStyle;
import mightypork.gamecore.render.fonts.FontBank; import mightypork.gamecore.render.fonts.FontBank;
import mightypork.gamecore.render.fonts.GLFont; import mightypork.gamecore.render.fonts.GLFont;
import mightypork.gamecore.render.fonts.Glyphs;
import mightypork.gamecore.render.fonts.impl.DeferredFont;
import mightypork.gamecore.render.textures.DeferredTexture;
import mightypork.gamecore.render.textures.FilterMode; import mightypork.gamecore.render.textures.FilterMode;
import mightypork.gamecore.render.textures.TextureBank; import mightypork.gamecore.render.textures.TextureBank;
import mightypork.gamecore.render.textures.TxQuad; import mightypork.gamecore.render.textures.TxQuad;
@ -54,23 +55,25 @@ public class Res {
private static void loadFonts() private static void loadFonts()
{ {
//@formatter:off DeferredFont font;
fonts.loadFont(
"default", font = new DeferredFont("/res/font/PolygonPixel5x7Standard.ttf", Glyphs.basic, 16);
new DeferredFont("/res/font/PolygonPixel5x7Standard.ttf", font.setAntialias(true);
null, font.setFilter(FilterMode.NEAREST);
16, fonts.loadFont("default", font);
FontStyle.PLAIN,
FilterMode.NEAREST
)
);
//@formatter:on
} }
private static void loadTextures() private static void loadTextures()
{ {
textures.loadTexture("test.kitten", "/res/img/kitten.png", FilterMode.LINEAR, FilterMode.NEAREST, WrapMode.CLAMP); DeferredTexture texture;
texture = new DeferredTexture("/res/img/kitten.png");
texture.setFilter(FilterMode.LINEAR);
texture.setWrap(WrapMode.CLAMP);
textures.loadTexture("test.kitten", texture);
} }

@ -2,7 +2,6 @@ package mightypork.rogue.screens;
import static mightypork.gamecore.gui.constraints.Constraints.*; import static mightypork.gamecore.gui.constraints.Constraints.*;
import static org.lwjgl.opengl.GL11.*;
import mightypork.gamecore.gui.components.painters.TextPainter; import mightypork.gamecore.gui.components.painters.TextPainter;
import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.gamecore.gui.screens.Screen; import mightypork.gamecore.gui.screens.Screen;
@ -11,6 +10,7 @@ import mightypork.gamecore.render.fonts.FontRenderer.Align;
import mightypork.gamecore.render.fonts.GLFont; import mightypork.gamecore.render.fonts.GLFont;
import mightypork.rogue.Res; import mightypork.rogue.Res;
import mightypork.utils.math.color.RGB; import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.string.StringProvider; import mightypork.utils.string.StringProvider;
@ -32,11 +32,13 @@ public class LayerFps extends ScreenLayer {
}; };
final GLFont font = Res.getFont("default"); final GLFont font = Res.getFont("default");
final RectConstraint constraint = _round(_move(_grow_down(_right_top(this), 32), -8, 8)); final RectConstraint constraint = _round(_move(_grow_down(_right_top(this), 32), -8, 8));
tp = new TextPainter(font, Align.RIGHT, RGB.WHITE, text); tp = new TextPainter(font, Align.RIGHT, RGB.WHITE, text);
tp.setContext(constraint); tp.setContext(constraint);
tp.setShadow(RGB.BLACK, Coord.at(1, 1));
} }

@ -9,7 +9,6 @@ import mightypork.gamecore.control.bus.events.MouseButtonEvent;
import mightypork.gamecore.control.timing.Updateable; import mightypork.gamecore.control.timing.Updateable;
import mightypork.gamecore.gui.components.painters.ImagePainter; import mightypork.gamecore.gui.components.painters.ImagePainter;
import mightypork.gamecore.gui.components.painters.TextPainter; import mightypork.gamecore.gui.components.painters.TextPainter;
import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.gamecore.gui.screens.Screen; import mightypork.gamecore.gui.screens.Screen;
import mightypork.gamecore.gui.screens.ScreenLayer; import mightypork.gamecore.gui.screens.ScreenLayer;
import mightypork.gamecore.input.KeyStroke; import mightypork.gamecore.input.KeyStroke;
@ -40,17 +39,20 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
xPos.setTo(DisplaySystem.getWidth() / 2); xPos.setTo(DisplaySystem.getWidth() / 2);
yPos.setTo(DisplaySystem.getHeight() / 2); yPos.setTo(DisplaySystem.getHeight() / 2);
final RectConstraint catbox = _centered(_box(size, size), xPos, yPos);
cat = new ImagePainter(Res.getTxQuad("test.kitten")); cat = new ImagePainter(Res.getTxQuad("test.kitten"));
cat.setContext(catbox); cat.setContext(_centered(_box(size, size), xPos, yPos));
final RectConstraint fpsbox = _centered(_box(64, 64), _mouseX, _mouseY); tp = new TextPainter(Res.getFont("default"));
tp.setAlign(Align.CENTER);
tp = new TextPainter(Res.getFont("default"), Align.CENTER, RGB.YELLOW, "Meow"); tp.setColor(RGB.YELLOW);
tp.setText("Meow!");
tp.setContext(fpsbox); tp.setShadow(RGB.dark(0.8), Coord.at(2, 2));
tp.setContext(_centered(_box(64, 64), _mouseX, _mouseY));
/*
* Register keys
*/
bindKeyStroke(new KeyStroke(Keys.KEY_RETURN), new Runnable() { bindKeyStroke(new KeyStroke(Keys.KEY_RETURN), new Runnable() {
@Override @Override
@ -69,6 +71,8 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
size.update(delta); size.update(delta);
xPos.update(delta); xPos.update(delta);
yPos.update(delta); yPos.update(delta);
System.out.println(cat.getRect());
} }
@ -81,8 +85,8 @@ public class LayerFlyingCat extends ScreenLayer implements Updateable, MouseButt
final double t = 2; final double t = 2;
size.fadeTo(100 + rand.nextInt(700), t / 2D); size.fadeTo(100 + rand.nextInt(700), t / 2D);
xPos.fadeTo(pos.x, t); xPos.fadeTo(pos.x(), t);
yPos.fadeTo(pos.y, t); yPos.fadeTo(pos.y(), t);
} }

@ -62,4 +62,11 @@ public class ScreenTestCat extends LayeredScreen {
return "test.cat"; return "test.cat";
} }
@Override
protected void renderScreen()
{
super.renderScreen();
}
} }

@ -62,7 +62,11 @@ public class Log {
*/ */
public static void w(String msg) public static void w(String msg)
{ {
if (staticLogging && ready()) main.w(msg); if (staticLogging && ready()) {
main.w(msg);
} else {
System.err.println(msg);
}
} }
@ -73,7 +77,11 @@ public class Log {
*/ */
public static void e(String msg) public static void e(String msg)
{ {
if (staticLogging && ready()) main.e(msg); if (staticLogging && ready()) {
main.e(msg);
} else {
System.err.println(msg);
}
} }
@ -85,7 +93,12 @@ public class Log {
*/ */
public static void e(String msg, Throwable thrown) public static void e(String msg, Throwable thrown)
{ {
if (staticLogging && ready()) main.e(msg, thrown); if (staticLogging && ready()) {
main.e(msg, thrown);
} else {
System.err.println(msg);
thrown.printStackTrace();
}
} }
@ -96,7 +109,11 @@ public class Log {
*/ */
public static void e(Throwable thrown) public static void e(Throwable thrown)
{ {
if (staticLogging && ready()) main.e(thrown); if (staticLogging && ready()) {
main.e(thrown);
} else {
thrown.printStackTrace();
}
} }

@ -34,37 +34,23 @@ public class Calc {
public static double linePointDist(Coord lineDirVec, Coord linePoint, Coord point) public static double linePointDist(Coord lineDirVec, Coord linePoint, Coord point)
{ {
// line point L[lx,ly] // line point L[lx,ly]
final double lx = linePoint.x; final double lx = linePoint.x();
final double ly = linePoint.y; final double ly = linePoint.y();
// line equation ax+by+c=0 // line equation ax+by+c=0
final double a = -lineDirVec.y; final double a = -lineDirVec.y();
final double b = lineDirVec.x; final double b = lineDirVec.x();
final double c = -a * lx - b * ly; final double c = -a * lx - b * ly;
// checked point P[x,y] // checked point P[x,y]
final double x = point.x; final double x = point.x();
final double y = point.y; final double y = point.y();
// distance // distance
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b); 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(Coord lineDirVec, Coord linePoint, Coord point)
{
return linePointDist(new Coord(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
}
/** /**
* Get longest side of a right-angled triangle * Get longest side of a right-angled triangle
* *

@ -106,7 +106,8 @@ public class Polar {
*/ */
public static Polar fromCoord(Coord coord) 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))); return Polar.fromCoord(coord.x(), coord.y());
} }
@ -119,7 +120,10 @@ public class Polar {
*/ */
public static Polar fromCoord(double x, double y) public static Polar fromCoord(double x, double y)
{ {
return Polar.fromCoord(new Coord(x, y)); final double a = Math.atan2(y, x);
final double r = Math.sqrt(x * x + y * y);
return new Polar(a, r);
} }

@ -380,4 +380,15 @@ public class RGB {
return Double.valueOf(r).hashCode() ^ Double.valueOf(g).hashCode() ^ Double.valueOf(b).hashCode() ^ Double.valueOf(a).hashCode(); return Double.valueOf(r).hashCode() ^ Double.valueOf(g).hashCode() ^ Double.valueOf(b).hashCode() ^ Double.valueOf(a).hashCode();
} }
public static RGB dark(double d)
{
return new RGB(0, 0, 0, d);
}
public static RGB light(double d)
{
return new RGB(1, 1, 1, d);
}
} }

@ -0,0 +1,42 @@
package mightypork.utils.math.coord;
import mightypork.gamecore.gui.constraints.NumberConstraint;
public class ConstraintCoordView extends CoordView {
private final NumberConstraint xc;
private final NumberConstraint yc;
private final NumberConstraint zc;
public ConstraintCoordView(NumberConstraint x, NumberConstraint y, NumberConstraint z) {
super(null);
this.xc = x;
this.yc = y;
this.zc = z;
}
@Override
public double x()
{
return xc == null ? 0 : xc.getValue();
}
@Override
public double y()
{
return yc == null ? 0 : yc.getValue();
}
@Override
public double z()
{
return zc == null ? 0 : zc.getValue();
}
}

@ -3,6 +3,7 @@ package mightypork.utils.math.coord;
import java.util.Random; import java.util.Random;
import mightypork.gamecore.gui.constraints.NumberConstraint;
import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.utils.math.Calc; import mightypork.utils.math.Calc;
@ -12,10 +13,13 @@ import mightypork.utils.math.Calc;
* *
* @author MightyPork * @author MightyPork
*/ */
public class Coord { public class Coord implements CoordValue {
protected static Random rand = new Random(); protected static Random rand = new Random();
public static final Coord ONE = new Coord(1).freeze();
public static final Coord ZERO = new Coord(0).freeze();
/** /**
* Get distance to other point * Get distance to other point
@ -30,13 +34,31 @@ public class Coord {
} }
/** X coordinate */ /** X coordinate */
public double x = 0; private double x = 0;
/** Y coordinate */ /** Y coordinate */
public double y = 0; private double y = 0;
/** Z coordinate */ /** Z coordinate */
public double z = 0; private double z = 0;
private Coord view = null;
private boolean frozen = false;
private NumberConstraint xc, yc, zc;
public static Coord at(double x, double y)
{
return new Coord(x, y);
}
public static Coord at(double x, double y, double z)
{
return new Coord(x, y, z);
}
/** /**
@ -61,7 +83,7 @@ public class Coord {
* *
* @param d coord value * @param d coord value
*/ */
public Coord(Number d) { public Coord(double d) {
setTo(d, d, d); setTo(d, d, d);
} }
@ -72,7 +94,7 @@ public class Coord {
* @param x x coordinate * @param x x coordinate
* @param y y coordinate * @param y y coordinate
*/ */
public Coord(Number x, Number y) { public Coord(double x, double y) {
setTo(x, y); setTo(x, y);
} }
@ -84,11 +106,63 @@ public class Coord {
* @param y y coordinate * @param y y coordinate
* @param z z coordinate * @param z z coordinate
*/ */
public Coord(Number x, Number y, Number z) { public Coord(double x, double y, double z) {
setTo(x, y, z); setTo(x, y, z);
} }
/**
* Freeze current coordinate values.
*
* @return this
*/
public Coord freeze()
{
frozen = true;
return this;
}
/**
* Get view at this coord
*
* @return the view
*/
public Coord view()
{
// cache last used view
if (view == null) view = new CoordView(this);
return view;
}
/**
* @return true if this coord is writable
*/
public boolean isWritable()
{
return !frozen && !isView();
}
/**
* @return true if this coord is a view at another
*/
public boolean isView()
{
return false;
}
protected void assertWritable()
{
if (!isWritable()) {
throw new UnsupportedOperationException("This Coord is not writable.");
}
}
/** /**
* Add a vector, in a copy * Add a vector, in a copy
* *
@ -109,7 +183,7 @@ public class Coord {
*/ */
public Coord add_ip(Coord vec) public Coord add_ip(Coord vec)
{ {
return add_ip(vec.x, vec.y, vec.z); return add_ip(vec.x(), vec.y(), vec.z());
} }
@ -121,7 +195,7 @@ public class Coord {
* @param y y offset * @param y y offset
* @return changed copy * @return changed copy
*/ */
public Coord add(Number x, Number y) public Coord add(double x, double y)
{ {
return copy().add_ip(x, y); return copy().add_ip(x, y);
} }
@ -135,7 +209,7 @@ public class Coord {
* @param y y offset * @param y y offset
* @return this * @return this
*/ */
public Coord add_ip(Number x, Number y) public Coord add_ip(double x, double y)
{ {
return add_ip(x, y, 0); return add_ip(x, y, 0);
} }
@ -149,7 +223,7 @@ public class Coord {
* @param z z offset * @param z z offset
* @return changed copy * @return changed copy
*/ */
public Coord add(Number x, Number y, Number z) public Coord add(double x, double y, double z)
{ {
return copy().add_ip(x, y, z); return copy().add_ip(x, y, z);
} }
@ -163,11 +237,12 @@ public class Coord {
* @param z z offset * @param z z offset
* @return this * @return this
*/ */
public Coord add_ip(Number x, Number y, Number z) public Coord add_ip(double x, double y, double z)
{ {
this.x += x.doubleValue(); assertWritable();
this.y += y.doubleValue(); this.x += x;
this.z += z.doubleValue(); this.y += y;
this.z += z;
return this; return this;
} }
@ -179,7 +254,7 @@ public class Coord {
*/ */
public Coord copy() public Coord copy()
{ {
return new Coord(x, y, z); return new Coord(this);
} }
@ -191,7 +266,11 @@ public class Coord {
*/ */
public double distTo(Coord point) 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)); final double dx = (point.x() - this.x());
final double dy = (point.y() - this.y());
final double dz = (point.z() - this.z());
return Math.sqrt(dx * dx + dy * dy + dz * dz);
} }
@ -217,7 +296,11 @@ public class Coord {
*/ */
public boolean isInRect(Coord min, Coord max) 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); if (!Calc.inRange(x(), min.x(), max.x())) return false;
if (!Calc.inRange(y(), min.y(), max.y())) return false;
if (!Calc.inRange(z(), min.z(), max.z())) return false;
return true;
} }
@ -268,18 +351,6 @@ public class Coord {
} }
/**
* Multiply each component, in place.
*
* @param d multiplier
* @return this
*/
public Coord mul_ip(double d)
{
return mul_ip(d, d, d);
}
/** /**
* Multiply each component, in a copy. * Multiply each component, in a copy.
* *
@ -300,23 +371,21 @@ public class Coord {
* @param y y multiplier * @param y y multiplier
* @return changed copy * @return changed copy
*/ */
public Coord mul(double x, int y) public Coord mul(double x, double y)
{ {
return copy().mul_ip(x, y); return copy().mul_ip(x, y);
} }
/** /**
* Multiply each component, in place.<br> * Multiply each component, in place.
* Z is unchanged.
* *
* @param x x multiplier * @param d multiplier
* @param y y multiplier
* @return this * @return this
*/ */
public Coord mul_ip(double x, double y) public Coord mul_ip(double d)
{ {
return mul_ip(x, y, 1); return mul_ip(d, d, d);
} }
@ -334,6 +403,20 @@ public class Coord {
} }
/**
* Multiply each component, in place.<br>
* Z is unchanged.
*
* @param x x multiplier
* @param y y multiplier
* @return this
*/
public Coord mul_ip(double x, double y)
{
return mul_ip(x, y, 1);
}
/** /**
* Multiply each component, in place. * Multiply each component, in place.
* *
@ -342,7 +425,7 @@ public class Coord {
*/ */
public Coord mul_ip(Coord vec) public Coord mul_ip(Coord vec)
{ {
return mul_ip(vec.x, vec.y, vec.z); return mul_ip(vec.x(), vec.y(), vec.z());
} }
@ -356,6 +439,7 @@ public class Coord {
*/ */
public Coord mul_ip(double x, double y, double z) public Coord mul_ip(double x, double y, double z)
{ {
assertWritable();
this.x *= x; this.x *= x;
this.y *= y; this.y *= y;
this.z *= z; this.z *= z;
@ -370,27 +454,27 @@ public class Coord {
/** /**
* offset randomly in place * offset randomly
* *
* @param max max +- offset * @param min min offset
* @return this * @param max max offset
* @return offset coord
*/ */
public Coord random_offset_ip(double max) public Coord random_offset(double min, double max)
{ {
return add(random(1).norm_ip(rand.nextDouble() * max)); return copy().random_offset_ip(min, max);
} }
/** /**
* offset randomly * offset randomly in place
* *
* @param min min offset * @param max max +- offset
* @param max max offset * @return this
* @return offset coord
*/ */
public Coord random_offset(double min, double max) public Coord random_offset_ip(double max)
{ {
return copy().random_offset_ip(min, max); return add_ip(random(1).norm_ip(rand.nextDouble() * max));
} }
@ -426,6 +510,7 @@ public class Coord {
*/ */
public Coord round_ip() public Coord round_ip()
{ {
assertWritable();
x = Math.round(x); x = Math.round(x);
y = Math.round(y); y = Math.round(y);
z = Math.round(z); z = Math.round(z);
@ -440,9 +525,10 @@ public class Coord {
*/ */
public void setToMax(Coord other) public void setToMax(Coord other)
{ {
x = Math.max(x, other.x); assertWritable();
y = Math.max(y, other.y); x = Math.max(x, other.x());
z = Math.max(z, other.z); y = Math.max(y, other.y());
z = Math.max(z, other.z());
} }
@ -453,9 +539,10 @@ public class Coord {
*/ */
public void setToMin(Coord other) public void setToMin(Coord other)
{ {
x = Math.min(x, other.x); assertWritable();
y = Math.min(y, other.y); x = Math.min(x, other.x());
z = Math.min(z, other.z); y = Math.min(y, other.y());
z = Math.min(z, other.z());
} }
@ -467,7 +554,7 @@ public class Coord {
*/ */
public Coord setTo(Coord copied) public Coord setTo(Coord copied)
{ {
return setTo(copied.x, copied.y, copied.z); return setTo(copied.x(), copied.y(), copied.z());
} }
@ -478,7 +565,7 @@ public class Coord {
* @param y y coordinate * @param y y coordinate
* @return this * @return this
*/ */
public Coord setTo(Number x, Number y) public Coord setTo(double x, double y)
{ {
return setTo(x, y, 0); return setTo(x, y, 0);
} }
@ -492,11 +579,12 @@ public class Coord {
* @param z z coordinate * @param z z coordinate
* @return this * @return this
*/ */
public Coord setTo(Number x, Number y, Number z) public Coord setTo(double x, double y, double z)
{ {
this.x = x.doubleValue(); assertWritable();
this.y = y.doubleValue(); this.x = x;
this.z = z.doubleValue(); this.y = y;
this.z = z;
return this; return this;
} }
@ -507,59 +595,61 @@ public class Coord {
* @param x x coordinate * @param x x coordinate
* @return copy with set coordinate * @return copy with set coordinate
*/ */
public Coord setX(Number x) public Coord setX(double x)
{ {
return copy().setX_ip(x); return copy().setX_ip(x);
} }
/** /**
* Set X coordinate in place * Set Y coordinate in a copy
* *
* @param x x coordinate * @param y y coordinate
* @return this * @return copy with set coordinate
*/ */
public Coord setX_ip(Number x) public Coord setY(double y)
{ {
this.x = x.doubleValue(); return copy().setY_ip(y);
return this;
} }
/** /**
* Set Y coordinate in a copy * Set Z coordinate in a copy
* *
* @param y y coordinate * @param z z coordinate
* @return copy with set coordinate * @return copy with set coordinate
*/ */
public Coord setY(Number y) public Coord setZ(double z)
{ {
return copy().setY_ip(y); return copy().setZ_ip(z);
} }
/** /**
* Set Y coordinate in place * Set X coordinate in place
* *
* @param y y coordinate * @param x x coordinate
* @return this * @return this
*/ */
public Coord setY_ip(Number y) public Coord setX_ip(double x)
{ {
this.y = y.doubleValue(); assertWritable();
this.x = x;
return this; return this;
} }
/** /**
* Set Z coordinate in a copy * Set Y coordinate in place
* *
* @param z z coordinate * @param y y coordinate
* @return copy with set coordinate * @return this
*/ */
public Coord setZ(Number z) public Coord setY_ip(double y)
{ {
return copy().setZ_ip(z); assertWritable();
this.y = y;
return this;
} }
@ -569,9 +659,10 @@ public class Coord {
* @param z z coordinate * @param z z coordinate
* @return this * @return this
*/ */
public Coord setZ_ip(Number z) public Coord setZ_ip(double z)
{ {
this.z = z.doubleValue(); assertWritable();
this.z = z;
return this; return this;
} }
@ -595,7 +686,7 @@ public class Coord {
* @param y y offset * @param y y offset
* @return the offset copy * @return the offset copy
*/ */
public Coord sub(Number x, Number y) public Coord sub(double x, double y)
{ {
return copy().sub_ip(x, y); return copy().sub_ip(x, y);
} }
@ -609,7 +700,7 @@ public class Coord {
* @param z z offset * @param z z offset
* @return the offset copy * @return the offset copy
*/ */
public Coord sub(Number x, Number y, Number z) public Coord sub(double x, double y, double z)
{ {
return copy().sub_ip(x, y, z); return copy().sub_ip(x, y, z);
} }
@ -623,7 +714,7 @@ public class Coord {
*/ */
public Coord sub_ip(Coord vec) public Coord sub_ip(Coord vec)
{ {
return sub_ip(vec.x, vec.y, vec.z); return sub_ip(vec.x(), vec.y(), vec.z());
} }
@ -634,7 +725,7 @@ public class Coord {
* @param y y offset * @param y y offset
* @return this * @return this
*/ */
public Coord sub_ip(Number x, Number y) public Coord sub_ip(double x, double y)
{ {
return sub_ip(x, y, 0); return sub_ip(x, y, 0);
} }
@ -648,11 +739,12 @@ public class Coord {
* @param z z offset * @param z z offset
* @return this * @return this
*/ */
public Coord sub_ip(Number x, Number y, Number z) public Coord sub_ip(double x, double y, double z)
{ {
this.x -= x.doubleValue(); assertWritable();
this.y -= y.doubleValue(); this.x -= x;
this.z -= z.doubleValue(); this.y -= y;
this.z -= z;
return this; return this;
} }
@ -672,6 +764,7 @@ public class Coord {
/** /**
* @return X as double * @return X as double
*/ */
@Override
public double x() public double x()
{ {
return x; return x;
@ -679,74 +772,127 @@ public class Coord {
/** /**
* @return X as float * @return Y as double
*/ */
public float xf() @Override
public double y()
{ {
return (float) x; return y;
} }
/** /**
* @return X as int * @return Z as double
*/ */
public int xi() @Override
public double z()
{ {
return (int) Math.round(x); return z;
} }
/** /**
* @return Y as double * @return X as constraint view
*/ */
public double y() @Override
public final NumberConstraint xc()
{ {
return y; return xc != null ? xc : new NumberConstraint() {
@Override
public double getValue()
{
return x();
}
};
} }
/** /**
* @return Y as float * @return Y as constraint view
*/ */
public float yf() @Override
public final NumberConstraint yc()
{ {
return (float) y; return yc != null ? yc : new NumberConstraint() {
@Override
public double getValue()
{
return y();
}
};
} }
/** /**
* @return Y as int * @return Z as constraint view
*/ */
public int yi() @Override
public final NumberConstraint zc()
{ {
return (int) Math.round(y); return zc != null ? zc : new NumberConstraint() {
@Override
public double getValue()
{
return z();
}
};
} }
/** /**
* @return Z as double * @return X as float
*/ */
public double z() public final float xf()
{ {
return z; return (float) x();
}
/**
* @return Y as float
*/
public final float yf()
{
return (float) y();
} }
/** /**
* @return Z as float * @return Z as float
*/ */
public float zf() public final float zf()
{
return (float) z();
}
/**
* @return X as int
*/
public final int xi()
{
return (int) Math.round(x());
}
/**
* @return Y as int
*/
public final int yi()
{ {
return (float) z; return (int) Math.round(y());
} }
/** /**
* @return Z as int * @return Z as int
*/ */
public int zi() public final int zi()
{ {
return (int) Math.round(z); return (int) Math.round(z());
} }
@ -798,9 +944,9 @@ public class Coord {
{ {
//@formatter:off //@formatter:off
setTo( setTo(
y * vec.z - z * vec.y, y * vec.z() - z * vec.y(),
z * vec.x - x * vec.z, z * vec.x() - x * vec.z(),
x * vec.y - y * vec.x x * vec.y() - y * vec.x()
); );
//@formatter:on //@formatter:on
return this; return this;
@ -815,7 +961,7 @@ public class Coord {
*/ */
public double dot(Coord vec) public double dot(Coord vec)
{ {
return x * vec.x + y * vec.y + z * vec.z; return x() * vec.x() + y() * vec.y() + z() * vec.z();
} }
@ -879,7 +1025,7 @@ public class Coord {
{ {
if (isZero()) return 0; if (isZero()) return 0;
return Math.sqrt(x * x + y * y + z * z); return Math.sqrt(x() * x() + y() * y() + z() * z());
} }
@ -888,14 +1034,14 @@ public class Coord {
*/ */
public boolean isZero() public boolean isZero()
{ {
return x == 0 && y == 0 && z == 0; return x() == 0 && y() == 0 && z() == 0;
} }
@Override @Override
public String toString() public String toString()
{ {
return "[" + x + ", " + y + ", " + z + "]"; return String.format("( %.2f, %.2f, %.2f )", x(), y(), z());
} }
@ -905,11 +1051,11 @@ public class Coord {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
long temp; long temp;
temp = Double.doubleToLongBits(x); temp = Double.doubleToLongBits(x());
result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y); temp = Double.doubleToLongBits(y());
result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(z); temp = Double.doubleToLongBits(z());
result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + (int) (temp ^ (temp >>> 32));
return result; return result;
} }
@ -922,9 +1068,9 @@ public class Coord {
if (obj == null) return false; if (obj == null) return false;
if (!(obj instanceof Coord)) return false; if (!(obj instanceof Coord)) return false;
final Coord other = (Coord) obj; final Coord other = (Coord) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) return false; if (Double.doubleToLongBits(x()) != Double.doubleToLongBits(other.x())) return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) return false; if (Double.doubleToLongBits(y()) != Double.doubleToLongBits(other.y())) return false;
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) return false; if (Double.doubleToLongBits(z()) != Double.doubleToLongBits(other.z())) return false;
return true; return true;
} }
@ -936,7 +1082,7 @@ public class Coord {
*/ */
public static Coord zero() public static Coord zero()
{ {
return new Coord(0); return ZERO.copy();
} }
@ -947,7 +1093,7 @@ public class Coord {
*/ */
public static Coord one() public static Coord one()
{ {
return new Coord(1); return ONE.copy();
} }

@ -0,0 +1,31 @@
package mightypork.utils.math.coord;
import mightypork.gamecore.gui.constraints.NumberConstraint;
/**
* Coord values provider
*
* @author MightyPork
*/
public interface CoordValue {
double x();
double y();
double z();
NumberConstraint xc();
NumberConstraint zc();
NumberConstraint yc();
}

@ -0,0 +1,62 @@
package mightypork.utils.math.coord;
public class CoordView extends Coord {
private CoordValue observed = null;
public CoordView(CoordValue coord) {
observed = coord;
}
@Override
public boolean isView()
{
return true;
}
@Override
public boolean isWritable()
{
return false;
}
@Override
public Coord view()
{
return this;
}
@Override
public Coord freeze()
{
return this; // no effect
}
@Override
public double x()
{
return observed.x();
}
@Override
public double y()
{
return observed.y();
}
@Override
public double z()
{
return observed.z();
}
}

@ -1,6 +1,7 @@
package mightypork.utils.math.coord; package mightypork.utils.math.coord;
import mightypork.gamecore.gui.constraints.NumberConstraint;
import mightypork.gamecore.gui.constraints.RectConstraint; import mightypork.gamecore.gui.constraints.RectConstraint;
import mightypork.utils.math.Calc; import mightypork.utils.math.Calc;
@ -12,8 +13,12 @@ import mightypork.utils.math.Calc;
*/ */
public class Rect implements RectConstraint { public class Rect implements RectConstraint {
public static final Rect ZERO = new Rect(0, 0, 0, 0).freeze();
public static final Rect ONE = new Rect(0, 0, 1, 1).freeze();
/** /**
* Rectangle from size * Rectangle from size; coords are copied.
* *
* @param min min coord * @param min min coord
* @param size rect size * @param size rect size
@ -26,7 +31,7 @@ public class Rect implements RectConstraint {
/** /**
* Make rect from min coord and size * Make rect from min coord and size; coords are copied.
* *
* @param min min coord * @param min min coord
* @param width size x * @param width size x
@ -35,7 +40,7 @@ public class Rect implements RectConstraint {
*/ */
public static Rect fromSize(Coord min, double width, double height) public static Rect fromSize(Coord min, double width, double height)
{ {
return new Rect(min, min.add(width, height)); return new Rect(min.copy(), min.add(width, height));
} }
@ -60,7 +65,7 @@ public class Rect implements RectConstraint {
*/ */
public static Rect fromSize(Coord size) public static Rect fromSize(Coord size)
{ {
return fromSize(0, 0, size.x, size.y); return fromSize(0, 0, size.x(), size.y());
} }
@ -79,10 +84,92 @@ public class Rect implements RectConstraint {
} }
/** Lowest coordinates xy */ /** Lowest coordinates xy */
private final Coord min = new Coord(); protected final Coord min;
/** Highest coordinates xy */ /** Highest coordinates xy */
private final Coord max = new Coord(); protected final Coord max;
// view of secondary corners.
//@formatter:off
private final ConstraintCoordView HMinVMax = new ConstraintCoordView(
new NumberConstraint() {
@Override
public double getValue()
{
return min.x();
}
},
new NumberConstraint() {
@Override
public double getValue()
{
return max.y();
}
},
null
);
private final ConstraintCoordView HMaxVMin = new ConstraintCoordView(
new NumberConstraint() {
@Override
public double getValue() {
return max.x();
}
},
new NumberConstraint() {
@Override
public double getValue()
{
return min.y();
}
},
null
);
//@formatter:on
private boolean frozen;
public boolean isWritable()
{
return !frozen && !isView();
}
public boolean isView()
{
return false;
}
public Rect freeze()
{
min.freeze();
max.freeze();
frozen = true;
return this;
}
/**
* Get a readonly view
*
* @return view
*/
public RectView view()
{
return new RectView(this);
}
protected void assertWritable()
{
if (!isWritable()) {
throw new UnsupportedOperationException("This Rect is not writable.");
}
}
/** /**
@ -99,18 +186,20 @@ public class Rect implements RectConstraint {
* @param size size coord * @param size size coord
*/ */
public Rect(Coord size) { public Rect(Coord size) {
this(0, 0, size.x, size.y); this(0, 0, size.x(), size.y());
} }
/** /**
* New rect of two coords * New rect of two coords; Coords are plugged in directly (ie. views can be
* used for frozen rect representing another)
* *
* @param c1 coord 1 * @param min coord 1
* @param c2 coord 2 * @param max coord 2
*/ */
public Rect(Coord c1, Coord c2) { public Rect(Coord min, Coord max) {
this(c1.x, c1.y, c2.x, c2.y); this.min = min; // must not copy
this.max = max; // must not copy
} }
@ -123,7 +212,8 @@ public class Rect implements RectConstraint {
* @param ymax upper y * @param ymax upper y
*/ */
public Rect(double xmin, double ymin, double xmax, double ymax) { public Rect(double xmin, double ymin, double xmax, double ymax) {
setTo(xmin, ymin, xmax, ymax); min = new Coord(xmin, ymin);
max = new Coord(xmax, ymax);
} }
@ -144,7 +234,7 @@ public class Rect implements RectConstraint {
* @param r other rect * @param r other rect
*/ */
public Rect(Rect r) { public Rect(Rect r) {
this(r.min.x, r.min.y, r.max.x, r.max.y); this(r.min.copy(), r.max.copy());
} }
@ -218,7 +308,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getCenter() public Coord getCenter()
{ {
return min.midTo(max); return min.midTo(max).freeze();
} }
@ -229,7 +319,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getCenterVMin() public Coord getCenterVMin()
{ {
return new Coord((max.x + min.x) / 2D, min.y); return new Coord((max.x() + min.x()) / 2D, min.y()).freeze();
} }
@ -240,7 +330,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getCenterHMin() public Coord getCenterHMin()
{ {
return new Coord(min.x, (max.y + min.y) / 2D); return new Coord(min.x(), (max.y() + min.y()) / 2D).freeze();
} }
@ -251,7 +341,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getCenterHMax() public Coord getCenterHMax()
{ {
return new Coord(max.x, (max.y + min.y) / 2D); return new Coord(max.x(), (max.y() + min.y()) / 2D).freeze();
} }
@ -262,7 +352,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getCenterVMax() public Coord getCenterVMax()
{ {
return new Coord((max.x + min.x) / 2D, max.y); return new Coord((max.x() + min.x()) / 2D, max.y()).freeze();
} }
@ -273,7 +363,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getHMinVMin() public Coord getHMinVMin()
{ {
return new Coord(min.x, min.y); return min.view();
} }
@ -284,62 +374,62 @@ public class Rect implements RectConstraint {
*/ */
public Coord getHMinVMax() public Coord getHMinVMax()
{ {
return new Coord(min.x, max.y); return HMinVMax;
} }
/** /**
* Alias for getX2Y2 * Get right bottom
* *
* @return highest coordinates xy * @return center
*/ */
public Coord getMax() public Coord getHMaxVMin()
{ {
return getHMaxVMax(); return HMaxVMin;
} }
/** /**
* Alias for getX1Y1 * Get right top
* *
* @return lowest coordinates xy * @return center
*/ */
public Coord getMin() public Coord getHMaxVMax()
{ {
return getHMinVMin(); return max.view();
} }
/** /**
* Alias for getX1Y1 * Alias for getX2Y2
* *
* @return lowest coordinates xy * @return highest coordinates xy
*/ */
public Coord getOrigin() public Coord getMax()
{ {
return getMin(); return getHMaxVMax();
} }
/** /**
* Get right bottom * Alias for getX1Y1
* *
* @return center * @return lowest coordinates xy
*/ */
public Coord getHMaxVMin() public Coord getMin()
{ {
return new Coord(max.x, min.y); return getHMinVMin();
} }
/** /**
* Get right top * Alias for getX1Y1
* *
* @return center * @return lowest coordinates xy
*/ */
public Coord getHMaxVMax() public Coord getOrigin()
{ {
return new Coord(max.x, max.y); return getHMinVMin();
} }
@ -376,7 +466,7 @@ public class Rect implements RectConstraint {
*/ */
public Rect shrink_ip(Coord shrink) public Rect shrink_ip(Coord shrink)
{ {
shrink_ip(shrink.x, shrink.y); shrink_ip(shrink.x(), shrink.y());
return this; return this;
} }
@ -461,7 +551,7 @@ public class Rect implements RectConstraint {
*/ */
public Rect grow_ip(Coord grow) public Rect grow_ip(Coord grow)
{ {
grow_ip(grow.x, grow.y); grow_ip(grow.x(), grow.y());
return this; return this;
} }
@ -521,7 +611,7 @@ public class Rect implements RectConstraint {
*/ */
public boolean isInside(Coord point) public boolean isInside(Coord point)
{ {
return Calc.inRange(point.x, min.x, max.x) && Calc.inRange(point.y, min.y, max.y); return Calc.inRange(point.x(), min.x(), max.x()) && Calc.inRange(point.y(), min.y(), max.y());
} }
@ -603,17 +693,6 @@ public class Rect implements RectConstraint {
} }
/**
* 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 * Set to coordinates
* *
@ -624,10 +703,8 @@ public class Rect implements RectConstraint {
*/ */
public void setTo(double xmin, double ymin, double xmax, double ymax) public void setTo(double xmin, double ymin, double xmax, double ymax)
{ {
min.x = Calc.min(xmin, xmax); min.setTo(Math.min(xmin, xmax), Math.min(ymin, ymax));
min.y = Calc.min(ymin, ymax); max.setTo(Math.max(xmin, xmax), Math.max(ymin, ymax));
max.x = Calc.max(xmin, xmax);
max.y = Calc.max(ymin, ymax);
} }
@ -695,28 +772,10 @@ public class Rect implements RectConstraint {
} }
/**
* @return rect [0,0-1,1]
*/
public static Rect one()
{
return new Rect(0, 0, 1, 1);
}
/**
* @return rect [0,0-0,0]
*/
public static Rect zero()
{
return new Rect(0, 0, 0, 0);
}
@Override @Override
public String toString() public String toString()
{ {
return String.format("[( %4.1f; %4.1f )-( %4.1f; %4.1f )]", min.x, min.y, max.x, max.y); return String.format("[%s-%s]", min.toString(), max.toString());
} }
@ -725,7 +784,7 @@ public class Rect implements RectConstraint {
*/ */
public double xMin() public double xMin()
{ {
return min.x; return min.x();
} }
@ -734,7 +793,7 @@ public class Rect implements RectConstraint {
*/ */
public double xMax() public double xMax()
{ {
return max.x; return max.x();
} }
@ -743,7 +802,7 @@ public class Rect implements RectConstraint {
*/ */
public double yMin() public double yMin()
{ {
return min.y; return min.y();
} }
@ -752,19 +811,19 @@ public class Rect implements RectConstraint {
*/ */
public double yMax() public double yMax()
{ {
return max.y; return max.y();
} }
public double getHeight() public double getHeight()
{ {
return max.y - min.y; return max.y() - min.y();
} }
public double getWidth() public double getWidth()
{ {
return max.x - min.x; return max.x() - min.x();
} }
@ -775,7 +834,7 @@ public class Rect implements RectConstraint {
*/ */
public Coord getSize() public Coord getSize()
{ {
return new Coord(max.x - min.x, max.y - min.y); return new Coord(max.x() - min.x(), max.y() - min.y());
} }
@ -784,4 +843,26 @@ public class Rect implements RectConstraint {
{ {
return this; return this;
} }
/**
* Generate zero rect
*
* @return zero
*/
public static Rect zero()
{
return ZERO.copy();
}
/**
* Generate 0,0-1,1 rect
*
* @return one
*/
public static Rect one()
{
return ZERO.copy();
}
} }

@ -0,0 +1,37 @@
package mightypork.utils.math.coord;
/**
* Read only rect
*
* @author MightyPork
*/
public class RectView extends Rect {
public RectView(Rect observed) {
super(observed.min.view(), observed.max.view());
}
@Override
public boolean isWritable()
{
return false;
}
@Override
public boolean isView()
{
return true;
}
@Override
public Rect freeze()
{
// do nothing
return this;
}
}

@ -126,7 +126,7 @@ public class Convert {
if (o instanceof Coord) { if (o instanceof Coord) {
final Coord c = (Coord) o; final Coord c = (Coord) o;
return String.format("[%f:%f:%f]", c.x, c.y, c.z); return String.format("[%f:%f:%f]", c.x(), c.y(), c.z());
} }
if (o instanceof Range) { if (o instanceof Range) {

Loading…
Cancel
Save