diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..aa3f1da --- /dev/null +++ b/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e660fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/ diff --git a/.project b/.project new file mode 100644 index 0000000..715de0b --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + axengine + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..8227c85 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,171 @@ +# Design 2.0 + +## Concepts + +- Node = a container or entity +- Scenes = contains a root node to draw & update +- Camera = a perspective in a scene +- View = a rectangular section on a window to render a scene from a camera's perspective +- Window = a place where one or more views are drawn +- Renderer = handles drawing a node +- Game = one or more windows + +## Generic Types + +- V = Vector +- A = Attribute +- T = any generic type +- E = enum +- I = input +- L = listener +- C = camera +- S = scene + +## Structure + +- Game + - Scenes + - Windows + - Views + - Camera + - Scene + +## Design + +- Game + - loop (the logic which defines how often updates & renders are performed) + - state (time data since last update, updated by loop) + - listeners (start, draw start, draw end, update start, update end, stop) + - settings (ie. opengl settings) + - input (key, mouse, controllers) + - scenes + - windows + - `start()` + - `stop()` +- Scene + - root node + - `draw(state, camera, view)` + - `update(state, mouse, key)` +- Window + - listeners (resize, focus, blur, close request, draw start, draw end) + - state (active, hidden, request close) + - state methods (stop, hide, show, minimize, maximize, focus) + - title + - fullscreen + - position + - dimension (pixel vs framebuffer) + - resizable + - vsync + - frame rate + - resolution (framebuffer dimensions / pixel) + +## Implementation + +``` +start_game() { + trigger game start events + initialize loop algorithm + initialize windows + while (playing) { + handle window events (request close, hiding) + if all windows are closed, no longer playing + if all windows hidden, pause X millis and continue + handle window events (blur, focus) + process input events + update input systems + run loop algorithm and determine how many updates are needed and if a draw is needed + for each update needed { + trigger game update start event + update scenes (state, input) + trigger game update end event + } + if updates needed { + clear input systems + } + if draw needed { + trigger game draw start event + for each window { + clear window buffer + trigger window draw start event + for each view { + draw scene on view with camera's perspective + } + trigger window draw end event + render window buffer + } + trigger game draw end event + } + } + trigger game stop event + destroy input + destroy windows + destroy game +} +``` + +## Example + +```java +AxeLWJGL.init(); + +GameSettings gameSettings = new GameSettingsLWJGL(2, 1); +Game game = Axe.game.create( gameSettings ); + +Scene3 scene3 = new Scene3(); +// populate scene3 +game.addScene( scene3 ); + +Scene scene2 = new Scene2(); +// populate scene2 +game.addScene( scene2 ); + +WindowSettings windowSettings = new WindowSettings(640, 480); +Window window = game.newWindow( windowSettings ); + +Camera2 camera2 = new Camera2(); +View view2 = window.newView(scene2, camera2); +view2.maximize(); + +Camera3 camera3 = new Camera3(); +View view3 = window.newView(scene3, camera3); +view3.maximize(); + +``` + +### Ideas + +- You can have objects which draw more frequently then they update - send interpolation data to draw method +- Handling Input + - Every frame - accumulate input into input state. + - Input is applied at a frequency (every update, or at X FPS) + - Apply the input to the state then clear the input state +- InputState modifies next EntityState which affects DrawState + + +### Todo + +- Fix mouse projection +- Finish calculator +- Modify Attribute by adding getCalculator and all default methods +X Implement default methods in Texture +X Implement TextureLWJGL +- Copy over Audio classes (implement AudioLWJGL) +- Copy over Font classes (implement FontLWJGL) +X Create Shader interface +X Create VertexBuffer interface +X Create ShaderLWJGL +X Create VertexBufferLWJGL Axe.graphics.newVertexBuffer(format, size) + - formats will have an ID so vertex buffers can be reused +- Add Version class +- Files +- Net (http) +- Application (os, version, preferences, clipboard, + + +- 2d hash scales = 1, 46340 +- 3d hash scales = 1, 1290, 1664100 +- 4d hash scales = 1, 215, 46225, 9938375 +- 5d hash scales = 1, 73, 5404, 397336, 29210829 +- 6d hash scales = 1, 36, 1296, 46656, 1679616, 60466176 +- 8d hash scales = 1, 14, 196, 2744, 38416, 537824, 7529536, 105413504 +- 9d hash scales = 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 \ No newline at end of file diff --git a/libs/azzet-1.0-src.jar b/libs/azzet-1.0-src.jar new file mode 100644 index 0000000..cc5370f Binary files /dev/null and b/libs/azzet-1.0-src.jar differ diff --git a/libs/reflector-1.0-src.jar b/libs/reflector-1.0-src.jar new file mode 100644 index 0000000..73e0268 Binary files /dev/null and b/libs/reflector-1.0-src.jar differ diff --git a/src/com/axe/AbstractCamera.java b/src/com/axe/AbstractCamera.java new file mode 100644 index 0000000..a2d41f8 --- /dev/null +++ b/src/com/axe/AbstractCamera.java @@ -0,0 +1,23 @@ +package com.axe; + +import com.axe.core.HasData; +import com.axe.math.Vec; + +public abstract class AbstractCamera> implements Camera, HasData +{ + + public Object data; + + @Override + public T getData() + { + return (T) data; + } + + @Override + public void setData(Object data) + { + this.data = data; + } + +} \ No newline at end of file diff --git a/src/com/axe/AbstractScene.java b/src/com/axe/AbstractScene.java new file mode 100644 index 0000000..ed8b8b5 --- /dev/null +++ b/src/com/axe/AbstractScene.java @@ -0,0 +1,57 @@ +package com.axe; + +import com.axe.collect.ListItem; +import com.axe.math.Vec; +import com.axe.node.Node; + +public class AbstractScene> implements Scene +{ + + public ListItem listItem; + public Object data; + public Node root; + public View view; + + @Override + public void setListItem(ListItem listItem) + { + this.listItem = listItem; + } + + @Override + public T getData() + { + return (T) data; + } + + @Override + public void setData(Object data) + { + this.data = data; + } + + @Override + public Node getRoot() + { + return root; + } + + @Override + public void setRoot(Node root) + { + this.root = root; + } + + @Override + public View getView() + { + return view; + } + + @Override + public void setView(View view) + { + this.view = view; + } + +} diff --git a/src/com/axe/AbstractView.java b/src/com/axe/AbstractView.java new file mode 100644 index 0000000..a7b4270 --- /dev/null +++ b/src/com/axe/AbstractView.java @@ -0,0 +1,52 @@ +package com.axe; + +import com.axe.collect.ListItem; +import com.axe.math.Vec; +import com.axe.ui.Placement; +import com.axe.window.Window; + +public abstract class AbstractView> implements View +{ + + public ListItem listItem; + public Window window; + public Placement placement; + public Object data; + + public AbstractView(Window window) + { + this.window = window; + this.placement = Placement.maximized(); + } + + @Override + public void setListItem(ListItem listItem) + { + this.listItem = listItem; + } + + @Override + public Placement getPlacement() + { + return placement; + } + + @Override + public Window getWindow() + { + return window; + } + + @Override + public T getData() + { + return (T) data; + } + + @Override + public void setData(Object data) + { + this.data = data; + } + +} diff --git a/src/com/axe/Axe.java b/src/com/axe/Axe.java new file mode 100644 index 0000000..5262558 --- /dev/null +++ b/src/com/axe/Axe.java @@ -0,0 +1,122 @@ +package com.axe; + +import org.magnos.asset.Assets; +import org.magnos.asset.csv.CsvFormat; +import org.magnos.asset.font.FontFormat; +import org.magnos.asset.java.ClassFormat; +import org.magnos.asset.java.JarFormat; +import org.magnos.asset.json.JsonFormat; +import org.magnos.asset.props.PropertyFormat; +import org.magnos.asset.source.SmartSource; +import org.magnos.asset.text.TextFormat; +import org.magnos.asset.xml.XmlFormat; +import org.magnos.asset.zip.GzipFormat; +import org.magnos.asset.zip.ZipFormat; + +import com.axe.audio.AudioEngine; +import com.axe.color.Color; +import com.axe.core.Factory; +import com.axe.game.Game; +import com.axe.gfx.Coord; +import com.axe.gfx.GraphicsEngine; +import com.axe.input.ControllerEngine; +import com.axe.input.KeyEngine; +import com.axe.input.MouseEngine; +import com.axe.math.Bound2f; +import com.axe.math.Bound2i; +import com.axe.math.Bound3f; +import com.axe.math.Bound3i; +import com.axe.math.Rangef; +import com.axe.math.Rangei; +import com.axe.math.Rect2f; +import com.axe.math.Rect2i; +import com.axe.math.Scalarf; +import com.axe.math.Scalari; +import com.axe.math.Vec2f; +import com.axe.math.Vec2i; +import com.axe.math.Vec3f; +import com.axe.math.Vec3i; +import com.axe.math.calc.CalculatorBound2f; +import com.axe.math.calc.CalculatorBound2i; +import com.axe.math.calc.CalculatorBound3f; +import com.axe.math.calc.CalculatorBound3i; +import com.axe.math.calc.CalculatorCamera2; +import com.axe.math.calc.CalculatorCamera3; +import com.axe.math.calc.CalculatorColor; +import com.axe.math.calc.CalculatorCoord; +import com.axe.math.calc.CalculatorRangef; +import com.axe.math.calc.CalculatorRangei; +import com.axe.math.calc.CalculatorRect2f; +import com.axe.math.calc.CalculatorRect2i; +import com.axe.math.calc.CalculatorRegistry; +import com.axe.math.calc.CalculatorScalarf; +import com.axe.math.calc.CalculatorScalari; +import com.axe.math.calc.CalculatorVec2f; +import com.axe.math.calc.CalculatorVec2i; +import com.axe.math.calc.CalculatorVec3f; +import com.axe.math.calc.CalculatorVec3i; +import com.axe.monitor.MonitorSystem; +import com.axe.render.Renderer; +import com.axe.resource.Resource; +import com.axe.resource.ResourceFutureAsset; +import com.axe.util.DefaultLogger; +import com.axe.util.Logger; +import com.axe.util.Registry; +import com.axe2d.Camera2; +import com.axe3d.Camera3; + +public class Axe +{ + + public static Factory games; + public static KeyEngine keys; + public static MouseEngine mouse; + public static ControllerEngine controllers; + + public static MonitorSystem monitors; + public static AudioEngine audio; + public static GraphicsEngine graphics; + + public static Registry renderers = new Registry<>(); + + public static Logger logger = new DefaultLogger(); + + public static void load() + { + Assets.addSource( "smart", new SmartSource( true ) ); + Assets.setDefaultSource( "smart" ); + + Assets.addFutureAssetFactory( ResourceFutureAsset.FACTORY, Resource.class ); + + Assets.addFormat( new CsvFormat() ); + Assets.addFormat( new FontFormat() ); + Assets.addFormat( new ClassFormat() ); + Assets.addFormat( new JarFormat() ); + Assets.addFormat( new JsonFormat() ); + Assets.addFormat( new PropertyFormat() ); + Assets.addFormat( new TextFormat() ); + Assets.addFormat( new XmlFormat() ); + Assets.addFormat( new GzipFormat() ); + Assets.addFormat( new ZipFormat() ); + + CalculatorRegistry.register( CalculatorScalarf.INSTANCE, Scalarf.class, "scalar", "scalarf", "number", "float" ); + CalculatorRegistry.register( CalculatorScalari.INSTANCE, Scalari.class, "scalari", "integer", "int" ); + CalculatorRegistry.register( CalculatorVec2f.INSTANCE, Vec2f.class, "vec2f", "vec2" ); + CalculatorRegistry.register( CalculatorVec2i.INSTANCE, Vec2i.class, "vec2i" ); + CalculatorRegistry.register( CalculatorVec3f.INSTANCE, Vec3f.class, "vec3f", "vec3" ); + CalculatorRegistry.register( CalculatorVec3i.INSTANCE, Vec3i.class, "vec3i" ); + CalculatorRegistry.register( CalculatorBound2f.INSTANCE, Bound2f.class, "bound2f", "bound2" ); + CalculatorRegistry.register( CalculatorBound2i.INSTANCE, Bound2i.class, "bound2i" ); + CalculatorRegistry.register( CalculatorBound3f.INSTANCE, Bound3f.class, "bound3f", "bound3" ); + CalculatorRegistry.register( CalculatorBound3i.INSTANCE, Bound3i.class, "bound3i" ); + CalculatorRegistry.register( CalculatorRect2f.INSTANCE, Rect2f.class, "rect2f", "rect2" ); + CalculatorRegistry.register( CalculatorRect2i.INSTANCE, Rect2i.class, "rect2i" ); + CalculatorRegistry.register( CalculatorRangef.INSTANCE, Rangef.class, "rangef", "range" ); + CalculatorRegistry.register( CalculatorRangei.INSTANCE, Rangei.class, "rangei" ); + CalculatorRegistry.register( CalculatorColor.INSTANCE, Color.class, "color" ); + CalculatorRegistry.register( CalculatorCoord.INSTANCE, Coord.class, "coord" ); + CalculatorRegistry.register( CalculatorCamera2.INSTANCE, Camera2.class, "camera2" ); + CalculatorRegistry.register( CalculatorCamera3.INSTANCE, Camera3.class, "camera3" ); + } + +} diff --git a/src/com/axe/Camera.java b/src/com/axe/Camera.java new file mode 100644 index 0000000..9d3d302 --- /dev/null +++ b/src/com/axe/Camera.java @@ -0,0 +1,15 @@ +package com.axe; + +import com.axe.core.HasData; +import com.axe.game.GameState; +import com.axe.math.Vec; +import com.axe.node.Node; + +public interface Camera> extends HasData +{ + + public void update(); + + public boolean intersects(GameState state, Node node); + +} \ No newline at end of file diff --git a/src/com/axe/Scene.java b/src/com/axe/Scene.java new file mode 100644 index 0000000..7b343b5 --- /dev/null +++ b/src/com/axe/Scene.java @@ -0,0 +1,50 @@ +package com.axe; + +import com.axe.collect.HasListItem; +import com.axe.core.HasData; +import com.axe.game.GameState; +import com.axe.math.Vec; +import com.axe.node.Node; + +public interface Scene> extends HasListItem, HasData +{ + + public Node getRoot(); + + public void setRoot(Node root); + + public View getView(); + + public void setView(View view); + + default public void update(GameState state) + { + Node root = getRoot(); + + if (root != null) + { + root.update( state, getView() ); + } + } + + default public void draw(GameState state, View view) + { + Node root = getRoot(); + + if (root != null) + { + Node.drawNode( root, state, view ); + } + } + + default public void activate() + { + Node root = getRoot(); + + if (root != null) + { + Node.activateNode( root ); + } + } + +} diff --git a/src/com/axe/View.java b/src/com/axe/View.java new file mode 100644 index 0000000..54a7062 --- /dev/null +++ b/src/com/axe/View.java @@ -0,0 +1,76 @@ +package com.axe; + +import com.axe.collect.HasListItem; +import com.axe.core.HasData; +import com.axe.game.Game; +import com.axe.game.GameState; +import com.axe.gfx.ProjectionOutside; +import com.axe.math.Matrix; +import com.axe.math.Vec2i; +import com.axe.math.Vec; +import com.axe.ui.Placement; +import com.axe.window.Window; + +public interface View> extends HasData, HasListItem +{ + + public > S getScene(); + + public > C getCamera(); + + public Placement getPlacement(); + + public Window getWindow(); + + default public Game getGame() + { + return getWindow().getGame(); + } + + public Matrix getProjectionMatrix(); + + public Matrix getViewMatrix(); + + public Matrix getCombinedMatrix(); + + public void update(); + + public V project(Vec2i mouse, ProjectionOutside outside, V out); + + default public V project(ProjectionOutside outside, V out) + { + Window window = getWindow(); + + if (!window.isFocused()) + { + return null; + } + + return project( window.getMouse().position, outside, out ); + } + + default public V project(V out) + { + Window window = getWindow(); + + if (!window.isFocused()) + { + return null; + } + + return project( window.getMouse().position, ProjectionOutside.Ignore, out ); + } + + public V unproject(V point, ProjectionOutside outside, V mouseOut); + + default public V unproject(V point, V mouseOut) + { + return unproject( point, ProjectionOutside.Ignore, mouseOut ); + } + + default public void draw(GameState state) + { + getScene().draw( state, this ); + } + +} \ No newline at end of file diff --git a/src/com/axe/audio/AudioEngine.java b/src/com/axe/audio/AudioEngine.java new file mode 100644 index 0000000..9c2142e --- /dev/null +++ b/src/com/axe/audio/AudioEngine.java @@ -0,0 +1,5 @@ +package com.axe.audio; + +public interface AudioEngine { + +} diff --git a/src/com/axe/collect/HasListItem.java b/src/com/axe/collect/HasListItem.java new file mode 100644 index 0000000..4c05304 --- /dev/null +++ b/src/com/axe/collect/HasListItem.java @@ -0,0 +1,8 @@ +package com.axe.collect; + +public interface HasListItem +{ + + public void setListItem(ListItem item); + +} diff --git a/src/com/axe/collect/List.java b/src/com/axe/collect/List.java new file mode 100644 index 0000000..50fce70 --- /dev/null +++ b/src/com/axe/collect/List.java @@ -0,0 +1,123 @@ +package com.axe.collect; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import com.axe.util.Ref; + +public interface List +{ + + public static final BiPredicate EQUALS_STRICT = (a, b) -> (a == b); + public static final BiPredicate EQUALS = (a, b) -> (a == b || a.equals(b)); + + public ListItem add(T value); + + default public void add(T[] values) + { + for (int i = 0; i < values.length; i++) + { + add( values[ i ] ); + } + } + + default public void add(List list) + { + list.forAll((value) -> add(value)); + } + + public ListItem find(T value, BiPredicate equals); + + default public ListItem find(T value) + { + return find( value, EQUALS_STRICT ); + } + + default public boolean remove(T value) + { + ListItem link = find(value); + boolean removed = link != null; + + if (removed) + { + link.remove( value ); + } + + return removed; + } + + public void clear(); + + public int size(); + + default public void update() + { + + } + + public int forEach(Predicate predicate); + + public int forEach(BiPredicate predicate); + + default public void forAll(Consumer consumer) + { + forEach((value) -> { + consumer.accept(value); + return true; + }); + } + + default public void forAllItem(Consumer consumer) + { + forEach((value, item) -> { + consumer.accept(item); + return true; + }); + } + + default public void where(Predicate predicate, Consumer consumer) + { + forAll((value) -> { + if (predicate.test(value)) { + consumer.accept(value); + } + }); + } + + default public int count(Predicate predicate) + { + return forEach( predicate ); + } + + default public T first(Predicate predicate) + { + Ref ref = new Ref<>(); + + forEach((item) -> { + if (ref.value == null && predicate.test(item)) { + ref.value = item; + } + return ref.value == null; + }); + + return ref.value; + } + + default public boolean exists(Predicate predicate) + { + AtomicBoolean exists = new AtomicBoolean(); + + forEach((item) -> { + if (predicate.test(item)) { + exists.set(true); + return false; + } + return true; + }); + + return exists.get(); + } + +} diff --git a/src/com/axe/collect/ListArray.java b/src/com/axe/collect/ListArray.java new file mode 100644 index 0000000..6143a9a --- /dev/null +++ b/src/com/axe/collect/ListArray.java @@ -0,0 +1,207 @@ +package com.axe.collect; + +import java.util.Arrays; +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import com.axe.mem.Memory; +import com.axe.mem.MemoryPool; +import com.axe.util.Array; + +public class ListArray implements List +{ + + public static int DEFAULT_INCREASE = 16; + public static int DEFAULT_INITIAL_CAPACITY = 16; + + protected static final Memory POOL = new MemoryPool<>(128, ListArrayItem.FACTORY, 1024, 16, false); + + public static ListArray create(E ... initial) + { + return new ListArray( DEFAULT_INITIAL_CAPACITY, DEFAULT_INCREASE, false, initial ); + } + + public static ListArray create(int capacity, E ... initial) + { + return new ListArray( capacity, DEFAULT_INCREASE, false, initial ); + } + + public static ListArray create(int capacity, int increase, E ... initial) + { + return new ListArray( capacity, increase, false, initial ); + } + + public static ListArray compacted(E ... initial) + { + return new ListArray( 0, 1, true, initial ); + } + + public ListArrayItem[] items; + public T[] values; + public int size; + public int increase; + public int effectiveSize; + public boolean compact; + + protected ListArray(int capacity, int increase, boolean compact, T ... initial) + { + this.items = new ListArrayItem[ capacity ]; + this.values = Arrays.copyOf( initial, Math.max( initial.length, capacity ) ); + this.size = initial.length; + this.effectiveSize = initial.length; + this.increase = increase; + this.compact = compact; + + for (int i = 0; i < initial.length; i++) + { + items[ i ] = POOL.alloc(); + items[ i ].set( this, i ); + items[ i ].register( values[ i ] ); + } + } + + @Override + public ListItem add(T value) + { + Objects.requireNonNull(value); + + ListArrayItem item = POOL.alloc(); + + assert !item.isLinked(); + + item.set( this, size ); + item.register( value ); + + values = Array.put( values, size, value, increase ); + items = Array.put( items, size, item, increase ); + size++; + effectiveSize++; + + return item; + } + + @Override + public ListItem find(T value, BiPredicate equals) + { + for (int i = 0; i < size; i++) + { + if (values[ i ] != null && equals.test(values[i], value)) + { + return items[ i ]; + } + } + + return null; + } + + @Override + public void clear() + { + for (int i = 0; i < size; i++) + { + ListArrayItem item = items[ i ]; + + if (item != null) + { + POOL.free( item ); + items[ i ] = null; + values[ i ] = null; + } + } + + size = 0; + effectiveSize = 0; + } + + @Override + public void update() + { + int live = 0; + + for (int i = 0; i < size; i++) + { + if (values[ i ] != null) + { + values[ live ] = values[ i ]; + items[ live ] = items[ i ]; + items[ live ].index = live; + live++; + } + } + + size = effectiveSize = live; + + if (compact) + { + values = Arrays.copyOf(values, size); + } + } + + @Override + public int size() + { + return effectiveSize; + } + + @Override + public int forEach(Predicate predicate) + { + int iterated = 0; + + for (int i = 0; i < size; i++) + { + if ( values[ i ] != null ) + { + if ( predicate.test( values[ i ] ) ) + { + iterated++; + } + else + { + break; + } + } + } + + return iterated; + } + + @Override + public int forEach(BiPredicate predicate) + { + int iterated = 0; + + for (int i = 0; i < size; i++) + { + if ( values[ i ] != null && items[ i ] != null ) + { + if ( predicate.test( values[ i ], items[ i ] ) ) + { + iterated++; + } + else + { + break; + } + } + } + + return iterated; + } + + public void swap(int j, int k) + { + ListArrayItem jitem = items[ j ]; + T jvalue = values[ j ]; + + items[ j ] = items[ k ]; + values[ j ] = values[ k ]; + items[ k ] = jitem; + values[ k ] = jvalue; + + items[ j ].index = j; + items[ k ].index = k; + } + +} diff --git a/src/com/axe/collect/ListArrayItem.java b/src/com/axe/collect/ListArrayItem.java new file mode 100644 index 0000000..64ab53a --- /dev/null +++ b/src/com/axe/collect/ListArrayItem.java @@ -0,0 +1,98 @@ +package com.axe.collect; + +import java.util.Objects; + +import com.axe.core.Factory; + +public class ListArrayItem implements ListItem +{ + + public static Factory FACTORY = () -> new ListArrayItem(); + + public ListArray source; + public int index; + + public ListArrayItem() + { + + } + + public void set(ListArray source, int index) + { + this.source = source; + this.index = index; + } + + @Override + public boolean isLinked() + { + return source != null && source.items[ index ] == this; + } + + @Override + public boolean isFor(Object originalValue) + { + Objects.requireNonNull(originalValue); + + return isLinked() && originalValue == source.values[ index ]; + } + + @Override + public void remove(Object originalValue) + { + if (isFor(originalValue)) + { + source.values[ index ] = null; + source.items[ index ] = null; + source.effectiveSize--; + + if (source.compact) + { + source.update(); + } + + ListArray.POOL.free(this); + } + } + + @Override + public void front(boolean very) + { + if (index > 0) + { + if (very) + { + while (index > 0) + { + source.swap( index, index - 1 ); + } + } + else + { + source.swap( index, index - 1 ); + } + } + } + + @Override + public void last(boolean very) + { + int last = source.size - 1; + + if (index < last) + { + if (very) + { + while (index < last) + { + source.swap( index, index + 1 ); + } + } + else + { + source.swap( index, index + 1 ); + } + } + } + +} \ No newline at end of file diff --git a/src/com/axe/collect/ListItem.java b/src/com/axe/collect/ListItem.java new file mode 100644 index 0000000..1b7c33b --- /dev/null +++ b/src/com/axe/collect/ListItem.java @@ -0,0 +1,24 @@ +package com.axe.collect; + +public interface ListItem +{ + + public boolean isLinked(); + + public boolean isFor(Object originalValue); + + public void remove(Object originalValue); + + public void front(boolean very); + + public void last(boolean very); + + default void register(Object value) + { + if (value instanceof HasListItem) + { + ((HasListItem)value).setListItem(this); + } + } + +} diff --git a/src/com/axe/collect/ListItemIgnored.java b/src/com/axe/collect/ListItemIgnored.java new file mode 100644 index 0000000..f0af569 --- /dev/null +++ b/src/com/axe/collect/ListItemIgnored.java @@ -0,0 +1,38 @@ +package com.axe.collect; + +public class ListItemIgnored implements ListItem +{ + + public static final ListItemIgnored INSTANCE = new ListItemIgnored(); + + @Override + public boolean isLinked() + { + return false; + } + + @Override + public boolean isFor(Object originalValue) + { + return false; + } + + @Override + public void remove(Object originalValue) + { + + } + + @Override + public void front(boolean very) + { + + } + + @Override + public void last(boolean very) + { + + } + +} \ No newline at end of file diff --git a/src/com/axe/collect/ListLinked.java b/src/com/axe/collect/ListLinked.java new file mode 100644 index 0000000..4ccad05 --- /dev/null +++ b/src/com/axe/collect/ListLinked.java @@ -0,0 +1,131 @@ +package com.axe.collect; + +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import com.axe.mem.Memory; +import com.axe.mem.MemoryPool; + +public class ListLinked implements List +{ + + protected static final Memory POOL = new MemoryPool<>(128, ListLinkedItem.FACTORY, 1024, 16, false); + + public static List create() + { + return new ListLinked(); + } + + public final ListLinkedItem head = new ListLinkedItem<>(); + + public ListLinkedItem add(T value) + { + Objects.requireNonNull(value); + + ListLinkedItem node = POOL.alloc(); + + assert !node.isLinked(); + + node.value = value; + node.insertAfter(head.prev); + node.register(value); + + return node; + } + + @Override + public void clear() + { + head.next.destroy(); + } + + @Override + public ListLinkedItem find(T value, BiPredicate equals) + { + ListLinkedItem node = head.next; + ListLinkedItem next = null; + + while (node != head) + { + next = node.next; + + if (equals.test(node.value, value)) + { + return node; + } + + node = next; + } + + return null; + } + + @Override + public int forEach(Predicate predicate) + { + ListLinkedItem node = head.next; + ListLinkedItem next = null; + int iterated = 0; + + while (node != head) + { + next = node.next; + + if (predicate.test(node.value)) + { + iterated++; + } + else + { + break; + } + + node = next; + } + + return iterated; + } + + @Override + public int forEach(BiPredicate predicate) + { + ListLinkedItem node = head.next; + ListLinkedItem next = null; + int iterated = 0; + + while (node != head) + { + next = node.next; + + if (predicate.test(node.value, node)) + { + iterated++; + } + else + { + break; + } + + node = next; + } + + return iterated; + } + + @Override + public int size() + { + ListLinkedItem node = head.next; + int size = 0; + + while (node != head) + { + size++; + node = node.next; + } + + return size; + } + +} \ No newline at end of file diff --git a/src/com/axe/collect/ListLinkedItem.java b/src/com/axe/collect/ListLinkedItem.java new file mode 100644 index 0000000..c4ccb4b --- /dev/null +++ b/src/com/axe/collect/ListLinkedItem.java @@ -0,0 +1,135 @@ +package com.axe.collect; + +import java.util.Objects; + +import com.axe.core.Factory; + +public class ListLinkedItem implements ListItem +{ + + public static Factory FACTORY = () -> new ListLinkedItem(); + + public ListLinkedItem next; + public ListLinkedItem prev; + public T value; + + public ListLinkedItem() + { + this.next = this; + this.prev = this; + } + + public void set(T value) + { + this.value = value; + } + + public void insertAfter(ListLinkedItem after) + { + after.next.prev = this; + next = after.next; + after.next = this; + prev = after; + } + + public void unlink() + { + next.prev = prev; + prev.next = next; + next = prev = this; + } + + public void destroy() + { + if (isLinked()) + { + ListLinkedItem n = this.next; + + remove(); + + n.destroy(); + } + } + + public void remove() + { + unlink(); + value = null; + + ListLinked.POOL.free(this); + } + + @Override + public boolean isLinked() + { + return !(value == null || prev == this || next == this); + } + + @Override + public boolean isFor(Object originalValue) + { + Objects.requireNonNull(originalValue); + + return value == originalValue; + } + + @Override + public void remove(Object originalValue) + { + if (isFor(originalValue)) + { + remove(); + } + } + + @Override + public void front(boolean very) + { + if (!isLinked()) + { + return; + } + + ListLinkedItem node = prev; + + if (very) + { + while (node.value != null) + { + node = node.prev; + } + } + + if (node != prev) + { + unlink(); + insertAfter(node); + } + } + + @Override + public void last(boolean very) + { + if (!isLinked()) + { + return; + } + + ListLinkedItem node = next; + + if (very) + { + while (node.next.value != null) + { + node = node.next; + } + } + + if (node != next) + { + unlink(); + insertAfter(node); + } + } + +} \ No newline at end of file diff --git a/src/com/axe/collect/ListSorted.java b/src/com/axe/collect/ListSorted.java new file mode 100644 index 0000000..66cbd8f --- /dev/null +++ b/src/com/axe/collect/ListSorted.java @@ -0,0 +1,85 @@ +package com.axe.collect; + +import java.util.Comparator; + + +public class ListSorted extends ListArray +{ + + public static ListSorted create(Comparator comparator, E ... initial) + { + return new ListSorted( comparator, DEFAULT_INITIAL_CAPACITY, DEFAULT_INCREASE, false, initial ); + } + + public static ListSorted create(Comparator comparator, int capacity, E ... initial) + { + return new ListSorted( comparator, capacity, DEFAULT_INCREASE, false, initial ); + } + + public static ListSorted create(Comparator comparator, int capacity, int increase, E ... initial) + { + return new ListSorted( comparator, capacity, increase, false, initial ); + } + + public static ListSorted compacted(Comparator comparator, E ... initial) + { + return new ListSorted( comparator, 0, 1, true, initial ); + } + + public Comparator comparator; + + protected ListSorted(Comparator comparator, int capacity, int increase, boolean compact, T ... initial) + { + super( capacity, increase, compact, initial ); + + this.comparator = comparator; + } + + @Override + public ListItem add(T value) + { + ListItem item = super.add( value ); + + sort(); + + return item; + } + + @Override + public void update() + { + super.update(); + + sort(); + } + + public void sort() + { + ListArrayItem tempItem; + T tempValue; + + for (int i = 1; i < size; i++) + { + tempItem = items[ i ]; + tempValue = values[ i ]; + + int j = i - 1; + + while (j >= 0 && comparator.compare( values[ j ], tempValue ) > 0) + { + items[ j + 1 ] = items[ j ]; + values[ j + 1 ] = values[ j ]; + j--; + } + + items[ j + 1 ] = tempItem; + values[ j + 1 ] = tempValue; + } + + for (int i = 0; i < size; i++) + { + items[ i ].index = i; + } + } + +} diff --git a/src/com/axe/color/Color.java b/src/com/axe/color/Color.java new file mode 100755 index 0000000..d5c3784 --- /dev/null +++ b/src/com/axe/color/Color.java @@ -0,0 +1,313 @@ +package com.axe.color; + +import java.io.Serializable; + +import com.axe.core.Alternative; +import com.axe.core.Attribute; +import com.axe.core.Factory; +import com.axe.math.Numbers; +import com.axe.math.calc.CalculatorColor; + +public class Color implements ColorInterface, Attribute, Alternative, Serializable +{ + + private static final long serialVersionUID = -8256268873078605411L; + + public static final Factory FACTORY = new Factory() { + public Color create() { + return new Color(); + } + }; + + public float r, b, g, a; + + public Color() + { + set(1f, 1f, 1f, 1f); + } + + public Color(float red, float green, float blue) + { + set(red, green, blue, 1f); + } + + public Color(float red, float green, float blue, float alpha) + { + set(red, green, blue, alpha); + } + + public Color(int red, int green, int blue) + { + set(red / 255f, green / 255f, blue / 255f, 1f); + } + + public Color(int red, int green, int blue, int alpha) + { + set(red / 255f, green / 255f, blue / 255f, alpha / 255f); + } + + public Color(ColorInterface c) + { + set(c); + } + + public Color(ColorInterface c, float alpha) + { + set(c); a = alpha; + } + + public void set(ColorInterface c) + { + set(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha()); + } + + public void set(float red, float green, float blue) + { + set(red, green, blue, 1f); + } + + public void set(float red, float green, float blue, float alpha) + { + r = red; + g = green; + b = blue; + a = alpha; + } + + public void set(int red, int green, int blue) + { + set(red, green, blue, 255); + } + + public void set(int red, int green, int blue, int alpha) + { + r = red / 255f; + g = green / 255f; + b = blue / 255f; + a = alpha / 255f; + } + + @Override + public Color alternative() + { + return this; + } + + public void mul(float s) + { + r = clamp(r * s); + g = clamp(g * s); + b = clamp(b * s); + } + + public void set(Color c, float scale) + { + r = clamp(c.r * scale); + g = clamp(c.g * scale); + b = clamp(c.b * scale); + a = clamp(c.a * scale); + } + + private float clamp(float r) + { + return Numbers.clamp(r, 0, 1); + } + + public Color invert() + { + r = 1 - r; + g = 1 - g; + b = 1 - b; + return this; + } + + public int r() + { + return Numbers.clamp( (int)(r * 255), 0, 255 ); + } + + public int g() + { + return Numbers.clamp( (int)(g * 255), 0, 255 ); + } + + public int b() + { + return Numbers.clamp( (int)(b * 255), 0, 255 ); + } + + public int a() + { + return Numbers.clamp( (int)(a * 255), 0, 255 ); + } + + public void rgb(int rgb) + { + unhash( rgb, 16, 8, 0 ); + } + + public void bgr(int bgr) + { + unhash( bgr, 0, 8, 16 ); + } + + public void argb(int argb) + { + unhash( argb, 16, 8, 0, 24 ); + } + + public void abgr(int abgr) + { + unhash( abgr, 0, 8, 16, 24 ); + } + + public void rgba(int rgba) + { + unhash( rgba, 24, 16, 8, 0 ); + } + + public void bgra(int bgra) + { + unhash( bgra, 8, 16, 24, 0 ); + } + + public void unhash(int hash, int r, int g, int b, int a) + { + set( (hash >> r) & 0xFF, (hash >> g) & 0xFF, (hash >> b) & 0xFF, (hash >> a) & 0xFF ); + } + + public void unhash(int hash, int r, int g, int b) + { + set( (hash >> r) & 0xFF, (hash >> g) & 0xFF, (hash >> b) & 0xFF ); + } + + public void clamp() + { + r = clamp(r); + g = clamp(g); + b = clamp(b); + a = clamp(a); + } + + public Color lighten( float delta ) + { + r += (1 - r) * delta; + g += (1 - g) * delta; + b += (1 - b) * delta; + return this; + } + + public Color darken( float delta ) + { + r -= r * delta; + g -= g * delta; + b -= b * delta; + return this; + } + + @Override + public Color clone() + { + return new Color(r, g, b, a); + } + + // ARGB + public Color fromHex( String hex, int alphaIndex, int redIndex, int greenIndex, int blueIndex ) + { + a = getComponentFromHex( hex, alphaIndex ); + r = getComponentFromHex( hex, redIndex ); + g = getComponentFromHex( hex, greenIndex ); + b = getComponentFromHex( hex, blueIndex ); + + return this; + } + + private float getComponentFromHex( String hex, int index ) + { + return ( index == -1 ? 1.0f : Integer.valueOf( hex.substring( index << 1, ( index + 1 ) << 1 ), 16 ) / 255.0f ); + } + + public void scaleShade( float d ) + { + r *= d; + g *= d; + b *= d; + } + + @Override + public Color mutable() + { + return this; + } + + @Override + public float getRed() + { + return r; + } + + @Override + public float getBlue() + { + return b; + } + + @Override + public float getGreen() + { + return g; + } + + @Override + public float getAlpha() + { + return a; + } + + @Override + public Color create(float r, float g, float b, float a) + { + return new Color( r, g, b, a ); + } + + @Override + public Color get() + { + return this; + } + + @Override + public CalculatorColor getCalculator() + { + return CalculatorColor.INSTANCE; + } + + @Override + public String toString() + { + return String.format( "(%d, %d, %d, %d)", r(), g(), b(), a() ); + } + + @Override + public int hashCode() + { + return bgra(); + } + + @Override + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + + if ( obj == null || obj.getClass() != getClass() ) + { + return false; + } + + return isEqual( (Color)obj ); + } + +} \ No newline at end of file diff --git a/src/com/axe/color/ColorInterface.java b/src/com/axe/color/ColorInterface.java new file mode 100644 index 0000000..6ae8cc8 --- /dev/null +++ b/src/com/axe/color/ColorInterface.java @@ -0,0 +1,148 @@ +package com.axe.color; + +import com.axe.math.Numbers; + +public interface ColorInterface> +{ + + public static final float DEFAULT_ALPHA = 1f; + public static final int DEFAULT_ALPHA_INT = 255; + + public float getRed(); + public float getBlue(); + public float getGreen(); + public float getAlpha(); + + public C create(float r, float g, float b, float a); + + default public Color mutable() + { + return new Color( getRed(), getGreen(), getBlue(), getAlpha() ); + } + + default public ImmutableColor immutable() + { + return new ImmutableColor( getRed(), getGreen(), getBlue(), getAlpha() ); + } + + default public boolean isImmutable() + { + return false; + } + + default public int r() + { + return Numbers.clamp( (int)(getRed() * 255), 0, 255 ); + } + + default public int g() + { + return Numbers.clamp( (int)(getGreen() * 255), 0, 255 ); + } + + default public int b() + { + return Numbers.clamp( (int)(getBlue() * 255), 0, 255 ); + } + + default public int a() + { + return Numbers.clamp( (int)(getAlpha() * 255), 0, 255 ); + } + + default public int rgb() + { + return hash(16, 8, 0); + } + + default public int bgr() + { + return hash(0, 8, 16); + } + + default public int argb() + { + return hash(16, 8, 0, 24); + } + + default public int abgr() + { + return hash(0, 8, 16, 24); + } + + default public int rgba() + { + return hash(24, 16, 8, 0); + } + + default public int bgra() + { + return hash(8, 16, 24, 0); + } + + default public int hash(int r, int g, int b) + { + return (r() << r) | (g() << g) | (b() << b); + } + + default public int hash(int r, int g, int b, int a) + { + return (a() << a) | (r() << r) | (g() << g) | (b() << b); + } + + /* TODO + default public C invert() + { + return create( 1 - getRed(), 1 - getGreen(), 1 - getBlue(), getAlpha() ); + } + + default public Color invert(Color out) + { + out.r = 1 - getRed(); + out.g = 1 - getGreen(); + out.b = 1 - getBlue(); + return out; + } + */ + + // 0 = no change, 1 = white + default public C lighter(float delta) + { + return create( + getRed() + (1 - getRed()) * delta, + getGreen() + (1 - getGreen()) * delta, + getBlue() + (1 - getBlue()) * delta, + getAlpha() + ); + } + + // 0 = no change, 1 = white + default public Color lighter(float delta, Color out) + { + out.r = getRed() + (1 - getRed()) * delta; + out.g = getGreen() + (1 - getGreen()) * delta; + out.b = getBlue() + (1 - getBlue()) * delta; + return out; + } + + // 0 = no change, 1 = black + default public C darker(float delta) + { + return create( + getRed() - getRed() * delta, + getGreen() - getGreen() * delta, + getBlue() - getBlue() * delta, + getAlpha() + ); + } + + // 0 = no change, 1 = black + default public Color darker(float delta, Color out) + { + out.r = getRed() - getRed() * delta; + out.g = getGreen() - getGreen() * delta; + out.b = getBlue()- getBlue() * delta; + return out; + } + +} diff --git a/src/com/axe/color/Colors.java b/src/com/axe/color/Colors.java new file mode 100755 index 0000000..9d2f259 --- /dev/null +++ b/src/com/axe/color/Colors.java @@ -0,0 +1,172 @@ + +package com.axe.color; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.axe.util.Reflections; + +public class Colors +{ + + public static final ImmutableColor Transparent = new ImmutableColor( 255, 255, 255, 0 ); + + public static final ImmutableColor AliceBlue = new ImmutableColor( 240, 248, 255 ); + public static final ImmutableColor AntiqueWhite = new ImmutableColor( 250, 235, 215 ); + public static final ImmutableColor Aqua = new ImmutableColor( 0, 255, 255 ); + public static final ImmutableColor Aquamarine = new ImmutableColor( 127, 255, 212 ); + public static final ImmutableColor Azure = new ImmutableColor( 240, 255, 255 ); + public static final ImmutableColor Beige = new ImmutableColor( 245, 245, 220 ); + public static final ImmutableColor Bisque = new ImmutableColor( 255, 228, 196 ); + public static final ImmutableColor Black = new ImmutableColor( 0, 0, 0 ); + public static final ImmutableColor BlanchedAlmond = new ImmutableColor( 255, 235, 205 ); + public static final ImmutableColor Blue = new ImmutableColor( 0, 0, 255 ); + public static final ImmutableColor BlueViolet = new ImmutableColor( 138, 43, 226 ); + public static final ImmutableColor Brown = new ImmutableColor( 165, 42, 42 ); + public static final ImmutableColor BurlyWood = new ImmutableColor( 222, 184, 135 ); + public static final ImmutableColor CadetBlue = new ImmutableColor( 95, 158, 160 ); + public static final ImmutableColor Chartreuse = new ImmutableColor( 127, 255, 0 ); + public static final ImmutableColor Chocolate = new ImmutableColor( 210, 105, 30 ); + public static final ImmutableColor Coral = new ImmutableColor( 255, 127, 80 ); + public static final ImmutableColor CornflowerBlue = new ImmutableColor( 100, 149, 237 );; + public static final ImmutableColor Cornsilk = new ImmutableColor( 255, 248, 220 ); + public static final ImmutableColor Crimson = new ImmutableColor( 220, 20, 60 ); + public static final ImmutableColor Cyan = new ImmutableColor( 0, 255, 255 ); + + public static final ImmutableColor DarkBlue = new ImmutableColor( 0, 0, 139 ); + public static final ImmutableColor DarkCyan = new ImmutableColor( 0, 139, 139 ); + public static final ImmutableColor DarkGoldenrod = new ImmutableColor( 184, 134, 11 ); + public static final ImmutableColor DarkGray = new ImmutableColor( 169, 169, 169 ); + public static final ImmutableColor DarkGreen = new ImmutableColor( 0, 100, 0 ); + public static final ImmutableColor DarkKhaki = new ImmutableColor( 189, 183, 107 ); + public static final ImmutableColor DarkMagenta = new ImmutableColor( 139, 0, 139 ); + public static final ImmutableColor DarkOliveGreen = new ImmutableColor( 85, 107, 47 ); + public static final ImmutableColor DarkOrange = new ImmutableColor( 255, 140, 0 ); + public static final ImmutableColor DarkOrchid = new ImmutableColor( 153, 50, 204 ); + public static final ImmutableColor DarkRed = new ImmutableColor( 139, 0, 0 ); + public static final ImmutableColor DarkSalmon = new ImmutableColor( 233, 150, 122 ); + public static final ImmutableColor DarkSeaGreen = new ImmutableColor( 143, 188, 139 ); + public static final ImmutableColor DarkSlateBlue = new ImmutableColor( 72, 61, 139 ); + public static final ImmutableColor DarkSlateGray = new ImmutableColor( 47, 79, 79 ); + public static final ImmutableColor DarkTurquoise = new ImmutableColor( 0, 206, 209 ); + public static final ImmutableColor DarkViolet = new ImmutableColor( 148, 0, 211 ); + + public static final ImmutableColor DeepPink = new ImmutableColor( 255, 20, 147 ); + public static final ImmutableColor DeepSkyBlue = new ImmutableColor( 0, 191, 255 ); + public static final ImmutableColor DimGray = new ImmutableColor( 105, 105, 105 ); + public static final ImmutableColor DodgerBlue = new ImmutableColor( 30, 144, 255 ); + public static final ImmutableColor Firebrick = new ImmutableColor( 178, 34, 34 ); + public static final ImmutableColor FloralWhite = new ImmutableColor( 255, 250, 240 ); + public static final ImmutableColor ForestGreen = new ImmutableColor( 34, 139, 34 ); + public static final ImmutableColor Fuchsia = new ImmutableColor( 255, 0, 255 ); + public static final ImmutableColor Gainsboro = new ImmutableColor( 220, 220, 220 ); + public static final ImmutableColor GhostWhite = new ImmutableColor( 248, 248, 255 ); + public static final ImmutableColor Gold = new ImmutableColor( 255, 215, 0 ); + public static final ImmutableColor Goldenrod = new ImmutableColor( 218, 165, 32 ); + public static final ImmutableColor Gray = new ImmutableColor( 128, 128, 128 ); + public static final ImmutableColor Green = new ImmutableColor( 0, 128, 0 ); + public static final ImmutableColor GreenYellow = new ImmutableColor( 173, 255, 47 ); + public static final ImmutableColor Honeydew = new ImmutableColor( 240, 255, 240 ); + public static final ImmutableColor HotPink = new ImmutableColor( 255, 105, 180 ); + public static final ImmutableColor IndianRed = new ImmutableColor( 205, 92, 92 ); + public static final ImmutableColor Indigo = new ImmutableColor( 75, 0, 130 ); + public static final ImmutableColor Ivory = new ImmutableColor( 255, 255, 240 ); + public static final ImmutableColor Khaki = new ImmutableColor( 240, 230, 140 ); + public static final ImmutableColor Lavender = new ImmutableColor( 230, 230, 250 ); + public static final ImmutableColor LavenderBlush = new ImmutableColor( 255, 240, 245 ); + public static final ImmutableColor LawnGreen = new ImmutableColor( 124, 252, 0 ); + public static final ImmutableColor LemonChiffon = new ImmutableColor( 255, 250, 205 ); + + public static final ImmutableColor LightBlue = new ImmutableColor( 173, 216, 230 ); + public static final ImmutableColor LightCoral = new ImmutableColor( 240, 128, 128 ); + public static final ImmutableColor LightCyan = new ImmutableColor( 224, 255, 255 ); + public static final ImmutableColor LightGoldenrodYellow = new ImmutableColor( 250, 250, 210 ); + public static final ImmutableColor LightGray = new ImmutableColor( 211, 211, 211 ); + public static final ImmutableColor LightGreen = new ImmutableColor( 144, 238, 144 ); + public static final ImmutableColor LightPink = new ImmutableColor( 255, 182, 193 ); + public static final ImmutableColor LightSalmon = new ImmutableColor( 255, 160, 122 ); + public static final ImmutableColor LightSeaGreen = new ImmutableColor( 32, 178, 170 ); + public static final ImmutableColor LightSkyBlue = new ImmutableColor( 135, 206, 250 ); + public static final ImmutableColor LightSlateGray = new ImmutableColor( 119, 136, 153 ); + public static final ImmutableColor LightSteelBlue = new ImmutableColor( 176, 196, 222 ); + public static final ImmutableColor LightYellow = new ImmutableColor( 255, 255, 224 ); + + public static final ImmutableColor Lime = new ImmutableColor( 0, 255, 0 ); + public static final ImmutableColor LimeGreen = new ImmutableColor( 50, 205, 50 ); + public static final ImmutableColor Linen = new ImmutableColor( 250, 240, 230 ); + public static final ImmutableColor Magenta = new ImmutableColor( 255, 0, 255 ); + public static final ImmutableColor Maroon = new ImmutableColor( 128, 0, 0 ); + + public static final ImmutableColor MediumAquamarine = new ImmutableColor( 102, 205, 170 ); + public static final ImmutableColor MediumBlue = new ImmutableColor( 0, 0, 205 ); + public static final ImmutableColor MediumOrchid = new ImmutableColor( 186, 85, 211 ); + public static final ImmutableColor MediumPurple = new ImmutableColor( 147, 112, 219 ); + public static final ImmutableColor MediumSeaGreen = new ImmutableColor( 60, 179, 113 ); + public static final ImmutableColor MediumSlateBlue = new ImmutableColor( 123, 104, 238 ); + public static final ImmutableColor MediumSpringGreen = new ImmutableColor( 0, 250, 154 ); + public static final ImmutableColor MediumTurquoise = new ImmutableColor( 72, 209, 204 ); + public static final ImmutableColor MediumVioletRed = new ImmutableColor( 199, 21, 133 ); + + public static final ImmutableColor MidnightBlue = new ImmutableColor( 25, 25, 112 ); + public static final ImmutableColor MintCream = new ImmutableColor( 245, 255, 250 ); + public static final ImmutableColor MistyRose = new ImmutableColor( 255, 228, 225 ); + public static final ImmutableColor Moccasin = new ImmutableColor( 255, 228, 181 ); + public static final ImmutableColor NavajoWhite = new ImmutableColor( 255, 222, 173 ); + public static final ImmutableColor Navy = new ImmutableColor( 0, 0, 128 ); + public static final ImmutableColor OldLace = new ImmutableColor( 253, 245, 230 ); + public static final ImmutableColor Olive = new ImmutableColor( 128, 128, 0 ); + public static final ImmutableColor OliveDrab = new ImmutableColor( 107, 142, 35 ); + public static final ImmutableColor Orange = new ImmutableColor( 255, 165, 0 ); + public static final ImmutableColor OrangeRed = new ImmutableColor( 255, 69, 0 ); + public static final ImmutableColor Orchid = new ImmutableColor( 218, 112, 214 ); + + public static final ImmutableColor PaleGoldenrod = new ImmutableColor( 238, 232, 170 ); + public static final ImmutableColor PaleGreen = new ImmutableColor( 152, 251, 152 ); + public static final ImmutableColor PaleTurquoise = new ImmutableColor( 175, 238, 238 ); + public static final ImmutableColor PaleVioletRed = new ImmutableColor( 219, 112, 147 ); + + public static final ImmutableColor PapayaWhip = new ImmutableColor( 255, 239, 213 ); + public static final ImmutableColor PeachPuff = new ImmutableColor( 255, 218, 185 ); + public static final ImmutableColor Peru = new ImmutableColor( 205, 133, 63 ); + public static final ImmutableColor Pink = new ImmutableColor( 255, 192, 203 ); + public static final ImmutableColor Plum = new ImmutableColor( 221, 160, 221 ); + public static final ImmutableColor PowderBlue = new ImmutableColor( 176, 224, 230 ); + public static final ImmutableColor Purple = new ImmutableColor( 128, 0, 128 ); + public static final ImmutableColor Red = new ImmutableColor( 255, 0, 0 ); + public static final ImmutableColor RosyBrown = new ImmutableColor( 188, 143, 143 ); + public static final ImmutableColor RoyalBlue = new ImmutableColor( 65, 105, 225 ); + public static final ImmutableColor SaddleBrown = new ImmutableColor( 139, 69, 19 ); + public static final ImmutableColor Salmon = new ImmutableColor( 250, 128, 114 ); + public static final ImmutableColor SandyBrown = new ImmutableColor( 244, 164, 96 ); + public static final ImmutableColor SeaGreen = new ImmutableColor( 46, 139, 87 ); + public static final ImmutableColor SeaShell = new ImmutableColor( 255, 245, 238 ); + public static final ImmutableColor Sienna = new ImmutableColor( 160, 82, 45 ); + public static final ImmutableColor Silver = new ImmutableColor( 192, 192, 192 ); + public static final ImmutableColor SkyBlue = new ImmutableColor( 135, 206, 235 ); + public static final ImmutableColor SlateBlue = new ImmutableColor( 106, 90, 205 ); + public static final ImmutableColor SlateGray = new ImmutableColor( 112, 128, 144 ); + public static final ImmutableColor Snow = new ImmutableColor( 255, 250, 250 ); + public static final ImmutableColor SpringGreen = new ImmutableColor( 0, 255, 127 ); + public static final ImmutableColor SteelBlue = new ImmutableColor( 70, 130, 180 ); + public static final ImmutableColor Tan = new ImmutableColor( 210, 180, 140 ); + public static final ImmutableColor Teal = new ImmutableColor( 0, 128, 128 ); + public static final ImmutableColor Thistle = new ImmutableColor( 216, 191, 216 ); + public static final ImmutableColor Tomato = new ImmutableColor( 255, 99, 71 ); + public static final ImmutableColor Turquoise = new ImmutableColor( 64, 224, 208 ); + public static final ImmutableColor Violet = new ImmutableColor( 238, 130, 238 ); + public static final ImmutableColor Wheat = new ImmutableColor( 245, 222, 179 ); + public static final ImmutableColor White = new ImmutableColor( 255, 255, 255 ); + public static final ImmutableColor WhiteSmoke = new ImmutableColor( 245, 245, 245 ); + public static final ImmutableColor Yellow = new ImmutableColor( 255, 255, 0 ); + public static final ImmutableColor YellowGreen = new ImmutableColor( 154, 205, 50 ); + + public static ImmutableColor[] Colors = + Reflections.getStaticFieldArray( ImmutableColor.class, true, Colors.class ); + + public static Map ColorMap = + Reflections.getStaticFieldMap( ImmutableColor.class, true, Colors.class, new LinkedHashMap() ); + + public static Map ColorMapInversed = + Reflections.getStaticFieldMapInversed( ImmutableColor.class, true, Colors.class, new LinkedHashMap() ); + +} diff --git a/src/com/axe/color/ImmutableColor.java b/src/com/axe/color/ImmutableColor.java new file mode 100644 index 0000000..b08b90d --- /dev/null +++ b/src/com/axe/color/ImmutableColor.java @@ -0,0 +1,86 @@ +package com.axe.color; + +import com.axe.core.Alternative; + +public class ImmutableColor implements ColorInterface, Alternative +{ + + private float r, g, b, a; + + public ImmutableColor(ColorInterface color) + { + this( color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() ); + } + + public ImmutableColor(float r, float g, float b) + { + this( r, g, b, DEFAULT_ALPHA ); + } + + public ImmutableColor(float r, float g, float b, float a ) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public ImmutableColor(int r, int g, int b) + { + this( r / 255f, g / 255f, b / 255f, DEFAULT_ALPHA ); + } + + public ImmutableColor(int r, int g, int b, int a) + { + this( r / 255f, g / 255f, b / 255f, a / 255f ); + } + + @Override + public float getRed() + { + return r; + } + + @Override + public float getBlue() + { + return b; + } + + @Override + public float getGreen() + { + return g; + } + + @Override + public float getAlpha() + { + return a; + } + + @Override + public ImmutableColor create(float r, float g, float b, float a) + { + return new ImmutableColor( r, g, b, a ); + } + + @Override + public ImmutableColor immutable() + { + return this; + } + + @Override + public boolean isImmutable() + { + return true; + } + + @Override + public Color alternative() + { + return new Color( this ); + } + +} diff --git a/src/com/axe/core/Alternative.java b/src/com/axe/core/Alternative.java new file mode 100644 index 0000000..d7f5c05 --- /dev/null +++ b/src/com/axe/core/Alternative.java @@ -0,0 +1,8 @@ +package com.axe.core; + +public interface Alternative +{ + + public T alternative(); + +} diff --git a/src/com/axe/core/Attribute.java b/src/com/axe/core/Attribute.java new file mode 100755 index 0000000..29b71ce --- /dev/null +++ b/src/com/axe/core/Attribute.java @@ -0,0 +1,703 @@ +package com.axe.core; + +import java.nio.ByteBuffer; + +import com.axe.io.DataModel; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.calc.Binary; +import com.axe.math.calc.Calculator; +import com.axe.math.calc.Unary; + + +/** + * Any class subject to animation. A subject has some value that can be + * set, returned, modulated between a start and end value by some delta, + * and can determine its distance from another value of the same type. + * + * @author Philip Diffenderfer + * + * @param + */ +public interface Attribute extends Cloned, Bufferable, DataModel, Factory +{ + + public T get(); + + public Calculator getCalculator(); + + default public T create() + { + return getCalculator().create(); + } + + default public boolean recycle() + { + return getCalculator().recycle( get() ); + } + + default public T clone() + { + return getCalculator().clone( get() ); + } + + default public T set( T value ) + { + return getCalculator().copy( get(), value ); + } + + default public T set( Alternative alternative ) + { + return getCalculator().copy( get(), alternative.alternative() ); + } + + default public T copy( T target ) + { + return getCalculator().copy( target, get() ); + } + + default public T clear() + { + return getCalculator().clear( get(), 0 ); + } + + default public T set( float component ) + { + return getCalculator().clear( get(), component ); + } + + + default public T unaryn( Unary unaryOperation ) + { + return getCalculator().unaryn( get(), unaryOperation ); + } + + default public T unary( Unary unaryOperation ) + { + return getCalculator().unaryi( get(), unaryOperation ); + } + + default public T unary( T value, Unary unaryOperation ) + { + return getCalculator().unary( get(), value, unaryOperation ); + } + + + default public T binaryn( T b, Binary binaryOperation ) + { + return getCalculator().binaryn( get(), b, binaryOperation ); + } + + default public T binary( T b, Binary binaryOperation ) + { + return getCalculator().binaryi( get(), b, binaryOperation ); + } + + default public T binary( T a, T b, Binary binaryOperation ) + { + return getCalculator().binary( get(), a, b, binaryOperation ); + } + + + default public T addsn( T addend, float scale ) + { + return getCalculator().addsn( get(), addend, scale ); + } + + default public T adds( T addend, float scale ) + { + return getCalculator().addsi( get(), addend, scale ); + } + + default public T adds( T augend, T addend, float scale ) + { + return getCalculator().adds( get(), augend, addend, scale ); + } + + + default public T scalen( float scale ) + { + return getCalculator().scalen( get(), scale ); + } + + default public T scale( float scale ) + { + return getCalculator().scalei( get(), scale ); + } + + default public T scale( T value, float scale ) + { + return getCalculator().scale( get(), value, scale ); + } + + + default public T divsn( float scale ) + { + return getCalculator().divsn( get(), scale ); + } + + default public T divs( float scale ) + { + return getCalculator().divsi( get(), scale ); + } + + default public T divs( T value, float scale ) + { + return getCalculator().divs( get(), value, scale ); + } + + + default public T addn( T addend ) + { + return getCalculator().addn( get(), addend ); + } + + default public T add( T augend, T addend ) + { + return getCalculator().add( get(), augend, addend ); + } + + default public T add( T amount ) + { + return getCalculator().addi( get(), amount ); + } + + + default public T subn( T subtrahend ) + { + return getCalculator().subn( get(), subtrahend ); + } + + default public T sub( T subtrahend ) + { + return getCalculator().subi( get(), subtrahend ); + } + + default public T sub( T minuend, T subtrahend ) + { + return getCalculator().sub( get(), minuend, subtrahend ); + } + + + default public T muln( T scale ) + { + return getCalculator().muln( get(), scale ); + } + + default public T mul( T scale ) + { + return getCalculator().muli( get(), scale ); + } + + default public T mul( T value, T scale ) + { + return getCalculator().mul( get(), value, scale ); + } + + + default public T divn( T divisor ) + { + return getCalculator().divn( get(), divisor ); + } + + default public T div( T divisor ) + { + return getCalculator().divi( get(), divisor ); + } + + default public T div( T dividend, T divisor ) + { + return getCalculator().div( get(), dividend, divisor ); + } + + + default public T interpolate( T start, T end, float delta ) + { + return getCalculator().interpolate( get(), start, end, delta ); + } + + + default public T reflectn( T normal ) + { + return getCalculator().reflectn( get(), normal ); + } + + default public T reflect( T normal ) + { + return getCalculator().reflecti( get(), normal ); + } + + default public T reflect( T vector, T normal ) + { + return getCalculator().reflect( get(), vector, normal ); + } + + + default public T refractn( T normal ) + { + return getCalculator().refractn( get(), normal ); + } + + default public T refract( T normal ) + { + return getCalculator().refracti( get(), normal ); + } + + default public T refract( T vector, T normal ) + { + return getCalculator().refract( get(), vector, normal ); + } + + + default public boolean isParallel( T value, float epsilon ) + { + return getCalculator().isParallel( get(), value, epsilon ); + } + + default public boolean isParallel( T value ) + { + return getCalculator().isParallel( get(), value ); + } + + + default public boolean isPerpendicular( T value, float epsilon ) + { + return getCalculator().isPerpendicular( get(), value, epsilon ); + } + + default public boolean isPerpendicular( T value ) + { + return getCalculator().isPerpendicular( get(), value ); + } + + + default public T contain( T min, T max ) + { + return getCalculator().contain( get(), min, max ); + } + + + default public boolean contains( T min, T max ) + { + return getCalculator().contains( get(), min, max ); + } + + + default public T random( T min, T max, Binary randomizer ) + { + return getCalculator().random( get(), min, max, randomizer ); + } + + default public T random( T min, T max ) + { + return getCalculator().random( get(), min, max, Numbers::randomFloat ); + } + + + default public float dot( T b ) + { + return getCalculator().dot( get(), b ); + } + + default public float distanceSq( T b ) + { + return getCalculator().distanceSq( get(), b ); + } + + default public float distance( T b ) + { + return getCalculator().distance( get(), b ); + } + + + default public float length() + { + return getCalculator().length( get() ); + } + + default public float lengthSq() + { + return getCalculator().lengthSq( get() ); + } + + + default public float normaln() + { + return getCalculator().normaln( get() ); + } + + default public float normal( T vector ) + { + return getCalculator().normal( get(), vector ); + } + + default public float normal() + { + return getCalculator().normali( get() ); + } + + + default public T negn() + { + return getCalculator().negn( get() ); + } + + default public T neg() + { + return getCalculator().negi( get() ); + } + + default public T neg( T value ) + { + return getCalculator().neg( get(), value ); + } + + + default public T absn() + { + return getCalculator().absn( get() ); + } + + default public T abs() + { + return getCalculator().absi( get() ); + } + + default public T abs( T value ) + { + return getCalculator().abs( get(), value ); + } + + + default public T modsn( float divisor ) + { + return getCalculator().modsn( get(), divisor ); + } + + default public T mods( float divisor ) + { + return getCalculator().modsi( get(), divisor ); + } + + default public T mods( T value, float divisor ) + { + return getCalculator().mods( get(), value, divisor ); + } + + + default public T modn( T divisor ) + { + return getCalculator().modn( get(), divisor ); + } + + default public T mod( T divisor ) + { + return getCalculator().modi( get(), divisor ); + } + + default public T mod( T value, T divisor ) + { + return getCalculator().mod( get(), value, divisor ); + } + + + default public T invertn() + { + return getCalculator().invertn( get() ); + } + + default public T invert() + { + return getCalculator().inverti( get() ); + } + + default public T invert( T value ) + { + return getCalculator().invert( get(), value ); + } + + + default public T floorn() + { + return getCalculator().floorn( get() ); + } + + default public T floor() + { + return getCalculator().floori( get() ); + } + + default public T floor( T value ) + { + return getCalculator().floor( get(), value ); + } + + + default public T ceiln() + { + return getCalculator().ceiln( get() ); + } + + default public T ceil() + { + return getCalculator().ceili( get() ); + } + + default public T ceil( T value ) + { + return getCalculator().ceil( get(), value ); + } + + + default public T minn( T b ) + { + return getCalculator().minn( get(), b ); + } + + default public T min( T other ) + { + return getCalculator().mini( get(), other ); + } + + default public T min( T a, T b ) + { + return getCalculator().min( get(), a, b ); + } + + + default public T maxn( T b ) + { + return getCalculator().maxn( get(), b ); + } + + default public T max( T other ) + { + return getCalculator().maxi( get(), other ); + } + + default public T max( T a, T b ) + { + return getCalculator().max( get(), a, b ); + } + + + default public T clamp( T min, T max ) + { + return getCalculator().clamp( get(), min, max ); + } + + + default public boolean isFinite() + { + return getCalculator().isFinite( get() ); + } + + default public boolean isZero( float epsilon ) + { + return getCalculator().isZero( get(), epsilon ); + } + + default public boolean isZero() + { + return getCalculator().isZero( get() ); + } + + default public boolean isEqual( T b ) + { + return getCalculator().isEqual( get(), b ); + } + + default public boolean isEqual( T b, float epsilon ) + { + return getCalculator().isEqual( get(), b, epsilon ); + } + + + default public boolean isUnit() + { + return getCalculator().isUnit( get() ); + } + + default public boolean isUnit( float epsilon ) + { + return getCalculator().isUnit( get(), epsilon ); + } + + default public boolean isLength( float length ) + { + return getCalculator().isLength( get(), length ); + } + + default public boolean isLength( float length, float epsilon ) + { + return getCalculator().isLength( get(), length, epsilon ); + } + + + default public T lengthenn( float length ) + { + return getCalculator().lengthenn( get(), length ); + } + + default public T lengthen( float length ) + { + return getCalculator().lengtheni( get(), length ); + } + + default public T lengthen( T value, float length ) + { + return getCalculator().lengthen( get(), value, length ); + } + + + default public T clampn( float min, float max ) + { + return getCalculator().clampn( get(), min, max ); + } + + default public T clamp( float min, float max ) + { + return getCalculator().clampi( get(), min, max ); + } + + default public T clamp( T value, float min, float max ) + { + return getCalculator().clamp( get(), value, min, max ); + } + + + default public int bytes() + { + return getCalculator().sizeof( get() ); + } + + default public void write( ByteBuffer to ) + { + getCalculator().write( get(), to ); + } + + default public void read( ByteBuffer from ) + { + getCalculator().read( get(), from ); + } + + default public void write( OutputModel to ) + { + getCalculator().write( get(), to ); + } + + default public void read( InputModel from ) + { + getCalculator().read( get(), from ); + } + + + default public int getComponents() + { + return getCalculator().getComponents(); + } + + default public float getComponent( int index ) + { + return getCalculator().getComponent( get(), index ); + } + + default public T setComponent( int index, float component ) + { + return getCalculator().setComponent( get(), index, component ); + } + + + default public int getDimensions() + { + return getCalculator().getDimensions(); + } + + default public float getDimension( int index ) + { + return getCalculator().getDimension( get(), index ); + } + + default public T setDimension( int index, float dimension ) + { + return getCalculator().setDimension( get(), index, dimension ); + } + + + default public T slerpDirect( T start, T end, float delta ) + { + return getCalculator().slerpDirect( get(), start, end, delta ); + } + + default public T slerpNormal( T start, T end, float delta ) + { + return getCalculator().slerpNormal( get(), start, end, delta ); + } + + default public T slerp( T start, T end, float delta, float angle ) + { + return getCalculator().slerp( get(), start, end, delta, angle ); + } + + + default public float delta( T start, T end ) + { + return getCalculator().delta( start, end, get() ); + } + + + default public T closest( T start, T end, T point ) + { + return getCalculator().closest( get(), start, end, point ); + } + + default public T closest( T start, T end, T point, boolean line ) + { + return getCalculator().closest( get(), start, end, point, line ); + } + + + default public float distanceFrom( T start, T end ) + { + return getCalculator().distanceFrom( start, end, get() ); + } + + default public float distanceFrom( T start, T end, boolean line ) + { + return getCalculator().distanceFrom( start, end, get(), line ); + } + + + default public boolean inView( T origin, T direction, float fovCos ) + { + return getCalculator().inView( origin, direction, fovCos, get() ); + } + + default public boolean inCircleView( T origin, T direction, float fovTan, float fovCos, float radius, boolean entirely ) + { + return getCalculator().inCircleView( origin, direction, fovTan, fovCos, get(), radius, entirely ); + } + + + default public T cubicCurve( float delta, T p0, T p1, T p2, T p3, float[][] matrix) + { + return getCalculator().cubicCurve( get(), delta, p0, p1, p2, p3, matrix ); + } + + default public T cubicCurve( float delta, T p0, T p1, T p2, T p3, float[][] matrix, boolean inverse ) + { + return getCalculator().cubicCurve( get(), delta, p0, p1, p2, p3, matrix, inverse ); + } + + default public T parametricCubicCurve( float delta, T[] points, float[][] matrix, float weight ) + { + return getCalculator().parametricCubicCurve( get(), delta, points, matrix, weight ); + } + + default public T parametricCubicCurve( float delta, T[] points, float[][] matrix, float weight, boolean inverse, boolean loop ) + { + return getCalculator().parametricCubicCurve( get(), delta, points, matrix, weight, inverse, loop ); + } + +} diff --git a/src/com/axe/core/Bufferable.java b/src/com/axe/core/Bufferable.java new file mode 100755 index 0000000..41e7438 --- /dev/null +++ b/src/com/axe/core/Bufferable.java @@ -0,0 +1,13 @@ +package com.axe.core; + +import java.nio.ByteBuffer; + + +public interface Bufferable +{ + public void write( ByteBuffer out ); + + public void read( ByteBuffer in ); + + public int bytes(); +} diff --git a/src/com/axe/core/Cloned.java b/src/com/axe/core/Cloned.java new file mode 100755 index 0000000..a79980c --- /dev/null +++ b/src/com/axe/core/Cloned.java @@ -0,0 +1,6 @@ +package com.axe.core; + +public interface Cloned +{ + public T clone(); +} diff --git a/src/com/axe/core/Expirable.java b/src/com/axe/core/Expirable.java new file mode 100755 index 0000000..44fa6a0 --- /dev/null +++ b/src/com/axe/core/Expirable.java @@ -0,0 +1,9 @@ +package com.axe.core; + + +public interface Expirable +{ + public void expire(); + public boolean hasExpired(); + public void onExpire(); +} diff --git a/src/com/axe/core/Factory.java b/src/com/axe/core/Factory.java new file mode 100755 index 0000000..d4e2c49 --- /dev/null +++ b/src/com/axe/core/Factory.java @@ -0,0 +1,7 @@ +package com.axe.core; + + +public interface Factory +{ + public T create(); +} diff --git a/src/com/axe/core/HasData.java b/src/com/axe/core/HasData.java new file mode 100644 index 0000000..22948d1 --- /dev/null +++ b/src/com/axe/core/HasData.java @@ -0,0 +1,15 @@ +package com.axe.core; + +public interface HasData +{ + + public T getData(); + + default public T getData(Class ofType) + { + return ofType.isInstance( getData() ) ? getData() : null; + } + + public void setData(Object data); + +} diff --git a/src/com/axe/core/Match.java b/src/com/axe/core/Match.java new file mode 100755 index 0000000..9991139 --- /dev/null +++ b/src/com/axe/core/Match.java @@ -0,0 +1,34 @@ +package com.axe.core; + + +public enum Match +{ + Exact() { + public final boolean isMatch( long set, long input ) { + return ( set == input ); + } + }, + All() { + public final boolean isMatch( long set, long input ) { + return ( set & input ) == input; + } + }, + AnyOf() { + public final boolean isMatch( long set, long input ) { + return ( set & input ) != 0; + } + }, + None() { + public final boolean isMatch( long set, long input ) { + return ( set & input ) == 0; + } + }, + NotAll() { + public final boolean isMatch( long set, long input ) { + return ( set & input ) != input; + } + }; + + public abstract boolean isMatch( long set, long input ); + +} diff --git a/src/com/axe/easing/AbstractEasingMethod.java b/src/com/axe/easing/AbstractEasingMethod.java new file mode 100755 index 0000000..9f3a965 --- /dev/null +++ b/src/com/axe/easing/AbstractEasingMethod.java @@ -0,0 +1,18 @@ + +package com.axe.easing; + +public abstract class AbstractEasingMethod implements EasingMethod +{ + + private final String name; + + public AbstractEasingMethod( String name ) + { + this.name = name; + } + + public String name() + { + return name; + } +} diff --git a/src/com/axe/easing/AbstractEasingType.java b/src/com/axe/easing/AbstractEasingType.java new file mode 100755 index 0000000..9c671b1 --- /dev/null +++ b/src/com/axe/easing/AbstractEasingType.java @@ -0,0 +1,18 @@ + +package com.axe.easing; + +public abstract class AbstractEasingType implements EasingType +{ + + private final String name; + + public AbstractEasingType( String name ) + { + this.name = name; + } + + public String name() + { + return name; + } +} diff --git a/src/com/axe/easing/Easing.java b/src/com/axe/easing/Easing.java new file mode 100755 index 0000000..62beb08 --- /dev/null +++ b/src/com/axe/easing/Easing.java @@ -0,0 +1,94 @@ + +package com.axe.easing; + +import com.axe.io.DataModel; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class Easing implements DataModel +{ + + private EasingType type; + private EasingMethod method; + private float scale; + + public Easing() + { + this( Easings.In, Easings.Linear, 1f ); + } + + public Easing( Easing e ) + { + this( e.type, e.method, e.scale ); + } + + public Easing( EasingType type, EasingMethod method ) + { + this( type, method, 1f ); + } + + public Easing( EasingType type, EasingMethod method, float scale ) + { + this.type = type; + this.method = method; + this.scale = scale; + } + + public float delta( float delta ) + { + float d = type.delta( delta, method ); + if (scale != 1f) + { + d = scale * d + (1 - scale) * delta; + } + return d; + } + + public EasingType type() + { + return type; + } + + public void type( EasingType type ) + { + this.type = type; + } + + public EasingMethod method() + { + return method; + } + + public void method( EasingMethod method ) + { + this.method = method; + } + + public float scale() + { + return scale; + } + + public void scale( float scale ) + { + this.scale = scale; + } + + @Override + public void write( OutputModel output ) + { + output.write( "scale", scale ); + output.write( "type", type.name() ); + output.write( "method", method.name() ); + } + + @Override + public void read( InputModel input ) + { + scale = input.readFloat( "scale" ); + type = Easings.TypeMap.get( input.readString( "type" ) ); + method = Easings.MethodMap.get( input.readString( "method" ) ); + } + +} diff --git a/src/com/axe/easing/EasingMethod.java b/src/com/axe/easing/EasingMethod.java new file mode 100755 index 0000000..0cbe0a4 --- /dev/null +++ b/src/com/axe/easing/EasingMethod.java @@ -0,0 +1,10 @@ + +package com.axe.easing; + +public interface EasingMethod +{ + + public float motion( float d ); + + public String name(); +} diff --git a/src/com/axe/easing/EasingType.java b/src/com/axe/easing/EasingType.java new file mode 100755 index 0000000..407fdb9 --- /dev/null +++ b/src/com/axe/easing/EasingType.java @@ -0,0 +1,10 @@ + +package com.axe.easing; + +public interface EasingType +{ + + public float delta( float d, EasingMethod f ); + + public String name(); +} diff --git a/src/com/axe/easing/Easings.java b/src/com/axe/easing/Easings.java new file mode 100755 index 0000000..d67f697 --- /dev/null +++ b/src/com/axe/easing/Easings.java @@ -0,0 +1,172 @@ +package com.axe.easing; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.axe.util.Reflections; + + +public class Easings +{ + + public static final EasingType In = new AbstractEasingType( "In" ) { + public float delta(float d, EasingMethod f) { + return f.motion(d); + } + }; + public static final EasingType Out = new AbstractEasingType( "Out" ) { + public float delta(float d, EasingMethod f) { + return (1 - f.motion(1 - d)); + } + }; + public static final EasingType InOut = new AbstractEasingType( "InOut" ) { + public float delta(float d, EasingMethod f) { + if (d < 0.5) + return f.motion(2 * d) * 0.5f; + + return (1 - f.motion(2 - 2 * d) * 0.5f); + } + }; + public static final EasingType PingPong = new AbstractEasingType( "PingPong" ) { + public float delta(float d, EasingMethod f) { + if (d < 0.5) + return f.motion(2 * d); + + return f.motion(2 - 2 * d); + } + }; + public static final EasingMethod Linear = new AbstractEasingMethod( "Linear" ) { + public float motion(float d) { + return d; + } + }; + public static EasingMethod Quadratic = new AbstractEasingMethod( "Quadratic" ) { + public float motion(float d) { + return d * d; + } + }; + public static EasingMethod Cubic = new AbstractEasingMethod( "Cubic" ) { + public float motion(float d) { + return d * d * d; + } + }; + public static EasingMethod Quartic = new AbstractEasingMethod( "Quartic" ) { + public float motion(float d) { + float d2 = d * d; + return d2 * d2; + } + }; + public static EasingMethod Quintic = new AbstractEasingMethod( "Quintic" ) { + public float motion(float d) { + float d2 = d * d; + return d2 * d2 * d; + } + }; + public static EasingMethod Back = new AbstractEasingMethod( "Back" ) { + public float motion(float d) { + float d2 = d * d; + float d3 = d2 * d; + return d3 + d2 - d; + } + }; + public static EasingMethod Sine = new AbstractEasingMethod( "Sine" ) { + private double FREQUENCY = Math.PI * 0.5; + public float motion(float d) { + return (float)Math.sin(d * FREQUENCY); + } + }; + public static EasingMethod Elastic = new AbstractEasingMethod( "Elastic" ) { + private double FREQUENCY = Math.PI * 3.5; + public float motion(float d) { + float d2 = d * d; + float d3 = d2 * d; + float scale = d2 * ((2 * d3) + d2 - (4 * d) + 2); + float wave = -(float)Math.sin(d * FREQUENCY); + return scale * wave; + } + }; + public static EasingMethod Revisit = new AbstractEasingMethod( "Revisit" ) { + private double FREQUENCY = Math.PI; + public float motion(float d) { + return (float)Math.abs(-Math.sin(d * FREQUENCY) + d); + } + }; + public static EasingMethod SlowBounce = new AbstractEasingMethod( "SlowBounce" ) { + private double FREQUENCY = Math.PI * Math.PI * 1.5; + public float motion(float d) { + float d2 = d * d; + return (float)(1 - Math.abs((1 - d2) * Math.cos(d2 * d * FREQUENCY))); + } + }; + public static EasingMethod Bounce = new AbstractEasingMethod( "Bounce" ) { + private double FREQUENCY = Math.PI * Math.PI * 1.5; + public float motion(float d) { + return (float)(1 - Math.abs((1 - d) * Math.cos(d * d * FREQUENCY))); + } + }; + public static EasingMethod SmallBounce = new AbstractEasingMethod( "SmallBounce" ) { + private double FREQUENCY = Math.PI * Math.PI * 1.5; + public float motion(float d) { + float inv = 1 - d; + return (float)(1 - Math.abs(inv * inv * Math.cos(d * d * FREQUENCY))); + } + }; + public static EasingMethod TinyBounce = new AbstractEasingMethod( "TinyBounce" ) { + private double FREQUENCY = 7; + public float motion(float d) { + float inv = 1 - d; + return (float)(1 - Math.abs(inv * inv * Math.cos(d * d * FREQUENCY))); + } + }; + public static EasingMethod Hesitant = new AbstractEasingMethod( "Hesitant" ) { + public float motion(float d) { + return (float)(Math.cos(d * d * 12) * d * (1 - d) + d); + } + }; + public static EasingMethod Lasso = new AbstractEasingMethod( "Lasso" ) { + public float motion(float d) { + float d2 = d * d; + return (float)(1 - Math.cos(d2 * d * 36) * (1 - d)); + } + }; + public static EasingMethod Sqrt = new AbstractEasingMethod( "Sqrt" ) { + public float motion(float d) { + return (float)Math.sqrt(d); + } + }; + public static EasingMethod Log10 = new AbstractEasingMethod( "Log10" ) { + public float motion(float d) { + return (float)((Math.log10(d) + 2) * 0.5); + } + }; + public static EasingMethod Slingshot = new AbstractEasingMethod( "Slingshot" ) { + public float motion(float d) { + if (d < 0.7f) + return (d * -0.357f); + + float x = d - 0.7f; + return ((x * x * 27.5f - 0.5f) * 0.5f); + } + }; + public static EasingMethod Circlular = new AbstractEasingMethod( "Circular" ) { + public float motion(float d) { + return 1 - (float)Math.sqrt( 1 - d * d ); + } + }; + public static EasingMethod Gentle = new AbstractEasingMethod( "Gentle" ) { + public float motion( float d ) { + return (3 * (1 - d) * d * d) + (d * d * d); + } + }; + + public static Easing Default = new Easing( In, Linear ); + + public static EasingType[] Types = Reflections.getStaticFieldArray( EasingType.class, true, Easings.class ); + + public static Map TypeMap = Reflections.getStaticFieldMap( EasingType.class, true, Easings.class, new LinkedHashMap() ); + + public static EasingMethod[] Methods = Reflections.getStaticFieldArray( EasingMethod.class, true, Easings.class ); + + public static Map MethodMap = Reflections.getStaticFieldMap( EasingMethod.class, true, Easings.class, new LinkedHashMap() ); + +} diff --git a/src/com/axe/event/Event.java b/src/com/axe/event/Event.java new file mode 100644 index 0000000..43765f1 --- /dev/null +++ b/src/com/axe/event/Event.java @@ -0,0 +1,11 @@ +package com.axe.event; + +public class Event { + public int id; + public Class listenerClass; + + public Event(int id, Class listenerClass) { + this.id = id; + this.listenerClass = listenerClass; + } +} \ No newline at end of file diff --git a/src/com/axe/event/EventListenerList.java b/src/com/axe/event/EventListenerList.java new file mode 100644 index 0000000..eeb898c --- /dev/null +++ b/src/com/axe/event/EventListenerList.java @@ -0,0 +1,41 @@ +package com.axe.event; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import com.axe.collect.ListArray; + +public class EventListenerList extends ListArray implements InvocationHandler +{ + + public final L proxy; + + public EventListenerList(Class listenerClass, L ... initial ) + { + super( DEFAULT_INITIAL_CAPACITY, DEFAULT_INCREASE, false, initial ); + + final ClassLoader loader = getClass().getClassLoader(); + final Class[] interfaces = { listenerClass }; + + this.proxy = (L) Proxy.newProxyInstance( loader, interfaces, this); + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + Object result = null; + + for (int i = 0; i < size; i++) + { + L listener = values[ i ]; + + if (listener != null) + { + result = method.invoke(listener, args); + } + } + + return result; + } + +} \ No newline at end of file diff --git a/src/com/axe/event/EventMap.java b/src/com/axe/event/EventMap.java new file mode 100644 index 0000000..387b79b --- /dev/null +++ b/src/com/axe/event/EventMap.java @@ -0,0 +1,52 @@ +package com.axe.event; + +import java.util.Arrays; + +import com.axe.collect.ListItem; + +public class EventMap implements HasEvents +{ + + private EventListenerList[] lists = {}; + + public EventMap getEvents() + { + return this; + } + + public > ListItem on(S event, L listener) + { + return getList(event).add(listener); + } + + public > L listeners(S event) + { + return getList(event).proxy; + } + + public > void off(S event) + { + getList(event).clear(); + } + + public > boolean off(S event, L listener) + { + return getList(event).remove(listener); + } + + protected > EventListenerList getList(S event) + { + if (lists.length <= event.id) + { + lists = Arrays.copyOf(lists, event.id + 1); + } + + if (lists[event.id] == null) + { + lists[event.id] = new EventListenerList<>(event.listenerClass); + } + + return lists[event.id]; + } + +} \ No newline at end of file diff --git a/src/com/axe/event/HasEvents.java b/src/com/axe/event/HasEvents.java new file mode 100644 index 0000000..e3f92c0 --- /dev/null +++ b/src/com/axe/event/HasEvents.java @@ -0,0 +1,30 @@ +package com.axe.event; + +import com.axe.collect.ListItem; + +public interface HasEvents +{ + + public EventMap getEvents(); + + default public > ListItem on(S event, L listener) + { + return getEvents().on( event, listener ); + } + + default public > void off(S event) + { + getEvents().off( event ); + } + + default public > boolean off(S event, L listener) + { + return getEvents().off( event, listener ); + } + + default public > L listeners(S event) + { + return getEvents().listeners( event ); + } + +} \ No newline at end of file diff --git a/src/com/axe/fx/Components.java b/src/com/axe/fx/Components.java new file mode 100644 index 0000000..38f5b70 --- /dev/null +++ b/src/com/axe/fx/Components.java @@ -0,0 +1,36 @@ +package com.axe.fx; + +public class Components +{ + + // Spatial + public static final int POSITION = 0; + public static final int VELOCITY = 1; + public static final int ACCELERATION = 2; + + // Rotation + public static final int ROTATION = 3; + public static final int ROTATION_VELOCITY = 4; + public static final int ROTATION_ACCELERATION = 5; + + // Scaling + public static final int SCALE = 6; + public static final int SCALE_VELOCITY = 7; + public static final int SCALE_ACCELERATION = 8; + + // Anchor + public static final int ANCHOR = 9; + + // Color + public static final int COLOR = 10; + public static final int ALPHA = 11; + + // Tile + public static final int TILE = 12; + + // Size + public static final int SIZE = 13; + public static final int SIZE_VELOCITY = 14; + public static final int SIZE_ACCELERATION = 15; + +} diff --git a/src/com/axe/fx/Particle.java b/src/com/axe/fx/Particle.java new file mode 100644 index 0000000..68c9985 --- /dev/null +++ b/src/com/axe/fx/Particle.java @@ -0,0 +1,15 @@ +package com.axe.fx; + +import com.axe.math.Vec; +import com.axe.node.Node; + +public interface Particle> extends Node +{ + + public void resetParticle(); + + public C get(int component); + + public float getDelta(); + +} diff --git a/src/com/axe/fx/ParticleListener.java b/src/com/axe/fx/ParticleListener.java new file mode 100644 index 0000000..786ecf4 --- /dev/null +++ b/src/com/axe/fx/ParticleListener.java @@ -0,0 +1,6 @@ +package com.axe.fx; + +public interface ParticleListener +{ + public void onEvent(T particle); +} diff --git a/src/com/axe/fx/ParticleModifier.java b/src/com/axe/fx/ParticleModifier.java new file mode 100644 index 0000000..97079aa --- /dev/null +++ b/src/com/axe/fx/ParticleModifier.java @@ -0,0 +1,8 @@ +package com.axe.fx; + +import com.axe.game.GameState; + +public interface ParticleModifier

+{ + public void modify( P[] particles, int particleCount, Particle particle, GameState state ); +} diff --git a/src/com/axe/fx/Particles.java b/src/com/axe/fx/Particles.java new file mode 100644 index 0000000..74350e0 --- /dev/null +++ b/src/com/axe/fx/Particles.java @@ -0,0 +1,20 @@ +package com.axe.fx; + +import com.axe.math.Vec; +import com.axe.node.Node; +import com.axe.node.Nodes; + +public class Particles, N extends Node> extends Nodes +{ + + public ParticlesTemplate template; + public ParticleListener[] initializers; + public ParticleListener[] finalizers; + + @Override + protected void destroyNode(N node) + { + template.memory.free( node ); + } + +} diff --git a/src/com/axe/fx/ParticlesTemplate.java b/src/com/axe/fx/ParticlesTemplate.java new file mode 100644 index 0000000..e0a20b8 --- /dev/null +++ b/src/com/axe/fx/ParticlesTemplate.java @@ -0,0 +1,10 @@ +package com.axe.fx; + +import com.axe.mem.Memory; + +public class ParticlesTemplate

+{ + + public Memory

memory; + +} diff --git a/src/com/axe/game/AbstractGame.java b/src/com/axe/game/AbstractGame.java new file mode 100644 index 0000000..be013f0 --- /dev/null +++ b/src/com/axe/game/AbstractGame.java @@ -0,0 +1,262 @@ +package com.axe.game; + +import java.util.concurrent.TimeUnit; + +import com.axe.Axe; +import com.axe.Scene; +import com.axe.collect.ListArray; +import com.axe.event.EventMap; +import com.axe.loop.Loop; +import com.axe.loop.LoopVariable; +import com.axe.math.Vec; +import com.axe.window.Window; +import com.axe2d.Scene2; +import com.axe3d.Scene3; + +public abstract class AbstractGame implements Game +{ + + public EventMap events; + public Loop loop; + public GameState state; + public ListArray scenes; + public ListArray windows; + public Window focusedWindow; + public boolean playing; + public long hiddenSleepTime; + public int liveWindows; + public int hiddenWindows; + public Object data; + + public AbstractGame() + { + this.events = new EventMap(); + this.loop = new LoopVariable(100, TimeUnit.MILLISECONDS); + this.state = new GameState(); + this.scenes = ListArray.compacted(); + this.windows = ListArray.compacted(); + this.hiddenSleepTime = DEFAULT_HIDDEN_SLEEP_TIME; + } + + @Override + public EventMap getEvents() + { + return events; + } + + @Override + public Loop getLoop() + { + return loop; + } + + @Override + public Game setLoop(Loop loop) + { + this.loop = loop; + return this; + } + + @Override + public GameState getState() + { + return state; + } + + @Override + public ListArray getScenes() + { + return scenes; + } + + @Override + public T getData() + { + return (T) data; + } + + @Override + public void setData(Object data) + { + this.data = data; + } + + @Override + public , S extends Scene> S newScene(int dimensions) + { + Scene scene = null; + + switch (dimensions) { + case 2: scene = new Scene2(); break; + case 3: scene = new Scene3(); break; + } + + if (scene == null) + { + throw new RuntimeException(dimensions + " is not a supported dimension"); + } + + scenes.add( scene ); + + return (S)scene; + } + + @Override + public ListArray getWindows() + { + return windows; + } + + @Override + public boolean isPlaying() + { + return playing; + } + + @Override + public long getHiddenSleepTime() + { + return hiddenSleepTime; + } + + @Override + public void setHiddenSleepTime(long hiddenSleepTime) + { + this.hiddenSleepTime = hiddenSleepTime; + } + + @Override + public void stop() + { + playing = false; + } + + @Override + public Window getFocusedWindow() + { + return focusedWindow; + } + + @Override + public void show() + { + windows.forAll(window -> window.show()); + } + + @Override + public void run() + { + show(); + + listeners(GameEvent.Start).onEvent(this); + + loop.onStart(state); + + prepare(); + + scenes.forAll( scene -> scene.activate() ); + + playing = true; + + while (playing) + { + liveWindows = 0; + hiddenWindows = 0; + + windows.forAll((window)-> + { + if ( window.isCloseRequest() ) + { + window.close(); + } + else + { + liveWindows++; + + if ( window.isHidden() ) + { + hiddenWindows++; + } + else if ( window.isFocused() ) + { + focusedWindow = window; + } + } + }); + + if ( liveWindows == 0 ) + { + break; + } + + if ( hiddenWindows == liveWindows ) + { + sleep(); + + continue; + } + + windows.forAll( window -> window.update() ); + + processInput(); + + loop.onLoop( state ); + + if ( state.updates > 0 ) + { + windows.forAll( window -> window.updateMouse() ); + + for (int i = 0; i < state.updates; i++) + { + listeners( GameEvent.UpdateStart ).onEvent( this ); + + scenes.forAll( scene -> scene.update( state ) ); + + listeners( GameEvent.UpdateEnd ).onEvent( this ); + } + + flushInput(); + } + + if ( state.draw ) + { + listeners( GameEvent.DrawStart ).onEvent( this ); + + windows.forAll(window -> + { + if ( !window.isHidden() ) + { + window.draw( state ); + } + }); + + listeners( GameEvent.DrawEnd ).onEvent( this ); + } + } + + listeners( GameEvent.Stop ).onEvent( this ); + + destroy(); + } + + protected void sleep() + { + try + { + Thread.sleep( hiddenSleepTime ); + } + catch (InterruptedException e) + { + Axe.logger.log(e); + } + } + + protected abstract void prepare(); + + protected abstract void processInput(); + + protected abstract void flushInput(); + + protected abstract void destroy(); + +} diff --git a/src/com/axe/game/Game.java b/src/com/axe/game/Game.java new file mode 100644 index 0000000..0acd73d --- /dev/null +++ b/src/com/axe/game/Game.java @@ -0,0 +1,44 @@ +package com.axe.game; + +import com.axe.Scene; +import com.axe.collect.ListArray; +import com.axe.core.HasData; +import com.axe.event.HasEvents; +import com.axe.loop.Loop; +import com.axe.math.Vec; +import com.axe.window.Window; + +public interface Game extends HasEvents, HasData +{ + + public static final long DEFAULT_HIDDEN_SLEEP_TIME = 100; + + public Loop getLoop(); + + public Game setLoop(Loop loop); + + public GameState getState(); + + public ListArray getScenes(); + + public , S extends Scene> S newScene(int dimensions); + + public ListArray getWindows(); + + public Window newWindow(); + + public Window getFocusedWindow(); + + public boolean isPlaying(); + + public long getHiddenSleepTime(); + + public void setHiddenSleepTime(long hiddenSleepTime); + + public void show(); + + public void run(); + + public void stop(); + +} \ No newline at end of file diff --git a/src/com/axe/game/GameEvent.java b/src/com/axe/game/GameEvent.java new file mode 100644 index 0000000..b0200ff --- /dev/null +++ b/src/com/axe/game/GameEvent.java @@ -0,0 +1,26 @@ +package com.axe.game; + +import com.axe.event.Event; + +public class GameEvent +{ + + public static final Event Start = + new Event<>(0, GameEventListener.class); + + public static final Event DrawStart = + new Event<>(1, GameEventListener.class); + + public static final Event DrawEnd = + new Event<>(2, GameEventListener.class); + + public static final Event UpdateStart = + new Event<>(3, GameEventListener.class); + + public static final Event UpdateEnd = + new Event<>(4, GameEventListener.class); + + public static final Event Stop = + new Event<>(5, GameEventListener.class); + +} \ No newline at end of file diff --git a/src/com/axe/game/GameEventListener.java b/src/com/axe/game/GameEventListener.java new file mode 100644 index 0000000..8e6954e --- /dev/null +++ b/src/com/axe/game/GameEventListener.java @@ -0,0 +1,5 @@ +package com.axe.game; + +public interface GameEventListener { + public void onEvent(Game game); +} \ No newline at end of file diff --git a/src/com/axe/game/GameSettings.java b/src/com/axe/game/GameSettings.java new file mode 100644 index 0000000..1ddd2d4 --- /dev/null +++ b/src/com/axe/game/GameSettings.java @@ -0,0 +1,2 @@ +package com.axe.game; +public class GameSettings {} \ No newline at end of file diff --git a/src/com/axe/game/GameState.java b/src/com/axe/game/GameState.java new file mode 100644 index 0000000..9a4f5a4 --- /dev/null +++ b/src/com/axe/game/GameState.java @@ -0,0 +1,38 @@ +package com.axe.game; + +public class GameState +{ + + public float seconds; + public long millis; + public long nanos; + public long micros; + public long startTime; + public long lastTime; + public long currentTime; + public float interpolation = 0; + public float reverse = 0; + public int updates = 1; + public boolean draw = true; + + public long reset() + { + return ( startTime = lastTime = currentTime = System.nanoTime() ); + } + + public long tick() + { + lastTime = currentTime; + currentTime = System.nanoTime(); + return (currentTime - lastTime); + } + + public void setElapsed(long nanosElapsed) + { + nanos = nanosElapsed; + micros = nanosElapsed / 1000L; + millis = nanosElapsed / 1000000L; + seconds = (float)(nanosElapsed * 0.000000001); + } + +} \ No newline at end of file diff --git a/src/com/axe/gfx/AbstractTexture.java b/src/com/axe/gfx/AbstractTexture.java new file mode 100644 index 0000000..c42abfb --- /dev/null +++ b/src/com/axe/gfx/AbstractTexture.java @@ -0,0 +1,155 @@ +package com.axe.gfx; + + +public abstract class AbstractTexture implements Texture +{ + + public TextureInfo info; + + // original width of the image in pixels + public int imageWidth; + // original height of the image in pixels + public int imageHeight; + + // computed image width in pixels + public int width; + // computed image height in pixels + public int height; + + // the normalized width of a pixel (1 / width) + public float pixelW; + // half of the width of a pixel (used for coordinate correction). + public float offW; + + // the normalized height of a pixel (1 / height) + public float pixelH; + // half of the height of a pixel (used for coordinate correction). + public float offH; + + // The texture wrapping function + public Wrap wrap = Wrap.Default; + // The texture minification function + public Minify min = Minify.Linear; + // The texture magnification function + public Magnify mag = Magnify.Linear; + + // The anisotropic filter + public int anisotrophy = 1; + + + public AbstractTexture(TextureInfo info, int imageWidth, int imageHeight, int textureWidth, int textureHeight) + { + this.info = info; + this.imageWidth = imageWidth; + this.imageHeight = imageHeight; + this.width = textureWidth; + this.pixelW = 1f / textureWidth; + this.offW = pixelW * 0.5f; + this.height = textureHeight; + this.pixelH = 1f / textureHeight; + this.offH = pixelH * 0.5f; + } + + @Override + public TextureInfo getInfo() + { + return info; + } + + @Override + public int width() + { + return width; + } + + @Override + public int height() + { + return height; + } + + @Override + public int getImageHeight() + { + return imageHeight; + } + + @Override + public int getImageWidth() + { + return imageWidth; + } + + @Override + public float getPixelWidth() + { + return pixelW; + } + + @Override + public float getPixelOffsetX() + { + return offW; + } + + @Override + public float getPixelHeight() + { + return pixelH; + } + + @Override + public float getPixelOffsetY() + { + return offH; + } + + @Override + public Magnify magnify() + { + return mag; + } + + @Override + public void magnify(Magnify mag) + { + this.mag = mag; + } + + @Override + public Minify minify() + { + return min; + } + + @Override + public void minify(Minify min) + { + this.min = min; + } + + @Override + public Wrap wrap() + { + return wrap; + } + + @Override + public void wrap(Wrap wrap) + { + this.wrap = wrap; + } + + @Override + public int anisotrophy() + { + return anisotrophy; + } + + @Override + public void anisotrophy(int anisotrophy) + { + this.anisotrophy = anisotrophy; + } + +} diff --git a/src/com/axe/gfx/AbstractVertexBuffer.java b/src/com/axe/gfx/AbstractVertexBuffer.java new file mode 100755 index 0000000..b3a3dfe --- /dev/null +++ b/src/com/axe/gfx/AbstractVertexBuffer.java @@ -0,0 +1,63 @@ +package com.axe.gfx; + +public abstract class AbstractVertexBuffer implements VertexBuffer +{ + + protected VertexMesh mesh; + protected int position; + + protected AbstractVertexBuffer() + { + } + + public void reset(VertexMesh mesh) + { + this.mesh = mesh; + this.position = 0; + this.data().position( 0 ); + } + + public VertexMesh mesh() + { + return mesh; + } + + public void next() + { + final int s = mesh.format().stride; + + data().position( position += s ); + } + + public void vertex(int index) + { + final int s = mesh.format().stride; + + data().position( position = index * s ); + } + + public int capacity() + { + final int s = mesh.format().stride; + + return data().capacity() / s; + } + + public int vertices() + { + final int s = mesh.format().stride; + final int p = data().position(); + + return (p + s - 1) / s; + } + + public int remaining() + { + final int s = mesh.format().stride; + final int p = data().position(); + final int r = data().remaining(); + + return (r + p % s) / s; + } + +} diff --git a/src/com/axe/gfx/Blend.java b/src/com/axe/gfx/Blend.java new file mode 100755 index 0000000..e4a1231 --- /dev/null +++ b/src/com/axe/gfx/Blend.java @@ -0,0 +1,15 @@ + +package com.axe.gfx; + +public enum Blend +{ + Add, + AlphaAdd, + Alpha, + Color, + Minus, + PremultAlpha, + Modulate, + Xor, + None +} diff --git a/src/com/axe/gfx/ColorOperation.java b/src/com/axe/gfx/ColorOperation.java new file mode 100755 index 0000000..ec16f2a --- /dev/null +++ b/src/com/axe/gfx/ColorOperation.java @@ -0,0 +1,22 @@ + +package com.axe.gfx; + +public enum ColorOperation +{ + Clear, + Set, + Copy, + CopyInverted, + Noop, + Inverted, + And, + NAnd, + Or, + NOr, + XOr, + Equiv, + AndReverse, + AndInverted, + OrReverse, + OrInverted +} diff --git a/src/com/axe/gfx/Coord.java b/src/com/axe/gfx/Coord.java new file mode 100755 index 0000000..ba8bdc7 --- /dev/null +++ b/src/com/axe/gfx/Coord.java @@ -0,0 +1,54 @@ + +package com.axe.gfx; + +import com.axe.core.Attribute; +import com.axe.core.Factory; +import com.axe.math.calc.CalculatorCoord; + + +public class Coord implements Attribute +{ + + public static Factory Factory = new Factory() { + public Coord create() + { + return new Coord(); + } + }; + + public float s, t; + + public Coord() + { + } + + public Coord( float s, float t ) + { + set( s, t ); + } + + public void set( float s, float t ) + { + this.s = s; + this.t = t; + } + + @Override + public Coord get() + { + return this; + } + + @Override + public Coord clone() + { + return new Coord( s, t ); + } + + @Override + public CalculatorCoord getCalculator() + { + return CalculatorCoord.INSTANCE; + } + +} diff --git a/src/com/axe/gfx/DataType.java b/src/com/axe/gfx/DataType.java new file mode 100755 index 0000000..314b9be --- /dev/null +++ b/src/com/axe/gfx/DataType.java @@ -0,0 +1,21 @@ +package com.axe.gfx; + + +public enum DataType +{ + Byte( 1 ), + Ubyte( 1 ), + Short( 2 ), + Ushort( 2 ), + Integer( 4 ), + Uint ( 4 ), + Float( 4 ), + Double( 8 ); + + public final int bytes; + + private DataType(int bytes) + { + this.bytes = bytes; + } +} \ No newline at end of file diff --git a/src/com/axe/gfx/FogMode.java b/src/com/axe/gfx/FogMode.java new file mode 100644 index 0000000..ebb4af6 --- /dev/null +++ b/src/com/axe/gfx/FogMode.java @@ -0,0 +1,8 @@ +package com.axe.gfx; + +public enum FogMode +{ + Exp, + Exp2, + Linear +} diff --git a/src/com/axe/gfx/GraphicsEngine.java b/src/com/axe/gfx/GraphicsEngine.java new file mode 100644 index 0000000..206455a --- /dev/null +++ b/src/com/axe/gfx/GraphicsEngine.java @@ -0,0 +1,60 @@ +package com.axe.gfx; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import com.axe.util.Buffers; + +public interface GraphicsEngine +{ + + public VertexMesh newVertexMesh(VertexMeshType type, VertexFormat format); + + public VertexBuffer newVertexBuffer(int chunkSizeInBytes, boolean autoExpand); + + default public VertexBuffer newVertexBufferFor(VertexMeshType type, VertexFormat format, int chunkSizeInVertices, boolean autoExpand) + { + VertexMesh mesh = newVertexMesh( type, format ); + VertexBuffer buffer = newVertexBuffer( format.stride * chunkSizeInVertices, autoExpand ); + + buffer.reset( mesh ); + + return buffer; + } + + default public ByteBuffer newDataBuffer(int count, DataType type) + { + return Buffers.bytes( count * type.bytes ); + } + + default public ShortBuffer newIndexBuffer(int indexCount) + { + return Buffers.shorts( indexCount ); + } + + default public IntBuffer newLargeIndexBuffer(int indexCount) + { + return Buffers.ints( indexCount ); + } + + default public ByteBuffer newSmallIndexBuffer(int indexCount) + { + return Buffers.bytes( indexCount ); + } + + public ShaderObject newShaderObject( ShaderObjectType type, String code ); + + public Shader newShader( ShaderObject ... objects ); + + public Shader newShader( String ... objectPaths ); + + public Shader newShader( Class> enumClass, ShaderObject ... objects ); + + public Shader newShader( Class> enumClass, String ... objectPaths ); + + public String getVersion(); + + public String getGraphicsCard(); + +} diff --git a/src/com/axe/gfx/Magnify.java b/src/com/axe/gfx/Magnify.java new file mode 100755 index 0000000..6c083cc --- /dev/null +++ b/src/com/axe/gfx/Magnify.java @@ -0,0 +1,8 @@ + +package com.axe.gfx; + +public enum Magnify +{ + Nearest, + Linear +} diff --git a/src/com/axe/gfx/Minify.java b/src/com/axe/gfx/Minify.java new file mode 100755 index 0000000..15c38fd --- /dev/null +++ b/src/com/axe/gfx/Minify.java @@ -0,0 +1,12 @@ + +package com.axe.gfx; + +public enum Minify +{ + Nearest, + Linear, + NearestNearest, + LinearNearest, + NearestLinear, + LinearLinear +} diff --git a/src/com/axe/gfx/Mode.java b/src/com/axe/gfx/Mode.java new file mode 100755 index 0000000..8f2df85 --- /dev/null +++ b/src/com/axe/gfx/Mode.java @@ -0,0 +1,19 @@ + +package com.axe.gfx; + +public enum Mode +{ + AlphaTest, + Blend, + Texture, + Culling, + Depth, + Lighting, + Normalize, + Smooth, + Clipping, + LineSmooth, + DepthMask, + Fog, + LogicalOperation +} diff --git a/src/com/axe/gfx/Primitive.java b/src/com/axe/gfx/Primitive.java new file mode 100755 index 0000000..2e35c22 --- /dev/null +++ b/src/com/axe/gfx/Primitive.java @@ -0,0 +1,16 @@ +package com.axe.gfx; + + +public enum Primitive +{ + Point, + Line, + LineStrip, + LineLoop, + Triangle, + TriangleStrip, + TriangleFan, + Quad, + QuadStrip, + Polygon; +} diff --git a/src/com/axe/gfx/ProjectionOutside.java b/src/com/axe/gfx/ProjectionOutside.java new file mode 100644 index 0000000..7224862 --- /dev/null +++ b/src/com/axe/gfx/ProjectionOutside.java @@ -0,0 +1,8 @@ +package com.axe.gfx; + +public enum ProjectionOutside +{ + Ignore, + Clamp, + Relative +} diff --git a/src/com/axe/gfx/ReadableVertex.java b/src/com/axe/gfx/ReadableVertex.java new file mode 100755 index 0000000..27a29c9 --- /dev/null +++ b/src/com/axe/gfx/ReadableVertex.java @@ -0,0 +1,8 @@ +package com.axe.gfx; + +import java.nio.ByteBuffer; + +public interface ReadableVertex +{ + public void read( ByteBuffer buffer ); +} \ No newline at end of file diff --git a/src/com/axe/gfx/Shader.java b/src/com/axe/gfx/Shader.java new file mode 100755 index 0000000..4e70b5c --- /dev/null +++ b/src/com/axe/gfx/Shader.java @@ -0,0 +1,337 @@ +package com.axe.gfx; + +import com.axe.color.Color; +import com.axe.math.Scalarf; +import com.axe.math.Scalari; +import com.axe.math.Vec2f; +import com.axe.math.Vec2i; +import com.axe.math.Vec3f; +import com.axe.math.Vec3i; +import com.axe.math.Vec4f; +import com.axe.resource.Resource; + +public interface Shader extends Resource +{ + + public ShaderObject[] getObjects(); + + public void bind(); + + public void unbind(); + + // Direct Location + + public void set( int location, boolean x ); + + public void set( int location, float x ); + + public void set( int location, float[] x ); + + public void set( int location, Scalarf v ); + + public void set( int location, float x, float y ); + + public void set( int location, Vec2f v ); + + public void set( int location, Vec2f[] v ); + + public void set( int location, float x, float y, float z ); + + public void set( int location, Vec3f v ); + + public void set( int location, Vec3f[] v ); + + public void set( int location, float x, float y, float z, float w ); + + public void set( int location, Vec4f v ); + + public void set( int location, Color color ); + + public void set( int location, Vec4f[] v ); + + public void set( int location, Color[] v ); + + public void set( int location, int x ); + + public void set( int location, int[] x ); + + public void set( int location, Scalari v ); + + public void set( int location, int x, int y ); + + public void set( int location, Vec2i v ); + + public void set( int location, Vec2i[] v ); + + public void set( int location, int x, int y, int z ); + + public void set( int location, Vec3i v ); + + public void set( int location, Vec3i[] v ); + + public void set( int location, int x, int y, int z, int w ); + + public int getLocation( Enum enumConstant ); + + public int getLocation( String name ); + + public int getAttributeLocation( String name ); + + public > int getAttributeLocation( E enumConstant ); + + + // Enumerated Variables + + default public void set( Enum enumConstant, boolean x ) + { + set( getLocation( enumConstant ), x ); + } + + default public void set( Enum enumConstant, float x ) + { + set( getLocation( enumConstant ), x ); + } + + default public void set( Enum enumConstant, float[] x ) + { + set( getLocation( enumConstant ), x ); + } + + default public void set( Enum enumConstant, Scalarf v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, float x, float y ) + { + set( getLocation( enumConstant ), x, y ); + } + + default public void set( Enum enumConstant, Vec2f v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, Vec2f[] v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, float x, float y, float z ) + { + set( getLocation( enumConstant ), x, y, z ); + } + + default public void set( Enum enumConstant, Vec3f v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, Vec3f[] v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, float x, float y, float z, float w ) + { + set( getLocation( enumConstant ), x, y, z, w ); + } + + default public void set( Enum enumConstant, Vec4f v ) + { + set( getLocation( enumConstant ), v.x, v.y, v.z, v.w ); + } + + default public void set( Enum enumConstant, Color color ) + { + set( getLocation( enumConstant ), color ); + } + + default public void set( Enum enumConstant, Vec4f[] v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, Color[] v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, int x ) + { + set( getLocation( enumConstant ), x ); + } + + default public void set( Enum enumConstant, int[] x ) + { + set( getLocation( enumConstant ), x ); + } + + default public void set( Enum enumConstant, Scalari v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, int x, int y ) + { + set( getLocation( enumConstant ), x, y ); + } + + default public void set( Enum enumConstant, Vec2i v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, Vec2i[] v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, int x, int y, int z ) + { + set( getLocation( enumConstant ), x, y, z ); + } + + default public void set( Enum enumConstant, Vec3i v ) + { + set( getLocation( enumConstant ), v.x, v.y, v.z ); + } + + default public void set( Enum enumConstant, Vec3i[] v ) + { + set( getLocation( enumConstant ), v ); + } + + default public void set( Enum enumConstant, int x, int y, int z, int w ) + { + set( getLocation( enumConstant ), x, y, z, w ); + } + + // Dynamic Variables + + default public void set( String name, boolean x ) + { + set( getLocation( name ), x ); + } + + default public void set( String name, float x ) + { + set( getLocation( name ), x ); + } + + default public void set( String name, float[] x ) + { + set( getLocation( name ), x ); + } + + default public void set( String name, Scalarf v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, float x, float y ) + { + set( getLocation( name ), x, y ); + } + + default public void set( String name, Vec2f v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, Vec2f[] v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, float x, float y, float z ) + { + set( getLocation( name ), x, y, z ); + } + + default public void set( String name, Vec3f v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, Vec3f[] v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, float x, float y, float z, float w ) + { + set( getLocation( name ), x, y, z, w ); + } + + default public void set( String name, Vec4f v ) + { + set( getLocation( name ), v.x, v.y, v.z, v.w ); + } + + default public void set( String name, Color color ) + { + set( getLocation( name ), color ); + } + + default public void set( String name, Vec4f[] v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, Color[] v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, int x ) + { + set( getLocation( name ), x ); + } + + default public void set( String name, int[] x ) + { + set( getLocation( name ), x ); + } + + default public void set( String name, Scalari v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, int x, int y ) + { + set( getLocation( name ), x, y ); + } + + default public void set( String name, Vec2i v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, Vec2i[] v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, int x, int y, int z ) + { + set( getLocation( name ), x, y, z ); + } + + default public void set( String name, Vec3i v ) + { + set( getLocation( name ), v.x, v.y, v.z ); + } + + default public void set( String name, Vec3i[] v ) + { + set( getLocation( name ), v ); + } + + default public void set( String name, int x, int y, int z, int w ) + { + set( getLocation( name ), x, y, z, w ); + } + +} diff --git a/src/com/axe/gfx/ShaderObject.java b/src/com/axe/gfx/ShaderObject.java new file mode 100755 index 0000000..62ae2e5 --- /dev/null +++ b/src/com/axe/gfx/ShaderObject.java @@ -0,0 +1,16 @@ +package com.axe.gfx; + +import org.magnos.asset.AssetInfo; + +import com.axe.resource.Resource; + +public interface ShaderObject extends Resource +{ + + public CharSequence getCode(); + + public ShaderObjectType getType(); + + public AssetInfo getInfo(); + +} diff --git a/src/com/axe/gfx/ShaderObjectType.java b/src/com/axe/gfx/ShaderObjectType.java new file mode 100755 index 0000000..9e7597a --- /dev/null +++ b/src/com/axe/gfx/ShaderObjectType.java @@ -0,0 +1,10 @@ +package com.axe.gfx; + +public enum ShaderObjectType +{ + Fragment, + Vertex, + Geometry, + TesselationControl, + TesselationEvaluation +} diff --git a/src/com/axe/gfx/Texture.java b/src/com/axe/gfx/Texture.java new file mode 100644 index 0000000..12892a2 --- /dev/null +++ b/src/com/axe/gfx/Texture.java @@ -0,0 +1,175 @@ +package com.axe.gfx; + +import com.axe.math.Bound2i; +import com.axe.resource.Resource; +import com.axe.tile.Tile; + +public interface Texture extends Resource +{ + + public TextureInfo getInfo(); + + public void bind(); + + public void unbind(); + + default public float s(float x) + { + return x * getPixelWidth() + getPixelOffsetX(); + } + + default public float t(float y) + { + return y * getPixelHeight() + getPixelOffsetY(); + } + + default public Coord coord(float x, float y) + { + return new Coord( + x * getPixelWidth() + getPixelOffsetX(), + y * getPixelHeight() + getPixelOffsetY() + ); + } + + default public Tile tileExact(float x, float y, float width, float height) + { + return tileExact( x, y, width, height, new Tile() ); + } + + default public Tile tileExact(float x, float y, float width, float height, Tile out) + { + float pixelW = getPixelWidth(); + float pixelH = getPixelHeight(); + + float left = x * pixelW; + float top = y * pixelH; + float right = (x + width) * pixelW; + float bottom = (y + height) * pixelH; + + return out.set(this, left, right, top, bottom); + } + + default public Tile tile(float x, float y, float width, float height) + { + return tile( x, y, width, height, new Tile() ); + } + + default public Tile tile(float x, float y, float width, float height, Tile out) + { + float pixelW = getPixelWidth(); + float pixelH = getPixelHeight(); + float offW = getPixelOffsetX(); + float offH = getPixelOffsetY(); + + float left = x * pixelW + offW; + float top = y * pixelH + offH; + float right = (x + width) * pixelW - offW; + float bottom = (y + height) * pixelH - offH; + + return out.set( this, left, right, top, bottom ); + } + + default public Tile tile() + { + return tile(0, 0, getImageWidth(), getImageHeight() ); + } + + default public Tile tile(Tile out) + { + return tile(0, 0, getImageWidth(), getImageHeight(), out ); + } + + default public Tile tileExact() + { + return tileExact( 0, 0, getImageWidth(), getImageHeight() ); + } + + default public Tile tileExact(Tile out) + { + return tileExact( 0, 0, getImageWidth(), getImageHeight(), out ); + } + + default public void unpad(Tile tile) + { + float offW = getPixelOffsetX(); + float offH = getPixelOffsetY(); + + tile.s0 -= offW; + tile.s1 += offW; + tile.t0 -= offH; + tile.t1 += offH; + } + + default public void unpad(Tile[] tiles) + { + for (int i = 0; i < tiles.length; i++) + { + unpad( tiles[i] ); + } + } + + default public void pad(Tile tile) + { + float offW = getPixelOffsetX(); + float offH = getPixelOffsetY(); + + tile.s0 += offW; + tile.s1 -= offW; + tile.t0 += offH; + tile.t1 -= offH; + } + + default public void pad(Tile[] tiles) + { + for (int i = 0; i < tiles.length; i++) + { + pad( tiles[i] ); + } + } + + default public Bound2i bound(Tile tile) + { + return bound( tile, new Bound2i() ); + } + + default public Bound2i bound(Tile tile, Bound2i out) + { + float offW = getPixelOffsetX(); + float offH = getPixelOffsetY(); + int w = width(); + int h = height(); + + out.l = (int)((tile.s0 - offW) * w); + out.t = (int)((tile.t0 - offH) * h); + out.r = (int)((tile.s1 + offW) * w); + out.b = (int)((tile.t1 + offH) * h); + + return out; + } + + public int width(); + public int height(); + + public int getImageHeight(); + public int getImageWidth(); + + public boolean hasAlpha(); + + public float getPixelWidth(); + public float getPixelOffsetX(); + public float getPixelHeight(); + public float getPixelOffsetY(); + + public Magnify magnify(); + public void magnify(Magnify mag); + + public Minify minify(); + public void minify(Minify min); + + public Wrap wrap(); + public void wrap(Wrap wrap); + + public int anisotrophy(); + public void anisotrophy(int anisotrophy); + +} diff --git a/src/com/axe/gfx/TextureInfo.java b/src/com/axe/gfx/TextureInfo.java new file mode 100755 index 0000000..76dd205 --- /dev/null +++ b/src/com/axe/gfx/TextureInfo.java @@ -0,0 +1,175 @@ + +package com.axe.gfx; + +import org.magnos.asset.base.BaseAssetInfo; + +import com.axe.io.DataModel; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class TextureInfo extends BaseAssetInfo implements DataModel +{ + + protected boolean mipmap; + protected Wrap wrap; + protected Minify minify; + protected Magnify magnify; + protected int anisotrophy = 1; + + public TextureInfo() + { + this( defaultMipMap, defaultWrap, defaultMinify, defaultMagnify, defaultAnisotrophy ); + } + + public TextureInfo( boolean mipmap, Wrap wrap ) + { + this( mipmap, wrap, defaultMinify, defaultMagnify, defaultAnisotrophy ); + } + + public TextureInfo( boolean mipmap, Wrap wrap, Minify minify, Magnify magnify ) + { + this( mipmap, wrap, minify, magnify, defaultAnisotrophy ); + } + + public TextureInfo( boolean mipmap, Wrap wrap, Minify minify, Magnify magnify, int anisotrophy ) + { + super( Texture.class ); + + this.mipmap = mipmap; + this.wrap = wrap; + this.minify = minify; + this.magnify = magnify; + this.anisotrophy = anisotrophy; + } + + public boolean isMipMap() + { + return mipmap; + } + + public void setMipMap( boolean mipmap ) + { + this.mipmap = mipmap; + } + + public Wrap getWrap() + { + return wrap; + } + + public void setWrap( Wrap wrap ) + { + this.wrap = wrap; + } + + public Minify getMinify() + { + return minify; + } + + public void setMinify( Minify minify ) + { + this.minify = minify; + } + + public Magnify getMagnify() + { + return magnify; + } + + public void setMagnify( Magnify magnify ) + { + this.magnify = magnify; + } + + public int getAnisotrophy() + { + return anisotrophy; + } + + public void setAnisotrophy( int anisotrophy ) + { + this.anisotrophy = anisotrophy; + } + + public boolean equals( Object o ) + { + if (o instanceof TextureInfo) + { + TextureInfo other = (TextureInfo)o; + return (other.anisotrophy == anisotrophy && + other.mipmap == mipmap && + other.magnify == magnify && + other.minify == minify && other.wrap == wrap); + } + return false; + } + + protected static boolean defaultMipMap = true; + protected static Wrap defaultWrap = Wrap.Edge; + protected static Minify defaultMinify = Minify.Linear; + protected static Magnify defaultMagnify = Magnify.Linear; + protected static int defaultAnisotrophy = 1; + + public static Wrap getDefaultWrap() + { + return defaultWrap; + } + + public static void setDefaultWrap( Wrap wrap ) + { + defaultWrap = wrap; + } + + public static Minify getDefaultMinify() + { + return defaultMinify; + } + + public static void setDefaultMinify( Minify minify ) + { + defaultMinify = minify; + } + + public static Magnify getDefaultMagnify() + { + return defaultMagnify; + } + + public static void setDefaultMagnify( Magnify magnify ) + { + defaultMagnify = magnify; + } + + public static int getDefaultAnisotrophy() + { + return defaultAnisotrophy; + } + + public static void setDefaultAnisotrophy( int anisotrophy ) + { + defaultAnisotrophy = anisotrophy; + } + + @Override + public void read( InputModel input ) + { + mipmap = input.readBoolean( "mipmap" ); + wrap = input.readEnum( "wrap", Wrap.class ); + minify = input.readEnum( "minify", Minify.class ); + magnify = input.readEnum( "magnify", Magnify.class ); + anisotrophy = input.readInt( "anistrophy" ); + } + + @Override + public void write( OutputModel output ) + { + output.write( "mipmap", mipmap ); + output.writeEnum( "wrap", wrap ); + output.writeEnum( "minify", minify ); + output.writeEnum( "magnify", magnify ); + output.write( "anistrophy", anisotrophy ); + } + +} diff --git a/src/com/axe/gfx/VertexBuffer.java b/src/com/axe/gfx/VertexBuffer.java new file mode 100644 index 0000000..8a5de17 --- /dev/null +++ b/src/com/axe/gfx/VertexBuffer.java @@ -0,0 +1,142 @@ +package com.axe.gfx; + +import java.nio.ByteBuffer; + +public interface VertexBuffer +{ + + // clear buffer data + public void reset(VertexMesh vb); + + // select mesh for reading + default public boolean load() + { + return load( mesh(), 0, mesh().vertices() ); + } + + default public boolean load(VertexMesh mesh) + { + return load( mesh, 0, mesh.vertices() ); + } + + public boolean load(VertexMesh mesh, int offset, int count); + + // the current mesh being read or written to. + public VertexMesh mesh(); + + // move position to next vertex. only necessary when writing or + // reading directly. + public void next(); + + // set position given vertex index + public void vertex(int index); + + // maximum number of vertex + public int capacity(); + + // number of vertices currently written + public int vertices(); + + // number of vertices currently available + public int remaining(); + + // the current byte buffer being written to + public ByteBuffer data(); + + + default public boolean write(WritableVertex vertex) + { + final boolean has = remaining() > 0; + + if (has) + { + vertex.write( data() ); + next(); + } + + return has; + } + + default public int write(WritableVertex[] vertex) + { + return write( vertex, 0, vertex.length ); + } + + default public int write(WritableVertex[] vertex, int offset) + { + return write( vertex, offset, vertex.length - offset ); + } + + default public int write(WritableVertex[] vertex, int offset, int length) + { + final int end = offset + length; + int written = 0; + + for (int i = offset; i < end; i++) + { + if ( write( vertex[ i ] ) ) + { + written++; + } + else + { + break; + } + } + + return written; + } + + default public boolean read(ReadableVertex vertex) + { + final boolean has = remaining() > 0; + + if (has) + { + vertex.read( data() ); + next(); + } + + return has; + } + + default public int read(ReadableVertex[] vertex) + { + return read( vertex, 0, vertex.length ); + } + + default public int read(ReadableVertex[] vertex, int offset) + { + return read( vertex, offset, vertex.length - offset ); + } + + default public int read(ReadableVertex[] vertex, int offset, int length) + { + final int end = offset + length; + int read = 0; + + for (int i = offset; i < end; i++) + { + if ( read( vertex[ i ] ) ) + { + read++; + } + else + { + break; + } + } + + return read; + } + + // with the current - update the given buffer + default public boolean save() + { + return save( 0 ); + } + + // update part of the buffer + public boolean save(int offset); + +} \ No newline at end of file diff --git a/src/com/axe/gfx/VertexFormat.java b/src/com/axe/gfx/VertexFormat.java new file mode 100755 index 0000000..298c2df --- /dev/null +++ b/src/com/axe/gfx/VertexFormat.java @@ -0,0 +1,119 @@ +package com.axe.gfx; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import com.axe.util.Buffers; + +public class VertexFormat +{ + public final Primitive primitive; + public final int vertexDimension; + public final DataType vertexType; + public final int colorComponents; + public final DataType colorType; + public final int secondaryColorComponents; + public final DataType secondaryColorType; + public final int normalDimension; + public final DataType normalType; + public final int textures; + public final int[] textureDimension; + public final DataType[] textureType; + public final int attributes; + public final int[] attributeIndex; + public final int[] attributeSize; + public final DataType[] attributeType; + public final boolean[] attributeNormalize; + + public final boolean aligned; + public final int size; + public final int stride; + public final int padding; + public final int vertexOffset; + public final int normalOffset; + public final int colorOffset; + public final int secondaryColorOffset; + public final int[] textureOffsets; + public final int[] attributeOffset; + + public final VertexFormatBuilder builder; + + public VertexFormat( VertexFormatBuilder builder ) + { + this.builder = builder; + + this.primitive = builder.getPrimitive(); + this.vertexDimension = builder.getVertexDimension(); + this.vertexType = builder.getVertexType(); + this.colorComponents = builder.getColorComponents(); + this.colorType = builder.getColorType(); + this.secondaryColorComponents = builder.getSecondaryColorComponents(); + this.secondaryColorType = builder.getSecondaryColorType(); + this.normalDimension = builder.getNormalDimension(); + this.normalType = builder.getNormalType(); + this.textures = builder.getTextures(); + this.textureDimension = builder.getTextureDimension(); + this.textureType = builder.getTextureType(); + this.attributes = builder.getAttributes(); + this.attributeIndex = builder.getAttributeIndex(); + this.attributeSize = builder.getAttributeSize(); + this.attributeType = builder.getAttributeType(); + this.attributeNormalize = builder.getAttributeNormalize(); + + this.aligned = builder.isAligned(); + this.size = builder.getSize(); + this.stride = (aligned ? builder.getStride() : size); + this.padding = builder.getPadding(); + this.vertexOffset = builder.getVertexOffset(); + this.normalOffset = builder.getNormalOffset(); + this.colorOffset = builder.getColorOffset(); + this.secondaryColorOffset = builder.getSecondaryColorOffset(); + this.textureOffsets = builder.getTextureOffsets(); + this.attributeOffset = builder.getAttributeOffsets(); + } + + public VertexFormat( Primitive primitive, VertexMeshType type, int vertexDimension, DataType vertexType, int colorComponents, DataType colorType, int secondaryColorComponents, DataType secondaryColorType, int normalDimension, DataType normalType, int textures, int[] textureDimension, DataType[] textureType, int attributes, int[] attributeIndex, int[] attributeSize, DataType[] attributeType, boolean[] attributeNormalize, boolean aligned, int size, int stride, int padding, int vertexOffset, int normalOffset, int colorOffset, int secondaryColorOffset, int[] textureOffsets, int[] attributeOffset ) + { + this.builder = null; + + this.primitive = primitive; + this.vertexDimension = vertexDimension; + this.vertexType = vertexType; + this.colorComponents = colorComponents; + this.colorType = colorType; + this.secondaryColorComponents = secondaryColorComponents; + this.secondaryColorType = secondaryColorType; + this.normalDimension = normalDimension; + this.normalType = normalType; + this.textures = textures; + this.textureDimension = textureDimension; + this.textureType = textureType; + this.attributes = attributes; + this.attributeIndex = attributeIndex; + this.attributeSize = attributeSize; + this.attributeType = attributeType; + this.attributeNormalize = attributeNormalize; + this.aligned = aligned; + this.size = size; + this.stride = (aligned ? stride : size); + this.padding = padding; + this.vertexOffset = vertexOffset; + this.normalOffset = normalOffset; + this.colorOffset = colorOffset; + this.secondaryColorOffset = secondaryColorOffset; + this.textureOffsets = textureOffsets; + this.attributeOffset = attributeOffset; + } + + public ByteBuffer getBuffer( int vertices ) + { + return Buffers.bytes( vertices * stride ); + } + + @Override + public String toString() + { + return "VertexFormat [primitive=" + primitive + ", vertexDimension=" + vertexDimension + ", vertexType=" + vertexType + ", colorComponents=" + colorComponents + ", colorType=" + colorType + ", secondaryColorComponents=" + secondaryColorComponents + ", secondaryColorType=" + secondaryColorType + ", normalDimension=" + normalDimension + ", normalType=" + normalType + ", textures=" + textures + ", textureDimension=" + Arrays.toString( textureDimension ) + ", textureType=" + Arrays.toString( textureType ) + ", attributes=" + attributes + ", attributeIndex=" + Arrays.toString( attributeIndex ) + ", attributeSize=" + Arrays.toString( attributeSize ) + ", attributeType=" + Arrays.toString( attributeType ) + ", attributeNormalize=" + Arrays.toString( attributeNormalize ) + ", aligned=" + aligned + ", size=" + size + ", stride=" + stride + ", padding=" + padding + ", vertexOffset=" + vertexOffset + ", normalOffset=" + normalOffset + ", colorOffset=" + colorOffset + ", secondaryColorOffset=" + secondaryColorOffset + ", textureOffsets=" + Arrays.toString( textureOffsets ) + ", attributeOffset=" + Arrays.toString( attributeOffset ) + ", builder=" + builder + "]"; + } + +} \ No newline at end of file diff --git a/src/com/axe/gfx/VertexFormatBuilder.java b/src/com/axe/gfx/VertexFormatBuilder.java new file mode 100755 index 0000000..0c91d8c --- /dev/null +++ b/src/com/axe/gfx/VertexFormatBuilder.java @@ -0,0 +1,544 @@ + +package com.axe.gfx; + +import java.util.Arrays; + +import com.axe.core.Factory; +import com.axe.util.Array; + + +public class VertexFormatBuilder implements Factory +{ + + public Primitive primitive = Primitive.Triangle; + public int vertexDimension = 3; // 2,3,4 + public DataType vertexType = DataType.Float; + public int colorComponents = 0; // 0,3,4 + public DataType colorType = DataType.Byte; + public int secondaryColorComponents = 0; // 0,3,4 + public DataType secondaryColorType = DataType.Byte; + public int normalDimension = 0; // 0,3 + public DataType normalType = DataType.Float; + public int textures = 1; // 0,1,2,3..GL_MAX_TEXTURE_COORDS-1 + public int[] textureDimension = { 2 }; // 1,2,3,4 + public DataType[] textureType = { DataType.Float }; + public int attributes = 0; + public int[] attributeIndex = {}; + public int[] attributeSize = {}; + public DataType[] attributeType = {}; + public boolean[] attributeNormalize = {}; + public boolean aligned = true; + + public boolean isValid() + { + return isVertexValid() && isColorValid() && isSecondaryColorValid() && isNormalValid() && isTextureValid() && isAttributesValid(); + } + + public boolean isVertexValid() + { + if (vertexDimension != 2 && vertexDimension != 3 && vertexDimension != 4) + { + return false; + } + + if (vertexType == null || + (vertexType != DataType.Short && vertexType != DataType.Integer && + vertexType != DataType.Float && vertexType != DataType.Double)) + { + return false; + } + + return true; + } + + public boolean isColorValid() + { + if (colorComponents != 0 && colorComponents != 3 && colorComponents != 4) + { + return false; + } + + if (colorType == null) + { + return false; + } + + return true; + } + + public boolean isSecondaryColorValid() + { + if (secondaryColorComponents != 0 && secondaryColorComponents != 3 && secondaryColorComponents != 4) + { + return false; + } + + if (secondaryColorType == null) + { + return false; + } + + return true; + } + + public boolean isNormalValid() + { + if (normalDimension != 0 && normalDimension != 3) + { + return false; + } + + if (normalType == null || + (normalType != DataType.Short && normalType != DataType.Integer && + normalType != DataType.Float && normalType != DataType.Double)) + { + return false; + } + + return true; + } + + public boolean isTextureValid() + { + /* + if (textures < 0 || textures >= glGetInteger( GL_MAX_TEXTURE_COORDS )) + { + return false; + } + */ + + if (textureDimension.length != textures) + { + return false; + } + + for (int i = 0; i < textures; i++) + { + int td = textureDimension[i]; + + if (td != 1 && td != 2 && td != 3 && td != 4) + { + return false; + } + } + + if (textureType.length != textures) + { + return false; + } + + for (int i = 0; i < textures; i++) + { + DataType tt = textureType[i]; + + if (tt == null || (tt != DataType.Short && tt != DataType.Integer && tt != DataType.Float && tt != DataType.Double)) + { + return false; + } + } + + return true; + } + + public boolean isAttributesValid() + { + for (int i = 0; i < attributes; i++) + { + if (attributeSize[i] < 1 || attributeSize[i] > 4) + { + return false; + } + + if (attributeType[i] == null) + { + return false; + } + } + + return true; + } + + public int getStride() + { + int stride = getSize(); + + stride--; + stride |= (stride >> 1); + stride |= (stride >> 2); + stride |= (stride >> 4); + stride |= (stride >> 8); + stride |= (stride >> 16); + stride++; + + return stride; + } + + public int getPadding() + { + return getStride() - getSize(); + } + + public int getSize() + { + int size = 0; + + size += vertexDimension * vertexType.bytes; + size += normalDimension * normalType.bytes; + size += colorComponents * colorType.bytes; + size += secondaryColorComponents * secondaryColorType.bytes; + + for (int i = 0; i < textures; i++) + { + size += textureDimension[i] * textureType[i].bytes; + } + + for (int i = 0; i < attributes; i++) + { + size += attributeSize[i] * attributeType[i].bytes; + } + + return size; + } + + public boolean isAligned() + { + return aligned; + } + + public VertexFormatBuilder setAligned( boolean aligned ) + { + this.aligned = aligned; + return this; + } + + public int getVertexOffset() + { + return 0; + } + + public int getNormalOffset() + { + return (vertexDimension * vertexType.bytes); + } + + public int getColorOffset() + { + return (vertexDimension * vertexType.bytes) + + (normalDimension * normalType.bytes); + } + + public int getSecondaryColorOffset() + { + return (vertexDimension * vertexType.bytes) + + (normalDimension * normalType.bytes) + + (colorComponents * colorType.bytes); + } + + public int[] getTextureOffsets() + { + int start = (vertexDimension * vertexType.bytes) + + (normalDimension * normalType.bytes) + + (colorComponents * colorType.bytes) + + (secondaryColorComponents * secondaryColorType.bytes); + + int[] offsets = new int[textures]; + + for (int i = 0; i < textures; i++) + { + offsets[i] = start; + + start += textureDimension[i] * textureType[i].bytes; + } + + return offsets; + } + + public int[] getAttributeOffsets() + { + int start = (vertexDimension * vertexType.bytes) + + (normalDimension * normalType.bytes) + + (colorComponents * colorType.bytes) + + (secondaryColorComponents * secondaryColorType.bytes); + + for (int i = 0; i < textures; i++) + { + start += textureDimension[i] * textureType[i].bytes; + } + + int[] offsets = new int[attributes]; + + for (int i = 0; i < attributes; i++) + { + offsets[i] = start; + + start += attributeSize[i] * attributeType[i].bytes; + } + + return offsets; + } + + public Primitive getPrimitive() + { + return primitive; + } + + public VertexFormatBuilder setPrimitive( Primitive primitive ) + { + this.primitive = primitive; + return this; + } + + public VertexFormatBuilder setVertex( int vertexDimension, DataType vertexType ) + { + this.vertexDimension = vertexDimension; + this.vertexType = vertexType; + return this; + } + + public int getVertexDimension() + { + return vertexDimension; + } + + public VertexFormatBuilder setVertexDimension( int vertexDimension ) + { + this.vertexDimension = vertexDimension; + return this; + } + + public DataType getVertexType() + { + return vertexType; + } + + public VertexFormatBuilder setVertexType( DataType vertexType ) + { + this.vertexType = vertexType; + return this; + } + + public VertexFormatBuilder setColor( int colorComponents, DataType colorType ) + { + this.colorComponents = colorComponents; + this.colorType = colorType; + return this; + } + + public int getColorComponents() + { + return colorComponents; + } + + public VertexFormatBuilder setColorComponents( int colorComponents ) + { + this.colorComponents = colorComponents; + return this; + } + + public DataType getColorType() + { + return colorType; + } + + public VertexFormatBuilder setColorType( DataType colorType ) + { + this.colorType = colorType; + return this; + } + + + public VertexFormatBuilder setSecondaryColor( int secondaryColorComponents, DataType secondaryColorType ) + { + this.secondaryColorComponents = secondaryColorComponents; + this.secondaryColorType = secondaryColorType; + return this; + } + + public int getSecondaryColorComponents() + { + return secondaryColorComponents; + } + + public VertexFormatBuilder setSecondaryColorComponents( int secondaryColorComponents ) + { + this.secondaryColorComponents = secondaryColorComponents; + return this; + } + + public DataType getSecondaryColorType() + { + return secondaryColorType; + } + + public VertexFormatBuilder setSecondaryColorType( DataType secondaryColorType ) + { + this.secondaryColorType = secondaryColorType; + return this; + } + + public VertexFormatBuilder setNormal( int normalDimension, DataType normalType ) + { + this.normalDimension = normalDimension; + this.normalType = normalType; + return this; + } + + public int getNormalDimension() + { + return normalDimension; + } + + public VertexFormatBuilder setNormalDimension( int normalDimension ) + { + this.normalDimension = normalDimension; + return this; + } + + public DataType getNormalType() + { + return normalType; + } + + public VertexFormatBuilder setNormalType( DataType normalType ) + { + this.normalType = normalType; + return this; + } + + public VertexFormatBuilder setTextures( int textures, int[] textureDimension, DataType[] textureType ) + { + this.textures = textures; + this.textureDimension = textureDimension; + this.textureType = textureType; + return this; + } + + public int getTextures() + { + return textures; + } + + public VertexFormatBuilder setTextures( int textures ) + { + this.textures = textures; + + if (textures != textureDimension.length) + { + textureDimension = Arrays.copyOf( textureDimension, textures ); + } + + if (textures != textureType.length) + { + textureType = Arrays.copyOf( textureType, textures ); + } + + return this; + } + + public VertexFormatBuilder addTexture( int dimensions, DataType type ) + { + textures++; + + textureDimension = Arrays.copyOf( textureDimension, textures ); + textureDimension[textures - 1] = dimensions; + + textureType = Arrays.copyOf( textureType, textures ); + textureType[textures - 1] = type; + + return this; + } + + public int[] getTextureDimension() + { + return textureDimension; + } + + public VertexFormatBuilder setTextureDimension( int ... textureDimension ) + { + this.textureDimension = textureDimension; + return this; + } + + public DataType[] getTextureType() + { + return textureType; + } + + public VertexFormatBuilder setTextureType( DataType ... textureType ) + { + this.textureType = textureType; + return this; + } + + public VertexFormatBuilder addAttribute( int index, int size, DataType type, boolean normalize ) + { + attributeIndex = Array.add( index, attributeIndex ); + attributeType = Array.add( type, attributeType ); + attributeSize = Array.add( size, attributeSize ); + attributeNormalize = Array.add( normalize, attributeNormalize ); + attributes++; + return this; + } + + public int getAttributes() + { + return attributes; + } + + public VertexFormatBuilder setAttributes( int attributes ) + { + this.attributes = attributes; + return this; + } + + public int[] getAttributeSize() + { + return attributeSize; + } + + public VertexFormatBuilder setAttributeSize( int[] attributeSize ) + { + this.attributeSize = attributeSize; + return this; + } + + public DataType[] getAttributeType() + { + return attributeType; + } + + public VertexFormatBuilder setAttributeType( DataType[] attributeType ) + { + this.attributeType = attributeType; + return this; + } + + public boolean[] getAttributeNormalize() + { + return attributeNormalize; + } + + public VertexFormatBuilder setAttributeNormalize( boolean[] attributeNormalize ) + { + this.attributeNormalize = attributeNormalize; + return this; + } + + public int[] getAttributeIndex() + { + return attributeIndex; + } + + public VertexFormatBuilder setAttributeIndex( int[] attributeIndex ) + { + this.attributeIndex = attributeIndex; + return this; + } + + @Override + public VertexFormat create() + { + return new VertexFormat( this ); + } + +} diff --git a/src/com/axe/gfx/VertexMesh.java b/src/com/axe/gfx/VertexMesh.java new file mode 100644 index 0000000..c0955cf --- /dev/null +++ b/src/com/axe/gfx/VertexMesh.java @@ -0,0 +1,59 @@ +package com.axe.gfx; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import com.axe.util.Buffers; + +public interface VertexMesh +{ + + public VertexFormat format(); + + public VertexMeshType type(); + + public int vertices(); + + public int size(); + + public void destroy(); + + // is the given mesh valid? + public boolean isValid(); + + // draw entire mesh + default public void draw() + { + draw( 0, vertices() ); + } + + // draw part of mesh + public void draw(int offset, int vertices); + + // draw vertices at the given indices + + default public void draw(short[] indices) + { + draw( Buffers.shorts( indices ) ); + } + + default public void draw(int[] indices) + { + draw( Buffers.ints( indices ) ); + } + + default public void draw(byte[] indices) + { + draw( Buffers.bytes( indices ) ); + } + + public void draw(ShortBuffer indices); + + public void draw(IntBuffer indices); + + public void draw(ByteBuffer indices); + + public void draw(ByteBuffer indices, DataType indexType); + +} \ No newline at end of file diff --git a/src/com/axe/gfx/VertexMeshType.java b/src/com/axe/gfx/VertexMeshType.java new file mode 100755 index 0000000..92f6498 --- /dev/null +++ b/src/com/axe/gfx/VertexMeshType.java @@ -0,0 +1,27 @@ +package com.axe.gfx; + +public enum VertexMeshType +{ + /* Static = set once, draw many times */ + /* Dynamic = set each frame draw many times */ + /* Stream = one time use */ + + Static, + + StaticRead, + + StaticCopy, + + Dynamic, + + DynamicRead, + + DynamicCopy, + + Stream, + + StreamRead, + + StreamCopy + +} diff --git a/src/com/axe/gfx/Wrap.java b/src/com/axe/gfx/Wrap.java new file mode 100755 index 0000000..bad4977 --- /dev/null +++ b/src/com/axe/gfx/Wrap.java @@ -0,0 +1,10 @@ +package com.axe.gfx; + +public enum Wrap +{ + Edge, + Border, + Default, + Repeat, + Mirror +} diff --git a/src/com/axe/gfx/WritableVertex.java b/src/com/axe/gfx/WritableVertex.java new file mode 100755 index 0000000..b68e19d --- /dev/null +++ b/src/com/axe/gfx/WritableVertex.java @@ -0,0 +1,8 @@ +package com.axe.gfx; + +import java.nio.ByteBuffer; + +public interface WritableVertex +{ + public void write( ByteBuffer buffer ); +} \ No newline at end of file diff --git a/src/com/axe/input/AbstractKeyEngine.java b/src/com/axe/input/AbstractKeyEngine.java new file mode 100644 index 0000000..7589ae0 --- /dev/null +++ b/src/com/axe/input/AbstractKeyEngine.java @@ -0,0 +1,255 @@ +package com.axe.input; + +import java.util.ArrayDeque; +import java.util.Queue; + +import com.axe.window.Window; + +public abstract class AbstractKeyEngine implements KeyEngine +{ + + public KeyState[] states; + public InputStateAny shift; + public KeyState leftShift; + public KeyState rightShift; + public InputStateAny control; + public InputStateAny meta; + public InputStateAny command; + public InputStateAny alt; + public Queue queue; + public float pressDelay; + public float pressInterval; + public char[][] keyChars; + + public AbstractKeyEngine() + { + int n = Key.values().length; + + this.states = new KeyState[ n ]; + + while (--n >= 0) + { + this.states[n] = new KeyState(n); + } + + this.leftShift = getKey( Key.LEFT_SHIFT ); + this.rightShift = getKey( Key.RIGHT_SHIFT ); + this.shift = new InputStateAny( + this.leftShift, + this.rightShift + ); + + this.control = new InputStateAny( + getKey( Key.LEFT_CONTROL ), + getKey( Key.RIGHT_CONTROL ) + ); + + this.command = new InputStateAny( + getKey( Key.LEFT_COMMAND ), + getKey( Key.RIGHT_COMMAND ) + ); + + this.alt = new InputStateAny( + getKey( Key.LEFT_ALT ), + getKey( Key.RIGHT_ALT ) + ); + + this.meta = new InputStateAny( + getKey( Key.LEFT_COMMAND ), + getKey( Key.RIGHT_COMMAND ), + getKey( Key.LEFT_CONTROL ), + getKey( Key.RIGHT_CONTROL ) + ); + + this.queue = new ArrayDeque<>(); + + this.pressDelay = DEFAULT_PRESS_DELAY; + this.pressInterval = DEFAULT_PRESS_INTERVAL; + + this.keyChars = new char[Key.values().length][2]; + this.loadChars(); + } + + public void init(long startTime) + { + for (int i = 0; i < states.length; i++) + { + states[ i ].changeTime = startTime; + } + } + + public void tick(long currentTime) + { + for (int i = 0; i < states.length; i++) + { + states[ i ].tick( currentTime, pressDelay, pressInterval ); + } + + shift.update(); + control.update(); + command.update(); + alt.update(); + meta.update(); + + int charIndex = shift.down ? 1 : 0; + + for (int i = 0; i < states.length; i++) + { + states[ i ].character = keyChars[ i ][ charIndex ]; + } + } + + public void queueKeyState(Key key, boolean down, long inputTime, Window inputWindow) + { + int charIndex = leftShift.down || rightShift.down ? 1 : 0; + + KeyState state = new KeyState( key ); + + state.setDown( down, inputTime, inputWindow ); + state.character = keyChars[ state.input ][ charIndex ]; + + queue.offer( state ); + } + + public void loadChars() + { + keyChars[ Key.N1.ordinal() ] = new char[] {'1', '!'}; + keyChars[ Key.N2.ordinal() ] = new char[] {'2', '@'}; + keyChars[ Key.N3.ordinal() ] = new char[] {'3', '#'}; + keyChars[ Key.N4.ordinal() ] = new char[] {'4', '$'}; + keyChars[ Key.N5.ordinal() ] = new char[] {'5', '%'}; + keyChars[ Key.N6.ordinal() ] = new char[] {'6', '^'}; + keyChars[ Key.N7.ordinal() ] = new char[] {'7', '&'}; + keyChars[ Key.N8.ordinal() ] = new char[] {'8', '*'}; + keyChars[ Key.N9.ordinal() ] = new char[] {'9', '('}; + keyChars[ Key.N0.ordinal() ] = new char[] {'0', ')'}; + keyChars[ Key.MINUS.ordinal() ] = new char[] {'-', '_'}; + keyChars[ Key.EQUALS.ordinal() ] = new char[] {'=', '+'}; + keyChars[ Key.BACK.ordinal() ] = new char[] {'\b', '\b'}; + keyChars[ Key.TAB.ordinal() ] = new char[] {'\t', '\t'}; + keyChars[ Key.Q.ordinal() ] = new char[] {'q', 'Q'}; + keyChars[ Key.W.ordinal() ] = new char[] {'w', 'W'}; + keyChars[ Key.E.ordinal() ] = new char[] {'e', 'E'}; + keyChars[ Key.R.ordinal() ] = new char[] {'r', 'R'}; + keyChars[ Key.T.ordinal() ] = new char[] {'t', 'T'}; + keyChars[ Key.Y.ordinal() ] = new char[] {'y', 'Y'}; + keyChars[ Key.U.ordinal() ] = new char[] {'u', 'U'}; + keyChars[ Key.I.ordinal() ] = new char[] {'i', 'I'}; + keyChars[ Key.O.ordinal() ] = new char[] {'o', 'O'}; + keyChars[ Key.P.ordinal() ] = new char[] {'p', 'P'}; + keyChars[ Key.LEFT_BRACKET.ordinal() ] = new char[] {'[', '{'}; + keyChars[ Key.RIGHT_BRACKET.ordinal() ] = new char[] {']', '}'}; + keyChars[ Key.RETURN.ordinal() ] = new char[] {'\n', '\n'}; + keyChars[ Key.A.ordinal() ] = new char[] {'a', 'A'}; + keyChars[ Key.S.ordinal() ] = new char[] {'s', 'S'}; + keyChars[ Key.D.ordinal() ] = new char[] {'d', 'D'}; + keyChars[ Key.F.ordinal() ] = new char[] {'f', 'F'}; + keyChars[ Key.G.ordinal() ] = new char[] {'g', 'G'}; + keyChars[ Key.H.ordinal() ] = new char[] {'h', 'H'}; + keyChars[ Key.J.ordinal() ] = new char[] {'j', 'J'}; + keyChars[ Key.K.ordinal() ] = new char[] {'k', 'K'}; + keyChars[ Key.L.ordinal() ] = new char[] {'l', 'L'}; + keyChars[ Key.SEMICOLON.ordinal() ] = new char[] {';', ':'}; + keyChars[ Key.APOSTROPHE.ordinal() ] = new char[] {'\'', '"'}; + keyChars[ Key.BACKSLASH.ordinal() ] = new char[] {'\\', '|'}; + keyChars[ Key.Z.ordinal() ] = new char[] {'z', 'Z'}; + keyChars[ Key.X.ordinal() ] = new char[] {'x', 'X'}; + keyChars[ Key.C.ordinal() ] = new char[] {'c', 'C'}; + keyChars[ Key.V.ordinal() ] = new char[] {'v', 'V'}; + keyChars[ Key.B.ordinal() ] = new char[] {'b', 'B'}; + keyChars[ Key.N.ordinal() ] = new char[] {'n', 'N'}; + keyChars[ Key.M.ordinal() ] = new char[] {'m', 'M'}; + keyChars[ Key.COMMA.ordinal() ] = new char[] {',', '<'}; + keyChars[ Key.PERIOD.ordinal() ] = new char[] {'.', '>'}; + keyChars[ Key.SLASH.ordinal() ] = new char[] {'/', '?'}; + keyChars[ Key.MULTIPLY.ordinal() ] = new char[] {'*', '*'}; + keyChars[ Key.SPACE.ordinal() ] = new char[] {' ', ' '}; + keyChars[ Key.NUMPAD7.ordinal() ] = new char[] {'7', '7'}; + keyChars[ Key.NUMPAD8.ordinal() ] = new char[] {'8', '8'}; + keyChars[ Key.NUMPAD9.ordinal() ] = new char[] {'9', '9'}; + keyChars[ Key.SUBTRACT.ordinal() ] = new char[] {'-', '-'}; + keyChars[ Key.NUMPAD4.ordinal() ] = new char[] {'4', '4'}; + keyChars[ Key.NUMPAD5.ordinal() ] = new char[] {'5', '5'}; + keyChars[ Key.NUMPAD6.ordinal() ] = new char[] {'6', '6'}; + keyChars[ Key.ADD.ordinal() ] = new char[] {'+', '+'}; + keyChars[ Key.NUMPAD1.ordinal() ] = new char[] {'1', '1'}; + keyChars[ Key.NUMPAD2.ordinal() ] = new char[] {'2', '2'}; + keyChars[ Key.NUMPAD3.ordinal() ] = new char[] {'3', '3'}; + keyChars[ Key.NUMPAD0.ordinal() ] = new char[] {'0', '0'}; + keyChars[ Key.DECIMAL.ordinal() ] = new char[] {'.', '.'}; + keyChars[ Key.NUMPADEQUALS.ordinal() ] = new char[] {'=', '='}; + keyChars[ Key.AT.ordinal() ] = new char[] {'@', '@'}; + keyChars[ Key.COLON.ordinal() ] = new char[] {':', ':'}; + keyChars[ Key.UNDERLINE.ordinal() ] = new char[] {'_', '_'}; + keyChars[ Key.NUMPADENTER.ordinal() ] = new char[] {'\n', '\n'}; + keyChars[ Key.NUMPADCOMMA.ordinal() ] = new char[] {',', ','}; + keyChars[ Key.DIVIDE.ordinal() ] = new char[] {'/', '/'}; + } + + @Override + public KeyState getKey(Key key) + { + return states[ key.ordinal() ]; + } + + @Override + public InputState getShift() + { + return shift; + } + + @Override + public InputState getControl() + { + return control; + } + + @Override + public InputState getMeta() + { + return meta; + } + + @Override + public InputState getCommand() + { + return command; + } + + @Override + public InputState getAlt() + { + return alt; + } + + @Override + public Queue getQueue() + { + return queue; + } + + @Override + public void setPressInterval(float pressInterval) + { + this.pressInterval = pressInterval; + } + + @Override + public float getPressInterval() + { + return pressInterval; + } + + @Override + public void setPressDelay(float pressDelay) + { + this.pressDelay = pressDelay; + } + + @Override + public float getPressDelay() + { + return pressDelay; + } + +} diff --git a/src/com/axe/input/AbstractMouseEngine.java b/src/com/axe/input/AbstractMouseEngine.java new file mode 100644 index 0000000..3bec165 --- /dev/null +++ b/src/com/axe/input/AbstractMouseEngine.java @@ -0,0 +1,119 @@ +package com.axe.input; + +import java.util.ArrayDeque; +import java.util.Queue; + +import com.axe.window.Window; + + +public abstract class AbstractMouseEngine implements MouseEngine +{ + + public static final int MAX_BUTTONS = 3; + + public InputState[] states; + public MouseState mouse; + public Queue queue; + public boolean grabbed; + public boolean hidden; + public float pressDelay; + public float pressInterval; + + public AbstractMouseEngine() + { + this.states = new InputState[ MAX_BUTTONS ]; + this.mouse = new MouseState(); + + for (int i = 0; i < MAX_BUTTONS; i++) + { + this.states[i] = new InputState(i); + } + + this.queue = new ArrayDeque<>(); + + this.pressDelay = DEFAULT_PRESS_DELAY; + this.pressInterval = DEFAULT_PRESS_INTERVAL; + } + + public void init(long startTime) + { + for (int i = 0; i < states.length; i++) + { + states[ i ].changeTime = startTime; + } + } + + public void tick(long currentTime) + { + for (int i = 0; i < states.length; i++) + { + states[ i ].tick( currentTime, pressDelay, pressInterval ); + } + } + + public void queueButtonState(int button, boolean down, long inputTime, Window inputWindow) + { + MouseButtonState state = new MouseButtonState( button ); + + state.setDown( down, inputTime, inputWindow ); + state.x = inputWindow.getMouse().x; + state.y = inputWindow.getMouse().y; + + queue.offer( state ); + } + + @Override + public InputState getButton(int button) + { + return states[ button ]; + } + + @Override + public MouseState getMouse() + { + return mouse; + } + + @Override + public Queue getQueue() + { + return queue; + } + + @Override + public boolean isGrabbed() + { + return grabbed; + } + + @Override + public boolean isHidden() + { + return hidden; + } + + @Override + public void setPressInterval(float pressInterval) + { + this.pressInterval = pressInterval; + } + + @Override + public float getPressInterval() + { + return pressInterval; + } + + @Override + public void setPressDelay(float pressDelay) + { + this.pressDelay = pressDelay; + } + + @Override + public float getPressDelay() + { + return pressDelay; + } + +} diff --git a/src/com/axe/input/ControllerEngine.java b/src/com/axe/input/ControllerEngine.java new file mode 100644 index 0000000..d7aed41 --- /dev/null +++ b/src/com/axe/input/ControllerEngine.java @@ -0,0 +1,5 @@ +package com.axe.input; + +public interface ControllerEngine { + +} diff --git a/src/com/axe/input/InputState.java b/src/com/axe/input/InputState.java new file mode 100644 index 0000000..4e7474d --- /dev/null +++ b/src/com/axe/input/InputState.java @@ -0,0 +1,74 @@ +package com.axe.input; + +import com.axe.window.Window; + +public class InputState +{ + + public Window window; + public final int input; + public boolean down; + public int frames; + public long time; + public float seconds; + public int presses; + public boolean press; + public long changeTime; + + public InputState(int input) + { + this.input = input; + } + + public boolean isDown() + { + return down && frames == 0; + } + + public boolean isUp() + { + return !down && frames == 0; + } + + public boolean isPress() + { + return press; + } + + public boolean isChange() + { + return frames == 0; + } + + public void setDown(boolean isDown, long inputTime, Window inputWindow) + { + if (down != isDown) + { + frames = 0; + time = 0; + seconds = 0; + down = isDown; + press = isDown; + presses = isDown ? 1 : 0; + changeTime = inputTime; + } + + window = inputWindow; + } + + public void tick(long currentTime, float delay, float interval) + { + time = (currentTime - changeTime); + seconds = time * 0.001f; + press = false; + + if (down) + { + int currentPresses = (int)Math.max(0, (seconds - delay) / interval) + 1; + + press = (currentPresses != presses); + presses = currentPresses; + } + } + +} diff --git a/src/com/axe/input/InputStateAll.java b/src/com/axe/input/InputStateAll.java new file mode 100644 index 0000000..0eb0838 --- /dev/null +++ b/src/com/axe/input/InputStateAll.java @@ -0,0 +1,39 @@ +package com.axe.input; + +public class InputStateAll extends InputState +{ + + public InputState[] states; + + public InputStateAll(InputState ... states) + { + super( states[0].input ); + + this.states = states; + } + + public void update() + { + down = true; + frames = Integer.MAX_VALUE; + time = Long.MAX_VALUE; + seconds = Float.MAX_VALUE; + presses = Integer.MAX_VALUE; + press = true; + changeTime = 0; + + for (int i = 0; i < states.length; i++) + { + InputState s = states[ i ]; + + down = down && s.down; + frames = Math.min( frames, s.frames ); + time = Math.min( time, s.time ); + seconds = Math.min( seconds, s.seconds ); + press = press && s.press; + presses = Math.min( presses, s.presses ); + changeTime = Math.max( changeTime, s.changeTime ); + } + } + +} diff --git a/src/com/axe/input/InputStateAny.java b/src/com/axe/input/InputStateAny.java new file mode 100644 index 0000000..599bfd8 --- /dev/null +++ b/src/com/axe/input/InputStateAny.java @@ -0,0 +1,39 @@ +package com.axe.input; + +public class InputStateAny extends InputState +{ + + public InputState[] states; + + public InputStateAny(InputState ... states) + { + super( states[0].input ); + + this.states = states; + } + + public void update() + { + down = false; + frames = 0; + time = 0; + seconds = 0; + presses = 0; + press = false; + changeTime = Long.MAX_VALUE; + + for (int i = 0; i < states.length; i++) + { + InputState s = states[ i ]; + + down = down || s.down; + frames = Math.max( frames, s.frames ); + time = Math.max( time, s.time ); + seconds = Math.max( seconds, s.seconds ); + press = press || s.press; + presses = Math.max( presses, s.presses ); + changeTime = Math.min( changeTime, s.changeTime ); + } + } + +} diff --git a/src/com/axe/input/Key.java b/src/com/axe/input/Key.java new file mode 100644 index 0000000..cd6b3a6 --- /dev/null +++ b/src/com/axe/input/Key.java @@ -0,0 +1,131 @@ +package com.axe.input; + +public enum Key +{ + + NONE, + ESCAPE, + N1, + N2, + N3, + N4, + N5, + N6, + N7, + N8, + N9, + N0, + MINUS, + EQUALS, + BACK, + TAB, + Q, + W, + E, + R, + T, + Y, + U, + I, + O, + P, + LEFT_BRACKET, // LBRACKET + RIGHT_BRACKET, // RBRACKET + RETURN, + LEFT_CONTROL, // LCONTROL + A, + S, + D, + F, + G, + H, + J, + K, + L, + SEMICOLON, + APOSTROPHE, + GRAVE, + LEFT_SHIFT, // LSHIFT + BACKSLASH, + Z, + X, + C, + V, + B, + N, + M, + COMMA, + PERIOD, + SLASH, + RIGHT_SHIFT, // RSHIFT + MULTIPLY, + LEFT_ALT, // LMENU + SPACE, + CAPITAL, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + NUMLOCK, + SCROLL, + NUMPAD7, + NUMPAD8, + NUMPAD9, + SUBTRACT, + NUMPAD4, + NUMPAD5, + NUMPAD6, + ADD, + NUMPAD1, + NUMPAD2, + NUMPAD3, + NUMPAD0, + DECIMAL, + F11, + F12, + F13, + F14, + F15, + KANA, + CONVERT, + NOCONVERT, + YEN, + NUMPADEQUALS, + CIRCUMFLEX, // ^ + AT, // +shift + COLON, // +shift + UNDERLINE, // +shift + KANJI, + STOP, + AX, + UNLABELED, + NUMPADENTER, + RIGHT_CONTROL, + NUMPADCOMMA, + DIVIDE, + SYSRQ, + RIGHT_ALT, // RMENU + PAUSE, + HOME, + UP, + PAGE_UP, // PRIOR + LEFT, + RIGHT, + END, + DOWN, + PAGE_DOWN, // NEXT + INSERT, + DELETE, + LEFT_COMMAND, // LMETA, WINDOWS, LCOMMAND + RIGHT_COMMAND, // RMETA, WINDOWS, RCOMMAND + APPS, + POWER, + SLEEP + +} \ No newline at end of file diff --git a/src/com/axe/input/KeyEngine.java b/src/com/axe/input/KeyEngine.java new file mode 100644 index 0000000..1100f87 --- /dev/null +++ b/src/com/axe/input/KeyEngine.java @@ -0,0 +1,32 @@ +package com.axe.input; + +import java.util.Queue; + +public interface KeyEngine +{ + public static final float DEFAULT_PRESS_DELAY = 0.5f; + public static final float DEFAULT_PRESS_INTERVAL = 0.1f; + + public KeyState getKey(Key key); + + public InputState getShift(); + + public InputState getControl(); + + public InputState getMeta(); + + public InputState getCommand(); + + public InputState getAlt(); + + public Queue getQueue(); + + public void setPressInterval(float pressInterval); + + public float getPressInterval(); + + public void setPressDelay(float pressDelay); + + public float getPressDelay(); + +} \ No newline at end of file diff --git a/src/com/axe/input/KeyState.java b/src/com/axe/input/KeyState.java new file mode 100644 index 0000000..87c478d --- /dev/null +++ b/src/com/axe/input/KeyState.java @@ -0,0 +1,23 @@ +package com.axe.input; + +public class KeyState extends InputState +{ + + public Key key; + public char character; + + public KeyState(int input) + { + super(input); + + this.key = Key.values()[ input ]; + } + + public KeyState(Key key) + { + super( key.ordinal() ); + + this.key = key; + } + +} \ No newline at end of file diff --git a/src/com/axe/input/MouseButtonState.java b/src/com/axe/input/MouseButtonState.java new file mode 100644 index 0000000..54d6c22 --- /dev/null +++ b/src/com/axe/input/MouseButtonState.java @@ -0,0 +1,14 @@ +package com.axe.input; + +public class MouseButtonState extends InputState +{ + + public int x; + public int y; + + public MouseButtonState(int input) + { + super( input ); + } + +} diff --git a/src/com/axe/input/MouseEngine.java b/src/com/axe/input/MouseEngine.java new file mode 100644 index 0000000..39a2c7f --- /dev/null +++ b/src/com/axe/input/MouseEngine.java @@ -0,0 +1,37 @@ +package com.axe.input; + +import java.util.Queue; + +public interface MouseEngine +{ + + public static final float DEFAULT_PRESS_DELAY = 0.5f; + public static final float DEFAULT_PRESS_INTERVAL = 0.1f; + + public static final int LEFT = 0; + public static final int RIGHT = 1; + public static final int MIDDLE = 2; + + public InputState getButton(int button); + + public MouseState getMouse(); + + public Queue getQueue(); + + public void setGrabbed(boolean grabbed); + + public boolean isGrabbed(); + + public void setHidden(boolean hidden); + + public boolean isHidden(); + + public void setPressInterval(float pressInterval); + + public float getPressInterval(); + + public void setPressDelay(float pressDelay); + + public float getPressDelay(); + +} \ No newline at end of file diff --git a/src/com/axe/input/MouseState.java b/src/com/axe/input/MouseState.java new file mode 100644 index 0000000..2890b5d --- /dev/null +++ b/src/com/axe/input/MouseState.java @@ -0,0 +1,57 @@ +package com.axe.input; + +import com.axe.math.Vec2i; + +public class MouseState +{ + + public int x, y, dx, dy, sdx, sdy, wheel, wheelSign; + + public boolean inside, insideChange; + + public final Vec2i position = new Vec2i(); + public final Vec2i delta = new Vec2i(); + public final Vec2i sign = new Vec2i(); + + public void reset(int mx, int my) + { + x = position.x = mx; + y = position.y = my; + inside = insideChange = false; + clear(); + } + + public void clear() + { + dx = dy = delta.x = delta.y = 0; + sdx = sdy = sign.x = sign.y = 0; + wheel = wheelSign = 0; + } + + public void accumulatePosition(int mx, int my) + { + delta.x = dx += (mx - x); + delta.y = dy += (my - y); + + position.x = x = mx; + position.y = y = my; + + sign.x = sdx = Integer.signum( dx ); + sign.y = sdy = Integer.signum( dy ); + } + + public void accumulateScroll(int ticks) + { + wheel += ticks; + wheelSign = Integer.signum( wheel ); + } + + public void updateInside(int windowWidth, int windowHeight) + { + boolean currentInside = !(x < 0 || x >= windowWidth || y < 0 || y >= windowHeight); + + insideChange = (currentInside != inside); + inside = currentInside; + } + +} \ No newline at end of file diff --git a/src/com/axe/integrate/Integrator.java b/src/com/axe/integrate/Integrator.java new file mode 100755 index 0000000..5bbac15 --- /dev/null +++ b/src/com/axe/integrate/Integrator.java @@ -0,0 +1,15 @@ +package com.axe.integrate; + +import com.axe.core.Attribute; +import com.axe.game.GameState; + +public interface Integrator> +{ + + public T position(); + + public T acceleration(); + + public void update(GameState state); + +} diff --git a/src/com/axe/integrate/IntegratorEuler.java b/src/com/axe/integrate/IntegratorEuler.java new file mode 100755 index 0000000..460960e --- /dev/null +++ b/src/com/axe/integrate/IntegratorEuler.java @@ -0,0 +1,40 @@ +package com.axe.integrate; + +import com.axe.core.Attribute; +import com.axe.game.GameState; + + +public class IntegratorEuler> implements Integrator +{ + + public T position; + public T velocity; + public T acceleration; + + public IntegratorEuler(T position, T velocity, T acceleration) + { + this.position = position; + this.velocity = velocity; + this.acceleration = acceleration; + } + + @Override + public void update( GameState state ) + { + velocity.adds( acceleration, state.seconds ); + position.adds( velocity, state.seconds ); + } + + @Override + public T position() + { + return position; + } + + @Override + public T acceleration() + { + return acceleration; + } + +} diff --git a/src/com/axe/integrate/IntegratorRungeKutta.java b/src/com/axe/integrate/IntegratorRungeKutta.java new file mode 100755 index 0000000..95709c3 --- /dev/null +++ b/src/com/axe/integrate/IntegratorRungeKutta.java @@ -0,0 +1,126 @@ +package com.axe.integrate; + +import com.axe.core.Attribute; +import com.axe.game.GameState; + + +public class IntegratorRungeKutta> implements Integrator +{ + + public static final int DEFAULT_ITERATIONS = 4; + + public T position; + public T acceleration; + public int iterations; + public Derivative d0; + public Derivative d1; + public Derivative d2; + public Derivative d3; + + public IntegratorRungeKutta(T position, T acceleration) + { + this( position, acceleration, DEFAULT_ITERATIONS ); + } + + public IntegratorRungeKutta(T position, T acceleration, int iterations) + { + this.position = position; + this.acceleration = acceleration; + this.d0 = new Derivative( position ); + this.d1 = new Derivative( position ); + this.d2 = new Derivative( position ); + this.d3 = new Derivative( position ); + } + + @Override + public void update( GameState state ) + { + // float delta = 1f / iterations; + + for (int i = 0; i < iterations; i++) + { + /* + a = evaluate( state, t, 0.0f, Derivative() ); + b = evaluate( state, t, dt*0.5f, a ); + c = evaluate( state, t, dt*0.5f, b ); + d = evaluate( state, t, dt, c ); + + float dxdt = 1.0f / 6.0f * + ( a.dx + 2.0f*(b.dx + c.dx) + d.dx ); + + float dvdt = 1.0f / 6.0f * + ( a.dv + 2.0f*(b.dv + c.dv) + d.dv ); + + state.x = state.x + dxdt * dt; + state.v = state.v + dvdt * dt; + */ + } + + } + + + // TODO implementations specific. + public T acceleration(T out, State state, float t) + { + // Spring Example + int springConstant = 10; + int springDamping = 1; + + out.set( state.x ); + out.scale( -springConstant ); + out.adds( state.v, -springDamping ); + + return out; + } + + public Derivative evaluate(Derivative out, State initial, float t, float dt, Derivative d) + { + State state = new State( initial.v ); + + state.x.set( initial.x ); + state.x.adds( d.dx, dt ); + + state.v.set( initial.v ); + state.v.adds( d.dv, dt ); + + out.dx = state.v; + out.dv = acceleration( initial.v.clone(), state, t + dt ); + + return out; + } + + @Override + public T position() + { + return position; + } + + @Override + public T acceleration() + { + return acceleration; + } + + public class State + { + public T x, v; + + public State(T factory) + { + this.x = factory.clone(); + this.v = factory.clone(); + } + } + + public class Derivative + { + public T dx, dv; + + public Derivative(T factory) + { + this.dx = factory.clone(); + this.dv = factory.clone(); + } + } + +} diff --git a/src/com/axe/integrate/IntegratorVerlet.java b/src/com/axe/integrate/IntegratorVerlet.java new file mode 100755 index 0000000..bce073e --- /dev/null +++ b/src/com/axe/integrate/IntegratorVerlet.java @@ -0,0 +1,45 @@ +package com.axe.integrate; + +import com.axe.core.Attribute; +import com.axe.game.GameState; + + +public class IntegratorVerlet> implements Integrator +{ + + public T position; + public T lastPosition; + public T acceleration; + private T temp; + + public IntegratorVerlet(T position, T acceleration) + { + this.position = position; + this.lastPosition = position.clone(); + this.acceleration = acceleration; + this.temp = position.clone(); + } + + @Override + public void update( GameState state ) + { + temp.set( position ); + position.scale( 2.0f ); + position.sub( lastPosition ); + position.adds( acceleration, state.seconds * state.seconds ); + lastPosition.set( temp ); + } + + @Override + public T position() + { + return position; + } + + @Override + public T acceleration() + { + return acceleration; + } + +} diff --git a/src/com/axe/io/DataBuffer.java b/src/com/axe/io/DataBuffer.java new file mode 100755 index 0000000..0734542 --- /dev/null +++ b/src/com/axe/io/DataBuffer.java @@ -0,0 +1,119 @@ +package com.axe.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + + +public class DataBuffer +{ + + private int write; + private int read; + private int resize; + private byte[] bytes; + + public final OutputStream out; + public final InputStream in; + + public DataBuffer(int initialCapacity, int resizeAmount) + { + this.bytes = new byte[initialCapacity]; + this.resize = resizeAmount; + this.out = new Output(); + this.in = new Input(); + } + + private class Output extends OutputStream + { + private void ensureSize(int s) + { + if (write + s >= bytes.length) + { + int minimumSize = write + s; + int desiredSize = bytes.length + resize; + int nextSize = Math.max( minimumSize, desiredSize ); + + bytes = Arrays.copyOf( bytes, nextSize ); + } + } + + @Override + public void write( int b ) throws IOException + { + ensureSize( 1 ); + + bytes[write++] = (byte)b; + } + + @Override + public void write(byte[] b, int offset, int length) + { + ensureSize( length ); + + System.arraycopy( b, offset, bytes, write, length ); + + write += length; + } + } + + private class Input extends InputStream + { + private int mark = -1; + + @Override + public int read() throws IOException + { + return read < write ? bytes[read++] : -1; + } + + @Override + public int available() + { + return (write - read); + } + + @Override + public void mark(int m) + { + mark = m; + } + + @Override + public void reset() + { + read = mark; + mark = -1; + } + + @Override + public boolean markSupported() + { + return true; + } + + @Override + public long skip(long bytes) + { + long skipped = Math.min( bytes, available() ); + + read += skipped; + + return skipped; + } + + @Override + public int read(byte[] b, int offset, int length) + { + int bytesRead = Math.min( available(), length ); + + System.arraycopy( bytes, read, b, offset, bytesRead ); + + read += bytesRead; + + return bytesRead == 0 ? -1 : bytesRead; + } + } + +} diff --git a/src/com/axe/io/DataBundle.java b/src/com/axe/io/DataBundle.java new file mode 100755 index 0000000..bd9f17c --- /dev/null +++ b/src/com/axe/io/DataBundle.java @@ -0,0 +1,88 @@ +package com.axe.io; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + + +public class DataBundle implements DataModel +{ + + private DataFormat format; + private String name; + private Map modelMap = new HashMap(); + + public DataBundle( InputModel input ) + { + read( input ); + } + + @Override + public void read( InputModel input ) + { + format = input.getFormat(); + name = input.getName(); + modelMap.clear(); + + for (InputModel child : input) + { + String name = child.getName(); + String type = child.readString( "data-type" ); + Class clazz = DataRegistry.getClass( type ); + + DataModel model = instantiate( clazz ); + model.read( child ); + + modelMap.put( name, model ); + } + } + + @Override + public void write( OutputModel output ) + { + for ( Entry entry : modelMap.entrySet() ) + { + DataModel model = entry.getValue(); + String name = entry.getKey(); + String type = DataRegistry.getClassName( model.getClass() ); + + OutputModel out = output.writeModel( name, model ); + out.write( "data-type", type ); + } + } + + @SuppressWarnings("unchecked") + private T instantiate( Class clazz ) throws DataException + { + try + { + return (T)clazz.newInstance(); + } + catch (Exception ex) + { + throw new DataException( ex ); + } + } + + public DataFormat getFormat() + { + return format; + } + + public String getName() + { + return name; + } + + public void add(String name, DataModel model) + { + modelMap.put( name, model ); + } + + @SuppressWarnings("unchecked") + public T get( String name ) + { + return (T)modelMap.get( name ); + } + +} diff --git a/src/com/axe/io/DataException.java b/src/com/axe/io/DataException.java new file mode 100755 index 0000000..aca8a8f --- /dev/null +++ b/src/com/axe/io/DataException.java @@ -0,0 +1,22 @@ +package com.axe.io; + +public class DataException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DataException(String message) + { + super( message ); + } + + public DataException(Exception cause) + { + super( cause ); + } + + public DataException(String format, Object ... args) + { + super( String.format(format, args) ); + } + +} \ No newline at end of file diff --git a/src/com/axe/io/DataFormat.java b/src/com/axe/io/DataFormat.java new file mode 100755 index 0000000..89d383a --- /dev/null +++ b/src/com/axe/io/DataFormat.java @@ -0,0 +1,44 @@ + +package com.axe.io; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; + + +public interface DataFormat +{ + + public OutputModel newOutput( String name ); + + public OutputModel newOutput( String name, DataModel model ); + + public OutputModel newOutput( String name, int scope ); + + public OutputModel newOutput( String name, int scope, DataModel model ); + + public OutputModel write( Writer writer, String name, DataModel model ) throws Exception; + + public OutputModel write( File file, String name, DataModel model ) throws Exception; + + public OutputModel write( OutputStream stream, String name, DataModel model ) throws Exception; + + public InputModel read( InputStream stream ) throws Exception; + + public InputModel read( Reader reader ) throws Exception; + + public InputModel read( File file ) throws Exception; + + public InputModel read( String string ) throws Exception; + + public T read( InputStream stream, T model ) throws Exception; + + public T read( Reader reader, T model ) throws Exception; + + public T read( File file, T model ) throws Exception; + + public T read( String string, T model ) throws Exception; + +} diff --git a/src/com/axe/io/DataModel.java b/src/com/axe/io/DataModel.java new file mode 100755 index 0000000..44ea44f --- /dev/null +++ b/src/com/axe/io/DataModel.java @@ -0,0 +1,8 @@ +package com.axe.io; + + +public interface DataModel +{ + public void read(InputModel input); + public void write(OutputModel output); +} diff --git a/src/com/axe/io/DataRegistry.java b/src/com/axe/io/DataRegistry.java new file mode 100755 index 0000000..b41dcfa --- /dev/null +++ b/src/com/axe/io/DataRegistry.java @@ -0,0 +1,299 @@ +package com.axe.io; + +import java.util.Map; + +import com.axe.Axe; +import com.axe.color.Color; +import com.axe.color.Colors; +import com.axe.easing.Easings; +import com.axe.gfx.Blend; +import com.axe.gfx.Coord; +import com.axe.math.Bound2f; +import com.axe.math.Bound2i; +import com.axe.math.Bound3f; +import com.axe.math.Bound3i; +import com.axe.math.Rangef; +import com.axe.math.Rangei; +import com.axe.math.Rect2i; +import com.axe.math.Scalarf; +import com.axe.math.Scalari; +import com.axe.math.Vec2f; +import com.axe.math.Vec2i; +import com.axe.math.Vec3f; +import com.axe.math.Vec3i; +import com.axe.noise.PerlinNoise; +import com.axe.noise.SimplexNoise; +import com.axe.path.AdditivePath; +import com.axe.path.BezierPath; +import com.axe.path.ComboPath; +import com.axe.path.CompiledPath; +import com.axe.path.CubicPath; +import com.axe.path.DurationPath; +import com.axe.path.EasedPath; +import com.axe.path.IntegralPath; +import com.axe.path.JumpPath; +import com.axe.path.LinearPath; +import com.axe.path.PointPath; +import com.axe.path.QuadraticPath; +import com.axe.path.ScaledPath; +import com.axe.path.SubPath; +import com.axe.path.TimedPath; +import com.axe.path.Tween; +import com.axe.path.UniformPath; +import com.axe.tile.Tile; + +public class DataRegistry +{ + + public static final RegistryMap objects = new RegistryMap(); + public static final RegistryMap> classes = new RegistryMap>(); + + public static void register(String name, Class clazz) + { + classes.put( name, clazz ); + } + + public static void registerClasses(Map> classMap) + { + classes.putAll( classMap ); + } + + public static void register(String name, Object object) + { + objects.put( name, object ); + } + + public static void registerObjects(Map objectMap) + { + objects.putAll( objectMap ); + } + + @SuppressWarnings("unchecked") + public static Class getClass(String name) + { + return (Class)classes.getForward( name ); + } + + @SuppressWarnings("unchecked") + public static T getInstance(String className, boolean throwRuntimeException) + { + try + { + return (T)classes.getForward( className ).newInstance(); + } + catch (Exception e) + { + if ( throwRuntimeException ) + { + throw new RuntimeException( e ); + } + + Axe.logger.log( null, e ); + + return null; + } + } + + public static String getClassName(Class clazz) + { + return classes.getBackward( clazz ); + } + + @SuppressWarnings("unchecked") + public static T get(String name) + { + return (T)objects.getForward( name ); + } + + public static String getName(Object value) + { + return objects.getBackward( value ); + } + + static + { + // Paths + register( "path-bezier", BezierPath.class ); + register( "path-combo", ComboPath.class ); + register( "path-compiled", CompiledPath.class ); + register( "path-cubic", CubicPath.class ); + register( "path-duration", DurationPath.class ); + register( "path-eased", EasedPath.class ); + register( "path-integral", IntegralPath.class ); + register( "path-jump", JumpPath.class ); + register( "path-linear", LinearPath.class ); + register( "path-point", PointPath.class ); + register( "path-quadratic", QuadraticPath.class ); + register( "path-sub", SubPath.class ); + register( "path-timed", TimedPath.class ); + register( "path-tween", Tween.class ); + register( "path-uniform", UniformPath.class ); + register( "path-scaled", ScaledPath.class ); + register( "path-additive", AdditivePath.class ); + + /* + // Particle Influences 2d + register( "influence2d-acceleration", InfluenceAcceleration.class ); + register( "influence2d-align", InfluenceAlign.class ); + register( "influence2d-alpha", InfluenceAlpha.class ); + register( "influence2d-angle", InfluenceAngle.class ); + register( "influence2d-color", InfluenceColor.class ); + register( "influence2d-constraint", InfluenceConstraint.class ); + register( "influence2d-damping", InfluenceDamping.class ); + register( "influence2d-scale", InfluenceScale.class ); + register( "influence2d-size", InfluenceSize.class ); + register( "influence2d-tile", InfluenceTile.class ); + register( "influence2d-velocity", InfluenceVelocity.class ); + + // Particle Initializers 2d + register( "initialize2d-acceleration", InitializeAcceleration.class ); + register( "initialize2d-acceleration-scalar", InitializeAccelerationScalar.class ); + register( "initialize2d-angle", InitializeAngle.class ); + register( "initialize2d-angle-acceleration", InitializeAngleAcceleration.class ); + register( "initialize2d-angle-velocity", InitializeAngleVelocity.class ); + register( "initialize2d-color", InitializeColor.class ); + register( "initialize2d-scale", InitializeScale.class ); + register( "initialize2d-scale-acceleration", InitializeScaleAcceleration.class ); + register( "initialize2d-scale-velocity", InitializeScaleVelocity.class ); + register( "initialize2d-size", InitializeSize.class ); + register( "initialize2d-tile", InitializeTile.class ); + + // Particle Velocity 2d + register( "velocity2d-default", VelocityDefault.class ); + register( "velocity2d-directional", VelocityDirectional.class ); + register( "velocity2d-ortho", VelocityOrtho.class ); + register( "velocity2d-outward", VelocityOutward.class ); + register( "velocity2d-towards", VelocityTowards.class ); + + // Particle Volume 2d + register( "volume2d-bounds", VolumeBounds.class ); + register( "volume2d-default", VolumeDefault.class ); + register( "volume2d-ellipse", VolumeEllipse.class ); + register( "volume2d-path", VolumePath.class ); + register( "volume2d-pinwheel", VolumePinwheel.class ); + + // Particle Factory 2d + register( "particle-factory2d-angle", AngleParticle.factory ); + register( "particle-factory2d-Base", BaseParticle.factory ); + register( "particle-factory2d-color", ColorParticle.factory ); + register( "particle-factory2d-colortile", ColorTileParticle.factory ); + register( "particle-factory2d-full", FullParticle.factory ); + register( "particle-factory2d-physics", PhysicsParticle.factory ); + register( "particle-factory2d-tile", TileParticle.factory ); + register( "particle-factory2d-twin", TwinParticle.factory ); + + // Particle Renderer 2d + register( "particle-renderer2d-angle", AngleParticle.VIEW ); + register( "particle-renderer2d-Base", BaseParticle.VIEW ); + register( "particle-renderer2d-color", ColorParticle.VIEW ); + register( "particle-renderer2d-colortile", ColorTileParticle.VIEW ); + register( "particle-renderer2d-full", FullParticle.VIEW ); + register( "particle-renderer2d-physics", PhysicsParticle.VIEW ); + register( "particle-renderer2d-tile", TileParticle.VIEW ); + register( "particle-renderer2d-twin", TwinParticle.VIEW ); + + // Particle Influences 3d + register( "influence3d-acceleration", com.axe3d.efx.influence.InfluenceAcceleration.class ); + register( "influence3d-alpha", com.axe3d.efx.influence.InfluenceAlpha.class ); + register( "influence3d-angle", com.axe3d.efx.influence.InfluenceAngle.class ); + register( "influence3d-color", com.axe3d.efx.influence.InfluenceColor.class ); + register( "influence3d-constraint", com.axe3d.efx.influence.InfluenceConstraint.class ); + register( "influence3d-damping", com.axe3d.efx.influence.InfluenceDamping.class ); + register( "influence3d-scale", com.axe3d.efx.influence.InfluenceScale.class ); + register( "influence3d-size", com.axe3d.efx.influence.InfluenceSize.class ); + register( "influence3d-tile", com.axe3d.efx.influence.InfluenceTile.class ); + register( "influence3d-velocity", com.axe3d.efx.influence.InfluenceVelocity.class ); + + // Particle Initializers 3d + register( "initialize3d-acceleration", com.axe3d.efx.initializer.InitializeAcceleration.class ); + register( "initialize3d-acceleration-scalar", com.axe3d.efx.initializer.InitializeAccelerationScalar.class ); + register( "initialize3d-angle", com.axe3d.efx.initializer.InitializeAngle.class ); + register( "initialize3d-angle-acceleration", com.axe3d.efx.initializer.InitializeAngleAcceleration.class ); + register( "initialize3d-angle-velocity", com.axe3d.efx.initializer.InitializeAngleVelocity.class ); + register( "initialize3d-color", com.axe3d.efx.initializer.InitializeColor.class ); + register( "initialize3d-scale", com.axe3d.efx.initializer.InitializeScale.class ); + register( "initialize3d-scale-acceleration", com.axe3d.efx.initializer.InitializeScaleAcceleration.class ); + register( "initialize3d-scale-velocity", com.axe3d.efx.initializer.InitializeScaleVelocity.class ); + register( "initialize3d-size", com.axe3d.efx.initializer.InitializeSize.class ); + register( "initialize3d-tile", com.axe3d.efx.initializer.InitializeTile.class ); + + // Particle Velocity 3d + register( "velocity3d-default", com.axe3d.efx.velocity.VelocityDefault.class ); + register( "velocity3d-directional", com.axe3d.efx.velocity.VelocityDirectional.class ); + register( "velocity3d-ortho", com.axe3d.efx.velocity.VelocityOrtho.class ); + register( "velocity3d-outward", com.axe3d.efx.velocity.VelocityOutward.class ); + register( "velocity3d-towards", com.axe3d.efx.velocity.VelocityTowards.class ); + + // Particle Volume 3d + register( "volume3d-bounds", com.axe3d.efx.volume.VolumeBounds.class ); + register( "volume3d-default", com.axe3d.efx.volume.VolumeDefault.class ); + register( "volume3d-ellipse", com.axe3d.efx.volume.VolumeEllipse.class ); + register( "volume3d-path", com.axe3d.efx.volume.VolumePath.class ); + + // Particle Factory 3d + register( "particle-factory3d-angle", com.axe3d.efx.particle.AngleParticle.factory ); + register( "particle-factory3d-Base", com.axe3d.efx.particle.BaseParticle.factory ); + register( "particle-factory3d-color", com.axe3d.efx.particle.ColorParticle.factory ); + register( "particle-factory3d-colortile", com.axe3d.efx.particle.ColorTileParticle.factory ); + register( "particle-factory3d-full", com.axe3d.efx.particle.FullParticle.factory ); + register( "particle-factory3d-physics", com.axe3d.efx.particle.PhysicsParticle.factory ); + register( "particle-factory3d-tile", com.axe3d.efx.particle.TileParticle.factory ); + register( "particle-factory3d-twin", com.axe3d.efx.particle.TwinParticle.factory ); + + // Particle Renderer 3d + register( "particle-renderer3d-angle", com.axe3d.efx.particle.AngleParticle.VIEW ); + register( "particle-renderer3d-Base", com.axe3d.efx.particle.BaseParticle.VIEW ); + register( "particle-renderer3d-color", com.axe3d.efx.particle.ColorParticle.VIEW ); + register( "particle-renderer3d-colortile", com.axe3d.efx.particle.ColorTileParticle.VIEW ); + register( "particle-renderer3d-full", com.axe3d.efx.particle.FullParticle.VIEW ); + register( "particle-renderer3d-physics", com.axe3d.efx.particle.PhysicsParticle.VIEW ); + register( "particle-renderer3d-tile", com.axe3d.efx.particle.TileParticle.VIEW ); + register( "particle-renderer3d-twin", com.axe3d.efx.particle.TwinParticle.VIEW ); + */ + + // Noise + DataRegistry.register( "noise-perlin", PerlinNoise.class ); + DataRegistry.register( "noise-simplex", SimplexNoise.class ); + + // Attributes + register( "color", Color.class ); + register( "coord", Coord.class ); + register( "tile", Tile.class ); + register( "scalarf", Scalarf.class ); + register( "scalari", Scalari.class ); + register( "bound2f", Bound2f.class ); + register( "bound2i", Bound2i.class ); + register( "bound3f", Bound3f.class ); + register( "bound3i", Bound3i.class ); + // register( "camera2", Camera2.class ); + // register( "camera3", Camera3.class ); + register( "rect2f", Bound2i.class ); + register( "rect2i", Rect2i.class ); + register( "vec2f", Vec2f.class ); + register( "vec2i", Vec2i.class ); + register( "vec3f", Vec3f.class ); + register( "vec3i", Vec3i.class ); + register( "rangef", Rangef.class ); + register( "rangei", Rangei.class ); + + // Colors + registerObjects( Colors.ColorMap ); + + // Easing Types + registerObjects( Easings.TypeMap ); + + // Easing Functions + registerObjects( Easings.MethodMap ); + + // Blends + register( "Add", Blend.Add ); + register( "AlphaAdd", Blend.AlphaAdd ); + register( "Alpha", Blend.Alpha ); + register( "Color", Blend.Color ); + register( "Minus", Blend.Minus ); + register( "PremultAlpha", Blend.PremultAlpha ); + register( "Modulate", Blend.Modulate ); + register( "Xor", Blend.Xor ); + register( "None", Blend.None ); + } +} diff --git a/src/com/axe/io/DataUtility.java b/src/com/axe/io/DataUtility.java new file mode 100755 index 0000000..c020715 --- /dev/null +++ b/src/com/axe/io/DataUtility.java @@ -0,0 +1,509 @@ +package com.axe.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class DataUtility +{ + + private static DataFormat defaultFormat = null; + + public static void setDefaultFormat( DataFormat format ) + { + defaultFormat = format; + } + + public static DataFormat getDefaultFormat() + { + return defaultFormat; + } + + public static final int BYTE_SHIFT = 7; + public static final int BYTE_MASK = 0x7F; + public static final int BYTE_MORE = 0x80; + + private static final int BYTES_BYTE = 1; + private static final int BYTES_INT = 4; + private static final int BYTES_LONG = 8; + private static final int[] VALUE_BYTES = { + /*0*/ 0, + /*1*/ 1 << (0 * 7), + /*2*/ 1 << (1 * 7), + /*3*/ 1 << (2 * 7), + /*4*/ 1 << (3 * 7), + /*5*/ 1 << (4 * 7), + /*6*/ 1 << (5 * 7), + /*7*/ 1 << (6 * 7), + /*8*/ 1 << (7 * 7), + }; + + public static int readInt( InputStream in ) throws IOException + { + int x = 0; + int b = 0; + int shift = 0; + + do + { + b = in.read(); + + if ( b == -1 ) + { + return x; + } + + x |= (b & BYTE_MASK) << shift; + shift += BYTE_SHIFT; + } + while ( (b & BYTE_MORE) == BYTE_MORE ); + + return x; + } + + public static int writeInt( OutputStream out, int value ) throws IOException + { + int bytes = 0; + + do + { + out.write( (value & BYTE_MASK) | (value > BYTE_MASK ? BYTE_MORE : 0) ); + value >>= BYTE_SHIFT; + bytes++; + } while ( value > 0 ); + + return bytes; + } + + public static long readLong( InputStream in ) throws IOException + { + long x = 0; + int b = 0; + long shift = 0; + + do + { + b = in.read(); + + if ( b == -1 ) + { + return x; + } + + x |= (b & BYTE_MASK) << shift; + shift += BYTE_SHIFT; + } + while ( (b & BYTE_MORE) == BYTE_MORE ); + + return x; + } + + public static int writeLong( OutputStream out, long value ) throws IOException + { + int bytes = 0; + + do + { + out.write( (int)((value & BYTE_MASK) | (value > BYTE_MASK ? BYTE_MORE : 0)) ); + value >>= BYTE_SHIFT; + bytes++; + } while ( value > 0 ); + + return bytes; + } + + public static int getInt( ByteBuffer in ) + { + int x = 0; + int b = 0; + int shift = 0; + + do + { + b = in.get() & 0xFF; + x |= (b & BYTE_MASK) << shift; + shift += BYTE_SHIFT; + } + while ( (b & BYTE_MORE) == BYTE_MORE ); + + return x; + } + + public static int putInt( ByteBuffer out, int value ) + { + int bytes = 0; + + do + { + out.put( (byte)((value & BYTE_MASK) | (value > BYTE_MASK ? BYTE_MORE : 0)) ); + value >>= BYTE_SHIFT; + bytes++; + } while ( value > 0 ); + + return bytes; + } + + public static long getLong( ByteBuffer in ) + { + long x = 0; + int b = 0; + long shift = 0; + + do + { + b = in.get() & 0xFF; + x |= (b & BYTE_MASK) << shift; + shift += BYTE_SHIFT; + } + while ( (b & BYTE_MORE) == BYTE_MORE ); + + return x; + } + + public static int putLong( ByteBuffer out, long value ) + { + int bytes = 0; + + do + { + out.put( (byte)((value & BYTE_MASK) | (value > BYTE_MASK ? BYTE_MORE : 0)) ); + value >>= BYTE_SHIFT; + bytes++; + } + while ( value > 0 ); + + return bytes; + } + + public static int sizeOf( int value ) + { + for (int i = BYTES_INT; i > BYTES_BYTE; i--) + { + if ( value >= VALUE_BYTES[i] ) + { + return i; + } + } + + return BYTES_BYTE; + } + + public static int sizeOf( long value ) + { + for (int i = BYTES_LONG; i > BYTES_BYTE; i--) + { + if ( value >= VALUE_BYTES[i] ) + { + return i; + } + } + + return BYTES_BYTE; + } + + public static I convertFromOutput( OutputModel out, I in ) + { + // out.copyTo( in ); + + return in; + } + + public static O convertFromInput( InputModel in, O out ) + { + in.copyTo( out ); + + return out; + } + + public static T clone(T model, T out) + { + try + { + DataFormat format = getDefaultFormat(); + DataBuffer buffer = new DataBuffer( 4096, 2048 ); + + OutputModel clone = format.newOutput( "clone" ); + model.write( clone ); + clone.output( buffer.out ); + + InputModel cloned = format.read( buffer.in ); + out.read( cloned ); + + return out; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static OutputModel convertFromInput( InputModel in, DataFormat format ) + { + OutputModel out = format.newOutput( in.getName() ); + + in.copyTo( out ); + + return out; + } + + public static void readFile( File file, ByteBuffer in ) throws Exception + { + RandomAccessFile stream = new RandomAccessFile( file, "r" ); + + try + { + in.clear(); + + stream.getChannel().read( in ); + + in.flip(); + } + finally + { + stream.close(); + } + } + + public static void writeFile( File file, ByteBuffer out ) throws Exception + { + RandomAccessFile stream = new RandomAccessFile( file, "rw" ); + + try + { + FileChannel channel = stream.getChannel(); + + int pos = out.position(); + out.flip(); + + channel.write( out ); + + out.limit( out.capacity() ); + out.position( pos ); + } + finally + { + stream.close(); + } + } + + public static ByteBuffer resize( ByteBuffer buffer, int expectedCapacity ) + { + ByteBuffer result = ( buffer != null ? buffer : ByteBuffer.allocateDirect( expectedCapacity ) ); + + if ( result.capacity() < expectedCapacity ) + { + result = ByteBuffer.allocateDirect( expectedCapacity ); + result.put( buffer ); + result.clear(); + } + + return result; + } + + public static ByteBuffer read( File file ) throws IOException + { + FileInputStream fis = new FileInputStream( file ); + + return read( fis.getChannel().size(), fis, true ); + } + + public static ByteBuffer read( long size, InputStream input, boolean close ) throws IOException + { + ByteBuffer buffer = ByteBuffer.allocateDirect( (int)size ); + byte[] data = new byte[ 4096 ]; + int read = 0; + + while ( (read = input.read( data ) ) > 0 ) + { + if ( buffer.remaining() < read ) + { + return null; + } + + buffer.put( data, 0, read ); + } + + buffer.clear(); + + if ( close ) + { + input.close(); + } + + return buffer; + } + + public static int transfer( InputStream from, OutputStream to, int chunkSize ) throws IOException + { + byte[] data = new byte[ chunkSize ]; + int totalRead = 0; + int read = 0; + + while ( (read = from.read( data )) > 0 ) + { + to.write( data, 0, read ); + totalRead += read; + } + + return totalRead; + } + + public static OutputStream toOutputStream(final ByteBuffer out) + { + if (out == null) + { + return null; + } + + return new OutputStream() + { + @Override + public void write( int b ) throws IOException + { + out.put( (byte)b ); + } + + @Override + public void write(byte[] b, int offset, int length) + { + out.put( b, offset, length ); + } + }; + } + + public static InputStream syphon(final InputStream in, final OutputStream out) + { + if (in == null) + { + return null; + } + + return new InputStream() + { + @Override + public int read() throws IOException + { + int b = in.read(); + + if (b != -1) + { + out.write( b ); + } + + return b; + } + + @Override + public int available() throws IOException + { + return in.available(); + } + + @Override + public void close() throws IOException + { + in.close(); + out.close(); + } + + @Override + public long skip(long bytes) throws IOException + { + return in.skip( bytes ); + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException + { + int actualLength = in.read( b, offset, length ); + + if (actualLength != -1) + { + out.write( b, offset, actualLength ); + } + + return actualLength; + } + }; + } + + public static InputStream toInputStream(final ByteBuffer in) + { + if (in == null) + { + return null; + } + + return new InputStream() + { + public int mark = -1; + + @Override + public int read() throws IOException + { + return in.hasRemaining() ? in.get() & 0xFF : -1; + } + + @Override + public int available() + { + return in.remaining(); + } + + @Override + public void mark(int m) + { + mark = m; + } + + @Override + public void reset() + { + in.position( mark ); + mark = -1; + } + + @Override + public boolean markSupported() + { + return true; + } + + @Override + public void close() + { + in.position( 0 ); + } + + @Override + public long skip(long bytes) + { + long skipped = Math.min( bytes, in.remaining() ); + + in.position( in.position() + (int)skipped ); + + return skipped; + } + + @Override + public int read(byte[] b, int offset, int length) + { + int read = Math.min( in.remaining(), length ); + + in.get( b, offset, read ); + + return read == 0 ? -1 : read; + } + }; + } + +} diff --git a/src/com/axe/io/DualInputModel.java b/src/com/axe/io/DualInputModel.java new file mode 100755 index 0000000..31ec555 --- /dev/null +++ b/src/com/axe/io/DualInputModel.java @@ -0,0 +1,139 @@ +package com.axe.io; + +import java.util.Iterator; + +import com.axe.io.base.BaseInputModel; + +public class DualInputModel extends BaseInputModel +{ + + private final InputModel first; + private final InputModel second; + private final DualIterator iterator; + + public DualInputModel( DataFormat format, String name, InputModel first, InputModel second ) + { + super( format, name ); + + this.first = first; + this.second = second; + this.iterator = new DualIterator(); + } + + @Override + public void copyTo( OutputModel out ) + { + second.copyTo( out ); + first.copyTo( out ); + } + + @Override + public boolean hasAttribute( String name ) + { + return first.hasAttribute( name ) || second.hasAttribute( name ); + } + + @Override + public boolean hasChild( String name ) + { + return first.hasChild( name ) || second.hasChild( name ); + } + + @Override + public Iterator iterator() + { + return iterator.take(); + } + + @Override + protected String getAttribute( String name ) + { + if ( first.hasAttribute( name ) ) + { + return first.readString( name ); + } + + return second.readString( name ); + } + + @Override + public InputModel readModel( String name ) + { + InputModel model = first.readModel( name ); + + if ( model == null ) + { + model = second.readModel( name ); + } + + return model; + } + + @Override + public T readModel( String name, String attributeName, boolean requires ) throws DataException + { + T result = first.readModel( name, attributeName, false ); + + if ( result == null ) + { + result = second.readModel( name, attributeName, requires ); + } + + return result; + } + + + private class DualIterator implements Iterator + { + public Iterator s, f; + public boolean nextSecond = true; + + public DualIterator() + { + s = second.iterator(); + f = first.iterator(); + } + + public Iterator take() + { + if ( hasNext() ) + { + return new DualIterator(); + } + + s = second.iterator(); + f = first.iterator(); + + nextSecond = s.hasNext(); + + return this; + } + + @Override + public boolean hasNext() + { + return s.hasNext() || f.hasNext(); + } + + @Override + public InputModel next() + { + return ( ( nextSecond = s.hasNext() ) ? s.next() : f.next() ); + } + + @Override + public void remove() + { + if ( nextSecond ) + { + s.remove(); + } + else + { + f.remove(); + } + } + + } + +} diff --git a/src/com/axe/io/InputModel.java b/src/com/axe/io/InputModel.java new file mode 100755 index 0000000..59b203b --- /dev/null +++ b/src/com/axe/io/InputModel.java @@ -0,0 +1,84 @@ +package com.axe.io; + +import java.util.List; + +import com.axe.math.calc.Calculator; + +public interface InputModel extends Iterable +{ + public String getName(); + public DataFormat getFormat(); + + public void copyTo( OutputModel out ); + + public InputModel[] getChildren( String name ); + + public boolean hasAttribute(String name); + public boolean hasChild( String name ); + + // Attributes + + public double readDouble(String name) throws DataException; + public double readDouble(String name, Double defaultValue) throws DataException; + public float readFloat(String name) throws DataException; + public float readFloat(String name, Float defaultValue) throws DataException; + public byte readByte(String name) throws DataException; + public byte readByte(String name, Byte defaultValue) throws DataException; + public byte readByte(String name, Byte defaultValue, int radix) throws DataException; + public short readShort(String name) throws DataException; + public short readShort(String name, Short defaultValue) throws DataException; + public short readShort(String name, Short defaultValue, int radix) throws DataException; + public int readInt(String name) throws DataException; + public int readInt(String name, Integer defaultValue) throws DataException; + public int readInt(String name, Integer defaultValue, int radix) throws DataException; + public long readLong(String name) throws DataException; + public long readLong(String name, Long defaultValue) throws DataException; + public long readLong(String name, Long defaultValue, int radix) throws DataException; + public boolean readBoolean(String name) throws DataException; + public boolean readBoolean(String name, Boolean defaultValue) throws DataException; + public char readChar(String name) throws DataException; + public char readChar(String name, Character defaultValue) throws DataException; + public String readString(String name) throws DataException; + public String readString(String name, String defaultValue) throws DataException; + public String readNullableString(String name) throws DataException; + public Class readClass(String name) throws DataException; + public Class readClass(String name, Class defaultClass) throws DataException; + public T readInstance(String name) throws DataException; + public > E readEnum(String name, Class enumType) throws DataException; + public > E readEnum(String name, Class enumType, E enumConstant) throws DataException; + public T[] readDelimited(String name, char delimiter, Class itemType) throws DataException; + + // Children + + public T[] readModelArray(String name, Class itemType) throws DataException; + public T[] readModelArray(String name, String attributeName) throws DataException; + public InputModel[] readModelArray(String name) throws DataException; + + public T[] readModelArrayQualified(String name, Class itemType, String className) throws DataException; + public T[] readModelArrayDynamic(String name, String attributeName ) throws DataException; + + public List readModelList(String name, Class itemType) throws DataException; + public List readModelList(String name, String attributeName) throws DataException; + public List readModelList(String name) throws DataException; + public List readModelListQualified(String name, String className) throws DataException; + + // Child + + public InputModel readImportable( String name ) throws DataException; + public InputModel readImportable( String name, String attributeName ) throws DataException; + + public T readImportableModel( String name, String className ) throws DataException; + public T readImportableModel( String name, T model ) throws DataException; + + public InputModel resolveImport() throws DataException; + public InputModel resolveImport(String attributeName) throws DataException; + + public InputModel readModel(String name) throws DataException; + public T readModel(String name, String className) throws DataException; + public T readModel(String name, T model) throws DataException; + public T readModel(String name, String attributeName, boolean requires) throws DataException; + + public T readModel(String name, T model, Calculator calculator) throws DataException; + + public List readModels(String name, InputModelListener listener) throws DataException; +} \ No newline at end of file diff --git a/src/com/axe/io/InputModelListener.java b/src/com/axe/io/InputModelListener.java new file mode 100755 index 0000000..08c3063 --- /dev/null +++ b/src/com/axe/io/InputModelListener.java @@ -0,0 +1,7 @@ +package com.axe.io; + + +public interface InputModelListener +{ + public Object onRead(InputModel parent, InputModel child) throws DataException; +} diff --git a/src/com/axe/io/OpenStream.java b/src/com/axe/io/OpenStream.java new file mode 100755 index 0000000..a07ff2d --- /dev/null +++ b/src/com/axe/io/OpenStream.java @@ -0,0 +1,27 @@ +package com.axe.io; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * YOU SHALL NOT CLOSE!!! + * + * @author Philip Diffenderfer + * + */ +public class OpenStream extends BufferedInputStream +{ + + public OpenStream( InputStream source ) + { + super( source ); + } + + @Override + public void close() throws IOException + { + // nothing + } + +} diff --git a/src/com/axe/io/OutputModel.java b/src/com/axe/io/OutputModel.java new file mode 100755 index 0000000..cff40ab --- /dev/null +++ b/src/com/axe/io/OutputModel.java @@ -0,0 +1,60 @@ +package com.axe.io; + +import java.io.File; +import java.io.OutputStream; +import java.io.Writer; +import java.util.List; + +import com.axe.math.calc.Calculator; + + +public interface OutputModel +{ + public String getName(); + public int getScope(); + public DataFormat getFormat(); + public OutputModel writeModel( String name ); + + // Attributes + + public void write(String name, Object value); + public void write(String name, long value, int radix); + public void writeClass(String name, Class type); + public void writeInstance(String name, Object value); + public void writeInstance(String name, OutputModel model); + public > void writeEnum(String name, E enumConstant); + public void writeDelimited(String name, char delimiter, Object[] array); + + // Children + + public OutputModel[] writeModelArray(String name, T[] models ); + public OutputModel[] writeModelArray(String name, T[] models, String attributeName ); + + public OutputModel[] writeModelArray(String name, T[] models, Calculator calculator ); + public OutputModel[] writeModelArray(String name, T[] models, String attributeName, Calculator calculator ); + + public OutputModel[] writeModelArrayQualified(String name, T[] models, String className); + + public OutputModel[] writeModelArrayDynamic(String name, T[] models, String attributeName ); + + public OutputModel[] writeModelListQualified(String name, List models, String className); + public OutputModel[] writeModelList(String name, List models); + + public OutputModel writeModel(String name, String className, DataModel model); + + public OutputModel writeModel(String name, DataModel model); + public OutputModel writeModel(String name, DataModel model, String attributeName ); + + public OutputModel writeModel(String name, T model, Calculator calculator ); + public OutputModel writeModel(String name, T model, String attributeName, Calculator calculator ); + + public List writeModels( String name, OutputModelListener listener, T[] models ); + + // Output + + public String toString(); + + public void output( Writer writer ) throws Exception; + public void output( File file ) throws Exception; + public void output( OutputStream stream ) throws Exception; +} \ No newline at end of file diff --git a/src/com/axe/io/OutputModelListener.java b/src/com/axe/io/OutputModelListener.java new file mode 100755 index 0000000..4d23c6e --- /dev/null +++ b/src/com/axe/io/OutputModelListener.java @@ -0,0 +1,7 @@ +package com.axe.io; + + +public interface OutputModelListener +{ + public void onWrite(OutputModel parent, OutputModel child, T value) throws DataException; +} diff --git a/src/com/axe/io/RegistryMap.java b/src/com/axe/io/RegistryMap.java new file mode 100755 index 0000000..4ef69bc --- /dev/null +++ b/src/com/axe/io/RegistryMap.java @@ -0,0 +1,123 @@ +package com.axe.io; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + + +public class RegistryMap implements Map +{ + public static int DEFAULT_CAPACITY = 32; + + private Map forward; + private Map backward; + + public RegistryMap() + { + this( DEFAULT_CAPACITY ); + } + + public RegistryMap( int initialCapacity ) + { + forward = new HashMap( initialCapacity ); + backward = new HashMap( initialCapacity ); + } + + public B getForward(A key) + { + return forward.get( key ); + } + + public A getBackward(B key) + { + return backward.get( key ); + } + + public void add(A a, B b) + { + forward.put( a, b ); + backward.put( b, a ); + } + + @Override + public int size() + { + return forward.size(); + } + + @Override + public boolean isEmpty() + { + return forward.isEmpty(); + } + + @Override + public boolean containsKey( Object key ) + { + return forward.containsKey( key ); + } + + @Override + public boolean containsValue( Object value ) + { + return backward.containsKey( value ); + } + + @Override + public B get( Object key ) + { + return forward.get( key ); + } + + @Override + public B put( A key, B value ) + { + backward.put( value, key ); + return forward.put( key, value ); + } + + @Override + public B remove( Object key ) + { + B data = forward.remove( key ); + backward.remove( data ); + return data; + } + + @Override + public void putAll( Map m ) + { + for ( Entry entry : m.entrySet() ) + { + put( entry.getKey(), entry.getValue() ); + } + } + + + @Override + public void clear() + { + forward.clear(); + backward.clear(); + } + + @Override + public Set keySet() + { + return forward.keySet(); + } + + @Override + public Collection values() + { + return backward.keySet(); + } + + @Override + public Set> entrySet() + { + return forward.entrySet(); + } + +} diff --git a/src/com/axe/io/base/BaseDataFormat.java b/src/com/axe/io/base/BaseDataFormat.java new file mode 100755 index 0000000..d9b8223 --- /dev/null +++ b/src/com/axe/io/base/BaseDataFormat.java @@ -0,0 +1,135 @@ +package com.axe.io.base; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; + +import com.axe.io.DataFormat; +import com.axe.io.DataModel; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + + +public abstract class BaseDataFormat implements DataFormat +{ + + public static final int DEFAULT_SCOPE = 0; + + @Override + public OutputModel newOutput( String name, int scope, DataModel model ) + { + OutputModel output = newOutput( name, scope ); + model.write( output ); + return output; + } + + @Override + public OutputModel newOutput( String name, DataModel model ) + { + OutputModel output = newOutput( name, DEFAULT_SCOPE ); + model.write( output ); + return output; + } + + @Override + public OutputModel newOutput( String name ) + { + return newOutput( name, DEFAULT_SCOPE ); + } + + @Override + public InputModel read( final Reader reader ) throws Exception + { + return read( new InputStream() + { + @Override + public int read() throws IOException + { + return reader.read(); + } + }); + } + + @Override + public InputModel read( File file ) throws Exception + { + return read( new FileInputStream( file ) ); + } + + @Override + public InputModel read( String string ) throws Exception + { + return read( new ByteArrayInputStream( string.getBytes() ) ); + } + + @Override + public T read( InputStream stream, T model ) throws Exception + { + model.read( read( stream ) ); + return model; + } + + @Override + public T read( Reader reader, T model ) throws Exception + { + model.read( read( reader ) ); + return model; + } + + @Override + public T read( File file, T model ) throws Exception + { + model.read( read( file ) ); + return model; + } + + @Override + public T read( String string, T model ) throws Exception + { + model.read( read( string ) ); + return model; + } + + @Override + public OutputModel write( Writer writer, String name, DataModel model ) throws Exception + { + OutputModel output = newOutput( name ); + + model.write( output ); + + output.output( writer ); + + return output; + } + + @Override + public OutputModel write( File file, String name, DataModel model ) throws Exception + { + OutputModel output = newOutput( name ); + + model.write( output ); + + output.output( file ); + + return output; + } + + @Override + public OutputModel write( OutputStream stream, String name, DataModel model ) throws Exception + { + OutputModel output = newOutput( name ); + + model.write( output ); + + output.output( stream ); + + return output; + } + +} diff --git a/src/com/axe/io/base/BaseInputModel.java b/src/com/axe/io/base/BaseInputModel.java new file mode 100755 index 0000000..8a1c963 --- /dev/null +++ b/src/com/axe/io/base/BaseInputModel.java @@ -0,0 +1,801 @@ +package com.axe.io.base; + + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.magnos.asset.Assets; + +import com.axe.io.DataException; +import com.axe.io.DataFormat; +import com.axe.io.DataModel; +import com.axe.io.DataRegistry; +import com.axe.io.DualInputModel; +import com.axe.io.InputModel; +import com.axe.io.InputModelListener; +import com.axe.math.calc.Calculator; + + +public abstract class BaseInputModel implements InputModel +{ + + public static String IMPORT_ATTRIBUTE = "import"; + + public static final int DEFAULT_RADIX = 10; + + private static final Set trues = new HashSet( + Arrays.asList( "1", "t", "true", "y", "ya", "yes", "yessums" ) + ); + + private static final Set falses = new HashSet( + Arrays.asList( "0", "f", "false", "n", "no", "nope" ) + ); + + protected final String name; + protected final F format; + + public BaseInputModel( F format, String name ) + { + this.format = format; + this.name = name; + } + + public String getName() + { + return name; + } + + public DataFormat getFormat() + { + return format; + } + + @Override + public double readDouble( String name ) + { + return readDouble( name, null ); + } + + protected abstract String getAttribute( String name ); + + @Override + public double readDouble( String name, Double defaultValue ) + { + if ( hasAttribute( name ) ) + { + try + { + return Double.parseDouble( getAttribute( name ) ); + } + catch ( NumberFormatException ex ) + { + throw new DataException( "Invalid attribute %s in input %s, double type expected", name, getName() ); + } + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public float readFloat( String name ) + { + return readFloat( name, null ); + } + + @Override + public float readFloat( String name, Float defaultValue ) + { + if ( hasAttribute( name ) ) + { + try + { + return Float.parseFloat( getAttribute( name ) ); + } + catch ( NumberFormatException ex ) + { + throw new DataException( "Invalid attribute %s in input %s, float type expected", name, getName() ); + } + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public byte readByte( String name ) + { + return readByte( name, null ); + } + + @Override + public byte readByte( String name, Byte defaultValue ) + { + return readByte( name, defaultValue, DEFAULT_RADIX ); + } + + @Override + public byte readByte( String name, Byte defaultValue, int radix ) + { + if ( hasAttribute( name ) ) + { + try + { + return Byte.parseByte( getAttribute( name ), radix ); + } + catch ( NumberFormatException ex ) + { + throw new DataException( "Invalid attribute %s in input %s, byte type expected", name, getName() ); + } + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public short readShort( String name ) + { + return readShort( name, null ); + } + + @Override + public short readShort( String name, Short defaultValue ) + { + return readShort( name, defaultValue, DEFAULT_RADIX ); + } + + @Override + public short readShort( String name, Short defaultValue, int radix ) + { + if ( hasAttribute( name ) ) + { + try + { + return Short.parseShort( getAttribute( name ), radix ); + } + catch ( NumberFormatException ex ) + { + throw new DataException( "Invalid attribute %s in input %s, short type expected", name, getName() ); + } + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public int readInt( String name ) + { + return readInt( name, null ); + } + + @Override + public int readInt( String name, Integer defaultValue ) + { + return readInt( name, defaultValue, DEFAULT_RADIX ); + } + + @Override + public int readInt( String name, Integer defaultValue, int radix ) + { + if ( hasAttribute( name ) ) + { + try + { + return Integer.parseInt( getAttribute( name ), radix ); + } + catch ( NumberFormatException ex ) + { + throw new DataException( "Invalid attribute %s in input %s, int type expected", name, getName() ); + } + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public long readLong(String name) throws DataException + { + return readLong( name, null ); + } + + @Override + public long readLong(String name, Long defaultValue) throws DataException + { + return readLong( name, defaultValue, DEFAULT_RADIX ); + } + + @Override + public long readLong(String name, Long defaultValue, int radix) throws DataException + { + if ( hasAttribute( name ) ) + { + try + { + return Long.parseLong( getAttribute( name ), radix ); + } + catch ( NumberFormatException ex ) + { + throw new DataException( "Invalid attribute %s in input %s, long type expected", name, getName() ); + } + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public boolean readBoolean( String name ) + { + return readBoolean( name, null ); + } + + @Override + public boolean readBoolean( String name, Boolean defaultValue ) + { + if ( hasAttribute( name ) ) + { + String value = getAttribute( name ).toLowerCase(); + + if ( trues.contains( value ) ) + { + return true; + } + + if ( falses.contains( value ) ) + { + return false; + } + + throw new DataException( "Invalid attribute %s in input %s, boolean type expected", name, getName() ); + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public char readChar( String name ) + { + return readChar( name, null ); + } + + @Override + public char readChar( String name, Character defaultValue ) + { + if ( hasAttribute( name ) ) + { + String value = getAttribute( name ); + + if ( value.length() > 1 ) + { + throw new DataException( "Invalid attribute %s in input %s, char type expected", name, getName() ); + } + + return value.charAt( 0 ); + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public String readString( String name ) + { + return readString( name, null ); + } + + @Override + public String readString( String name, String defaultValue ) + { + if ( hasAttribute( name ) ) + { + return getAttribute( name ); + } + else if ( defaultValue == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultValue; + } + + @Override + public String readNullableString( String name ) + { + return (hasAttribute( name ) ? getAttribute( name ) : null); + } + + @Override + public Class readClass( String name ) + { + return readClass( name, null ); + } + + @SuppressWarnings("unchecked") + @Override + public Class readClass( String name, Class defaultClass ) + { + if ( hasAttribute( name ) ) + { + try + { + String attribute = getAttribute( name ); + + Class clazz = DataRegistry.getClass( attribute ); + + if ( clazz == null ) + { + clazz = (Class)Class.forName( attribute ); + } + + return clazz; + } + catch ( Exception ex ) + { + throw new DataException( "Invalid attribute %s in input %s, class type expected: %s", name, getName(), ex.getMessage() ); + } + } + else if ( defaultClass == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + return defaultClass; + } + + @SuppressWarnings("unchecked") + @Override + public T readInstance( String name ) + { + if ( hasAttribute( name ) ) + { + String attribute = getAttribute( name ); + + T value = DataRegistry.get( attribute ); + + if ( value == null ) + { + value = DataRegistry.getInstance( attribute, false ); + + if ( value == null ) + { + try + { + value = (T)instantiate( Class.forName( attribute ) ); + } + catch (Exception ex) + { + throw new DataException( "Invalid attribute %s in input %s, was not found as an object in the registry, a class in the registry, or a path to a class: %s", name, getName(), ex.getMessage() ); + } + } + } + + return value; + } + + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + + @Override + public > E readEnum( String name, Class enumType ) + { + return readEnum( name, enumType, null ); + } + + @Override + public > E readEnum( String name, Class enumType, E enumConstant ) + { + if ( hasAttribute( name ) ) + { + try + { + return Enum.valueOf( enumType, getAttribute( name ) ); + } + catch ( Exception ex ) + { + throw new DataException( "Invalid attribute %s in input %s, enum type expected: %s", name, getName(), ex.getMessage() ); + } + } + else if ( enumConstant == null ) + { + throw new DataException( "Expecting attribute %s in input %s", name, getName() ); + } + return enumConstant; + } + + @Override + public T[] readDelimited( String name, char delimiter, Class itemType ) + { + return null; + } + + public InputModel[] getChildren( String name ) + { + List modelList = readModelList( name ); + + return modelList.toArray( new InputModel[ modelList.size() ] ); + } + + @Override + public T[] readModelArray( String name, Class itemType ) + { + List list = readModelList( name, itemType ); + + @SuppressWarnings("unchecked") + T[] array = (T[])Array.newInstance( itemType, list.size() ); + + return list.toArray( array ); + } + + @SuppressWarnings("unchecked") + @Override + public T[] readModelArrayQualified( String name, Class itemType, String className ) throws DataException + { + List list = readModelListQualified( name, className ); + + return list.toArray( (T[])Array.newInstance( itemType, list.size() ) ); + } + + @Override + public InputModel[] readModelArray( String name ) throws DataException + { + List list = readModelList( name ); + + return list.toArray( new InputModel[ list.size() ] ); + } + + @Override + @SuppressWarnings("unchecked") + public T[] readModelArrayDynamic( String name, String attributeName ) throws DataException + { + List children = readModelList( name ); + T[] models = (T[])new DataModel[ children.size() ]; + + for ( int i = 0; i < children.size(); i++ ) + { + InputModel child = children.get( i ); + + models[i] = child.readInstance( attributeName ); + models[i].read( child ); + } + + return models; + } + + @Override + public List readModelList( String name, Class itemType ) + { + List modelList = readModelList( name ); + List list = new ArrayList( modelList.size() ); + + for ( int i = 0; i < modelList.size(); i++ ) + { + T instance = instantiate( itemType ); + instance.read( modelList.get( i ) ); + list.add( instance ); + } + + return list; + } + + @Override + public List readModelList( String name ) throws DataException + { + List list = new ArrayList(); + + for ( InputModel c : this ) + { + if ( name.equals( c.getName() ) ) + { + list.add( c ); + } + } + + return list; + } + + @Override + public InputModel readImportable( String name ) + { + return readImportable( name, IMPORT_ATTRIBUTE ); + } + + @Override + public InputModel readImportable( String name, String attributeName) throws DataException + { + InputModel input = readModel( name ); + + return ( input == null ? null : ((BaseInputModel)input).resolveImport( attributeName ) ); + } + + @Override + public T readImportableModel( String name, String className ) throws DataException + { + try + { + InputModel input = readImportable( name ); + + if ( input == null ) + { + return null; + } + + T model = input.readInstance( className ); + + model.read( input ); + + return model; + } + catch ( DataException e ) + { + throw e; + } + catch ( Exception e ) + { + throw new DataException( e ); + } + } + + @Override + public T readImportableModel( String name, T model ) throws DataException + { + try + { + InputModel input = readImportable( name ); + + if ( input == null ) + { + return null; + } + + model.read( input ); + + return model; + } + catch ( DataException e ) + { + throw e; + } + catch ( Exception e ) + { + throw new DataException( e ); + } + } + + @Override + public InputModel resolveImport() throws DataException + { + return resolveImport( IMPORT_ATTRIBUTE ); + } + + + @Override + public InputModel resolveImport(String attributeName) throws DataException + { + if ( !hasAttribute( attributeName ) ) + { + return this; + } + + String attribute = readString( attributeName ); + + InputModel external = Assets.load( attribute ); + + if ( external == null ) + { + throw new DataException( "Externalized input on attribue '%s' of '%s' with value '%s' does not exist", attributeName, name, attribute ); + } + + return new DualInputModel( format, name, this, external ); + } + + @Override + public T readModel( String name, String className ) throws DataException + { + try + { + InputModel model = readModel( name ); + T instance = model.readInstance( className ); + instance.read( model ); + + return instance; + } + catch ( Exception e ) + { + throw new DataException( e ); + } + } + + @Override + public T readModel( String name, T model ) + { + InputModel input = readModel( name ); + T read = null; + + if ( input != null ) + { + model.read( input ); + read = model; + } + + return read; + } + + @Override + public T readModel(String name, T model, Calculator calculator) throws DataException + { + InputModel input = readModel( name ); + + return calculator.read( model, input ); + } + + @Override + public InputModel readModel( String name ) + { + List modelList = readModelList( name ); + + if ( modelList == null || modelList.size() == 0 ) + { + return null; + } + else if ( modelList.size() > 1 ) + { + throw new DataException( "Expected only one input with name %s but found %d.", name, modelList.size() ); + } + + return modelList.get( 0 ); + } + + @Override + public T readModel( String name, String attributeName, boolean requires ) throws DataException + { + List modelList = readModelList( name ); + T result = null; + + if ( modelList.size() == 0 ) + { + if ( requires ) + { + throw new DataException( "Missing child %s", attributeName ); + } + } + else + { + InputModel inputModel = modelList.get( 0 ); + + result = inputModel.readInstance( attributeName ); + result.read( inputModel ); + } + + return result; + } + + @SuppressWarnings("unchecked") + public List readModels( String name, InputModelListener listener ) throws DataException + { + List modelList = readModelList( name ); + List list = new ArrayList(); + + for ( int i = 0; i < modelList.size(); i++ ) + { + list.add( (T)listener.onRead( this, modelList.get( i ) ) ); + } + + return list; + } + + private T instantiate( Class type ) + { + try + { + return type.newInstance(); + } + catch ( Exception ex ) + { + throw new DataException( ex ); + } + } + + @Override + public T[] readModelArray( String name, String attributeName ) throws DataException + { + Class type = readClass( attributeName ); + + return readModelArray( name, type ); + } + + @Override + public List readModelList( String name, String attributeName ) throws DataException + { + Class type = readClass( attributeName ); + + return readModelList( name, type ); + } + + @Override + public List readModelListQualified( String name, String className ) throws DataException + { + List modelList = readModelList( name ); + List list = new ArrayList(); + + try + { + for ( int i = 0; i < modelList.size(); i++ ) + { + InputModel model = modelList.get( i ); + + T instance = model.readInstance( className ); + instance.read( model ); + + list.add( instance ); + } + } + catch ( DataException e ) + { + throw e; + } + catch ( Exception e ) + { + throw new DataException( e ); + } + + return list; + } + + public boolean isLoaded() + { + return true; + } + + public void load() throws Exception + { + + } + + public boolean isActivated() + { + return true; + } + + public void activate() + { + + } + + public void delete() + { + + } + +} \ No newline at end of file diff --git a/src/com/axe/io/base/BaseOutputModel.java b/src/com/axe/io/base/BaseOutputModel.java new file mode 100755 index 0000000..1069266 --- /dev/null +++ b/src/com/axe/io/base/BaseOutputModel.java @@ -0,0 +1,367 @@ +package com.axe.io.base; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +import com.axe.io.DataFormat; +import com.axe.io.DataModel; +import com.axe.io.OutputModel; +import com.axe.io.OutputModelListener; +import com.axe.io.DataRegistry; +import com.axe.math.calc.Calculator; + + +public abstract class BaseOutputModel implements OutputModel +{ + + protected final String name; + protected final F format; + protected final int scope; + + public BaseOutputModel(F format, String name, int scope) + { + this.format = format; + this.name = name; + this.scope = scope; + } + + @Override + public String getName() + { + return name; + } + + @Override + public DataFormat getFormat() + { + return format; + } + + @Override + public int getScope() + { + return scope; + } + + @Override + public void writeClass( String name, Class type ) + { + String className = DataRegistry.getClassName( type ); + + if ( className == null ) + { + className = type.getName(); + } + + write( name, className ); + } + + @Override + public void writeInstance( String name, OutputModel model ) + { + + } + + @Override + public void writeInstance(String name, Object instance) + { + if ( instance == null ) + { + return; + } + + String value = DataRegistry.getName( instance ); + + if ( value == null ) + { + value = DataRegistry.getClassName( instance.getClass() ); + + if ( value == null ) + { + value = instance.getClass().getName(); + } + } + + write( name, value ); + } + + @Override + public void write(String name, long value, int radix) + { + write( name, Long.toString( value, radix ) ); + } + + @Override + public > void writeEnum( String name, E enumConstant ) + { + write( name, enumConstant.name() ); + } + + @Override + public void writeDelimited( String name, char delimiter, Object[] array ) + { + + } + + @Override + public OutputModel[] writeModelArray( String name, T[] models ) + { + OutputModel[] output = new OutputModel[ models.length ]; + + for(int i = 0; i < models.length; i++) + { + if (models[i] != null) + { + models[i].write( output[i] = writeModel( name ) ); + } + } + + return output; + } + + @Override + public OutputModel[] writeModelArray( String name, T[] models, String attributeName, Calculator calculator ) + { + writeClass( attributeName, models[0].getClass() ); + + return writeModelArray( name, models, calculator ); + } + + @Override + public OutputModel[] writeModelArray( String name, T[] models, Calculator calculator ) + { + OutputModel[] output = new OutputModel[ models.length ]; + + for(int i = 0; i < models.length; i++) + { + if (models[i] != null) + { + calculator.write( models[i], output[i] = writeModel( name ) ); + } + } + + return output; + } + + @Override + public OutputModel[] writeModelArray( String name, T[] models, String attributeName ) + { + writeClass( attributeName, models[0].getClass() ); + + return writeModelArray( name, models ); + } + + @Override + public OutputModel[] writeModelArrayQualified(String name, T[] models, String className) + { + OutputModel[] output = new OutputModel[ models.length ]; + + for (int i = 0; i < models.length; i++) + { + T model = models[ i ]; + + if (model != null) + { + output[i] = writeModel( name ); + model.write( output[i] ); + output[i].writeInstance( className, model ); + } + } + + return output; + } + + @Override + public OutputModel[] writeModelArrayDynamic(String name, T[] models,String attributeName ) + { + OutputModel[] output = new OutputModel[ models.length ]; + + for (int i = 0; i < models.length; i++) + { + if (models[i] != null) + { + output[i] = writeModel( name ); + output[i].writeInstance( attributeName, models[i] ); + models[i].write( output[i] ); + } + } + + return output; + } + + @Override + public OutputModel[] writeModelListQualified(String name, List models, String className) + { + OutputModel[] output = new OutputModel[ models.size() ]; + + for (int i = 0; i < models.size(); i++) + { + T model = models.get( i ); + + if (model != null) + { + output[i] = writeModel( name ); + model.write( output[i] ); + output[i].writeInstance( className, model ); + } + } + + return output; + } + + @Override + public OutputModel[] writeModelList( String name, List models ) + { + OutputModel[] output = new OutputModel[ models.size() ]; + + for(int i = 0; i < models.size(); i++) + { + if (models.get(i) != null) + { + models.get(i).write( output[i] = writeModel( name ) ); + } + } + + return output; + } + + @Override + public OutputModel writeModel(String name, String className, DataModel model) + { + if (model == null) + { + return null; + } + + OutputModel out = writeModel( name ); + + out.write( className, model.getClass().getName() ); + + model.write( out ); + + return out; + } + + @Override + public OutputModel writeModel( String name, DataModel model ) + { + if ( model == null ) + { + return null; + } + + OutputModel out = writeModel( name ); + + model.write( out ); + + return out; + } + + @Override + public OutputModel writeModel( String name, DataModel model, String attributeName ) + { + if ( model == null ) + { + return null; + } + + OutputModel out = writeModel( name ); + + out.writeClass( attributeName, model.getClass() ); + + model.write( out ); + + return out; + } + + @Override + public OutputModel writeModel(String name, T model, Calculator calculator ) + { + if ( model == null ) + { + return null; + } + + OutputModel out = writeModel( name ); + + calculator.write( model, out ); + + return out; + } + + @Override + public OutputModel writeModel(String name, T model, String attributeName, Calculator calculator ) + { + if ( model == null ) + { + return null; + } + + OutputModel out = writeModel( name ); + + out.writeClass( attributeName, model.getClass() ); + + calculator.write( model, out ); + + return out; + } + + + @Override + public List writeModels( String name, OutputModelListener listener, T[] models ) + { + List list = new ArrayList(); + + for (T m : models) + { + OutputModel out = writeModel( name ); + + listener.onWrite( this, out, m ); + + list.add( out ); + } + + return list; + } + + @Override + public void output( final Writer writer ) throws Exception + { + output( new OutputStream() + { + @Override + public void write( int b ) throws IOException + { + writer.write( b ); + } + }); + } + + @Override + public void output( File file ) throws Exception + { + output( new FileOutputStream( file ) ); + } + + @Override + public String toString() + { + StringWriter writer = new StringWriter(); + try + { + output( writer ); + } + catch (Exception e) + { + + } + return writer.toString(); + } + +} \ No newline at end of file diff --git a/src/com/axe/io/xml/XmlCustomOutputModel.java b/src/com/axe/io/xml/XmlCustomOutputModel.java new file mode 100755 index 0000000..1e6651e --- /dev/null +++ b/src/com/axe/io/xml/XmlCustomOutputModel.java @@ -0,0 +1,167 @@ +package com.axe.io.xml; + + +import java.io.File; +import java.io.FileWriter; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; + +import com.axe.io.OutputModel; +import com.axe.io.base.BaseOutputModel; + + +public class XmlCustomOutputModel extends BaseOutputModel +{ + + private static final int[] ENTITIES = { + '<', '&', '"', '\n', '\r' + }; + private static final boolean[] ENTITY_TABLE = new boolean[128]; + + static + { + for ( int i = 0; i < ENTITIES.length; i++ ) + { + ENTITY_TABLE[ENTITIES[i]] = true; + } + } + + private final int indent; + private final int depth; + private final StringBuilder attributes; + private final ArrayList children; + + public XmlCustomOutputModel( String name, int scope, int depth, int indent ) + { + super( XmlDataFormat.get(), name, scope ); + + this.depth = depth; + this.indent = indent; + this.attributes = new StringBuilder(); + this.children = new ArrayList(); + } + + private XmlCustomOutputModel( String name, int scope, XmlCustomOutputModel parent ) + { + super( XmlDataFormat.get(), name, scope ); + + this.depth = parent.depth + 1; + this.indent = parent.indent; + this.attributes = new StringBuilder(); + this.children = new ArrayList(); + + parent.children.add( this ); + } + + @Override + public OutputModel writeModel( String name ) + { + return new XmlCustomOutputModel( name, scope, this ); + } + + @Override + public void write( String name, Object value ) + { + if ( value != null ) + { + attributes.append( ' ' ); + attributes.append( name ); + attributes.append( '=' ); + attributes.append( '"' ); + + char[] chars = value.toString().toCharArray(); + + for ( int k = 0; k < chars.length; k++ ) + { + char c = chars[k]; + + if ( ENTITY_TABLE[c] ) + { + attributes.append( "&#" ); + attributes.append( (int)c ); + attributes.append( ";" ); + } + else + { + attributes.append( c ); + } + } + + attributes.append( '"' ); + } + } + + @Override + public void output( Writer writer ) throws Exception + { + if ( depth == 0 ) + { + writer.append( "\n" ); + } + + writeIndent( writer, depth * indent ); + writer.append( "<" ); + writer.append( name ); + writer.append( attributes ); + + if ( children.size() == 0 ) + { + writer.append( " />\n" ); + } + else + { + writer.append( ">\n" ); + + for ( XmlCustomOutputModel child : children ) + { + child.output( writer ); + } + + writeIndent( writer, depth * indent ); + writer.append( "\n" ); + } + } + + private void writeIndent( Writer writer, int spaces ) throws Exception + { + while ( --spaces >= 0 ) + { + writer.append( ' ' ); + } + } + + @Override + public void output( File file ) throws Exception + { + Writer writer = new FileWriter( file ); + + try + { + output( writer ); + } + finally + { + writer.close(); + } + } + + @Override + public void output( OutputStream stream ) throws Exception + { + Writer writer = new OutputStreamWriter( stream ); + + try + { + output( writer ); + } + finally + { + writer.close(); + } + } + +} \ No newline at end of file diff --git a/src/com/axe/io/xml/XmlDataFormat.java b/src/com/axe/io/xml/XmlDataFormat.java new file mode 100755 index 0000000..bc7d330 --- /dev/null +++ b/src/com/axe/io/xml/XmlDataFormat.java @@ -0,0 +1,160 @@ +package com.axe.io.xml; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.Charset; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; + +import com.axe.io.InputModel; +import com.axe.io.OpenStream; +import com.axe.io.OutputModel; +import com.axe.io.base.BaseDataFormat; + + +public class XmlDataFormat extends BaseDataFormat +{ + + private static final XmlDataFormat instance = new XmlDataFormat(); + + public static XmlDataFormat get() + { + return instance; + } + + private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + private static final TransformerFactory transformerFactory = TransformerFactory.newInstance(); + private static ThreadLocal documentBuilderLocal = new ThreadLocal(); + private static Transformer transformer; + private static Charset encoding; + + static + { + try + { + transformerFactory.setAttribute( "indent-number", 2 ); + transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty( OutputKeys.INDENT, "yes" ); + encoding = Charset.forName( "utf-8" ); + } + catch ( Exception e ) + { + e.printStackTrace(); + } + } + + @Override + public OutputModel newOutput( String name, int scope ) + { + return new XmlOutputModel( this, scope, getDocumentBuilder().newDocument(), name ); + } + + @Override + public InputModel read( InputStream stream ) throws Exception + { + return read( new InputSource( new OpenStream( stream ) ) ); + } + + @Override + public InputModel read( Reader reader ) throws Exception + { + return read( new InputSource( reader ) ); + } + + @Override + public InputModel read( File file ) throws Exception + { + return read( new InputSource( new OpenStream( new FileInputStream( file ) ) ) ); + } + + @Override + public InputModel read( String string ) throws Exception + { + return read( new InputSource( new StringReader( string ) ) ); + } + + private InputModel read( InputSource source ) throws Exception + { + Document doc = getDocumentBuilder().parse( source ); + + return new XmlInputModel( this, doc.getDocumentElement() ); + } + + private DocumentBuilder getDocumentBuilder() + { + DocumentBuilder docBuilder = documentBuilderLocal.get(); + + if (docBuilder == null) + { + try + { + docBuilder = documentBuilderFactory.newDocumentBuilder(); + + documentBuilderLocal.set( docBuilder ); + } + catch (ParserConfigurationException e) + { + throw new RuntimeException( e ); + } + } + + return docBuilder; + } + + protected String write( Node node ) throws Exception + { + StringWriter writer = new StringWriter(); + write( node, writer ); + return writer.toString(); + } + + protected void write( Node node, Writer writer ) throws Exception + { + final Source source = new DOMSource( node ); + final Result result = new StreamResult( writer ); + + transformer.transform( source, result ); + } + + protected void write( Node node, OutputStream stream ) throws Exception + { + final Source source = new DOMSource( node ); + final Writer writer = new OutputStreamWriter( stream, encoding ); + final Result result = new StreamResult( writer ); + + transformer.transform( source, result ); + } + + protected void write( Node node, File file ) throws Exception + { + final Source source = new DOMSource( node ); + final OutputStream stream = new FileOutputStream( file ); + final Writer writer = new OutputStreamWriter( stream, encoding ); + final Result result = new StreamResult( writer ); + + transformer.transform( source, result ); + } + +} \ No newline at end of file diff --git a/src/com/axe/io/xml/XmlDataModelFormat.java b/src/com/axe/io/xml/XmlDataModelFormat.java new file mode 100755 index 0000000..9438faf --- /dev/null +++ b/src/com/axe/io/xml/XmlDataModelFormat.java @@ -0,0 +1,40 @@ +package com.axe.io.xml; + +import java.io.InputStream; + +import org.magnos.asset.AssetInfo; +import org.magnos.asset.base.BaseAssetFormat; + +import com.axe.core.Factory; +import com.axe.io.DataFormat; +import com.axe.io.DataModel; +import com.axe.io.InputModel; + + +public class XmlDataModelFormat extends BaseAssetFormat +{ + + public final Factory factory; + + public XmlDataModelFormat( Factory factory, String ... extensions ) + { + super( extensions, DataModel.class ); + + this.factory = factory; + } + + @Override + public T loadAsset(InputStream input, AssetInfo assetInfo) throws Exception + { + DataFormat xml = XmlDataFormat.get(); + + InputModel in = xml.read( input ); + + T model = factory.create(); + + model.read( in ); + + return model; + } + +} diff --git a/src/com/axe/io/xml/XmlInputModel.java b/src/com/axe/io/xml/XmlInputModel.java new file mode 100755 index 0000000..2bfc7c8 --- /dev/null +++ b/src/com/axe/io/xml/XmlInputModel.java @@ -0,0 +1,71 @@ + +package com.axe.io.xml; + +import java.util.Iterator; + +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.io.base.BaseInputModel; + + +public class XmlInputModel extends BaseInputModel +{ + + private final Element e; + + public XmlInputModel( XmlDataFormat format, Element e ) + { + super( format, e.getTagName() ); + + this.e = e; + } + + @Override + public void copyTo( OutputModel out ) + { + NamedNodeMap map = e.getAttributes(); + + for ( int i = 0; i < map.getLength(); i++ ) + { + Node node = map.item( i ); + + out.write( node.getNodeName(), node.getNodeValue() ); + } + + for (InputModel child : this) + { + OutputModel childModel = out.writeModel( child.getName() ); + + child.copyTo( childModel ); + } + } + + @Override + public Iterator iterator() + { + return new XmlIterator( format, e.getChildNodes() ); + } + + @Override + public boolean hasAttribute( String name ) + { + return e.hasAttribute( name ); + } + + @Override + protected String getAttribute( String name ) + { + return e.getAttribute( name ); + } + + @Override + public boolean hasChild( String name ) + { + return e.getElementsByTagName( name ).getLength() > 0; + } + +} diff --git a/src/com/axe/io/xml/XmlIterator.java b/src/com/axe/io/xml/XmlIterator.java new file mode 100755 index 0000000..5646284 --- /dev/null +++ b/src/com/axe/io/xml/XmlIterator.java @@ -0,0 +1,61 @@ +package com.axe.io.xml; + +import java.util.Iterator; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.axe.io.InputModel; + + +public class XmlIterator implements Iterator, Iterable +{ + + private final XmlDataFormat format; + private final NodeList nodes; + private int index = -1; + + public XmlIterator(XmlDataFormat format, NodeList nodes) + { + this.format = format; + this.nodes = nodes; + } + + @Override + public Iterator iterator() + { + return this; + } + + @Override + public boolean hasNext() + { + return (getNextNode( index ) > index); + } + + @Override + public InputModel next() + { + return new XmlInputModel( format, (Element)nodes.item( index = getNextNode( index ) ) ); + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + + private int getNextNode(int i) + { + while (++i < nodes.getLength()) + { + if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) + { + return i; + } + } + return -1; + } + +} \ No newline at end of file diff --git a/src/com/axe/io/xml/XmlOutputModel.java b/src/com/axe/io/xml/XmlOutputModel.java new file mode 100755 index 0000000..0f99e52 --- /dev/null +++ b/src/com/axe/io/xml/XmlOutputModel.java @@ -0,0 +1,79 @@ +package com.axe.io.xml; + + +import java.io.File; +import java.io.OutputStream; +import java.io.Writer; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.axe.io.OutputModel; +import com.axe.io.base.BaseOutputModel; + + +public class XmlOutputModel extends BaseOutputModel +{ + + private final Document document; + private final Element e; + + public XmlOutputModel( XmlDataFormat format, int scope, Document document, String rootName ) + { + this( format, scope, document, (Element)document.appendChild( document.createElement( rootName ) ) ); + } + + public XmlOutputModel( XmlDataFormat format, int scope, Element e ) + { + this( format, scope, e.getOwnerDocument(), e ); + } + + public XmlOutputModel( XmlDataFormat format, int scope, Document document, Element parent, String tagName ) + { + this( format, scope, document, document.createElement( tagName ) ); + + parent.appendChild( e ); + } + + private XmlOutputModel( XmlDataFormat format, int scope, Document document, Element e ) + { + super( format, e.getNodeName(), scope ); + + this.document = document; + this.e = e; + } + + @Override + public OutputModel writeModel( String name ) + { + return new XmlOutputModel( format, scope, document, e, name ); + } + + @Override + public void write( String name, Object value ) + { + if ( value != null ) + { + e.setAttribute( name, value.toString() ); + } + } + + @Override + public void output( OutputStream stream ) throws Exception + { + format.write( e, stream ); + } + + @Override + public void output( Writer writer ) throws Exception + { + format.write( e, writer ); + } + + @Override + public void output( File file ) throws Exception + { + format.write( e, file ); + } + +} \ No newline at end of file diff --git a/src/com/axe/loop/Loop.java b/src/com/axe/loop/Loop.java new file mode 100644 index 0000000..69b996d --- /dev/null +++ b/src/com/axe/loop/Loop.java @@ -0,0 +1,12 @@ +package com.axe.loop; + +import com.axe.game.GameState; + +public interface Loop +{ + + public void onStart(GameState state); + + public void onLoop(GameState state); + +} \ No newline at end of file diff --git a/src/com/axe/loop/LoopFixed.java b/src/com/axe/loop/LoopFixed.java new file mode 100644 index 0000000..8c8e4dc --- /dev/null +++ b/src/com/axe/loop/LoopFixed.java @@ -0,0 +1,76 @@ +package com.axe.loop; + +import java.util.concurrent.TimeUnit; + +import com.axe.game.GameState; + +public class LoopFixed implements Loop +{ + public long updateRate; + public long drawRate; + public int maxUpdates; + public boolean sleep = false; + private long updateTime; + private long drawTime; + + public LoopFixed(int maxUpdates, double updateRate, double drawRate) + { + this.maxUpdates = maxUpdates; + this.updateRate = (long)(updateRate * 1000000000L); + this.drawRate = (long)(drawRate * 1000000000L); + } + + public LoopFixed(int maxUpdates, long updateRate, long drawRate, TimeUnit timeUnit) + { + this.maxUpdates = maxUpdates; + this.updateRate = timeUnit.toNanos( updateRate ); + this.drawRate = timeUnit.toNanos( drawRate ); + } + + @Override + public void onStart( GameState state ) + { + state.reset(); + state.setElapsed( updateRate ); + } + + @Override + public void onLoop( GameState state ) + { + long nanosElapsed = state.tick(); + + updateTime += nanosElapsed; + drawTime += nanosElapsed; + + state.updates = 0; + state.draw = false; + + while (updateTime >= updateRate && state.updates < maxUpdates) + { + state.updates++; + updateTime -= updateRate; + } + + if (drawTime >= drawRate || state.updates > 0) + { + state.draw = true; + drawTime -= drawRate; + + float delta = (float)((double)updateTime / (double)updateRate); + + state.interpolation = delta * state.seconds; + state.reverse = state.seconds - state.interpolation; + } + + if (sleep && !state.draw && state.updates == 0) + { + long sleep = (updateRate - updateTime) / 1000000L; + + if (sleep > 1) + { + try { Thread.sleep( sleep - 1 ); } catch (Exception e) { } + } + } + } + +} \ No newline at end of file diff --git a/src/com/axe/loop/LoopInterpolated.java b/src/com/axe/loop/LoopInterpolated.java new file mode 100644 index 0000000..6e63495 --- /dev/null +++ b/src/com/axe/loop/LoopInterpolated.java @@ -0,0 +1,63 @@ +package com.axe.loop; + +import java.util.concurrent.TimeUnit; + +import com.axe.game.GameState; + +public class LoopInterpolated implements Loop +{ + public long frameRate; + public int maxUpdates; + public boolean yield = false; + private long time; + private int draws; + + public LoopInterpolated(int maxUpdates, double frameRate) + { + this.maxUpdates = maxUpdates; + this.frameRate = (long)(frameRate * 1000000000L); + } + + public LoopInterpolated(int maxUpdates, long frameRate, TimeUnit timeUnit) + { + this.maxUpdates = maxUpdates; + this.frameRate = timeUnit.toNanos( frameRate ); + } + + @Override + public void onStart( GameState state ) + { + state.reset(); + state.setElapsed( frameRate ); + } + + @Override + public void onLoop( GameState state ) + { + long nanosElapsed = state.tick(); + + time += nanosElapsed; + + state.updates = 0; + + while (time >= frameRate && state.updates < maxUpdates) + { + state.updates++; + time -= frameRate; + draws = 0; + } + draws++; + + if (yield && draws > 2 && state.updates == 0) + { + Thread.yield(); + draws = 0; + } + + float delta = (float)((double)time / (double)frameRate); + + state.interpolation = delta * state.seconds; + state.reverse = state.seconds - state.interpolation; + } + +} \ No newline at end of file diff --git a/src/com/axe/loop/LoopVariable.java b/src/com/axe/loop/LoopVariable.java new file mode 100644 index 0000000..9348e75 --- /dev/null +++ b/src/com/axe/loop/LoopVariable.java @@ -0,0 +1,33 @@ +package com.axe.loop; + +import java.util.concurrent.TimeUnit; + +import com.axe.game.GameState; + +public class LoopVariable implements Loop +{ + public long maximumElapsed; + + public LoopVariable(double maximumElapsedSeconds) + { + this.maximumElapsed = (long)(maximumElapsedSeconds * 1000000000L); + } + + public LoopVariable(long maximumElapsed, TimeUnit timeUnit) + { + this.maximumElapsed = timeUnit.toNanos( maximumElapsed ); + } + + @Override + public void onStart( GameState state ) + { + state.reset(); + } + + @Override + public void onLoop( GameState state ) + { + state.setElapsed( Math.min( maximumElapsed, state.tick() ) ); + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Bound2f.java b/src/com/axe/math/Bound2f.java new file mode 100755 index 0000000..67a9cf2 --- /dev/null +++ b/src/com/axe/math/Bound2f.java @@ -0,0 +1,256 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorBound2f; + +public class Bound2f implements Attribute +{ + public float l, r, t, b; + + public Bound2f() + { + } + + public Bound2f(float x, float y) + { + clear(x, y); + } + + public Bound2f(float left, float top, float right, float bottom) + { + set(left, top, right, bottom); + } + + public Bound2f(Bound2f x) + { + set(x); + } + + public Bound2f clone() + { + return new Bound2f( this ); + } + + public void include(float x, float y) + { + if (x < l) l = x; + if (x > r) r = x; + if (y > t) t = y; + if (y < b) b = y; + } + + public void clear( Vec2f v ) { + clear( v.x, v.y ); + } + + public void clear(float x, float y) { + l = r = x; + t = b = y; + } + + public void set(float left, float top, float right, float bottom) { + l = left; + r = right; + t = top; + b = bottom; + } + + public void set(Vec2f min, Vec2f max) + { + l = min.x; + b = min.y; + r = max.x; + t = max.y; + } + + public void setWidthFromLeft(float w) { + r = l + w; + } + public void setWidthFromRight(float w) { + l = r - w; + } + public void setHeightFromTop(float h) { + b = t - h; + } + public void setHeightFromBottom(float h) { + t = b + h; + } + + public void expand(float gx, float gy) { + l -= gx; + r += gx; + t += gy; + b -= gy; + } + + public boolean isNegative() { + return r < l || t < b; + } + + public void rect(float x, float y, float width, float height) { + set(x, y, x + width, y - height); + } + + public void line(float x0, float y0, float x1, float y1) { + l = Math.min(x0, x1); + r = Math.max(x0, x1); + t = Math.max(y0, y1); + b = Math.min(y0, y1); + } + + public void ellipse(float cx, float cy, float rw, float rh) { + rw = Math.abs(rw); + rh = Math.abs(rh); + l = cx - rw; + r = cx + rw; + t = cy + rh; + b = cy - rh; + } + + public void quad(float cx, float cy, float w, float h, float angle) { + quad(cx, cy, w, h, Numbers.cos(angle), Numbers.sin(angle)); + } + + public void quad(float cx, float cy, float w, float h, float vx, float vy) { + vx = Math.abs(vx); + vy = Math.abs(vy); + float hw = (vx * w + vy * h) * 0.5f; + float hh = (vx * h + vy * w) * 0.5f; + ellipse(cx, cy, hw, hh); + } + + public void center(float x, float y) { + float w = width() * 0.5f; + float h = height() * 0.5f; + l = x - w; + r = x + w; + t = y + h; + b = y - h; + } + + public void move(float dx, float dy) { + l += dx; r += dx; + t += dy; b += dy; + } + + public void zoom(float sx, float sy) { + float hw = width() * 0.5f * Math.abs(sx); + float hh = height() * 0.5f * Math.abs(sy); + ellipse(cx(), cy(), hw, hh); + } + + public float x(float dx) { + return (r - l) * dx + l; + } + + public float y(float dy) { + return (b - t) * dy + t; + } + + public float dx(float x) { + return (x - l) / (r - l); + } + + public float dy(float y) { + return (y - t) / (b - t); + } + + public float width() { + return (r - l); + } + + public float height() { + return (t - b); + } + + public float area() { + return (r - l) * (t - b); + } + + public float cx() { + return (l + r) * 0.5f; + } + + public float cy() { + return (t + b) * 0.5f; + } + + public void intersection(Bound2f x, Bound2f y) { + l = Math.max(x.l, y.l); + r = Math.min(x.r, y.r); + t = Math.min(x.t, y.t); + b = Math.max(x.b, y.b); + } + + public void union(Bound2f x, Bound2f y) { + l = Math.min(x.l, y.l); + r = Math.max(x.r, y.r); + t = Math.max(x.t, y.t); + b = Math.min(x.b, y.b); + } + + public int evaluate(Bound2f x) { + if (x.r <= l || x.l >= r || x.t >= b || x.b <= t) { + return 1; + } + if (x.l < l || x.r > r || x.t > t || x.b < b) { + return 0; + } + return -1; + } + + public boolean intersects(Bound2f x) { + return !(x.l >= r || x.r <= l || x.t <= b || x.b >= t); + } + + public boolean touches(Bound2f x) { + return !(x.l > r || x.r < l || x.t < b || x.b > t); + } + + public boolean contains(Bound2f x) { + return !(x.l < l || x.r > r || x.t > t || x.b < b); + } + + public boolean inside(Bound2f x) { + return !(x.l <= l || x.r >= r || x.t >= t || x.b <= b); + } + + public String toString() { + return String.format("{%.2f, %.2f, %.2f, %.2f}", l, t, width(), height()); + } + + public boolean contains(float x, float y) { + return !(x < l || x > r || y > t || y < b); + } + + public void closest(Vec2f v, Vec2f out) { + out.x = v.x; + out.y = v.y; + + if (out.x < l) out.x = l; + if (out.x > r) out.x = r; + if (out.y > t) out.y = t; + if (out.y < b) out.y = b; + } + + @Override + public Bound2f get() + { + return this; + } + + @Override + public final CalculatorBound2f getCalculator() + { + return CalculatorBound2f.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(t + b * 215 + r * 46225 + l * 9938375); + } + +} + + diff --git a/src/com/axe/math/Bound2i.java b/src/com/axe/math/Bound2i.java new file mode 100755 index 0000000..854b359 --- /dev/null +++ b/src/com/axe/math/Bound2i.java @@ -0,0 +1,182 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorBound2i; + +public class Bound2i implements Attribute +{ + public int l, r, t, b; + + public Bound2i() { + } + + public Bound2i(int x, int y) { + clear(x, y); + } + + public Bound2i(int left, int top, int right, int bottom) { + set(left, top, right, bottom); + } + + public Bound2i(Bound2i x) { + set(x); + } + + public void include(int x, int y) { + if (x < l) l = x; + if (x > r) r = x; + if (y > t) t = y; + if (y < b) b = y; + } + + public void clear(int x, int y) { + l = r = x; + t = b = y; + } + + public void set(int left, int top, int right, int bottom) { + l = left; + r = right; + t = top; + b = bottom; + } + + public boolean isNegative() { + return r < l || b > t; + } + + public void rect(int x, int y, int width, int height) { + set(x, y, x + width, y + height); + } + + public void line(int x0, int y0, int x1, int y1) { + l = Math.min(x0, x1); + r = Math.max(x0, x1); + t = Math.max(y0, y1); + b = Math.min(y0, y1); + } + + public void ellipse(int cx, int cy, int rw, int rh) { + rw = Math.abs(rw); + rh = Math.abs(rh); + l = cx - rw; + r = cx + rw; + t = cy + rh; + b = cy - rh; + } + + + public void center(int x, int y) { + int w = width() / 2; + int h = height() / 2; + l = x - w; + r = x + w; + t = y + h; + b = y - h; + } + + public void move(int dx, int dy) { + l += dx; r += dx; + t += dy; b += dy; + } + + public void zoom(float sx, float sy) { + float hw = width() * 0.5f * Math.abs(sx); + float hh = height() * 0.5f * Math.abs(sy); + ellipse(cx(), cy(), (int)hw, (int)hh); + } + + public int width() { + return (r - l); + } + + public int height() { + return (t - b); + } + + public int cx() { + return (l + r) / 2; + } + + public int cy() { + return (t + b) / 2; + } + + public int area() { + return (r - l) * (t - b); + } + + public void intersection(Bound2i x, Bound2i y) { + l = Math.max(x.l, y.l); + r = Math.min(x.r, y.r); + t = Math.min(x.t, y.t); + b = Math.max(x.b, y.b); + } + + public void union(Bound2i x, Bound2i y) { + l = Math.min(x.l, y.l); + r = Math.max(x.r, y.r); + t = Math.max(x.t, y.t); + b = Math.min(x.b, y.b); + } + + public boolean inBounds(Vec2i v) { + return inBounds( v.x, v.y ); + } + + public boolean inBounds(Vec2f v) { + return inBounds( v.x, v.y ); + } + + public boolean inBounds(float x, float y) { + return !(x < l || x >= r || y > t || y <= b); + } + + public boolean intersects(Bound2i x) { + return !(x.l >= r || x.r <= l || x.t <= b || x.b >= t); + } + + public boolean touches(Bound2i x) { + return !(x.l > r || x.r < l || x.t < b || x.b > t); + } + + public boolean contains(Bound2i x) { + return !(x.l < l || x.r > r || x.t > t || x.b < b); + } + + public boolean inside(Bound2i x) { + return !(x.l <= l || x.r >= r || x.t >= t || x.b <= b); + } + + @Override + public String toString() + { + return String.format("{%d, %d, %d, %d}", l, t, width(), height()); + } + + @Override + public Bound2i clone() + { + return new Bound2i( this ); + } + + @Override + public Bound2i get() + { + return this; + } + + @Override + public int hashCode() + { + return (int)(t + b * 215 + r * 46225 + l * 9938375); + } + + + @Override + public final CalculatorBound2i getCalculator() + { + return CalculatorBound2i.INSTANCE; + } + +} diff --git a/src/com/axe/math/Bound3f.java b/src/com/axe/math/Bound3f.java new file mode 100755 index 0000000..52650ec --- /dev/null +++ b/src/com/axe/math/Bound3f.java @@ -0,0 +1,401 @@ + +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorBound3f; + + +public class Bound3f implements Attribute +{ + + public float l, r, t, b, n, f; + + public Bound3f() + { + } + + public Bound3f( float v ) + { + l = r = t = b = n = f = v; + } + + public Bound3f( float x, float y, float z ) + { + clear( x, y, z ); + } + + public Bound3f( float left, float top, float right, float bottom, float near, float far ) + { + set( left, top, right, bottom, near, far ); + } + + public Bound3f( Bound3f x ) + { + set( x ); + } + + public void include( float x, float y, float z ) + { + if (x < l) l = x; + if (x > r) r = x; + if (y > t) t = y; + if (y < b) b = y; + if (z < n) n = z; + if (z > f) f = z; + } + + public void include( Vec3f v ) + { + include( v.x, v.y, v.z ); + } + + public void clear( float x, float y, float z ) + { + l = r = x; + t = b = y; + n = f = z; + } + + public void clear( Vec3f v ) + { + clear( v.x, v.y, v.z ); + } + + public boolean isNegative() + { + return r < l || b < t || f < n; + } + + public void set( float left, float top, float right, float bottom, float near, float far ) + { + l = left; + r = right; + t = top; + b = bottom; + n = near; + f = far; + } + + public void set(Vec3f min, Vec3f max) + { + l = min.x; + t = max.y; + n = min.z; + r = max.x; + b = min.y; + f = max.z; + } + + public void rect( float x, float y, float z, float width, float height, float depth ) + { + set( x, y, x + width, y - height, z, z + depth ); + } + + public void line( float x0, float y0, float z0, float x1, float y1, float z1 ) + { + l = Math.min( x0, x1 ); + r = Math.max( x0, x1 ); + t = Math.max( y0, y1 ); + b = Math.min( y0, y1 ); + n = Math.min( z0, z1 ); + f = Math.max( z0, z1 ); + } + + public void ellipse( float cx, float cy, float cz, float rw, float rh, float rz ) + { + rw = Math.abs( rw ); + rh = Math.abs( rh ); + rz = Math.abs( rz ); + l = cx - rw; + r = cx + rw; + t = cy + rh; + b = cy - rh; + n = cz - rz; + f = cz + rz; + } + + public void center( float x, float y, float z ) + { + float w = width() * 0.5f; + float h = height() * 0.5f; + float d = depth() * 0.5f; + l = x - w; + r = x + w; + t = y + h; + b = y - h; + n = z - d; + f = z + d; + } + + public void move( Vec3f delta ) + { + move( delta.x, delta.y, delta.z ); + } + + public void move( float dx, float dy, float dz ) + { + l += dx; + r += dx; + t += dy; + b += dy; + n += dz; + f += dz; + } + + public void expand( float radius ) + { + expand( radius, radius, radius ); + } + + public void expand( float gx, float gy, float gz ) + { + l -= gx; + r += gx; + t += gy; + b -= gy; + n -= gz; + f += gz; + } + + public void expand( float dl, float dt, float dr, float db, float dn, float df ) + { + l -= dl; + r += dr; + t += dt; + b -= db; + n -= dn; + f += df; + } + + public void zoom( float sx, float sy, float sz ) + { + float hw = width() * 0.5f * Math.abs( sx ); + float hh = height() * 0.5f * Math.abs( sy ); + float hd = depth() * 0.5f * Math.abs( sz ); + ellipse( cx(), cy(), cz(), hw, hh, hd ); + } + + public float width() + { + return (r - l); + } + + public float height() + { + return (t - b); + } + + public float depth() + { + return (f - n); + } + + public float area() + { + return (r - l) * (t - b) * (f - n); + } + + public float cx() + { + return (l + r) * 0.5f; + } + + public float cy() + { + return (t + b) * 0.5f; + } + + public float cz() + { + return (n + f) * 0.5f; + } + + public float dx( float dx ) + { + return (r - l) * dx + l; + } + + public float dy( float dy ) + { + return (b - t) * dy + t; + } + + public float dz( float dz ) + { + return (f - n) * dz + n; + } + + public Vec3f delta( Vec3f delta, Vec3f out ) + { + return delta( delta.x, delta.y, delta.z, out ); + } + + public Vec3f delta( float dx, float dy, float dz, Vec3f out ) + { + out.x = dx( dx ); + out.y = dy( dy ); + out.z = dz( dz ); + + return out; + } + + public void getCorners( Vec3f[] x ) + { + x[0].set( l, t, n ); + x[1].set( l, b, n ); + x[2].set( l, t, f ); + x[3].set( l, b, f ); + x[4].set( r, t, n ); + x[5].set( r, b, n ); + x[6].set( r, t, f ); + x[7].set( r, b, f ); + } + + public void getBorder( Line3f[] x ) + { + // n => f + x[0].set( l, t, n, l, t, f ); + x[1].set( l, b, n, l, b, f ); + x[2].set( r, t, n, r, t, f ); + x[3].set( r, b, n, r, b, f ); + // l => r + x[4].set( l, t, n, r, t, n ); + x[5].set( l, b, n, r, b, n ); + x[6].set( l, t, f, r, t, f ); + x[7].set( l, b, f, r, b, f ); + // b => t + x[8].set( l, b, n, l, t, n ); + x[9].set( r, b, n, r, t, n ); + x[10].set( l, b, f, l, t, f ); + x[11].set( r, b, f, r, t, f ); + } + + public void intersection( Bound3f x, Bound3f y ) + { + l = Math.max( x.l, y.l ); + r = Math.min( x.r, y.r ); + t = Math.min( x.t, y.t ); + b = Math.max( x.b, y.b ); + n = Math.max( x.n, y.n ); + f = Math.min( x.f, y.f ); + } + + public void union( Bound3f x, Bound3f y ) + { + l = Math.min( x.l, y.l ); + r = Math.max( x.r, y.r ); + t = Math.max( x.t, y.t ); + b = Math.min( x.b, y.b ); + n = Math.min( x.n, y.n ); + f = Math.max( x.f, y.f ); + } + + public boolean intersects( Bound3f x ) + { + return !outside( x ); + } + + public boolean outside( Bound3f x ) + { + return (x.l >= r || x.r <= l || x.t <= b || x.b >= t || x.n >= f || x.f <= n); + } + + public boolean touches( Bound3f x ) + { + return !(x.l > r || x.r < l || x.t < b || x.b > t || x.n > f || x.f < n); + } + + public boolean contains( Bound3f x ) + { + return !(x.l < l || x.r > r || x.t > t || x.b < b || x.n < n || x.f > f); + } + + public boolean inside( Bound3f x ) + { + return !(x.l <= l || x.r >= r || x.t >= t || x.b <= b || x.n <= n || x.f >= f); + } + + public boolean isValid() + { + return this.isFinite() && l <= r && b <= t && n <= f; + } + + public String toString() + { + return String.format( "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", l, t, n, width(), height(), depth() ); + } + + public String toExtentString() + { + return String.format( "{T=%.2f B=%.2f R=%.2f L=%.2f N=%.2f F=%.2f}", t, b, r, l, n, f ); + } + + public boolean contains( float x, float y, float z ) + { + return !(x < l || x > r || y > t || y < b || z > f || z < n); + } + + public boolean contains( Vec3f v ) + { + return contains( v.x, v.y, v.z ); + } + + public float intersectsRay( Vec3f origin, Vec3f dir ) + { + float invdirx = (dir.x == 0 ? 0 : 1.0f / dir.x); + float invdiry = (dir.y == 0 ? 0 : 1.0f / dir.y); + float invdirz = (dir.z == 0 ? 0 : 1.0f / dir.z); + + float t1 = (l - origin.x) * invdirx; + float t2 = (r - origin.x) * invdirx; + float t3 = (b - origin.y) * invdiry; + float t4 = (t - origin.y) * invdiry; + float t5 = (n - origin.z) * invdirz; + float t6 = (f - origin.z) * invdirz; + + float tmin = Math.max( Math.max( Math.min( t1, t2 ), Math.min( t3, t4 ) ), Math.min( t5, t6 ) ); + float tmax = Math.min( Math.min( Math.max( t1, t2 ), Math.max( t3, t4 ) ), Math.max( t5, t6 ) ); + + return (tmax >= 0 && tmin <= tmax) ? tmin : -1.0f; + } + + public void scale( float sx, float sy, float sz ) + { + l *= sx; + r *= sx; + t *= sy; + b *= sy; + f *= sz; + n *= sz; + } + + public float distanceSq( Vec3f v ) + { + return 0f; + } + + @Override + public Bound3f get() + { + return this; + } + + @Override + public Bound3f clone() + { + return new Bound3f( l, t, r, b, n, f ); + } + + @Override + public CalculatorBound3f getCalculator() + { + return CalculatorBound3f.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(t + b * 36 + r * 1296 + l * 46656 + n * 1679616 + f * 60466176); + } + +} diff --git a/src/com/axe/math/Bound3i.java b/src/com/axe/math/Bound3i.java new file mode 100755 index 0000000..4a3289b --- /dev/null +++ b/src/com/axe/math/Bound3i.java @@ -0,0 +1,234 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorBound3i; + +public class Bound3i implements Attribute +{ + public int l, r, t, b, n, f; + + public Bound3i() { + } + + public Bound3i(int x, int y, int z) { + clear(x, y, z); + } + + public Bound3i(int left, int top, int right, int bottom, int near, int far) { + set(left, top, right, bottom, near, far); + } + + public Bound3i(Bound3i x) { + set(x); + } + + public Bound3i(Vec3i x, Vec3i y) { + clear( x ); + include( y ); + } + + public void include(Vec3i v) { + include( v.x, v.y, v.z ); + } + + public void include(int x, int y, int z) { + if (x < l) l = x; + if (x > r) r = x; + if (y > t) t = y; + if (y < b) b = y; + if (z < n) n = z; + if (z > f) f = z; + } + + public void clear(Vec3i v) { + clear( v.x, v.y, v.z ); + } + + public void clear(int x, int y, int z) { + l = r = x; + t = b = y; + n = f = z; + } + + public void translate(Vec3i v) { + translate( v.x, v.y, v.z ); + } + + public void translate(int dx, int dy, int dz) { + l += dx; + r += dx; + t += dy; + b += dy; + n += dz; + f += dz; + } + + public void set(int left, int top, int right, int bottom, int near, int far) { + l = left; + r = right; + t = top; + b = bottom; + n = near; + f = far; + } + + public void rect(int x, int y, int z, int width, int height, int depth) { + set(x, y, x + width, y - height, z, z + depth); + } + + public void line(int x0, int y0, int z0, int x1, int y1, int z1) { + l = Math.min(x0, x1); + r = Math.max(x0, x1); + t = Math.max(y0, y1); + b = Math.min(y0, y1); + n = Math.min(z0, z1); + f = Math.max(z0, z1); + } + + public void ellipse(int cx, int cy, int cz, int rw, int rh, int rz) { + rw = Math.abs(rw); + rh = Math.abs(rh); + rz = Math.abs(rz); + l = cx - rw; + r = cx + rw; + t = cy + rh; + b = cy - rh; + n = cz - rz; + f = cz + rz; + } + + public void center(int x, int y, int z) { + int w = width() >> 1; + int h = height() >> 1; + int d = depth() >> 1; + l = x - w; + r = x + w; + t = y + h; + b = y - h; + n = z - d; + f = z + d; + } + + public void move(int dx, int dy, int dz) { + l += dx; r += dx; + t += dy; b += dy; + n += dz; f += dz; + } + + public void zoom(int sx, int sy, int sz) { + int hw = (width() >> 1) * Math.abs(sx); + int hh = (height() >> 1) * Math.abs(sy); + int hd = (depth() >> 1) * Math.abs(sz); + ellipse(cx(), cy(), cz(), hw, hh, hd); + } + + public int width() { + return (r - l); + } + + public int height() { + return (t - b); + } + + public int depth() { + return (f - n); + } + + public int area() { + return (r - l) * (t - b) * (f - n); + } + + public int cx() { + return (l + r) >> 1; + } + + public int cy() { + return (t + b) >> 1; + } + + public int cz() { + return (n + f) >> 1; + } + + public void intersection(Bound3i x, Bound3i y) { + l = Math.max(x.l, y.l); + r = Math.min(x.r, y.r); + t = Math.min(x.t, y.t); + b = Math.max(x.b, y.b); + n = Math.max(x.n, y.n); + f = Math.min(x.f, y.f); + } + + public void union(Bound3i x, Bound3i y) { + l = Math.min(x.l, y.l); + r = Math.max(x.r, y.r); + t = Math.max(x.t, y.t); + b = Math.min(x.b, y.b); + n = Math.min(x.n, y.n); + f = Math.max(x.f, y.f); + } + + public boolean intersects(Bound3i x) { + return !(x.l >= r || x.r <= l || x.t <= b || x.b >= t || x.n >= f || x.f <= n); + } + + public boolean touches(Bound3i x) { + return !(x.l > r || x.r < l || x.t < b || x.b > t || x.n > f || x.f < n); + } + + public boolean contains(Bound3i x) { + return !(x.l <= l || x.r >= r || x.t >= t || x.b <= b || x.n <= n || x.f >= f); + } + + public boolean inside(Bound3i x) { + return !(x.l < l || x.r > r || x.t > t || x.b < b || x.n < n || x.f > f); + } + + public String toString() { + return String.format("{x=%d, y=%d, z=%d, w=%d, h=%d, d=%d}", l, b, n, width(), height(), depth()); + } + + public boolean contains(int x, int y, int z) { + return !(x < l || x > r || y > t || y < b || z > f || z < n); + } + + @Override + public Bound3i get() + { + return this; + } + + @Override + public Bound3i clone() + { + return new Bound3i( l, t, r, b, n, f ); + } + + @Override + public CalculatorBound3i getCalculator() + { + return CalculatorBound3i.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(t + b * 36 + r * 1296 + l * 46656 + n * 1679616 + f * 60466176); + } + + public static Bound3i getIntersection(Bound3i x, Bound3i y) + { + Bound3i b = new Bound3i(); + b.intersection( x, y ); + return b; + } + + public static Bound3i getUnion(Bound3i x, Bound3i y) + { + Bound3i b = new Bound3i(); + b.union( x, y ); + return b; + } + +} + diff --git a/src/com/axe/math/Line3f.java b/src/com/axe/math/Line3f.java new file mode 100755 index 0000000..bea23ae --- /dev/null +++ b/src/com/axe/math/Line3f.java @@ -0,0 +1,59 @@ +package com.axe.math; + +public class Line3f +{ + + public final Vec3f s; + public final Vec3f e; + + public Line3f() { + this( new Vec3f(), new Vec3f() ); + } + + public Line3f(float x0, float y0, float z0, float x1, float y1, float z1) { + this ( new Vec3f(x0, y0, z0), new Vec3f(x1, y1, z1) ); + } + + public Line3f(Line3f line) { + this( line.s, line.e ); + } + + public Line3f(Vec3f s, Vec3f e) { + this.s = s; + this.e = e; + } + + public void set(Line3f line) { + s.set(line.s); + e.set(line.e); + } + + public void set(float x0, float y0, float z0, float x1, float y1, float z1) { + s.set(x0, y0, z0); + e.set(x1, y1, z1); + } + + public void set(Vec3f start, Vec3f end) { + s.set(start); + e.set(end); + } + + public float length() { + return s.distance(e); + } + + public Vec3f dir() { + Vec3f v = new Vec3f(); + v.normal(e.x - s.x, e.y - s.y, e.z - s.z); + return v; + } + + public Vec3f diff() { + return new Vec3f(e.x - s.x, e.y - s.y, e.z - s.z); + } + + public String toString() { + return String.format("{(%.2f, %.2f, %.2f) => (%.2f, %.2f ,%.2f)}", s.x, s.y, s.z, e.x, e.y, e.z); + } + +} diff --git a/src/com/axe/math/Matrix.java b/src/com/axe/math/Matrix.java new file mode 100644 index 0000000..2693f0f --- /dev/null +++ b/src/com/axe/math/Matrix.java @@ -0,0 +1,209 @@ +package com.axe.math; + +import java.nio.FloatBuffer; + +public interface Matrix> +{ + + public Matrix identity(); + + public float determinant(); + + public Matrix ortho(V min, V max); + + public V transform(V point, V out); + + public V transform(V point); + + public V transformVector(V vector, V out); + + public V transformVector(V vector); + + public Matrix translate(V by, Matrix out); + + default public Matrix translaten(V by) + { + return translate( by, create() ); + } + + default public Matrix translatei(V by) + { + return translate( by, this ); + } + + public Matrix pretranslate(V by); + + public V getTranslation(V out); + + default public V getTranslation() + { + return getTranslation( createVector() ); + } + + public Matrix setTranslation(V by); + + public Matrix scale(V by, Matrix out); + + default public Matrix scalen(V by) + { + return scale( by, create() ); + } + + default public Matrix scalei(V by) + { + return scale( by, this ); + } + + public Matrix scale(float by, Matrix out); + + default public Matrix scalen(float by) + { + return scale( by, create() ); + } + + default public Matrix scalei(float by) + { + return scale( by, this ); + } + + public Matrix prescale(V by); + + public Matrix prescale(float by); + + public V getScaling(V out); + + default public V getScaling() + { + return getScaling( createVector() ); + } + + public Matrix setScaling(V by); + + public Matrix setScaling(float by); + + public Matrix setRotation(float cos, float sin, Vec3f axis); + + default public Matrix setRotation(float radians, Vec3f axis) + { + return setRotation( Numbers.cos( radians ), Numbers.sin( radians ), axis ); + } + + public Matrix rotate(float cos, float sin, Vec3f axis, Matrix out); + + default public Matrix rotate(float radians, Vec3f axis, Matrix out) + { + return rotate( Numbers.cos( radians ), Numbers.sin( radians ), axis, out ); + } + + default public Matrix rotateDegrees(float degrees, Vec3f axis, Matrix out) + { + return rotate( degrees * Numbers.DEGREE_TO_RADIAN, axis, out ); + } + + default public Matrix rotaten(float radians, Vec3f axis) + { + return rotate( radians, axis, create() ); + } + + default public Matrix rotatenDegrees(float degrees, Vec3f axis) + { + return rotate( degrees * Numbers.DEGREE_TO_RADIAN, axis, create() ); + } + + default public Matrix rotaten(float cos, float sin, Vec3f axis) + { + return rotate( cos, sin, axis, create() ); + } + + default public Matrix rotatei(float radians, Vec3f axis) + { + return rotate( radians, axis, this ); + } + + default public Matrix rotateiDegrees(float degrees, Vec3f axis) + { + return rotate( degrees * Numbers.DEGREE_TO_RADIAN, axis, this ); + } + + default public Matrix rotatei(float cos, float sin, Vec3f axis) + { + return rotate( cos, sin, axis, this ); + } + + public Matrix transpose(Matrix out); + + default Matrix transposen() + { + return transpose( create() ); + } + + default Matrix transposei() + { + return transpose( this ); + } + + public Matrix invert(Matrix out); + + default public Matrix invertn() + { + return invert( create() ); + } + + default public Matrix inverti() + { + return invert( this ); + } + + public Matrix multiply(Matrix base, Matrix by, Matrix out); + + default public Matrix multiply(Matrix by, Matrix out) + { + return multiply( this, by, out ); + } + + default Matrix multiplyn(Matrix by) + { + return multiply( this, by, create() ); + } + + default Matrix multiplyi(Matrix by) + { + return multiply( this, by, this ); + } + + default public Matrix premultiply(Matrix by, Matrix out) + { + return multiply( by, this, out ); + } + + default Matrix premultiplyn(Matrix by) + { + return multiply( by, this, create() ); + } + + default Matrix premultiplyi(Matrix by) + { + return multiply( by, this, this ); + } + + public Matrix set(float[] values); + + default public Matrix set(Matrix matrix) + { + return set( matrix.get() ); + } + + public Matrix create(); + + public V createVector(); + + public Matrix create(float[] values); + + public float[] get(); + + default public void get(FloatBuffer out) + { + out.put( get() ); + } + +} diff --git a/src/com/axe/math/Matrix2f.java b/src/com/axe/math/Matrix2f.java new file mode 100755 index 0000000..31d9273 --- /dev/null +++ b/src/com/axe/math/Matrix2f.java @@ -0,0 +1,317 @@ +package com.axe.math; + +public class Matrix2f +{ + public static final Matrix2f IDENTITY = new Matrix2f(); + + public float transX; + public float transY; + public float shearX; + public float shearY; + public float scaleX; + public float scaleY; + + public Matrix2f() + { + set(1, 1, 0, 0, 0, 0); + } + + public Matrix2f(Matrix2f m) + { + set(m); + } + + public Matrix2f(float scaleX, float scaleY, float shearX, float shearY, float transX, float transY) + { + set(scaleX, scaleY, shearX, shearY, transX, transY); + } + + public void set(float scaleX, float scaleY, float shearX, float shearY, float transX, float transY) + { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.shearX = shearX; + this.shearY = shearY; + this.transX = transX; + this.transY = transY; + } + + public void set(Matrix2f m) + { + set(m.scaleX, m.scaleY, m.shearX, m.shearY, m.transX, m.transY); + } + + public void setIdentity() + { + scaleX = scaleY = 1f; + shearX = shearY = transX = transY = 0f; + } + + public void setTranslation( Vec2f v ) + { + setTranslation( v.x, v.y ); + } + + public void setTranslation(float x, float y) + { + scaleX = scaleY = 1f; + shearX = shearY = 0f; + transX = x; + transY = y; + } + + public void setScaling( Vec2f scale ) + { + setScaling( scale.x, scale.y ); + } + + public void setScaling(float x, float y) + { + scaleX = x; + scaleY = y; + shearX = shearY = transX = transY = 0f; + } + + public void setRadians( Scalarf radians ) + { + setRadians( radians.v ); + } + + public void setRadians(float radians) + { + float cos = Numbers.cos( radians ); + float sin = Numbers.sin( radians ); + + scaleX = scaleY = cos; + shearX = -(shearY = sin); + transX = transY = 0f; + } + + public void setDegrees( Scalarf angle ) + { + setDegrees( angle.v ); + } + + public void setDegrees(float angle) + { + setRadians( angle * Numbers.DEGREE_TO_RADIAN ); + } + + public void setShear( Vec2f shear ) + { + setShear( shear.x, shear.y ); + } + + public void setShear(float x, float y) + { + scaleX = scaleY = 1f; + shearX = x; + shearY = y; + transX = transY = 0f; + } + + public void transform(Vec2f inout) + { + transform(inout.x, inout.y, inout); + } + + public Vec2f transform(Vec2f in, Vec2f out) + { + transform(in.x, in.y, out); + + return out; + } + + public void transform(float x, float y, Vec2f out) + { + out.x = (scaleX * x) + (shearX * y) + transX; + out.y = (shearY * x) + (scaleY * y) + transY; + } + + public void transform(Vec2f[] in, Vec2f[] out) + { + for (int i = in.length - 1; i >= 0; i--) + { + if (out[i] == null) { + out[i] = new Vec2f(); + } + transform( in[i].x, in[i].y, out[i] ); + } + } + + public Vec2f[] transform(Vec2f[] in) + { + Vec2f[] out = new Vec2f[ in.length ]; + transform( in, out ); + return out; + } + + public void mul(Matrix2f t) + { + float scX, scY, shX, shY, trX, trY; + + scX = (scaleX * t.scaleX) + (shearY * t.shearX); + scY = (shearX * t.shearY) + (scaleY * t.scaleY); + shX = (shearX * t.scaleX) + (scaleY * t.shearX); + shY = (scaleX * t.shearY) + (shearY * t.scaleY); + trX = (transX * t.scaleX) + (transY * t.transX) + t.transX; + trY = (transX * t.shearY) + (transY * t.transY) + t.transY; + + set(scX, scY, shX, shY, trX, trY); + } + + public void set(float angle, float scaleX, float scaleY, float translationX, float translationY) + { + if (angle != 0) + { + float cos = Numbers.cos(angle); + float sin = Numbers.sin(angle); + + this.scaleX = scaleX * cos; + this.scaleY = scaleY * cos; + this.shearX = scaleY * sin; + this.shearY = scaleX * sin; + this.transX = translationX; + this.transY = translationY; + } + else + { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.shearX = this.shearY = 0f; + this.transX = translationX; + this.transY = translationY; + } + } + + public void set(float angle, Vec2f scale, Vec2f translation) + { + set(angle, scale.x, scale.y, translation.x, translation.y); + } + + public void translate( Vec2f v ) + { + translate( v.x, v.y ); + } + + public void translate(float x, float y) + { + transX += x; + transY += y; + } + + public void scale( Vec2f scale ) + { + scale( scale.x, scale.y ); + } + + public void scale(float x, float y) + { + scaleX *= x; + scaleY *= y; + shearX *= x; + shearY *= y; + transX *= x; + transY *= y; + } + + public void shear(Vec2f shear) + { + shearX( shear.x ); + shearY( shear.y ); + } + + public void shearX( Scalarf x ) + { + shearX( x.v ); + } + + public void shearX(float x) + { + scaleX += shearY * x; + shearX += scaleY * x; + transX += transY * x; + } + + public void shearY( Scalarf y ) + { + shearY( y.v ); + } + + public void shearY(float y) + { + shearY += scaleX * y; + scaleY += shearX * y; + transY += transX * y; + } + + public void rotate( Scalarf angle ) + { + rotate( angle.v ); + } + + public void rotate(float angle) + { + if (angle == 0) + return; + + float cos = Numbers.cos(angle); + float sin = Numbers.sin(angle); + float scX, scY, shX, shY, trX, trY; + + scX = (scaleX * cos) - (shearY * sin); + scY = (shearX * sin) + (scaleY * cos); + shX = (shearX * cos) - (scaleY * sin); + shY = (scaleX * sin) + (shearY * cos); + trX = (transX * cos) - (transY * sin); + trY = (transX * sin) + (transY * cos); + + set(scX, scY, shX, shY, trX, trY); + } + + public void rotate( Scalarf angle, Vec2f origin ) + { + rotate( angle.v, origin.x, origin.y ); + } + + public void rotate(float angle, Vec2f origin) + { + rotate( angle, origin.x, origin.y ); + } + + public void rotate(float angle, float originX, float originY) + { + if (angle != 0) + { + translate(-originX, -originY); + rotate(angle); + translate(originX, originY); + } + } + + public void invert() + { + float det = determinant(); + + if (det == 0f) + return; + + float detInv = 1f / det; + float scX, scY, shX, shY, trX, trY; + + scX = scaleY * detInv; + scY = scaleX * detInv; + shX = -shearX * detInv; + shY = -shearY * detInv; + trX = (shearX * transY - scaleY * transX) * detInv; + trY = (shearY * transX - scaleX * transY) * detInv; + + set(scX, scY, shX, shY, trX, trY); + } + + public float determinant() + { + return (scaleX * scaleY - shearY * shearX); + } + +} diff --git a/src/com/axe/math/Matrix3f.java b/src/com/axe/math/Matrix3f.java new file mode 100755 index 0000000..48067e0 --- /dev/null +++ b/src/com/axe/math/Matrix3f.java @@ -0,0 +1,577 @@ +package com.axe.math; + +import java.nio.FloatBuffer; + +import com.axe.util.Buffers; + +public class Matrix3f +{ + public static final float EPSILON = 0.0000001f; + + public static final FloatBuffer buffer = Buffers.floats( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + + public float m00, m01, m02; + public float m10, m11, m12; + public float m20, m21, m22; + public float tx, ty, tz; + public Vec3f scale = new Vec3f( 1.0f ); + + public Matrix3f() + { + reset(); + } + + public Matrix3f(Matrix3f m) + { + set(m); + } + + public void reset() + { + m00 = m11 = m22 = 1.0f; + m01 = m02 = m10 = m12 = m20 = m21 = 0.0f; + tx = ty = tz = 0.0f; + scale.set( 1.0f ); + } + + public float determinant() + { + return (-m02 * m11 * m20 + m01 * m12 * m20 + + m02 * m10 * m21 - m00 * m12 * m21 - + m01 * m10 * m22 + m00 * m11 * m22); + } + + public FloatBuffer get() + { + buffer.put( 0, m00 ); + buffer.put( 1, m10 ); + buffer.put( 2, m20 ); + buffer.put( 4, m01 ); + buffer.put( 5, m11 ); + buffer.put( 6, m21 ); + buffer.put( 8, m02 ); + buffer.put( 9, m12 ); + buffer.put( 10, m22 ); + buffer.put( 12, tx ); + buffer.put( 13, ty ); + buffer.put( 14, tz ); + + return buffer; + } + + public void set( FloatBuffer buffer ) + { + m00 = buffer.get( 0 ); + m10 = buffer.get( 1 ); + m20 = buffer.get( 2 ); + m01 = buffer.get( 4 ); + m11 = buffer.get( 5 ); + m21 = buffer.get( 6 ); + m02 = buffer.get( 8 ); + m12 = buffer.get( 9 ); + m22 = buffer.get( 10 ); + tx = buffer.get( 12 ); + ty = buffer.get( 13 ); + tz = buffer.get( 14 ); + } + + public void set(Matrix3f m) + { + m00 = m.m00; + m01 = m.m01; + m02 = m.m02; + m10 = m.m10; + m11 = m.m11; + m12 = m.m12; + m20 = m.m20; + m21 = m.m21; + m22 = m.m22; + tx = m.tx; + ty = m.ty; + tz = m.tz; + scale.set( m.scale ); + } + + public Vec3f transform(Vec3f p, Vec3f out) + { + final float px = p.x; + final float py = p.y; + final float pz = p.z; + + out.x = (px * m00) + (py * m01) + (pz * m02) + tx; + out.y = (px * m10) + (py * m11) + (pz * m12) + ty; + out.z = (px * m20) + (py * m21) + (pz * m22) + tz; + + return out; + } + + public Vec3f transform(Vec3f p) + { + return transform( p, p ); + } + + public Vec3f transformVector(Vec3f p, Vec3f out) + { + final float px = p.x; + final float py = p.y; + final float pz = p.z; + + out.x = (px * m00) + (py * m01) + (pz * m02); + out.y = (px * m10) + (py * m11) + (pz * m12); + out.z = (px * m20) + (py * m21) + (pz * m22); + + return out; + } + + public Vec3f transformVector(Vec3f p) + { + return transformVector( p, p ); + } + + public Vec3f transformInverse(Vec3f p, Vec3f out) + { + final float det = determinant(); + + if (det == 0.0f ) + { + return null; + } + + final float invdet = 1.0f / det; + final float px = p.x; + final float py = p.y; + final float pz = p.z; + final float am00 = (-m12 * m21 + m11 * m22) * invdet; + final float am01 = (m02 * m21- m01 * m22) * invdet; + final float am02 = (-m02 * m11 * 1 + m01 * m12) * invdet; + final float am10 = (m12 * m20 - m10 * m22) * invdet; + final float am11 = (-m02 * m20 + m00 * m22) * invdet; + final float am12 = (m02 * m10 - m00 * m12) * invdet; + final float am20 = (-m11 * m20 + m10 * m21) * invdet; + final float am21 = (m01 * m20 - m00 * m21) * invdet; + final float am22 = (-m01 * m10 + m00 * m11) * invdet; + final float atx = (tx * m12 * m21 - m02 * ty * m21 - tx * m11 * m22 + m01 * ty * m22 + m02 * m11 * tz - m01 * m12 * tz) * invdet; + final float aty = (m02 * ty * m20 - tx * m12 * m20 + tx * m10 * m22 - m00 * ty * m22 - m02 * m10 * tz + m00 * m12 * tz) * invdet; + final float atz = (tx * m11 * m20 - m01 * ty * m20 - tx * m10 * m21 + m00 * ty * m21 + m01 * m10 * tz - m00 * m11 * tz) * invdet; + + out.x = (px * am00) + (py * am01) + (pz * am02) + atx; + out.y = (px * am10) + (py * am11) + (pz * am12) + aty; + out.z = (px * am20) + (py * am21) + (pz * am22) + atz; + + return out; + } + + public Vec3f transformInverse(Vec3f p) + { + return transformInverse( p, p ); + } + + public Vec3f transformInverseVector(Vec3f p, Vec3f out) + { + final float det = determinant(); + + if (det == 0.0f ) + { + return null; + } + + final float invdet = 1.0f / det; + final float px = p.x; + final float py = p.y; + final float pz = p.z; + final float am00 = (-m12 * m21 + m11 * m22) * invdet; + final float am01 = (m02 * m21- m01 * m22) * invdet; + final float am02 = (-m02 * m11 * 1 + m01 * m12) * invdet; + final float am10 = (m12 * m20 - m10 * m22) * invdet; + final float am11 = (-m02 * m20 + m00 * m22) * invdet; + final float am12 = (m02 * m10 - m00 * m12) * invdet; + final float am20 = (-m11 * m20 + m10 * m21) * invdet; + final float am21 = (m01 * m20 - m00 * m21) * invdet; + final float am22 = (-m01 * m10 + m00 * m11) * invdet; + + out.x = (px * am00) + (py * am01) + (pz * am02); + out.y = (px * am10) + (py * am11) + (pz * am12); + out.z = (px * am20) + (py * am21) + (pz * am22); + + return out; + } + + public Vec3f transformInverseVector(Vec3f p) + { + return transformInverseVector( p, p ); + } + + public Vec3f getX(Vec3f out) { + out.x = m00; + out.y = m10; + out.z = m20; + return out; + } + + public Vec3f getY(Vec3f out) { + out.x = m01; + out.y = m11; + out.z = m21; + return out; + } + + public Vec3f getZ(Vec3f out) { + out.x = m02; + out.y = m12; + out.z = m22; + return out; + } + + public Vec3f getTranslation(Vec3f out) { + out.x = tx; + out.y = ty; + out.z = tz; + return out; + } + + public void setEulerAngles(float yaw, float pitch, float roll) { + float sinX = (float)Math.sin(pitch); + float cosX = (float)Math.cos(pitch); + float sinY = (float)Math.sin(yaw); + float cosY = (float)Math.cos(yaw); + float sinZ = (float)Math.sin(roll); + float cosZ = (float)Math.cos(roll); + m00 = (cosZ * cosY); + m01 = (cosZ * -sinY * -sinX + sinZ * cosX); + m02 = (cosZ * -sinY * cosX + sinZ * sinX); + m10 = (-sinZ * cosY); + m11 = (-sinZ * -sinY * -sinX + cosZ * cosX); + m12 = (-sinZ * -sinY * cosX + cosZ * sinX); + m20 = sinY; + m21 = cosY * -sinX; + m22 = cosY * cosX; + scale.set( 1.0f ); + } + + public void setEulerAngles(float yaw, float pitch) { + float sinX = (float)Math.sin(pitch); + float cosX = (float)Math.cos(pitch); + float sinY = (float)Math.sin(yaw); + float cosY = (float)Math.cos(yaw); + m00 = cosY; + m01 = sinY * sinX; + m02 = -sinY * cosX; + m10 = 0.0f; + m11 = cosX; + m12 = sinX; + m20 = sinY; + m21 = cosY * -sinX; + m22 = cosY * cosX; + scale.set( 1.0f ); + } + + public Matrix3f rotate(double theta, Vec3f v) + { + return rotate( theta, v.x, v.y, v.z ); + } + + public Matrix3f rotate(double theta, float vx, float vy, float vz) + { + // Dot product of the rotation vector + float dot = (vx * vx) + (vy * vy) + (vz * vz); + + // If the vector isn't normalized then normalize it. + if (!equal(dot, 1.0f)) { + float invlength = 1.0f / (float)Math.sqrt(dot); + vx *= invlength; + vy *= invlength; + vz *= invlength; + } + + // Cache values used in quaternion matrix calculation + float cos, sin, mcos, vx_mcos, vy_mcos, vz_mcos, vx_sin, vy_sin, vz_sin; + cos = (float)Math.cos(theta); + sin = (float)Math.sin(theta); + mcos = 1.0f - cos; + vx_mcos = vx * mcos; + vy_mcos = vy * mcos; + vz_mcos = vz * mcos; + vx_sin = vx * sin; + vy_sin = vy * sin; + vz_sin = vz * sin; + + // Copy current matrix state into A + float a00, a01, a02, a10, a11, a12, a20, a21, a22; + a00 = m00; a01 = m01; a02 = m02; + a10 = m10; a11 = m11; a12 = m12; + a20 = m20; a21 = m21; a22 = m22; + + // Derived quaternion matrix into B + float b00, b01, b02, b10, b11, b12, b20, b21, b22; + b00 = vx * vx_mcos + cos; + b10 = vy * vx_mcos + vz_sin; + b20 = vz * vx_mcos - vy_sin; + b01 = vx * vy_mcos - vz_sin; + b11 = vy * vy_mcos + cos; + b21 = vz * vy_mcos + vx_sin; + b02 = vx * vz_mcos + vy_sin; + b12 = vy * vz_mcos + vx_sin; + b22 = vz * vz_mcos + cos; + + // Matrix multiplication (A x B) + m00 = (a00 * b00) + (a01 * b10) + (a02 * b20); + m10 = (a10 * b00) + (a11 * b10) + (a12 * b20); + m20 = (a20 * b00) + (a21 * b10) + (a22 * b20); + m01 = (a00 * b01) + (a01 * b11) + (a02 * b21); + m11 = (a10 * b01) + (a11 * b11) + (a12 * b21); + m21 = (a20 * b01) + (a21 * b11) + (a22 * b21); + m02 = (a00 * b02) + (a01 * b12) + (a02 * b22); + m12 = (a10 * b02) + (a11 * b12) + (a12 * b22); + m22 = (a20 * b02) + (a21 * b12) + (a22 * b22); + + return this; + } + + public Matrix3f rotatex(double pitch) { + if ( pitch != 0 ) { + float c = (float)Math.cos(pitch); + float s = (float)Math.sin(pitch); + float a01 = m01, a11 = m11, a21 = m21; + m01 = (a01 * c) + (m02 * s); + m02 = (m02 * c) - (a01 * s); + m11 = (a11 * c) + (m12 * s); + m12 = (m12 * c) - (a11 * s); + m21 = (a21 * c) + (m22 * s); + m22 = (m22 * c) - (a21 * s); + } + return this; + } + + public Matrix3f rotatey(double yaw) { + if ( yaw != 0 ) { + float c = (float)Math.cos(yaw); + float s = (float)Math.sin(yaw); + float a00 = m00, a10 = m10, a20 = m20; + m00 = (a00 * c) - (m02 * s); + m02 = (m02 * c) + (a00 * s); + m10 = (a10 * c) - (m12 * s); + m12 = (m12 * c) + (a10 * s); + m20 = (a20 * c) - (m22 * s); + m22 = (m22 * c) + (a20 * s); + } + return this; + } + + public Matrix3f rotatez(double roll) { + if ( roll != 0 ) { + float c = (float)Math.cos(roll); + float s = (float)Math.sin(roll); + float a00 = m00, a10 = m10, a20 = m20; + m00 = (a00 * c) + (m01 * s); + m01 = (m01 * c) - (a00 * s); + m10 = (a10 * c) + (m11 * s); + m11 = (m11 * c) - (a10 * s); + m20 = (a20 * c) + (m21 * s); + m21 = (m21 * c) - (a20 * s); + } + return this; + } + + public Matrix3f translate(Vec3f v) { + return translate( v.x, v.y, v.z ); + } + + public Matrix3f translate(float dx, float dy, float dz) { + tx += (m00 * dx) + (m01 * dy) + (m02 * dz); + ty += (m10 * dx) + (m11 * dy) + (m12 * dz); + tz += (m20 * dx) + (m21 * dy) + (m22 * dz); + return this; + } + + public Matrix3f translateBefore(Vec3f v) { + return translateBefore( v.x, v.y, v.z ); + } + + public Matrix3f translateBefore(float dx, float dy, float dz) { + tx += dx; + ty += dy; + tz += dz; + return this; + } + + public Matrix3f setTranslate(Vec3f v) { + return setTranslate( v.x, v.y, v.z ); + } + + public Matrix3f setTranslate(float x, float y, float z) { + reset(); + tx = x; + ty = y; + tz = z; + return this; + } + + public Matrix3f scale(Vec3f v) { + return scale(v.x, v.y, v.z); + } + + public Matrix3f scale(float sx, float sy, float sz) { + m00 *= sx; m01 *= sy; m02 *= sz; + m10 *= sx; m11 *= sy; m12 *= sz; + m20 *= sx; m21 *= sy; m22 *= sz; + + scale.x *= sx; + scale.y *= sy; + scale.z *= sz; + + return this; + } + + public Matrix3f setScale(Vec3f v) { + return setScale( v.x, v.y, v.z ); + } + + public Matrix3f setScale(float sx, float sy, float sz) { + reset(); + m00 = sx; + m11 = sy; + m22 = sz; + scale.set( sx, sy, sz ); + return this; + } + + public Matrix3f shearxy(float hx, float hy) { + m02 += (m00 * hx) + (m01 * hy); + m12 += (m10 * hx) + (m11 * hy); + m22 += (m20 * hx) + (m21 * hy); + return this; + } + + public Matrix3f shearxz(float hx, float hz) { + m01 += (m00 * hx) + (m02 * hz); + m11 += (m10 * hx) + (m12 * hz); + m21 += (m20 * hx) + (m22 * hz); + return this; + } + + public Matrix3f shearyz(float hy, float hz) { + m00 += (m01 * hy) + (m02 * hz); + m10 += (m11 * hy) + (m12 * hz); + m20 += (m21 * hy) + (m22 * hz); + return this; + } + + public void multiply(Matrix3f b) + { + float am00 = m00; + float am01 = m01; + float am02 = m02; + float am10 = m10; + float am11 = m11; + float am12 = m12; + float am20 = m20; + float am21 = m21; + float am22 = m22; + float atx = tx; + float aty = ty; + float atz = tz; + + m00 = (am00 * b.m00) + (am01 * b.m10) + (am02 * b.m20); + m01 = (am00 * b.m01) + (am01 * b.m11) + (am02 * b.m21); + m02 = (am00 * b.m02) + (am01 * b.m12) + (am02 * b.m22); + tx = (am00 * b.tx) + (am01 * b.ty) + (am02 * b.tz) + atx; + + m10 = (am10 * b.m00) + (am11 * b.m10) + (am12 * b.m20); + m11 = (am10 * b.m01) + (am11 * b.m11) + (am12 * b.m21); + m12 = (am10 * b.m02) + (am11 * b.m12) + (am12 * b.m22); + ty = (am10 * b.tx) + (am11 * b.ty) + (am12 * b.tz) + aty; + + m20 = (am20 * b.m00) + (am21 * b.m10) + (am22 * b.m20); + m21 = (am20 * b.m01) + (am21 * b.m11) + (am22 * b.m21); + m22 = (am20 * b.m02) + (am21 * b.m12) + (am22 * b.m22); + tz = (am20 * b.tx) + (am21 * b.ty) + (am22 * b.tz) + atz; + + scale.mul( b.scale ); + } + + private boolean equal(float a, float b) { + return Math.abs(a - b) < EPSILON; + } + + public static Matrix3f multiply(Matrix3f a, Matrix3f b, Matrix3f out) + { + out.m00 = (a.m00 * b.m00) + (a.m01 * b.m10) + (a.m02 * b.m20); + out.m01 = (a.m00 * b.m01) + (a.m01 * b.m11) + (a.m02 * b.m21); + out.m02 = (a.m00 * b.m02) + (a.m02 * b.m12) + (a.m02 * b.m22); + out.tx = (a.m00 * b.tx) + (a.m01 * b.ty) + (a.m02 * b.tz) + a.tx; + + out.m10 = (a.m10 * b.m00) + (a.m11 * b.m10) + (a.m12 * b.m20); + out.m11 = (a.m10 * b.m01) + (a.m11 * b.m11) + (a.m12 * b.m21); + out.m12 = (a.m10 * b.m02) + (a.m11 * b.m12) + (a.m12 * b.m22); + out.ty = (a.m10 * b.tx) + (a.m11 * b.ty) + (a.m12 * b.tz) + a.ty; + + out.m20 = (a.m20 * b.m00) + (a.m21 * b.m10) + (a.m22 * b.m20); + out.m21 = (a.m20 * b.m01) + (a.m21 * b.m11) + (a.m22 * b.m21); + out.m22 = (a.m20 * b.m02) + (a.m21 * b.m12) + (a.m22 * b.m22); + out.tz = (a.m20 * b.tx) + (a.m21 * b.ty) + (a.m22 * b.tz) + a.tz; + + out.scale.set( a.scale ); + out.scale.mul( b.scale ); + + return out; + } + + public static Matrix3f multiply(Matrix3f a, Matrix3f b) + { + return multiply( a, b, new Matrix3f() ); + } + + public Matrix3f invert(Matrix3f out) + { + float det = determinant(); + + if (det == 0.0f ) + { + return null; + } + + float invdet = 1.0f / det; + + out.m00 = (-m12 * m21 + m11 * m22) * invdet; + out.m01 = (m02 * m21- m01 * m22) * invdet; + out.m02 = (-m02 * m11 * 1 + m01 * m12) * invdet; + + out.m10 = (m12 * m20 - m10 * m22) * invdet; + out.m11 = (-m02 * m20 + m00 * m22) * invdet; + out.m12 = (m02 * m10 - m00 * m12) * invdet; + + out.m20 = (-m11 * m20 + m10 * m21) * invdet; + out.m21 = (m01 * m20 - m00 * m21) * invdet; + out.m22 = (-m01 * m10 + m00 * m11) * invdet; + + out.tx = (tx * m12 * m21 - m02 * ty * m21 - tx * m11 * m22 + m01 * ty * m22 + m02 * m11 * tz - m01 * m12 * tz) * invdet; + out.ty = (m02 * ty * m20 - tx * m12 * m20 + tx * m10 * m22 - m00 * ty * m22 - m02 * m10 * tz + m00 * m12 * tz) * invdet; + out.tz = (tx * m11 * m20 - m01 * ty * m20 - tx * m10 * m21 + m00 * ty * m21 + m01 * m10 * tz - m00 * m11 * tz) * invdet; + + out.scale.set( 1.0f ); + out.scale.div( scale ); + + return out; + } + + public void ortho(float l, float t, float r, float b, float n, float f) + { + float w = r - l; + float h = t - b; + float d = f - n; + + m01 = m02 = m10 = m12 = m20 = m21 = 0; + m00 = 2 / w; + m11 = 2 / h; + m22 = -2 / d; + tx = -(r + l) / w; + ty = -(t + b) / h; + tz = -(f + n) / d; + } + +} diff --git a/src/com/axe/math/Numbers.java b/src/com/axe/math/Numbers.java new file mode 100755 index 0000000..84adc7e --- /dev/null +++ b/src/com/axe/math/Numbers.java @@ -0,0 +1,388 @@ +package com.axe.math; + +import java.util.List; +import java.util.Random; + + +public class Numbers +{ + + public static float EPSILON = 0.00001f; + + public static final Random random = new Random(); + + public static final float PI = 3.14159265358979323846f; + public static final float PI2 = PI * 2; + public static final float PIHALF = PI * 0.5f; + public static final float DEGREE_TO_RADIAN = PI / 180; + public static final float RADIAN_TO_DEGREE = 180 / PI; + + public static final float[] RADIAN; + public static final float[] COS; + public static final float[] SIN; + + static + { + RADIAN = new float[ 361 ]; + COS = new float[ 361 ]; + SIN = new float[ 361 ]; + for (int i = 0; i < 361; i++ ) { + RADIAN[i] = i * DEGREE_TO_RADIAN; + COS[i] = (float)StrictMath.cos( RADIAN[i] ); + SIN[i] = (float)StrictMath.sin( RADIAN[i] ); + } + } + + public static float divide(float dividend, float divisor) + { + return divisor == 0 ? 0 : dividend / divisor; + } + + public static int divide(int dividend, int divisor) + { + return divisor == 0 ? 0 : dividend / divisor; + } + + public static float truncate(float value, float divisor) + { + return value - value % divisor; + } + + public static int truncate(int value, int divisor) + { + return value - value % divisor; + } + + public static float clamp(float value, float min, float max) + { + return value < min ? min : (value > max ? max : value); + } + + public static int clamp(int value, int min, int max) + { + return value < min ? min : (value > max ? max : value); + } + + public static void setRandomSeed( long randomSeed ) + { + random.setSeed( randomSeed ); + } + + public static float randomFloat() + { + return random.nextFloat(); + } + + public static float randomFloat( float x ) + { + return random.nextFloat() * x; + } + + public static float randomFloat( float min, float max ) + { + return random.nextFloat() * (max - min) + min; + } + + public static int randomSign() + { + return (random.nextInt( 2 ) << 1) - 1; + } + + public static int randomInt() + { + return random.nextInt(); + } + + public static int randomInt( int x ) + { + return random.nextInt( x ); + } + + public static int randomInt( int min, int max ) + { + return random.nextInt( max - min + 1 ) + min; + } + + public static long randomLong() + { + return random.nextLong(); + } + + public static boolean randomBoolean() + { + return random.nextBoolean(); + } + + public static > E random( Class enumClass ) + { + return random( enumClass.getEnumConstants() ); + } + + public static T random( T[] elements ) + { + return elements[ random.nextInt( elements.length) ]; + } + + public static T random( T[] elements, T returnOnNull ) + { + return ( elements == null || elements.length == 0 ? returnOnNull : elements[ random.nextInt( elements.length) ] ); + } + + public static T random( T[] elements, int min, int max, T returnOnNull ) + { + return ( elements == null || elements.length == 0 ? returnOnNull : elements[ random.nextInt( max - min + 1) + min ] ); + } + + public static T random( List elements ) + { + return elements.get( random.nextInt( elements.size() ) ); + } + + public static T random( List elements, int min, int max, T returnOnNull ) + { + return ( elements == null || elements.size() == 0 ? returnOnNull : elements.get( random.nextInt( max - min + 1) + min ) ); + } + + public static float sqrt( float x ) + { + return (float)StrictMath.sqrt( x ); + } + + public static float cos( float r ) + { + return (float)StrictMath.cos( r ); + } + + public static float cos( float r, float scale ) + { + return (float)StrictMath.cos( r ) * scale; + } + + public static float sin( float r ) + { + return (float)StrictMath.sin( r ); + } + + public static float acos( float r ) + { + return (float)StrictMath.acos( r ); + } + + public static float sin( float r, float scale ) + { + return (float)StrictMath.sin( r ) * scale; + } + + public static float floor( float x ) + { + return (float)StrictMath.floor( x ); + } + + public static float ceil( float x ) + { + return (float)StrictMath.ceil( x ); + } + + public static boolean equals( float a, float b ) + { + return equals( a, b, EPSILON ); + } + + public static boolean equals( float a, float b, float epsilon ) + { + return Math.abs( a - b ) < epsilon; + } + + public static void randomDirection(Rangef yaw, Rangef pitch, Vec3f out) + { + direction( yaw.rnd(), pitch.rnd(), out ); + } + + public static void direction(float yaw, float pitch, Vec3f out) + { + float tcos = cos( yaw ); + float tsin = sin( yaw ); + float rcos = cos( pitch ); + float rsin = sin( pitch ); + + out.x = tcos * rcos; + out.z = tsin * rcos; + out.y = rsin; + } + + public static int factorial(int x) + { + int n = x; + while (--x >= 1) { + n *= x; + } + return n; + } + + // greatest common divisor, 32-bit integer + public static int gcd( int a, int b ) + { + int shift = 0; + + if ( a == 0 || b == 0 ) { + return (a | b); + } + + for (shift = 0; ((a | b) & 1) == 0; ++shift) { + a >>= 1; + b >>= 1; + } + + while ((a & 1) == 0) { + a >>= 1; + } + + do { + while ((b & 1) == 0) { + b >>= 1; + } + if (a < b) { + b -= a; + } else { + int d = a - b; + a = b; + b = d; + } + b >>= 1; + } while (b != 0); + + return (a << shift); + } + + // greatest common divisor, 64-bit integer + public static long gcd( long a, long b ) + { + int shift = 0; + + if ( a == 0 || b == 0 ) { + return (a | b); + } + + for (shift = 0; ((a | b) & 1) == 0; ++shift) { + a >>= 1; + b >>= 1; + } + + while ((a & 1) == 0) { + a >>= 1; + } + + do { + while ((b & 1) == 0) { + b >>= 1; + } + if (a < b) { + b -= a; + } else { + long d = a - b; + a = b; + b = d; + } + b >>= 1; + } while (b != 0); + + return (a << shift); + } + + // Calculates the combination of the given integer n and m. Un-ordered collection of distinct elements. + // C(n,m) = n! / m!(n - m)! + public static long choose(long n, long m) + { + long num = 1, den = 1, gcd; + + if (m > (n >> 1)) { + m = n - m; + } + + while (m >= 1) + { + num *= n--; + den *= m--; + gcd = gcd(num, den); + num /= gcd; + den /= gcd; + } + + return num; + } + + // Calculates the combination of the given integer n and m. Un-ordered collection of distinct elements. + // C(n,m) = n! / m!(n - m)! + public static int choose(int n, int m) + { + int num = 1, den = 1, gcd; + + if (m > (n >> 1)) { + m = n - m; + } + + while (m >= 1) + { + num *= n--; + den *= m--; + gcd = gcd(num, den); + num /= gcd; + den /= gcd; + } + + return num; + } + + public static float quadraticFormula(float a, float b, float c, float noSolution) + { + float t0 = Float.MIN_VALUE; + float t1 = Float.MIN_VALUE; + + if ( Math.abs( a ) < EPSILON ) + { + if ( Math.abs( b ) < EPSILON ) + { + if ( Math.abs( c ) < EPSILON ) + { + t0 = 0.0f; + t1 = 0.0f; + } + } + else + { + t0 = -c / b; + t1 = -c / b; + } + } + else + { + float disc = b * b - 4 * a * c; + + if ( disc >= 0 ) + { + disc = sqrt( disc ); + a = 2 * a; + t0 = (-b - disc) / a; + t1 = (-b + disc) / a; + } + } + + if ( t0 != Float.MIN_VALUE ) + { + float t = Math.min( t0, t1 ); + + if ( t < 0 ) + { + t = Math.max( t0, t1 ); + } + + if ( t > 0 ) + { + return t; + } + } + + return noSolution; + } + +} diff --git a/src/com/axe/math/Plane3f.java b/src/com/axe/math/Plane3f.java new file mode 100755 index 0000000..103239e --- /dev/null +++ b/src/com/axe/math/Plane3f.java @@ -0,0 +1,167 @@ +package com.axe.math; + +public class Plane3f +{ + public static final int ON_PLANE = 0; + public static final int FRONT = 1; + public static final int BACK = -1; + public static final int INTERSECTS = 2; + + public float a, b, c, d; + + public Plane3f() + { + this(1, 0, 0, 0); + } + + public Plane3f(Vec3f normal, Vec3f position) + { + set(normal, position); + } + + public Plane3f(float a, float b, float c, float d) + { + set(a, b, c, d); + } + + public void set(Vec3f normal, Vec3f position) + { + this.a = normal.x; + this.b = normal.y; + this.c = normal.z; + this.d = -a * position.x - b * position.y - c * position.z; + } + + public void set(float a, float b, float c, float d) + { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + public void norm() + { + float mag = (float)Math.sqrt(a * a + b * b + c * c); + if (mag != 0) { + mag = 1f / mag; + a *= mag; + b *= mag; + c *= mag; + d *= mag; + } + } + + public int sign(Bound3f b) + { + boolean dif = false; + int s = sign(b.l, b.t, b.n); + dif = dif || (sign(b.l, b.t, b.f) != s); + dif = dif || (sign(b.r, b.t, b.n) != s); + dif = dif || (sign(b.r, b.t, b.f) != s); + dif = dif || (sign(b.l, b.b, b.n) != s); + dif = dif || (sign(b.l, b.b, b.f) != s); + dif = dif || (sign(b.r, b.b, b.n) != s); + dif = dif || (sign(b.r, b.b, b.f) != s); + return (dif ? INTERSECTS : s); + } + + public int sign(Vec3f [] vs) + { + boolean dif = false; + int s = sign(vs[0]); + for (int i = 1; i < vs.length; i++) { + dif |= (sign(vs[i]) != s); + } + return (dif ? INTERSECTS : s); + } + + public int sign(float x, float y, float z) + { + return (int)Math.signum(distance(x, y, z)); + } + + public int sign(Line3f l) + { + int s = sign(l.s); + int e = sign(l.e); + return (s == 0 ? e : (e == 0 || s == e ? s : INTERSECTS)); + } + + public int sign(Vec3f v) + { + return (int)Math.signum(distance(v)); + } + + public int sign(Sphere3f s) + { + return (int)Math.signum(distance(s)); + } + + + public float distance(float x, float y, float z) + { + return a * x + b * y + c * z + d; + } + + public float distance(Vec3f v) + { + return a * v.x + b * v.y + c * v.z + d; + } + + public float distance(Vec3i v) + { + return a * v.x + b * v.y + c * v.z + d; + } + + public float distance(Sphere3f s) + { + float d = distance(s.x, s.y, s.z); + d -= (int)Math.signum(d) * s.r; + return d; + } + + public float distance(Line3f x) + { + float ds = distance(x.s); + float de = distance(x.e); + int ss = (int)Math.signum(ds); + int se = (int)Math.signum(de); + if (ss != se) { + return 0; + } + return Math.min(ds * ss, de * se); + } + + + + public Line3f clip(Line3f x, int side) + { + Vec3f dir = x.diff(); + Vec3f normal = new Vec3f(a, b, c); + + float d = normal.dot(dir); + float sd = distance(x.s); + int q = (int)Math.signum(sd); + + if (d == 0.0) { + return (q == side || sign(x.e) == side ? x : null); + } + + float u = -sd / d; + + if (u >= 1.0 || u <= 0.0) { + return (q == side || sign(x.e) == side ? x : null); + } + + if (q == side || q == ON_PLANE) { + x.e.interpolate(x.s, x.e, u); + } + else { + x.s.interpolate(x.s, x.e, u); + } + + return x; + } + +} diff --git a/src/com/axe/math/Poly3f.java b/src/com/axe/math/Poly3f.java new file mode 100755 index 0000000..f526f40 --- /dev/null +++ b/src/com/axe/math/Poly3f.java @@ -0,0 +1,13 @@ +package com.axe.math; + +public class Poly3f +{ + + public Vec3f[] v; + + public Poly3f() { + + } + +} + diff --git a/src/com/axe/math/Quaternion3.java b/src/com/axe/math/Quaternion3.java new file mode 100755 index 0000000..2b88395 --- /dev/null +++ b/src/com/axe/math/Quaternion3.java @@ -0,0 +1,104 @@ +package com.axe.math; + + +public class Quaternion3 +{ + + public float x, y, z, w; + + public Quaternion3() + { + } + + public Quaternion3(float x, float y, float z, float w) + { + set(x, y, z, w); + } + + public Quaternion3(Vec3f v, float angle) + { + set(v, angle); + } + + public void set(float x, float y, float z, float w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public void set(Vec3f v, float angle) + { + float half = angle * 0.5f; + float sin = Numbers.sin(half) / v.length(); + float cos = Numbers.cos(half); + x = v.x * sin; + y = v.y * sin; + z = v.z * sin; + w = cos; + } + + public Quaternion3 setEuler(float yaw, float pitch, float roll) + { + final float hr = roll * 0.5f; + final float shr = Numbers.sin(hr); + final float chr = Numbers.cos(hr); + final float hp = pitch * 0.5f; + final float shp = Numbers.sin(hp); + final float chp = Numbers.cos(hp); + final float hy = yaw * 0.5f; + final float shy = Numbers.sin(hy); + final float chy = Numbers.cos(hy); + final float chy_shp = chy * shp; + final float shy_chp = shy * chp; + final float chy_chp = chy * chp; + final float shy_shp = shy * shp; + + x = (chy_shp * chr) + (shy_chp * shr); // cos(yaw/2) * sin(pitch/2) * cos(roll/2) + sin(yaw/2) * cos(pitch/2) * sin(roll/2) + y = (shy_chp * chr) - (chy_shp * shr); // sin(yaw/2) * cos(pitch/2) * cos(roll/2) - cos(yaw/2) * sin(pitch/2) * sin(roll/2) + z = (chy_chp * shr) - (shy_shp * chr); // cos(yaw/2) * cos(pitch/2) * sin(roll/2) - sin(yaw/2) * sin(pitch/2) * cos(roll/2) + w = (chy_chp * chr) + (shy_shp * shr); // cos(yaw/2) * cos(pitch/2) * cos(roll/2) + sin(yaw/2) * sin(pitch/2) * sin(roll/2) + return this; + } + + public void invert() + { + x = -x; + y = -y; + z = -z; + } + + public void multiply(Quaternion3 a, Quaternion3 b) + { + x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; + y = a.w * b.y + a.y * b.w + a.z * b.x - a.x * b.z; + z = a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x; + w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + } + + public void multiply(float ax, float ay, float az, float aw, float bx, float by, float bz, float bw) + { + x = aw * bx + ax * bw + ay * bz - az * by; + y = aw * by + ay * bw + az * bx - ax * bz; + z = aw * bz + az * bw + ax * by - ay * bx; + w = aw * bw - ax * bx - ay * by - az * bz; + } + + private static final Quaternion3 rot = new Quaternion3(); + + public void rotate(Vec3f v) + { + float m = v.normal(); + rot.multiply(v.x, v.y, v.z, 0f, -x, -y, -z, w); + rot.multiply(x, y, z, w, rot.x, rot.y, rot.z, rot.w); + v.set(rot.x, rot.y, rot.z); + v.scale( m ); + } + + public Quaternion3 copy() + { + return new Quaternion3(x, y, z, w); + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Rangef.java b/src/com/axe/math/Rangef.java new file mode 100755 index 0000000..336dcf2 --- /dev/null +++ b/src/com/axe/math/Rangef.java @@ -0,0 +1,68 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorRangef; + + +public class Rangef implements Attribute +{ + + public float min, max; + + public Rangef() + { + this(0, 0); + } + + public Rangef(Rangef r) + { + this(r.min, r.max); + } + + public Rangef(float x) + { + this(x, x); + } + + public Rangef(float min, float max) + { + this.min = min; + this.max = max; + } + + public void set( float min, float max ) + { + this.min = min; + this.max = max; + } + + public float rnd() + { + return Numbers.randomFloat( min, max ); + } + + @Override + public Rangef get() + { + return this; + } + + @Override + public Rangef clone() + { + return new Rangef( this ); + } + + @Override + public CalculatorRangef getCalculator() + { + return CalculatorRangef.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(min + max * 46340); + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Rangei.java b/src/com/axe/math/Rangei.java new file mode 100755 index 0000000..40c7e78 --- /dev/null +++ b/src/com/axe/math/Rangei.java @@ -0,0 +1,72 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorRangei; + +public class Rangei implements Attribute +{ + + public int min, max; + + public Rangei() + { + this(0, 0); + } + + public Rangei(Rangei r) + { + this(r.min, r.max); + } + + public Rangei(int x) + { + this(x, x); + } + + public Rangei(int min, int max) + { + this.min = min; + this.max = max; + } + + public int rnd() + { + return Numbers.randomInt( min, max ); + } + + @Override + public Rangei get() + { + return this; + } + + public void set( int x ) + { + min = max = x; + } + + public void set( int min, int max ) + { + this.min = min; + this.max = max; + } + + @Override + public Rangei clone() + { + return new Rangei( min, max ); + } + + @Override + public CalculatorRangei getCalculator() + { + return CalculatorRangei.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(min + max * 46340); + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Rect2f.java b/src/com/axe/math/Rect2f.java new file mode 100755 index 0000000..665ce17 --- /dev/null +++ b/src/com/axe/math/Rect2f.java @@ -0,0 +1,83 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorRect2f; + +public class Rect2f implements Attribute +{ + + public float x, y, w, h; + + public Rect2f() + { + } + + public Rect2f(Rect2f r) + { + set(r); + } + + public Rect2f(float x, float y) + { + set(x, y); + } + + public Rect2f(float x, float y, float width, float height) + { + set(x, y, width, height); + } + + public void set(float x, float y) + { + set(x, y, 0, 0); + } + + public void set(float x, float y, float width, float height) + { + this.x = x; + this.y = y; + this.w = width; + this.h = height; + } + + public float area() + { + return w * h; + } + + public boolean isNegative() + { + return w < 0 || h < 0; + } + + @Override + public Rect2f get() + { + return this; + } + + @Override + public Rect2f clone() + { + return new Rect2f( x, y, w, h ); + } + + @Override + public CalculatorRect2f getCalculator() + { + return CalculatorRect2f.INSTANCE; + } + + @Override + public String toString() + { + return "{x="+x+",y="+y+",w="+w+",h="+h+"}"; + } + + @Override + public int hashCode() + { + return (int)(x + y * 215 + w * 46225 + h * 9938375); + } + +} diff --git a/src/com/axe/math/Rect2i.java b/src/com/axe/math/Rect2i.java new file mode 100755 index 0000000..490d732 --- /dev/null +++ b/src/com/axe/math/Rect2i.java @@ -0,0 +1,133 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorRect2i; + +public class Rect2i implements Attribute +{ + + public int x, y, w, h; + + public Rect2i() + { + } + + public Rect2i(Rect2i r) + { + set(r); + } + + public Rect2i(int x, int y) + { + set(x, y); + } + + public Rect2i(Vec2i p, Vec2i size) + { + set( p.x, p.y, size.x, size.y ); + } + + public Rect2i(int x, int y, int width, int height) + { + set(x, y, width, height); + } + + public void set(int x, int y) + { + set(x, y, 0, 0); + } + + public void set(int x, int y, int width, int height) + { + this.x = x; + this.y = y; + this.w = width; + this.h = height; + } + + public boolean contains(int px, int py) + { + return (px > x && py > y && px < x + w && py < y + h); + } + + public boolean isNegative() + { + return w < 0 || h < 0; + } + + public int area() + { + return w * h; + } + + public int left() + { + return x; + } + + public int right() + { + return x + w; + } + + public int top() + { + return y; + } + + public int bottom() + { + return y + h; + } + + public int cx() + { + return x + (w >> 1); + } + + public int cy() + { + return y + (h >> 1); + } + + public int dx(float d) + { + return x + (int)(w * d); + } + + public int dy(float d) + { + return y + (int)(h * d); + } + + @Override + public Rect2i get() + { + return this; + } + + @Override + public Rect2i clone() + { + return new Rect2i( x, y, w, h ); + } + + @Override + public CalculatorRect2i getCalculator() + { + return CalculatorRect2i.INSTANCE; + } + + @Override + public String toString() + { + return "{x="+x+",y="+y+",w="+w+",h="+h+"}"; + } + + @Override + public int hashCode() + { + return (int)(x + y * 215 + w * 46225 + h * 9938375); + } + +} diff --git a/src/com/axe/math/Scalarf.java b/src/com/axe/math/Scalarf.java new file mode 100755 index 0000000..78e6f48 --- /dev/null +++ b/src/com/axe/math/Scalarf.java @@ -0,0 +1,139 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorScalarf; + + +public class Scalarf implements Attribute +{ + + public static final Scalarf ZERO = new Scalarf( 0f ); + public static final Scalarf ONE = new Scalarf( 1f ); + + public float v; + + public Scalarf() + { + } + + public Scalarf(float v) + { + this.v = v; + } + + public Scalarf(double v) + { + this.v = (float)v; + } + + public Scalarf add(float s) + { + v += s; + return this; + } + + public Scalarf sub(float s) + { + v -= s; + return this; + } + + public Scalarf mul(float s) + { + v *= s; + return this; + } + + public Scalarf div(float s) + { + v = Numbers.divide(v, s); + return this; + } + + public Scalarf max(float s) + { + v = (v > s ? s : v); + return this; + } + + public Scalarf min(float s) + { + v = (v < s ? s : v); + return this; + } + + public Scalarf delta(float start, float end, float delta) + { + v = (end - start) * delta + start; + return this; + } + + public Scalarf mod(float s) + { + v = v % s; + return this; + } + + public float cos() + { + return (float)Math.cos(v); + } + + public float sin() + { + return (float)Math.sin(v); + } + + public float degrees() + { + return (float)Math.toDegrees(v); + } + + public float radians() + { + return (float)Math.toRadians(v); + } + + @Override + public Scalarf get() + { + return this; + } + + @Override + public Scalarf clone() + { + return new Scalarf( v ); + } + + @Override + public CalculatorScalarf getCalculator() + { + return CalculatorScalarf.INSTANCE; + } + + @Override + public String toString() + { + return Float.toString( v ); + } + + @Override + public int hashCode() + { + return (int)(v * 46340); + } + + public static Scalarf[] array( float ... values ) + { + Scalarf[] array = new Scalarf[ values.length ]; + + for ( int i = 0; i < values.length; i++ ) + { + array[i] = new Scalarf( values[i] ); + } + + return array; + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Scalari.java b/src/com/axe/math/Scalari.java new file mode 100755 index 0000000..5709b32 --- /dev/null +++ b/src/com/axe/math/Scalari.java @@ -0,0 +1,145 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorScalari; + + +public class Scalari implements Attribute +{ + + public static final Scalari ZERO = new Scalari( 0 ); + public static final Scalari ONE = new Scalari( 1 ); + + public int v; + + public Scalari() + { + } + + public Scalari(int v) + { + this.v = v; + } + + public Scalari set(int v) + { + this.v = v; + return this; + } + + public Scalari add(int s) + { + v += s; + return this; + } + + public Scalari sub(int s) + { + v -= s; + return this; + } + + public Scalari mul(int s) + { + v *= s; + return this; + } + + public Scalari div(int s) + { + v = Numbers.divide(v, s); + return this; + } + + public Scalari max(int s) + { + v = (v > s ? s : v); + return this; + } + + public Scalari min(int s) + { + v = (v < s ? s : v); + return this; + } + + public Scalari clamp(int max, int min) + { + v = (v < min ? min : (v > max ? max : v)); + return this; + } + + public Scalari delta(int start, int end, float delta) + { + v = (int)((end - start) * delta + start); + return this; + } + + public void mod(int s) + { + v -= v % s; + } + + public float cos() + { + return Numbers.COS[v % 360]; + } + + public float sin() + { + return Numbers.SIN[v % 360]; + } + + public float degrees() + { + return v; + } + + public float radians() + { + return Numbers.RADIAN[v % 360]; + } + + @Override + public Scalari get() + { + return this; + } + + @Override + public Scalari clone() + { + return new Scalari( v ); + } + + @Override + public CalculatorScalari getCalculator() + { + return CalculatorScalari.INSTANCE; + } + + @Override + public String toString() + { + return Integer.toString( v ); + } + + @Override + public int hashCode() + { + return (int)(v * 46340); + } + + public static Scalari[] array( int ... values ) + { + Scalari[] array = new Scalari[ values.length ]; + + for ( int i = 0; i < values.length; i++ ) + { + array[i] = new Scalari( values[i] ); + } + + return array; + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Sphere3f.java b/src/com/axe/math/Sphere3f.java new file mode 100755 index 0000000..bcb0689 --- /dev/null +++ b/src/com/axe/math/Sphere3f.java @@ -0,0 +1,11 @@ +package com.axe.math; + +public class Sphere3f +{ + public float x, y, z, r; + + public Vec3f center() + { + return new Vec3f(x, y, z); + } +} diff --git a/src/com/axe/math/Vec.java b/src/com/axe/math/Vec.java new file mode 100644 index 0000000..1e6efd9 --- /dev/null +++ b/src/com/axe/math/Vec.java @@ -0,0 +1,38 @@ +package com.axe.math; + +import com.axe.core.Attribute; + +public interface Vec> extends Attribute +{ + + /** + * Rotates this Vector by the given unit V and returns this. + */ + public V rotate( V cossin ); + + /** + * Sets out to this Vector unit V by the normal and returns out. + */ + public V rotate( V out, V cossin ); + + /** + * Returns a new Vector that is this Vector rotated by the unit V. + */ + public V rotaten( V cossin ); + + /** + * Un-Rotates this Vector by the given unit V and returns this. + */ + public V unrotate( V cossin ); + + /** + * Sets out to this Vector unit V by the normal and returns out. + */ + public V unrotate( V out, V cossin ); + + /** + * Returns a new Vector that is this Vector unrotated by the unit V. + */ + public V unrotaten( V cossin ); + +} diff --git a/src/com/axe/math/Vec2f.java b/src/com/axe/math/Vec2f.java new file mode 100755 index 0000000..0b7b5ce --- /dev/null +++ b/src/com/axe/math/Vec2f.java @@ -0,0 +1,276 @@ +package com.axe.math; + +import java.io.Serializable; + +import com.axe.core.Attribute; +import com.axe.core.Factory; +import com.axe.math.calc.CalculatorVec2f; + +public class Vec2f implements Attribute, Vec, Serializable +{ + + private static final long serialVersionUID = 5839854881676932911L; + + public static Factory FACTORY = new Factory() { + public Vec2f create() { + return new Vec2f(); + } + }; + + public static final Vec2f UP = new Vec2f(0, 1); + public static final Vec2f DOWN = new Vec2f(0, -1); + public static final Vec2f RIGHT = new Vec2f(1, 0); + public static final Vec2f LEFT = new Vec2f(-1, 0); + public static final Vec2f ONE = new Vec2f(1); + public static final Vec2f ZERO = new Vec2f(0); + + public float x, y; + + public Vec2f() + { + } + + public Vec2f(float x, float y) + { + set(x, y); + } + + public Vec2f(float v) + { + x = y = v; + } + + public Vec2f(Vec2f v) + { + set(v); + } + + public Vec2f(Vec2f v, float scale) + { + set( v, scale ); + } + + public void set(Vec2f v, float scale) + { + x = v.x * scale; + y = v.y * scale; + } + + public void set(float x, float y) + { + this.x = x; + this.y = y; + } + + public float normal(float x, float y) + { + set(x, y); + return normal(); + } + + /** Rotates 90 */ + public void tangent() { + float t = x; + x = -y; + y = t; + } + + public void turn(int times) + { + turn(times, this); + } + + public void turn(int times, Vec2f v) + { + float ox = v.x; + float oy = v.y; + switch(times & 3) { + case 1: + x = -oy; + y = +ox; + break; + case 2: + x = -ox; + y = -oy; + break; + case 3: + x = +oy; + y = -ox; + break; + } + } + + public void angle(float radians, float magnitude) + { + x = (float)Math.cos(radians) * magnitude; + y = (float)Math.sin(radians) * magnitude; + } + + public float angle() + { + float a = (float)StrictMath.atan2( y, x ); + + if ( a < 0 ) + { + a = Numbers.PI2 + a; + } + return a; + } + + public float angleTo( Vec2f to ) + { + float a = (float)StrictMath.atan2( to.y - y, to.x - x ); + + if ( a < 0 ) + { + a = Numbers.PI2 + a; + } + return a; + } + + public void add(float vx, float vy) + { + x += vx; + y += vy; + } + + public void rotate( float cos, float sin ) + { + float ox = x; + float oy = y; + x = ox * cos - oy * sin; + y = ox * sin + oy * cos; + } + + public void rotate( float angle ) + { + rotate( (float)StrictMath.cos(angle), -(float)StrictMath.sin(angle) ); + } + + // point is in front of this vector (1=front, 0=back) + // public float dot(Vec2f v) + + public float dot(float vx, float vy) + { + return (x * vx) + (y * vy); + } + + // point is on a side of this vector (0=on, 1=left, -1=right) + public float cross(Vec2f v) + { + return (y * v.x) - (x * v.y); + } + + public float cross(float vx, float vy) + { + return (y * vx) - (x * vy); + } + + public void clamp( float minX, float maxX, float minY, float maxY ) + { + if ( x < minX ) x = minX; + if ( x > maxX ) x = maxX; + if ( y < minY ) y = minY; + if ( y > maxY ) y = maxY; + } + + public void average(Vec2f[] v) + { + if (v.length > 0) + { + x = y = 0f; + + for (int i = 0; i < v.length; i++) { + add(v[i]); + } + + divs( v.length ); + } + } + + public boolean isEqual(float vx, float vy) + { + return (x == vx && y == vy); + } + + @Override + public Vec2f get() + { + return this; + } + + @Override + public Vec2f clone() + { + return new Vec2f(x, y); + } + + @Override + public String toString() + { + return String.format("{%.2f, %.2f}", x, y); + } + + @Override + public final CalculatorVec2f getCalculator() + { + return CalculatorVec2f.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(x + y * 46340); + } + + public static Vec2f inter(Vec2f s, Vec2f e, float delta) + { + return new Vec2f( + (e.x - s.x) * delta + s.x, + (e.y - s.y) * delta + s.y); + } + + public Vec2f rotate(Vec2f n) + { + rotate(n.x, n.y); + return this; + } + + public void rotater(Vec2f n) + { + rotate(-n.x, -n.y); + } + + // TODO below + + @Override + public Vec2f rotate(Vec2f out, Vec2f cossin) + { + return null; + } + + @Override + public Vec2f rotaten(Vec2f cossin) + { + return null; + } + + @Override + public Vec2f unrotate(Vec2f cossin) + { + return null; + } + + @Override + public Vec2f unrotate(Vec2f out, Vec2f cossin) + { + return null; + } + + @Override + public Vec2f unrotaten(Vec2f cossin) + { + return null; + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Vec2i.java b/src/com/axe/math/Vec2i.java new file mode 100755 index 0000000..108c33f --- /dev/null +++ b/src/com/axe/math/Vec2i.java @@ -0,0 +1,198 @@ +package com.axe.math; + +import com.axe.core.Attribute; +import com.axe.core.Factory; +import com.axe.math.calc.CalculatorVec2i; + +public class Vec2i implements Attribute +{ + + public static final Factory FACTORY = new Factory() { + public Vec2i create() { + return new Vec2i(); + } + }; + + public static final Vec2i UP = new Vec2i(0, 1); + public static final Vec2i DOWN = new Vec2i(0, -1); + public static final Vec2i RIGHT = new Vec2i(1, 0); + public static final Vec2i LEFT = new Vec2i(-1, 0); + public static final Vec2i ONE = new Vec2i(1); + public static final Vec2i ZERO = new Vec2i(0); + + public int x, y; + + public Vec2i() + { + } + + public Vec2i(int x, int y) + { + set(x, y); + } + + public Vec2i(int v) + { + x = y = v; + } + + public Vec2i(Vec2i v) + { + set(v); + } + + public Vec2i(Vec2i v, float scale) + { + set( v, scale ); + } + + public void set(Vec2i v, float scale) + { + x = (int)(v.x * scale); + y = (int)(v.y * scale); + } + + public void set( int v ) + { + x = y = v; + } + + public void set(int x, int y) + { + this.x = x; + this.y = y; + } + + public float normal(int x, int y) + { + set(x, y); + return normal(); + } + + public void tangent() + { + int t = x; + x = y; + y = t; + } + + public void angle(float radians, float magnitude) + { + x = (int)(Math.cos( radians ) * magnitude); + y = (int)(Math.sin( radians ) * magnitude); + } + + public float angle() + { + float a = (float)StrictMath.atan2( y, x ); + + if ( a < 0) + { + a = Numbers.PI2 + a; + } + + return a; + } + + public float angleTo(Vec2i to) + { + float a = (float)StrictMath.atan2( to.y - y, to.x - x ); + + if ( a < 0 ) + { + a = Numbers.PI2 + a; + } + + return a; + } + + public void rotate( float cos, float sin ) + { + int ox = x; + int oy = y; + x = (int)(cos * ox + sin * oy); + y = (int)(cos * oy - sin * ox); + } + + public void rotate( float angle ) + { + rotate( (float)StrictMath.cos(angle), -(float)StrictMath.sin(angle) ); + } + + public int cross(Vec2i v) + { + return (y * v.x) - (x * v.y); + } + + public int cross(int vx, int vy) + { + return (y * vx) - (x * vy); + } + + public void clamp( int minX, int maxX, int minY, int maxY ) + { + if ( x < minX ) x = minX; + if ( x > maxX ) x = maxX; + if ( y < minY ) y = minY; + if ( y > maxY ) y = maxY; + } + + public void average(Vec2i[] v) + { + if (v.length > 0) { + x = y = 0; + for (int i = 0; i < v.length; i++) { + add(v[i]); + } + divs(v.length); + } + } + + @Override + public Vec2i get() + { + return this; + } + + @Override + public Vec2i clone() + { + return new Vec2i( x, y ); + } + + @Override + public CalculatorVec2i getCalculator() + { + return CalculatorVec2i.INSTANCE; + } + + @Override + public String toString() + { + return String.format("{%d, %d}", x, y); + } + + @Override + public boolean equals(Object o) + { + if (o instanceof Vec2i) + { + return equals((Vec2i)o); + } + return false; + } + + @Override + public int hashCode() + { + return (int)(x + y * 46340); + } + + public static Vec2i inter(Vec2i s, Vec2i e, float delta) + { + return new Vec2i( + (int)((e.x - s.x) * delta) + s.x, + (int)((e.y - s.y) * delta) + s.y); + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Vec3f.java b/src/com/axe/math/Vec3f.java new file mode 100755 index 0000000..632ee2c --- /dev/null +++ b/src/com/axe/math/Vec3f.java @@ -0,0 +1,433 @@ +package com.axe.math; + +import java.io.Serializable; + +import com.axe.core.Attribute; +import com.axe.core.Factory; +import com.axe.math.calc.CalculatorVec3f; + +public class Vec3f implements Attribute, Vec, Serializable +{ + + private static final long serialVersionUID = 2108149669637913963L; + + public static final Factory FACTORY = new Factory() { + public Vec3f create() { + return new Vec3f(); + } + }; + + public static final Vec3f RIGHT = new Vec3f(1, 0, 0); + public static final Vec3f LEFT = new Vec3f(-1, 0, 0); + public static final Vec3f UP = new Vec3f(0, 1, 0); + public static final Vec3f DOWN = new Vec3f(0, -1, 0); + public static final Vec3f NEAR = new Vec3f(0, 0, -1); + public static final Vec3f FAR = new Vec3f(0, 0, 1); + public static final Vec3f ONE = new Vec3f(1, 1, 1); + public static final Vec3f ZERO = new Vec3f(); + + public float x, y, z; + + public Vec3f() + { + } + + public Vec3f(Vec3f v) + { + set(v); + } + + public Vec3f(float v) + { + x = y = z = v; + } + + public Vec3f(float x, float y) + { + set(x, y, 0); + } + + public Vec3f(float x, float y, float z) + { + set(x, y, z); + } + + public Vec3f(Vec3f v, float dx, float dy, float dz) + { + set(v.x + dx, v.y + dy, v.z + dz); + } + + public void set(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public void cross(Vec3f a, Vec3f b) + { + x = a.y * b.z - b.y * a.z; + y = a.z * b.x - b.z * a.x; + z = a.x * b.y - b.x * a.y; + } + + public float normal(Vec3f origin, Vec3f target) + { + set(target); + sub(origin); + return normal(); + } + + public float normal(float x, float y, float z) + { + set(x, y, z); + return normal(); + } + + public float normal(Vec3f a, Vec3f b, Vec3f c) + { + x = a.y * (b.z - c.z) + b.y * (c.z - a.z) + c.y * (a.z - b.z); + y = a.z * (b.x - c.x) + b.z * (c.x - a.x) + c.z * (a.x - b.x); + z = a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y); + + float sq = ( x * x + y * y + z * z ); + + if (sq != 0 && sq != 1) + { + sq = Numbers.sqrt( sq ); + float length = 1f / sq; + x *= length; + y *= length; + z *= length; + } + + return sq; + } + + public void cross(Vec3f r, Vec3f o, Vec3f l) + { + float x0 = r.x-o.x, y0 = r.y-o.y, z0 = r.z-o.z; + float x1 = l.x-o.x, y1 = l.y-o.y, z1 = l.z-o.z; + float d0 = (float)(1.0 / Math.sqrt((x0*x0)+(y0*y0)+(z0*z0))); + float d1 = (float)(1.0 / Math.sqrt((x1*x1)+(y1*y1)+(z1*z1))); + + x0 *= d0; y0 *= d0; z0 *= d0; + x1 *= d1; y1 *= d1; z0 *= d1; + x = y0 * z1 - y1 * z0; + y = z0 * x1 - z1 * x0; + z = x0 * y1 - x1 * y0; + } + + public void rotateY(float angle) + { + rotateY( Numbers.cos( angle ), Numbers.sin( angle ) ); + } + + public void rotateY( float cos, float sin ) + { + float ox = x; + float oz = z; + x = (cos * ox + sin * oz); + z = (cos * oz - sin * ox); + } + + public void rotateZ(float angle) + { + rotateZ( Numbers.cos( angle ), Numbers.sin( angle ) ); + } + + public void rotateZ( float cos, float sin ) + { + float ox = x; + float oy = y; + x = (cos * ox + sin * oy); + y = (cos * oy - sin * ox); + } + + public void rotateX(float angle) + { + rotateX( Numbers.cos( angle ), Numbers.sin( angle ) ); + } + + public void rotateX( float cos, float sin ) { + float oy = y; + float oz = z; + y = (cos * oy + sin * oz); + z = (cos * oz - sin * oy); + } + + public static float projectScalar(Vec3f ray, Vec3f onto) + { + float bdot = onto.lengthSq(); + if (bdot != 0) { + bdot = ray.dot( onto ) / Numbers.sqrt( bdot ); + } + return bdot; + } + + public static float projectUnitScalar(Vec3f ray, Vec3f ontoUnit) + { + return ray.dot( ontoUnit ); + } + + public float project(Vec3f ray, Vec3f onto) + { + float bdot = onto.lengthSq(); + if (bdot != 0) { + set( onto ); + scale( ray.dot( onto ) / bdot ); + } + return bdot; + } + + public float reject(Vec3f ray, Vec3f onto) + { + float bdot = onto.lengthSq(); + if (bdot != 0) { + set( ray ); + adds( onto, -ray.dot( onto ) / bdot ); + } + return bdot; + } + + public void clampOnUnitAxis(float min, float max, Vec3f axis) + { + float ps = projectUnitScalar( this, axis ); + + if (ps == 0) { + return; + } + + if (ps < min) { +// float d = length(); + scale( min / ps ); +// System.out.println( "min: " + min + " previous: " + d + " projected: " + ps + " resulting: " + length() ); + } else if (ps > max) { +// float d = length(); + scale( max / ps ); +// System.out.println( "max: " + max + " previous: " + d + " projected: " + ps + " resulting: " + length() ); + } + } + + public void clampOnAxis(float min, float max, Vec3f axis) + { + float ps = projectScalar( this, axis ); + + if (ps == 0) { + return; + } + + if (ps < min) { + scale( min / ps ); +// System.out.println( "min: " + min + " resulting: " + length() ); + } else if (ps > max) { + scale( max / ps ); +// System.out.println( "max: " + max + " resulting: " + length() ); + } + } + + public void add(float vx, float vy, float vz) { + x += vx; y += vy; z += vz; + } + + public void average(Vec3f[] v) + { + if (v.length > 0) + { + x = y = z = 0f; + for (int i = 0; i < v.length; i++) { + add(v[i]); + } + divs(v.length); + } + } + + @Override + public Vec3f get() + { + return this; + } + + public float yaw() + { + return (float)Math.atan2( x, z ); + } + + public float pitch() + { + return (float)Math.atan2( y, (float)Math.sqrt(x * x + z * z) ); + } + + public float angle(Vec3f v) + { + float a = length(); + float b = v.length(); + + if ( a == 0 || b == 0 ) { + return 0; + } + + return (float)Math.acos( dot( v ) / (a * b) ); + } + + public float angleNormalized(Vec3f v) + { + return (float)Math.acos( dot(v) ); + } + + public void move( float t, Vec3f velocity ) + { + adds( velocity, t ); + } + + public Vec3i floor( Vec3i out ) + { + out.x = (int)Math.floor( x ); + out.y = (int)Math.floor( y ); + out.z = (int)Math.floor( z ); + return out; + } + + @Override + public String toString() + { + return String.format("{%.2f, %.2f, %.2f}", x, y, z); + } + + @Override + public Vec3f clone() + { + return new Vec3f(x, y, z); + } + + @Override + public CalculatorVec3f getCalculator() + { + return CalculatorVec3f.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(x + y * 1290 + z * 1664100); + } + + @Override + public Vec3f rotate(Vec3f cossin) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Vec3f rotate(Vec3f out, Vec3f cossin) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Vec3f rotaten(Vec3f cossin) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Vec3f unrotate(Vec3f cossin) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Vec3f unrotate(Vec3f out, Vec3f cossin) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Vec3f unrotaten(Vec3f cossin) { + // TODO Auto-generated method stub + return null; + } + + public static Vec3f mul(Vec3f v, float s) + { + return new Vec3f(v.x * s, v.y * s, v.z * s); + } + + public static Vec3f inter(Vec3f s, Vec3f e, float delta) + { + return new Vec3f((e.x - s.x) * delta + s.x, + (e.y - s.y) * delta + s.y, (e.z - s.z) * delta + s.z); + } + + public static Vec3f newAdd(Vec3f a, Vec3f b) + { + return new Vec3f(a.x + b.x, a.y + b.y, a.z + b.z); + } + + public static Vec3f newNeg(Vec3f v) + { + return new Vec3f(-v.x, -v.y, -v.z); + } + + public static Vec3f newScaled(Vec3f a, float scale) + { + return new Vec3f(a.x * scale, a.y * scale, a.z * scale); + } + + public static Vec3f newCross(Vec3f a, Vec3f b) { + Vec3f x = new Vec3f(); + x.cross( a, b ); + return x; + } + + public static Vec3f newUnitCross(Vec3f a, Vec3f b) + { + Vec3f x = new Vec3f(); + x.cross( a, b ); + x.normal(); + return x; + } + + public static Vec3f newNormal(Vec3f origin, Vec3f point) + { + Vec3f x = new Vec3f(); + x.set( point ); + x.sub( origin ); + x.normal(); + return x; + } + + public static Vec3f newNormal(Vec3f v) + { + Vec3f x = new Vec3f(); + x.normal( v ); + return x; + } + + public static void setComplementBasis(Vec3f a, Vec3f b, Vec3f n) + { + if (Math.abs(n.x) >= Math.abs(n.y)) + { + // n.x or n.z is the largest magnitude component, swap them + float invLength = 1.0f / (float)Math.sqrt(n.x * n.x + n.z * n.z); + a.x = -n.z * invLength; + a.y = 0.0f; + a.z = +n.x * invLength; + b.x = n.y * a.z; + b.y = n.z * a.x - n.x * a.z; + b.z = -n.y * a.x; + } + else + { + // n.y or n.z is the largest magnitude component, swap them + float invLength = 1.0f / (float)Math.sqrt(n.y * n.y + n.z * n.z); + a.x = 0.0f; + a.y = +n.z * invLength; + a.z = -n.y * invLength; + b.x = n.y * a.z - n.z * a.y; + b.y = -n.x * a.z; + b.z = n.x * a.y; + } + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Vec3i.java b/src/com/axe/math/Vec3i.java new file mode 100755 index 0000000..e855a5b --- /dev/null +++ b/src/com/axe/math/Vec3i.java @@ -0,0 +1,169 @@ +package com.axe.math; + +import java.io.Serializable; + +import com.axe.core.Attribute; +import com.axe.math.calc.CalculatorVec3i; + +public class Vec3i implements Attribute, Serializable +{ + + private static final long serialVersionUID = -6351119441833706561L; + + public static final Vec3i RIGHT = new Vec3i(1, 0, 0); + public static final Vec3i LEFT = new Vec3i(-1, 0, 0); + public static final Vec3i UP = new Vec3i(0, 1, 0); + public static final Vec3i DOWN = new Vec3i(0, -1, 0); + public static final Vec3i NEAR = new Vec3i(0, 0, 1); + public static final Vec3i FAR = new Vec3i(0, 0, -1); + public static final Vec3i ZERO = new Vec3i(0, 0, 0); + public static final Vec3i ONE = new Vec3i(1, 1, 1); + + public int x, y, z; + + public Vec3i() + { + } + + public Vec3i(Vec3i v) + { + set(v); + } + + public Vec3i(int v) + { + x = y = z = v; + } + + public Vec3i(int x, int y, int z) + { + set(x, y, z); + } + + public Vec3i( Vec3f v) + { + set( (int)v.x, (int)v.y, (int)v.z ); + } + + public void set(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public void cross(Vec3i a, Vec3i b) + { + x = a.y * b.z - b.y * a.z; + y = a.z * b.x - b.z * a.x; + z = a.x * b.y - b.x * a.y; + } + + public float norm(int x, int y, int z) + { + set(x, y, z); + return normal(); + } + + public void cross(Vec3i r, Vec3i o, Vec3i l) + { + float x0 = r.x-o.x, y0 = r.y-o.y, z0 = r.z-o.z; + float x1 = l.x-o.x, y1 = l.y-o.y, z1 = l.z-o.z; + float d0 = (float)(1.0 / Math.sqrt((x0*x0)+(y0*y0)+(z0*z0))); + float d1 = (float)(1.0 / Math.sqrt((x1*x1)+(y1*y1)+(z1*z1))); + x0 *= d0; y0 *= d0; z0 *= d0; + x1 *= d1; y1 *= d1; z0 *= d1; + x = (int)(y0 * z1 - y1 * z0); + y = (int)(z0 * x1 - z1 * x0); + z = (int)(x0 * y1 - x1 * y0); + } + + public Vec3i reflect(Vec3i v, Vec3i n) + { + set(v); adds(n, -2f * v.dot(n)); + return this; + } + + public Vec3i refract(Vec3i v, Vec3i n) + { + reflect(v, n); neg(); + return this; + } + public void div(float s) { + if (s != 0.0) { + s = 1f / s; + x *= s; + y *= s; + z *= s; + } + } + public void mul(float s) { + x *= s; + y *= s; + z *= s; + } + public void add(int vx, int vy, int vz) { + x += vx; + y += vy; + z += vz; + } + public void add(Vec3i v, float scale) { + x += v.x * scale; + y += v.y * scale; + z += v.z * scale; + } + public void average(Vec3i[] v) { + if (v.length > 0) { + x = y = z = 0; + for (int i = 0; i < v.length; i++) { + add(v[i]); + } + div(v.length); + } + } + + @Override + public Vec3i get() + { + return this; + } + + @Override + public int hashCode() + { + return (int)(x + y * 1290 + z * 1664100); + } + + public boolean equals(Object o) { + if (o instanceof Vec3i) { + return isEqual((Vec3i)o); + } + return false; + } + + @Override + public String toString() + { + return String.format("{%d, %d, %d}", x, y, z); + } + + @Override + public Vec3i clone() + { + return new Vec3i( x, y, z ); + } + + @Override + public CalculatorVec3i getCalculator() + { + return CalculatorVec3i.INSTANCE; + } + + public static Vec3i inter(Vec3i s, Vec3i e, float delta) + { + return new Vec3i((int)((e.x - s.x) * delta) + s.x, + (int)((e.y - s.y) * delta) + s.y, + (int)((e.z - s.z) * delta) + s.z); + } + +} \ No newline at end of file diff --git a/src/com/axe/math/Vec4f.java b/src/com/axe/math/Vec4f.java new file mode 100755 index 0000000..88657f5 --- /dev/null +++ b/src/com/axe/math/Vec4f.java @@ -0,0 +1,44 @@ +package com.axe.math; + +import java.io.Serializable; + +import com.axe.core.Factory; + +public class Vec4f implements Serializable +{ + private static final long serialVersionUID = 6065062711339366453L; + + public float x, y, z, w; + + public Vec4f() { + } + public Vec4f(float v) { + set(v, v, v, v); + } + public Vec4f(float x, float y) { + set(x, y, 0, 0); + } + public Vec4f(float x, float y, float z) { + set(x, y, z, 0); + } + public Vec4f(float x, float y, float z, float w) { + set(x, y, z, w); + } + + public void set(Vec4f v) { + set( v.x, v.y, v.z, v.w ); + } + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public static Factory FACTORY = new Factory() { + public Vec4f create(){ + return new Vec4f(); + } + }; + +} \ No newline at end of file diff --git a/src/com/axe/math/calc/AbstractCalculator.java b/src/com/axe/math/calc/AbstractCalculator.java new file mode 100644 index 0000000..44a478a --- /dev/null +++ b/src/com/axe/math/calc/AbstractCalculator.java @@ -0,0 +1,188 @@ +package com.axe.math.calc; + +import com.axe.math.Numbers; + +public abstract class AbstractCalculator implements Calculator +{ + + protected T zero; + protected T one; + + protected T temp0; + protected T temp1; + + protected T[] pool; + protected int poolSize; + + protected AbstractCalculator() + { + this.zero = this.createUniform( 0 ); + this.one = this.createUniform( 1 ); + this.temp0 = this.createUniform( 0 ); + this.temp1 = this.createUniform( 0 ); + } + + protected abstract T instantiate(); + + @Override + public T ZERO() + { + return zero; + } + + @Override + public T ONE() + { + return one; + } + + @Override + public T create() + { + return poolSize > 0 ? clear( pool[ --poolSize ], 0 ) : instantiate(); + } + + @Override + public void setPool(T[] pool) + { + this.pool = pool; + } + + @Override + public boolean recycle(T unused) + { + boolean recycled = poolSize < pool.length; + + if (recycled) + { + pool[ poolSize++ ] = unused; + } + + return recycled; + } + + @Override + public float distanceSq( T a, T b ) + { + T diff = this.sub( temp0, a, b ); + + return this.dot( diff, diff ); + } + + @Override + public float delta( T start, T end, T point ) + { + T p0 = this.sub( temp0, end, start ); + T p1 = this.sub( temp1, start, point ); + + float dot = this.dot( p0, p1 ); + float dsq = this.lengthSq( p0 ); + float delta = dot / dsq; + return delta; + } + + @Override + public float interceptionTime( T shooter, float speed, T targetPosition, T targetVelocity ) + { + T direct = this.sub( temp0, targetPosition, shooter ); + + float a = this.lengthSq( targetVelocity ) - ( speed * speed ); + float b = 2.0f * this.dot( targetVelocity, direct ); + float c = this.lengthSq( direct ); + + return Numbers.quadraticFormula( a, b, c, -1 ); + } + + @Override + public float distanceFrom( T start, T end, T point, boolean line ) + { + float delta = this.delta( start, end, point ); + + if (!line) + { + delta = Numbers.clamp( delta, 0, 1 ); + } + + T projected = this.interpolate( temp0, start, end, delta ); + + return this.distance( point, projected ); + } + + public boolean inView( T origin, T direction, float fovCos, T point ) + { + T diff = this.sub( temp0, origin, point ); + + return this.dot( diff, direction ) > fovCos; + } + + public boolean inCircleView( T origin, T direction, float fovTan, float fovCos, T center, float radius, boolean entirely ) + { + T circleToOrigin = this.sub( temp0, center, origin ); + + float distanceAlongDirection = this.dot( circleToOrigin, direction ); + float coneRadius = distanceAlongDirection * fovTan; + float distanceFromAxis = Numbers.sqrt( this.lengthSq( circleToOrigin ) - distanceAlongDirection * distanceAlongDirection ); + float distanceFromCenterToCone = distanceFromAxis - coneRadius; + float shortestDistance = distanceFromCenterToCone * fovCos; + + if (entirely) + { + shortestDistance += radius; + } + else + { + shortestDistance -= radius; + } + + return shortestDistance <= 0; + } + + public T cubicCurve( T out, float delta, T p0, T p1, T p2, T p3, float[][] matrix, boolean inverse ) + { + float d0 = 1.0f; + float d1 = delta; + float d2 = d1 * d1; + float d3 = d2 * d1; + + float[] ts = { + inverse ? d3 : d0, + inverse ? d2 : d1, + inverse ? d1 : d2, + inverse ? d1 : d3 + }; + + T temp = temp0; + out = this.clear( out, 0 ); + + for (int i = 0; i < 4; i++) + { + temp = this.clear( temp, 0 ); + temp = this.addsi( temp, p0, matrix[ i ][ 0 ] ); + temp = this.addsi( temp, p1, matrix[ i ][ 1 ] ); + temp = this.addsi( temp, p2, matrix[ i ][ 2 ] ); + temp = this.addsi( temp, p3, matrix[ i ][ 3 ] ); + out = this.addsi( out, temp, ts[ i ] ); + } + + return out; + } + + public T parametricCubicCurve( T out, float delta, T[] points, float[][] matrix, float weight, boolean inverse, boolean loop ) + { + int n = points.length - 1; + float a = delta * n; + int i = (int)Numbers.clamp( Numbers.floor( a ), 0, n - 1 ); + float d = a - i; + + T p0 = (i == 0 ? (loop ? points[n] : points[0]) : points[i - 1]); + T p1 = points[i]; + T p2 = points[i + 1]; + T p3 = (i == n - 1 ? (loop ? points[0] : points[n]) : points[i + 2]); + + out = this.cubicCurve( out, d, p0, p1, p2, p3, matrix, inverse ); + out = this.scalei( out, weight ); + + return out; + } + +} diff --git a/src/com/axe/math/calc/Binary.java b/src/com/axe/math/calc/Binary.java new file mode 100644 index 0000000..089580e --- /dev/null +++ b/src/com/axe/math/calc/Binary.java @@ -0,0 +1,6 @@ +package com.axe.math.calc; + +public interface Binary +{ + public float binary(float a, float b); +} diff --git a/src/com/axe/math/calc/Calculator.java b/src/com/axe/math/calc/Calculator.java new file mode 100644 index 0000000..f6f1b0a --- /dev/null +++ b/src/com/axe/math/calc/Calculator.java @@ -0,0 +1,695 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +public interface Calculator +{ + + public T ZERO(); + + public T ONE(); + + public T create(); + + default public T[] createArray(int count, T ... initial) + { + T[] out = Arrays.copyOf( initial, count ); + + for (int i = 0; i < count; i++) + { + out[ i ] = create(); + } + + return out; + } + + default public T createUniform(float component) + { + return clear( create(), component ); + } + + public void setPool(T[] pool); + + public boolean recycle(T unused); + + default public T clone(T source) + { + return copy( create(), source ); + } + + // out.x = source.x + public T copy( T out, T source ); + + public T parse( Object input, T defaultValue ); + + default public T[] parseArray( T[] input, T[] output, T defaultValue ) + { + if (output == null) + { + output = Arrays.copyOf( input, input.length ); + } + + for (int i = 0; i < input.length; i++) + { + output[ i ] = parse( input[ i ], defaultValue ); + } + + return output; + } + + // out.x = component + public T clear( T out, float component ); + + // out.x = op( a.x ) + public T unary( T out, T value, Unary unaryOoperation ); + + default public T unaryi( T out, Unary unaryOoperation ) + { + return unary( out, out, unaryOoperation ); + } + + // return op( a.x ) + default public T unaryn( T value, Unary unaryOperation ) + { + return unary( create(), value, unaryOperation ); + } + + // out.x = op( a.x, b.x ) + public T binary( T out, T a, T b, Binary binaryOperation ); + + default public T binaryi( T out, T b, Binary binaryOperation ) + { + return binary( out, out, b, binaryOperation ); + } + + // return op( a.x, b.x ) + default public T binaryn( T a, T b, Binary binaryOperation ) + { + return binary( create(), a, b, binaryOperation ); + } + + // out.x = augend.x + addend.x * scale; + public T adds( T out, T augend, T addend, float scale ); + + // return augend.x + addend.x * scale; + default public T addsn( T augend, T addend, float scale ) + { + return adds( create(), augend, addend, scale ); + } + + // out.x = out.x + addend.x * scale; + default public T addsi( T out, T addend, float scale ) + { + return adds( out, out, addend, scale ); + } + + // out.x = value.x * scale + default public T scale( T out, T value, float scale ) + { + return adds( out, ZERO(), value, scale ); + } + + // return value.x * scale + default public T scalen( T value, float scale ) + { + return adds( create(), ZERO(), value, scale ); + } + + // out.x *= scale + default public T scalei( T out, float scale ) + { + return adds( out, ZERO(), out, scale ); + } + + // out.x = value.x / divisor + default public T divs( T out, T value, float divisor ) + { + return divisor == 0 ? clear( out, 0 ) : scale( out, value, 1.0f / divisor ); + } + + // return value.x / divisor + default public T divsn( T value, float divisor ) + { + return divs( create(), value, divisor ); + } + + // out.x /= divisor + default public T divsi( T out, float divisor ) + { + return divs( out, out, divisor ); + } + + // out.x = augend.x + addend.x; + default public T add( T out, T augend, T addend ) + { + return adds( out, augend, addend, 1 ); + } + + // return augend.x + addend.x; + default public T addn( T augend, T addend ) + { + return this.adds( this.create(), augend, addend, 1 ); + } + + // out.x += amount.x; + default public T addi( T out, T amount ) + { + return this.adds( out, out, amount, 1 ); + } + + // out.x = minuend.x - subtrahend.x; + default public T sub( T out, T minuend, T subtrahend ) + { + return this.adds( out, minuend, subtrahend, -1 ); + } + + // return minuend.x - subtrahend.x; + default public T subn( T minuend, T subtrahend ) + { + return this.adds( this.create(), minuend, subtrahend, -1 ); + } + + // out.x -= subtrahend.x + default public T subi( T out, T subtrahend ) + { + return this.adds( out, out, subtrahend, -1 ); + } + + // out.x = value.x * scale.x + public T mul( T out, T value, T scale ); + + // return value.x * scale.x + default public T muln( T value, T scale ) + { + return this.mul( this.create(), value, scale ); + } + + // out.x *= scale.x + default public T muli( T out, T scale ) + { + return this.mul( out, out, scale ); + } + + // out.x = Numbers.divide( dividend.x, divisor.x ) + public T div( T out, T dividend, T divisor ); + + // return Numbers.divide( out.x, denominator.x ) + default public T divn( T dividend, T divisor ) + { + return this.div( this.create(), dividend, divisor ); + } + + // out.x = Numbers.divide( out.x, denominator.x ) + default public T divi( T out, T divisor ) + { + return this.div( out, out, divisor ); + } + + // out.x = (end.x - start.x) * delta + start.x + default public T interpolate( T out, T start, T end, float delta ) + { + out = this.adds( out, this.ZERO(), start, 1 - delta ); + out = this.adds( out, out, end, delta ); + return out; + } + + // out.x = (end.x - start.x) * delta + start.x + default public T interpolaten( T start, T end, float delta ) + { + return this.interpolate( this.create(), start, end, delta ); + } + + default public T reflect( T out, T vector, T normal ) + { + float scole = -2 * dot( vector, normal ); + return adds( out, vector, normal, scole ); + } + + default public T reflectn( T vector, T normal ) + { + return reflect( create(), vector, normal ); + } + + default public T reflecti( T out, T normal ) + { + return reflect( out, out, normal ); + } + + default public T refract( T out, T vector, T normal ) + { + return this.negi( this.reflect( out, vector, normal ) ); + } + + default public T refractn( T vector, T normal ) + { + return refract( create(), vector, normal ); + } + + default public T refracti( T out, T normal ) + { + return refract( out, out, normal ); + } + + default public boolean isParallel( T a, T b, float epsilon ) + { + return Math.abs( length( a ) * length( b ) - dot( a, b ) ) < epsilon; + } + + default public boolean isParallel( T a, T b ) + { + return isParallel( a, b, Numbers.EPSILON ); + } + + default public boolean isPerpendicular( T a, T b, float epsilon ) + { + return Math.abs( dot( a, b ) ) < epsilon; + } + + default public boolean isPerpendicular( T a, T b ) + { + return isPerpendicular( a, b, Numbers.EPSILON ); + } + + // out.x = OpenMath.clamp( out.x, min.x, max.x ) + public T contain( T out, T min, T max ); + + // return point >= min && point <= max + public boolean contains( T point, T min, T max ); + + // out.x = randomizer( min.x, max.x ) + public T random( T out, T min, T max, Binary randomizer ); + + // return a.x * b.x + a.y * b.y + ... + public float dot( T a, T b ); + + default public float distance( T a, T b ) + { + return Numbers.sqrt( this.distanceSq( a, b ) ); + } + + public float distanceSq( T a, T b ); + + default public float length( T value ) + { + return Numbers.sqrt( this.lengthSq( value ) ); + } + + default public float lengthSq( T value ) + { + return this.dot( value, value ); + } + + default public float normali( T out ) + { + return this.normal( out, out ); + } + + default public float normal( T out, T vector ) + { + float lsq = this.lengthSq( vector ); + + out = this.copy( out, vector ); + + if (lsq != 1.0 && lsq != 0.0) + { + lsq = Numbers.sqrt( lsq ); + + this.scalei( out, 1 / lsq ); + } + + return lsq; + } + + default public float normaln( T vector ) + { + return this.normal( this.create(), vector ); + } + + default public T negi( T out ) + { + return this.neg( out, out ); + } + + default public T negn( T value ) + { + return this.neg( this.create(), value ); + } + + default public T neg( T out, T value ) + { + return this.scale( out, value, -1 ); + } + + default public T absi( T out ) + { + return this.abs( out, out ); + } + + default public T absn( T value ) + { + return this.abs( this.create(), value ); + } + + default public T abs( T out, T value ) + { + return this.unary( out, value, Math::abs ); + } + + default public T modsi( T out, float divisor ) + { + return this.mods( out, out, divisor ); + } + + default public T modsn( T value, float divisor ) + { + return this.mods( this.create(), value, divisor ); + } + + public T mods( T out, T value, float divisor ); + + + default public T modi( T out, T divisor ) + { + return this.mod( out, out, divisor ); + } + + default public T modn( T value, T divisor ) + { + return this.mod( this.create(), value, divisor ); + } + + public T mod( T out, T value, T divisor ); + + default public T truncatesi( T out, float divisor ) + { + return truncates( out, out, divisor ); + } + + default public T truncatesn( T value, float divisor ) + { + return truncates( create(), value, divisor ); + } + + public T truncates( T out, T value, float divisor ); + + default public T truncatei( T out, T divisor ) + { + return truncate( out, out, divisor ); + } + + default public T truncaten( T value, T divisor ) + { + return truncate( create(), value, divisor ); + } + + public T truncate( T out, T value, T divisor ); + + default public T floori( T out ) + { + return this.floor( out, out ); + } + + default public T floorn( T value ) + { + return this.floor( this.create(), value ); + } + + default public T floor( T out, T value ) + { + return this.unary( out, value, Numbers::floor ); + } + + default public T ceili( T out ) + { + return this.ceil( out, out ); + } + + default public T ceiln( T value ) + { + return this.ceil( this.create(), value ); + } + + default public T ceil( T out, T value ) + { + return this.unary( out, value, Numbers::ceil ); + } + + default public T mini( T out, T other ) + { + return this.min( out, out, other ); + } + + default public T minn( T a, T b ) + { + return this.min( this.create(), a, b ); + } + + default public T min( T out, T a, T b ) + { + return this.binary( out, a, b, Math::min ); + } + + default public T maxi( T out, T other ) + { + return this.max( out, out, other ); + } + + default public T maxn( T a, T b ) + { + return this.max( this.create(), a, b ); + } + + default public T max( T out, T a, T b ) + { + return this.binary( out, a, b, Math::max ); + } + + public T clamp( T out, T min, T max ); + + public boolean isValue( Object value ); + + public boolean isFinite( T value ); + + default public boolean isZero( T value, float epsilon ) + { + return this.isEqual( value, this.ZERO(), epsilon ); + } + + default public boolean isZero( T value ) + { + return this.isEqual( value, this.ZERO(), Numbers.EPSILON ); + } + + default public boolean isEqual( T a, T b ) + { + return isEqual( a, b, Numbers.EPSILON ); + } + + // return OpenMath.equals( a.x, b.x, epsilon ) && ... + public boolean isEqual( T a, T b, float epsilon ); + + default public boolean isUnit( T vector ) + { + return Numbers.equals( this.lengthSq( vector ), 1 ); + } + + default public boolean isUnit( T vector, float epsilon ) + { + return Numbers.equals( this.lengthSq( vector ), 1, epsilon ); + } + + default public boolean isLength( T vector, float length ) + { + return Numbers.equals( this.lengthSq( vector ), length * length ); + } + + default public boolean isLength( T vector, float length, float epsilon ) + { + return Numbers.equals( this.lengthSq( vector ), length * length, epsilon ); + } + + default public T lengthen( T out, T value, float length ) + { + return this.clamp( out, value, length, length ); + } + + default public T lengthenn( T value, float length ) + { + return this.clampn( value, length, length ); + } + + default public T lengtheni( T out, float length ) + { + return this.clampi( out, length, length ); + } + + default public T clamp( T out, T value, float min, float max ) + { + float valueSq = this.lengthSq( value ); + + if (valueSq != 0.0) + { + if (valueSq < min * min) + { + return this.scale( out, value, min / Numbers.sqrt( valueSq ) ); + } + else if (valueSq > max * max) + { + return this.scale( out, value, max / Numbers.sqrt( valueSq ) ); + } + } + + return this.copy( out, value ); + } + + default public T clampn( T value, float min, float max ) + { + return this.clamp( this.create(), value, min, max ); + } + + default public T clampi( T out, float min, float max ) + { + return this.clamp( out, out, min, max ); + } + + default public T inverti( T out ) + { + return this.invert( out, out ); + } + + default public T invertn( T value ) + { + return this.invert( this.create(), value ); + } + + default public T invert( T out, T value ) + { + return this.div( out, ONE(), value ); + } + + public int sizeof( T value ); + + public int write( T value, ByteBuffer to ); + + public T read( T out, ByteBuffer from ); + + public void write( T value, OutputModel to ); + + public T read( T out, InputModel from ); + + public int getComponents(); + + public float getComponent( T value, int index ); + + public T setComponent( T value, int index, float component ); + + default public int getDimensions() + { + return this.getComponents(); + } + + default public float getDimension( T value, int index ) + { + return this.getComponent( value, index ); + } + + default public T setDimension( T value, int index, float dimension ) + { + return this.setComponent( value, index, dimension ); + } + + default public T slerpDirect( T out, T start, T end, float delta ) + { + float startLength = this.length( start ); + float endLength = this.length( end ); + float lengthSq = startLength * endLength; + float dot = this.dot( start, end ); + float angle = Numbers.acos( dot / lengthSq ); + + return this.slerp( out, start, end, delta, angle ); + } + + default public T slerpNormal( T out, T start, T end, float delta ) + { + float dot = this.dot( start, end ); + float angle = Numbers.acos( dot ); + + return this.slerp( out, start, end, delta, angle ); + } + + default public T slerp( T out, T start, T end, float delta, float angle ) + { + float invDenom = Numbers.divide( 1, Numbers.sin( angle ) ); + float d0 = Numbers.sin( (1 - delta) * angle ) * invDenom; + float d1 = Numbers.cos( delta * angle ) * invDenom; + + out = this.scale( out, end, d1 ); + out = this.addsi( out, start, d0 ); + + return out; + } + + public float delta( T start, T end, T point ); + + default public T closest( T out, T start, T end, T point ) + { + return closest( out, start, end, point, false ); + } + + default public T closest( T out, T start, T end, T point, boolean line ) + { + float delta = this.delta( start, end, point ); + + if (!line) + { + delta = Numbers.clamp( delta, 0, 1 ); + } + + return this.interpolate( out, start, end, delta ); + } + + public float interceptionTime( T shooter, float speed, T targetPosition, T targetVelocity ); + + default public float distanceFrom( T start, T end, T point ) + { + return distanceFrom( start, end, point, false ); + } + + public float distanceFrom( T start, T end, T point, boolean line ); + + public boolean inView( T origin, T direction, float fovCos, T point ); + + public boolean inCircleView( T origin, T direction, float fovTan, float fovCos, T center, float radius, boolean entirely ); + + default public T cubicCurve( T out, float delta, T p0, T p1, T p2, T p3, float[][] matrix) + { + return cubicCurve( out, delta, p0, p1, p2, p3, matrix, false ); + } + + public T cubicCurve( T out, float delta, T p0, T p1, T p2, T p3, float[][] matrix, boolean inverse ); + + default public T parametricCubicCurve( T out, float delta, T[] points, float[][] matrix, float weight ) + { + return parametricCubicCurve( out, delta, points, matrix, weight, false, false ); + } + + public T parametricCubicCurve( T out, float delta, T[] points, float[][] matrix, float weight, boolean inverse, boolean loop ); + + + + public static T createLike( T value ) + { + return CalculatorRegistry.getFor( value ).create(); + } + + public static T cloneLike( T value ) + { + return CalculatorRegistry.getFor( value ).clone( value ); + } + +} diff --git a/src/com/axe/math/calc/CalculatorBound2f.java b/src/com/axe/math/calc/CalculatorBound2f.java new file mode 100644 index 0000000..ead2143 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorBound2f.java @@ -0,0 +1,331 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Bound2f; +import com.axe.math.Numbers; + +public final class CalculatorBound2f extends AbstractCalculator +{ + + public static final CalculatorBound2f INSTANCE = new CalculatorBound2f(); + + @Override + public Bound2f copy(Bound2f out, Bound2f source) + { + out.l = source.l; + out.t = source.t; + out.r = source.r; + out.b = source.b; + return out; + } + + @Override + public Bound2f parse(Object input, Bound2f defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Bound2f( floats[0], floats[1], floats[2], floats[3] ); + } + + if (input instanceof Bound2f) + { + return (Bound2f)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Bound2f instantiate() + { + return new Bound2f(); + } + + @Override + public Bound2f clear(Bound2f out, float component) + { + out.l = out.t = out.r = out.b = component; + return out; + } + + @Override + public Bound2f unary(Bound2f out, Bound2f value, Unary unaryOperation) + { + out.l = unaryOperation.unary( value.l ); + out.t = unaryOperation.unary( value.t ); + out.r = unaryOperation.unary( value.r ); + out.b = unaryOperation.unary( value.b ); + return out; + } + + @Override + public Bound2f binary(Bound2f out, Bound2f a, Bound2f b, Binary binaryOperation) + { + out.l = binaryOperation.binary( a.l, b.l ); + out.t = binaryOperation.binary( a.t, b.t ); + out.r = binaryOperation.binary( a.r, b.r ); + out.b = binaryOperation.binary( a.r, b.b ); + return out; + } + + @Override + public Bound2f adds(Bound2f out, Bound2f augend, Bound2f addend, float scale) + { + out.l = augend.l + addend.l * scale; + out.t = augend.t + addend.t * scale; + out.r = augend.r + addend.r * scale; + out.b = augend.b + addend.b * scale; + return out; + } + + @Override + public Bound2f mul( Bound2f out, Bound2f value, Bound2f scale ) + { + out.l = value.l * scale.l; + out.t = value.t * scale.t; + out.r = value.r * scale.r; + out.b = value.b * scale.b; + return out; + } + + @Override + public Bound2f div( Bound2f out, Bound2f dividend, Bound2f divisor ) + { + out.l = Numbers.divide( dividend.l, divisor.l ); + out.t = Numbers.divide( dividend.t, divisor.t ); + out.r = Numbers.divide( dividend.r, divisor.r ); + out.b = Numbers.divide( dividend.b, divisor.b ); + return out; + } + + @Override + public Bound2f interpolate( Bound2f out, Bound2f start, Bound2f end, float delta ) + { + out.l = (end.l - start.l) * delta + start.l; + out.t = (end.t - start.t) * delta + start.t; + out.r = (end.r - start.r) * delta + start.r; + out.b = (end.b - start.b) * delta + start.b; + return out; + } + + @Override + public Bound2f contain( Bound2f out, Bound2f min, Bound2f max ) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + return out; + } + + @Override + public boolean contains( Bound2f point, Bound2f min, Bound2f max ) + { + return point.l >= min.l && point.l <= max.l && + point.t >= min.t && point.t <= max.t && + point.r >= min.r && point.r <= max.r && + point.b >= min.b && point.b <= max.b; + } + + @Override + public Bound2f random( Bound2f out, Bound2f min, Bound2f max, Binary randomizer ) + { + out.l = randomizer.binary( min.l, max.l ); + out.t = randomizer.binary( min.t, max.t ); + out.r = randomizer.binary( min.r, max.r ); + out.b = randomizer.binary( min.b, max.b ); + return out; + } + + @Override + public float dot( Bound2f a, Bound2f b ) + { + return a.l * b.l + a.t * b.t + a.r * b.r + a.b * b.b; + } + + @Override + public float distanceSq( Bound2f a, Bound2f b ) + { + float dr = a.l - b.l; + float dg = a.t - b.t; + float db = a.r - b.r; + float da = a.b - b.b; + + return dr * dr + dg * dg + db * db + da * da; + } + + @Override + public float lengthSq( Bound2f value ) + { + return value.l * value.l + value.t * value.t + value.r * value.r + value.b * value.b; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Bound2f; + } + + @Override + public boolean isFinite( Bound2f value ) + { + return Float.isFinite( value.l ) && + Float.isFinite( value.t ) && + Float.isFinite( value.r ) && + Float.isFinite( value.b ); + } + + @Override + public boolean isEqual( Bound2f a, Bound2f b, float epsilon ) + { + return Numbers.equals( a.l, b.l, epsilon ) && + Numbers.equals( a.t, b.t, epsilon ) && + Numbers.equals( a.r, b.r, epsilon ) && + Numbers.equals( a.b, b.b, epsilon ); + } + + @Override + public int sizeof( Bound2f value ) + { + return 16; + } + + @Override + public int write( Bound2f value, ByteBuffer to ) + { + to.putFloat( value.l ); + to.putFloat( value.t ); + to.putFloat( value.r ); + to.putFloat( value.b ); + return 16; + } + + @Override + public Bound2f read( Bound2f out, ByteBuffer from ) + { + out.l = from.getFloat(); + out.t = from.getFloat(); + out.r = from.getFloat(); + out.b = from.getFloat(); + return out; + } + + @Override + public Bound2f read( Bound2f out, InputModel input ) + { + out.l = input.readFloat( "l" ); + out.t = input.readFloat( "t" ); + out.r = input.readFloat( "r" ); + out.b = input.readFloat( "b" ); + return out; + } + + @Override + public void write( Bound2f value, OutputModel output ) + { + output.write( "l", value.l ); + output.write( "t", value.t ); + output.write( "r", value.r ); + output.write( "b", value.b ); + } + + @Override + public int getComponents() + { + return 4; + } + + @Override + public float getComponent( Bound2f value, int index ) + { + switch (index) { + case 0: return value.l; + case 1: return value.t; + case 2: return value.r; + case 3: return value.b; + } + + return 0; + } + + @Override + public Bound2f setComponent( Bound2f value, int index, float component ) + { + switch (index) { + case 0: + value.l = component; + break; + case 1: + value.t = component; + break; + case 2: + value.r = component; + break; + case 3: + value.b = component; + break; + } + return value; + } + + @Override + public Bound2f mods(Bound2f out, Bound2f value, float divisor) + { + out.l = value.l % divisor; + out.t = value.t % divisor; + out.r = value.r % divisor; + out.b = value.b % divisor; + return out; + } + + @Override + public Bound2f mod(Bound2f out, Bound2f value, Bound2f divisor) + { + out.l = value.l % divisor.l; + out.t = value.t % divisor.t; + out.r = value.r % divisor.r; + out.b = value.b % divisor.b; + return out; + } + + @Override + public Bound2f truncates(Bound2f out, Bound2f value, float divisor) + { + out.l = value.l - value.l % divisor; + out.t = value.t - value.t % divisor; + out.r = value.r - value.r % divisor; + out.b = value.b - value.b % divisor; + return out; + } + + @Override + public Bound2f truncate(Bound2f out, Bound2f value, Bound2f divisor) + { + out.l = value.l - value.l % divisor.l; + out.t = value.t - value.t % divisor.t; + out.r = value.r - value.r % divisor.r; + out.b = value.b - value.b % divisor.b; + return out; + } + + @Override + public Bound2f clamp(Bound2f out, Bound2f min, Bound2f max) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorBound2i.java b/src/com/axe/math/calc/CalculatorBound2i.java new file mode 100644 index 0000000..d64fcee --- /dev/null +++ b/src/com/axe/math/calc/CalculatorBound2i.java @@ -0,0 +1,331 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Bound2i; +import com.axe.math.Numbers; + +public final class CalculatorBound2i extends AbstractCalculator +{ + + public static final CalculatorBound2i INSTANCE = new CalculatorBound2i(); + + @Override + public Bound2i copy(Bound2i out, Bound2i source) + { + out.l = source.l; + out.t = source.t; + out.r = source.r; + out.b = source.b; + return out; + } + + @Override + public Bound2i parse(Object input, Bound2i defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof int[]) + { + int[] ints = (int[])input; + + return new Bound2i( ints[0], ints[1], ints[2], ints[3] ); + } + + if (input instanceof Bound2i) + { + return (Bound2i)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Bound2i instantiate() + { + return new Bound2i(); + } + + @Override + public Bound2i clear(Bound2i out, float component) + { + out.l = out.t = out.r = out.b = (int)component; + return out; + } + + @Override + public Bound2i unary(Bound2i out, Bound2i value, Unary unaryOperation) + { + out.l = (int)unaryOperation.unary( value.l ); + out.t = (int)unaryOperation.unary( value.t ); + out.r = (int)unaryOperation.unary( value.r ); + out.b = (int)unaryOperation.unary( value.b ); + return out; + } + + @Override + public Bound2i binary(Bound2i out, Bound2i a, Bound2i b, Binary binaryOperation) + { + out.l = (int)binaryOperation.binary( a.l, b.l ); + out.t = (int)binaryOperation.binary( a.t, b.t ); + out.r = (int)binaryOperation.binary( a.r, b.r ); + out.b = (int)binaryOperation.binary( a.r, b.b ); + return out; + } + + @Override + public Bound2i adds(Bound2i out, Bound2i augend, Bound2i addend, float scale) + { + out.l = (int)(augend.l + addend.l * scale); + out.t = (int)(augend.t + addend.t * scale); + out.r = (int)(augend.r + addend.r * scale); + out.b = (int)(augend.b + addend.b * scale); + return out; + } + + @Override + public Bound2i mul( Bound2i out, Bound2i value, Bound2i scale ) + { + out.l = value.l * scale.l; + out.t = value.t * scale.t; + out.r = value.r * scale.r; + out.b = value.b * scale.b; + return out; + } + + @Override + public Bound2i div( Bound2i out, Bound2i dividend, Bound2i divisor ) + { + out.l = Numbers.divide( dividend.l, divisor.l ); + out.t = Numbers.divide( dividend.t, divisor.t ); + out.r = Numbers.divide( dividend.r, divisor.r ); + out.b = Numbers.divide( dividend.b, divisor.b ); + return out; + } + + @Override + public Bound2i interpolate( Bound2i out, Bound2i start, Bound2i end, float delta ) + { + out.l = (int)((end.l - start.l) * delta + start.l); + out.t = (int)((end.t - start.t) * delta + start.t); + out.r = (int)((end.r - start.r) * delta + start.r); + out.b = (int)((end.b - start.b) * delta + start.b); + return out; + } + + @Override + public Bound2i contain( Bound2i out, Bound2i min, Bound2i max ) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + return out; + } + + @Override + public boolean contains( Bound2i point, Bound2i min, Bound2i max ) + { + return point.l >= min.l && point.l <= max.l && + point.t >= min.t && point.t <= max.t && + point.r >= min.r && point.r <= max.r && + point.b >= min.b && point.b <= max.b; + } + + @Override + public Bound2i random( Bound2i out, Bound2i min, Bound2i max, Binary randomizer ) + { + out.l = (int)randomizer.binary( min.l, max.l ); + out.t = (int)randomizer.binary( min.t, max.t ); + out.r = (int)randomizer.binary( min.r, max.r ); + out.b = (int)randomizer.binary( min.b, max.b ); + return out; + } + + @Override + public float dot( Bound2i a, Bound2i b ) + { + return a.l * b.l + a.t * b.t + a.r * b.r + a.b * b.b; + } + + @Override + public float distanceSq( Bound2i a, Bound2i b ) + { + float dr = a.l - b.l; + float dg = a.t - b.t; + float db = a.r - b.r; + float da = a.b - b.b; + + return dr * dr + dg * dg + db * db + da * da; + } + + @Override + public float lengthSq( Bound2i value ) + { + return value.l * value.l + value.t * value.t + value.r * value.r + value.b * value.b; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Bound2i; + } + + @Override + public boolean isFinite( Bound2i value ) + { + return Float.isFinite( value.l ) && + Float.isFinite( value.t ) && + Float.isFinite( value.r ) && + Float.isFinite( value.b ); + } + + @Override + public boolean isEqual( Bound2i a, Bound2i b, float epsilon ) + { + return Numbers.equals( a.l, b.l, epsilon ) && + Numbers.equals( a.t, b.t, epsilon ) && + Numbers.equals( a.r, b.r, epsilon ) && + Numbers.equals( a.b, b.b, epsilon ); + } + + @Override + public int sizeof( Bound2i value ) + { + return 16; + } + + @Override + public int write( Bound2i value, ByteBuffer to ) + { + to.putFloat( value.l ); + to.putFloat( value.t ); + to.putFloat( value.r ); + to.putFloat( value.b ); + return 16; + } + + @Override + public Bound2i read( Bound2i out, ByteBuffer from ) + { + out.l = from.getInt(); + out.t = from.getInt(); + out.r = from.getInt(); + out.b = from.getInt(); + return out; + } + + @Override + public Bound2i read( Bound2i out, InputModel input ) + { + out.l = input.readInt( "l" ); + out.t = input.readInt( "t" ); + out.r = input.readInt( "r" ); + out.b = input.readInt( "b" ); + return out; + } + + @Override + public void write( Bound2i value, OutputModel output ) + { + output.write( "l", value.l ); + output.write( "t", value.t ); + output.write( "r", value.r ); + output.write( "b", value.b ); + } + + @Override + public int getComponents() + { + return 4; + } + + @Override + public float getComponent( Bound2i value, int index ) + { + switch (index) { + case 0: return value.l; + case 1: return value.t; + case 2: return value.r; + case 3: return value.b; + } + + return 0; + } + + @Override + public Bound2i setComponent( Bound2i value, int index, float component ) + { + switch (index) { + case 0: + value.l = (int)component; + break; + case 1: + value.t = (int)component; + break; + case 2: + value.r = (int)component; + break; + case 3: + value.b = (int)component; + break; + } + return value; + } + + @Override + public Bound2i mods(Bound2i out, Bound2i value, float divisor) + { + out.l = value.l % (int)divisor; + out.t = value.t % (int)divisor; + out.r = value.r % (int)divisor; + out.b = value.b % (int)divisor; + return out; + } + + @Override + public Bound2i mod(Bound2i out, Bound2i value, Bound2i divisor) + { + out.l = value.l % divisor.l; + out.t = value.t % divisor.t; + out.r = value.r % divisor.r; + out.b = value.b % divisor.b; + return out; + } + + @Override + public Bound2i truncates(Bound2i out, Bound2i value, float divisor) + { + out.l = value.l - value.l % (int)divisor; + out.t = value.t - value.t % (int)divisor; + out.r = value.r - value.r % (int)divisor; + out.b = value.b - value.b % (int)divisor; + return out; + } + + @Override + public Bound2i truncate(Bound2i out, Bound2i value, Bound2i divisor) + { + out.l = value.l - value.l % divisor.l; + out.t = value.t - value.t % divisor.t; + out.r = value.r - value.r % divisor.r; + out.b = value.b - value.b % divisor.b; + return out; + } + + @Override + public Bound2i clamp(Bound2i out, Bound2i min, Bound2i max) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorBound3f.java b/src/com/axe/math/calc/CalculatorBound3f.java new file mode 100644 index 0000000..c6eee28 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorBound3f.java @@ -0,0 +1,383 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Bound3f; +import com.axe.math.Numbers; + +public final class CalculatorBound3f extends AbstractCalculator +{ + + public static final CalculatorBound3f INSTANCE = new CalculatorBound3f(); + + @Override + public Bound3f copy(Bound3f out, Bound3f source) + { + out.l = source.l; + out.t = source.t; + out.r = source.r; + out.b = source.b; + out.n = source.n; + out.f = source.f; + return out; + } + + @Override + public Bound3f parse(Object input, Bound3f defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Bound3f( floats[0], floats[1], floats[2], floats[3], floats[4], floats[5] ); + } + + if (input instanceof Bound3f) + { + return (Bound3f)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Bound3f instantiate() + { + return new Bound3f(); + } + + @Override + public Bound3f clear(Bound3f out, float component) + { + out.l = out.t = out.r = out.b = out.n = out.f = component; + return out; + } + + @Override + public Bound3f unary(Bound3f out, Bound3f value, Unary unaryOperation) + { + out.l = unaryOperation.unary( value.l ); + out.t = unaryOperation.unary( value.t ); + out.r = unaryOperation.unary( value.r ); + out.b = unaryOperation.unary( value.b ); + out.n = unaryOperation.unary( value.n ); + out.f = unaryOperation.unary( value.f ); + return out; + } + + @Override + public Bound3f binary(Bound3f out, Bound3f a, Bound3f b, Binary binaryOperation) + { + out.l = binaryOperation.binary( a.l, b.l ); + out.t = binaryOperation.binary( a.t, b.t ); + out.r = binaryOperation.binary( a.r, b.r ); + out.b = binaryOperation.binary( a.r, b.b ); + out.n = binaryOperation.binary( a.n, b.n ); + out.f = binaryOperation.binary( a.f, b.f ); + return out; + } + + @Override + public Bound3f adds(Bound3f out, Bound3f augend, Bound3f addend, float scale) + { + out.l = augend.l + addend.l * scale; + out.t = augend.t + addend.t * scale; + out.r = augend.r + addend.r * scale; + out.b = augend.b + addend.b * scale; + out.n = augend.n + addend.n * scale; + out.f = augend.f + addend.f * scale; + return out; + } + + @Override + public Bound3f mul( Bound3f out, Bound3f value, Bound3f scale ) + { + out.l = value.l * scale.l; + out.t = value.t * scale.t; + out.r = value.r * scale.r; + out.b = value.b * scale.b; + out.n = value.n * scale.n; + out.f = value.f * scale.f; + return out; + } + + @Override + public Bound3f div( Bound3f out, Bound3f dividend, Bound3f divisor ) + { + out.l = Numbers.divide( dividend.l, divisor.l ); + out.t = Numbers.divide( dividend.t, divisor.t ); + out.r = Numbers.divide( dividend.r, divisor.r ); + out.b = Numbers.divide( dividend.b, divisor.b ); + out.n = Numbers.divide( dividend.n, divisor.n ); + out.f = Numbers.divide( dividend.f, divisor.f ); + return out; + } + + @Override + public Bound3f interpolate( Bound3f out, Bound3f start, Bound3f end, float delta ) + { + out.l = (end.l - start.l) * delta + start.l; + out.t = (end.t - start.t) * delta + start.t; + out.r = (end.r - start.r) * delta + start.r; + out.b = (end.b - start.b) * delta + start.b; + out.n = (end.n - start.n) * delta + start.n; + out.f = (end.f - start.f) * delta + start.f; + return out; + } + + @Override + public Bound3f contain( Bound3f out, Bound3f min, Bound3f max ) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + out.n = Numbers.clamp( out.n, min.n, max.n ); + out.f = Numbers.clamp( out.f, min.f, max.f ); + return out; + } + + @Override + public boolean contains( Bound3f point, Bound3f min, Bound3f max ) + { + return point.l >= min.l && point.l <= max.l && + point.t >= min.t && point.t <= max.t && + point.r >= min.r && point.r <= max.r && + point.b >= min.b && point.b <= max.b && + point.n >= min.n && point.n <= max.n && + point.f >= min.f && point.f <= max.f; + } + + @Override + public Bound3f random( Bound3f out, Bound3f min, Bound3f max, Binary randomizer ) + { + out.l = randomizer.binary( min.l, max.l ); + out.t = randomizer.binary( min.t, max.t ); + out.r = randomizer.binary( min.r, max.r ); + out.b = randomizer.binary( min.b, max.b ); + out.n = randomizer.binary( min.n, max.n ); + out.f = randomizer.binary( min.f, max.f ); + return out; + } + + @Override + public float dot( Bound3f a, Bound3f b ) + { + return a.l * b.l + a.t * b.t + a.r * b.r + a.b * b.b + a.n * b.n + a.f * b.f; + } + + @Override + public float distanceSq( Bound3f a, Bound3f b ) + { + float dl = a.l - b.l; + float dt = a.t - b.t; + float dr = a.r - b.r; + float db = a.b - b.b; + float dn = a.n - b.n; + float df = a.f - b.f; + + return dl * dl + dt * dt + dr * dr + db * db + dn * dn + df * df; + } + + @Override + public float lengthSq( Bound3f value ) + { + return value.l * value.l + value.t * value.t + value.r * value.r + value.b * value.b + value.n * value.n + value.f * value.f; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Bound3f; + } + + @Override + public boolean isFinite( Bound3f value ) + { + return Float.isFinite( value.l ) && + Float.isFinite( value.t ) && + Float.isFinite( value.r ) && + Float.isFinite( value.b ) && + Float.isFinite( value.n ) && + Float.isFinite( value.f ); + } + + @Override + public boolean isEqual( Bound3f a, Bound3f b, float epsilon ) + { + return Numbers.equals( a.l, b.l, epsilon ) && + Numbers.equals( a.t, b.t, epsilon ) && + Numbers.equals( a.r, b.r, epsilon ) && + Numbers.equals( a.b, b.b, epsilon ) && + Numbers.equals( a.n, b.n, epsilon ) && + Numbers.equals( a.f, b.f, epsilon ); + } + + @Override + public int sizeof( Bound3f value ) + { + return 24; + } + + @Override + public int write( Bound3f value, ByteBuffer to ) + { + to.putFloat( value.l ); + to.putFloat( value.t ); + to.putFloat( value.r ); + to.putFloat( value.b ); + to.putFloat( value.n ); + to.putFloat( value.f ); + return 24; + } + + @Override + public Bound3f read( Bound3f out, ByteBuffer from ) + { + out.l = from.getFloat(); + out.t = from.getFloat(); + out.r = from.getFloat(); + out.b = from.getFloat(); + out.n = from.getFloat(); + out.f = from.getFloat(); + return out; + } + + @Override + public Bound3f read( Bound3f out, InputModel input ) + { + out.l = input.readFloat( "l" ); + out.t = input.readFloat( "t" ); + out.r = input.readFloat( "r" ); + out.b = input.readFloat( "b" ); + out.n = input.readFloat( "n" ); + out.f = input.readFloat( "f" ); + return out; + } + + @Override + public void write( Bound3f value, OutputModel output ) + { + output.write( "l", value.l ); + output.write( "t", value.t ); + output.write( "r", value.r ); + output.write( "b", value.b ); + output.write( "n", value.n ); + output.write( "f", value.f ); + } + + @Override + public int getComponents() + { + return 6; + } + + @Override + public float getComponent( Bound3f value, int index ) + { + switch (index) { + case 0: return value.l; + case 1: return value.t; + case 2: return value.r; + case 3: return value.b; + case 4: return value.n; + case 5: return value.f; + } + + return 0; + } + + @Override + public Bound3f setComponent( Bound3f value, int index, float component ) + { + switch (index) { + case 0: + value.l = component; + break; + case 1: + value.t = component; + break; + case 2: + value.r = component; + break; + case 3: + value.b = component; + break; + case 4: + value.n = component; + break; + case 5: + value.f = component; + break; + } + return value; + } + + @Override + public Bound3f mods(Bound3f out, Bound3f value, float divisor) + { + out.l = value.l % divisor; + out.t = value.t % divisor; + out.r = value.r % divisor; + out.b = value.b % divisor; + out.n = value.n % divisor; + out.f = value.f % divisor; + return out; + } + + @Override + public Bound3f mod(Bound3f out, Bound3f value, Bound3f divisor) + { + out.l = value.l % divisor.l; + out.t = value.t % divisor.t; + out.r = value.r % divisor.r; + out.b = value.b % divisor.b; + out.n = value.n % divisor.n; + out.f = value.f % divisor.f; + return out; + } + + @Override + public Bound3f truncates(Bound3f out, Bound3f value, float divisor) + { + out.l = value.l - value.l % divisor; + out.t = value.t - value.t % divisor; + out.r = value.r - value.r % divisor; + out.b = value.b - value.b % divisor; + out.n = value.n - value.n % divisor; + out.f = value.f - value.f % divisor; + return out; + } + + @Override + public Bound3f truncate(Bound3f out, Bound3f value, Bound3f divisor) + { + out.l = value.l - value.l % divisor.l; + out.t = value.t - value.t % divisor.t; + out.r = value.r - value.r % divisor.r; + out.b = value.b - value.b % divisor.b; + out.n = value.n - value.n % divisor.n; + out.f = value.f - value.f % divisor.f; + return out; + } + + @Override + public Bound3f clamp(Bound3f out, Bound3f min, Bound3f max) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + out.n = Numbers.clamp( out.n, min.n, max.n ); + out.f = Numbers.clamp( out.f, min.f, max.f ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorBound3i.java b/src/com/axe/math/calc/CalculatorBound3i.java new file mode 100644 index 0000000..aae671f --- /dev/null +++ b/src/com/axe/math/calc/CalculatorBound3i.java @@ -0,0 +1,383 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Bound3i; +import com.axe.math.Numbers; + +public final class CalculatorBound3i extends AbstractCalculator +{ + + public static final CalculatorBound3i INSTANCE = new CalculatorBound3i(); + + @Override + public Bound3i copy(Bound3i out, Bound3i source) + { + out.l = source.l; + out.t = source.t; + out.r = source.r; + out.b = source.b; + out.n = source.n; + out.f = source.f; + return out; + } + + @Override + public Bound3i parse(Object input, Bound3i defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof int[]) + { + int[] ints = (int[])input; + + return new Bound3i( ints[0], ints[1], ints[2], ints[3], ints[4], ints[5] ); + } + + if (input instanceof Bound3i) + { + return (Bound3i)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Bound3i instantiate() + { + return new Bound3i(); + } + + @Override + public Bound3i clear(Bound3i out, float component) + { + out.l = out.t = out.r = out.b = out.n = out.f = (int)component; + return out; + } + + @Override + public Bound3i unary(Bound3i out, Bound3i value, Unary unaryOperation) + { + out.l = (int)unaryOperation.unary( value.l ); + out.t = (int)unaryOperation.unary( value.t ); + out.r = (int)unaryOperation.unary( value.r ); + out.b = (int)unaryOperation.unary( value.b ); + out.n = (int)unaryOperation.unary( value.n ); + out.f = (int)unaryOperation.unary( value.f ); + return out; + } + + @Override + public Bound3i binary(Bound3i out, Bound3i a, Bound3i b, Binary binaryOperation) + { + out.l = (int)binaryOperation.binary( a.l, b.l ); + out.t = (int)binaryOperation.binary( a.t, b.t ); + out.r = (int)binaryOperation.binary( a.r, b.r ); + out.b = (int)binaryOperation.binary( a.r, b.b ); + out.n = (int)binaryOperation.binary( a.n, b.n ); + out.f = (int)binaryOperation.binary( a.f, b.f ); + return out; + } + + @Override + public Bound3i adds(Bound3i out, Bound3i augend, Bound3i addend, float scale) + { + out.l = (int)(augend.l + addend.l * scale); + out.t = (int)(augend.t + addend.t * scale); + out.r = (int)(augend.r + addend.r * scale); + out.b = (int)(augend.b + addend.b * scale); + out.n = (int)(augend.n + addend.n * scale); + out.f = (int)(augend.f + addend.f * scale); + return out; + } + + @Override + public Bound3i mul( Bound3i out, Bound3i value, Bound3i scale ) + { + out.l = value.l * scale.l; + out.t = value.t * scale.t; + out.r = value.r * scale.r; + out.b = value.b * scale.b; + out.n = value.n * scale.n; + out.f = value.f * scale.f; + return out; + } + + @Override + public Bound3i div( Bound3i out, Bound3i dividend, Bound3i divisor ) + { + out.l = Numbers.divide( dividend.l, divisor.l ); + out.t = Numbers.divide( dividend.t, divisor.t ); + out.r = Numbers.divide( dividend.r, divisor.r ); + out.b = Numbers.divide( dividend.b, divisor.b ); + out.n = Numbers.divide( dividend.n, divisor.n ); + out.f = Numbers.divide( dividend.f, divisor.f ); + return out; + } + + @Override + public Bound3i interpolate( Bound3i out, Bound3i start, Bound3i end, float delta ) + { + out.l = (int)((end.l - start.l) * delta + start.l); + out.t = (int)((end.t - start.t) * delta + start.t); + out.r = (int)((end.r - start.r) * delta + start.r); + out.b = (int)((end.b - start.b) * delta + start.b); + out.n = (int)((end.n - start.n) * delta + start.n); + out.f = (int)((end.f - start.f) * delta + start.f); + return out; + } + + @Override + public Bound3i contain( Bound3i out, Bound3i min, Bound3i max ) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + out.n = Numbers.clamp( out.n, min.n, max.n ); + out.f = Numbers.clamp( out.f, min.f, max.f ); + return out; + } + + @Override + public boolean contains( Bound3i point, Bound3i min, Bound3i max ) + { + return point.l >= min.l && point.l <= max.l && + point.t >= min.t && point.t <= max.t && + point.r >= min.r && point.r <= max.r && + point.b >= min.b && point.b <= max.b && + point.n >= min.n && point.n <= max.n && + point.f >= min.f && point.f <= max.f; + } + + @Override + public Bound3i random( Bound3i out, Bound3i min, Bound3i max, Binary randomizer ) + { + out.l = (int)randomizer.binary( min.l, max.l ); + out.t = (int)randomizer.binary( min.t, max.t ); + out.r = (int)randomizer.binary( min.r, max.r ); + out.b = (int)randomizer.binary( min.b, max.b ); + out.n = (int)randomizer.binary( min.n, max.n ); + out.f = (int)randomizer.binary( min.f, max.f ); + return out; + } + + @Override + public float dot( Bound3i a, Bound3i b ) + { + return a.l * b.l + a.t * b.t + a.r * b.r + a.b * b.b + a.n * b.n + a.f * b.f; + } + + @Override + public float distanceSq( Bound3i a, Bound3i b ) + { + float dl = a.l - b.l; + float dt = a.t - b.t; + float dr = a.r - b.r; + float db = a.b - b.b; + float dn = a.n - b.n; + float df = a.f - b.f; + + return dl * dl + dt * dt + dr * dr + db * db + dn * dn + df * df; + } + + @Override + public float lengthSq( Bound3i value ) + { + return value.l * value.l + value.t * value.t + value.r * value.r + value.b * value.b + value.n * value.n + value.f * value.f; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Bound3i; + } + + @Override + public boolean isFinite( Bound3i value ) + { + return Float.isFinite( value.l ) && + Float.isFinite( value.t ) && + Float.isFinite( value.r ) && + Float.isFinite( value.b ) && + Float.isFinite( value.n ) && + Float.isFinite( value.f ); + } + + @Override + public boolean isEqual( Bound3i a, Bound3i b, float epsilon ) + { + return Numbers.equals( a.l, b.l, epsilon ) && + Numbers.equals( a.t, b.t, epsilon ) && + Numbers.equals( a.r, b.r, epsilon ) && + Numbers.equals( a.b, b.b, epsilon ) && + Numbers.equals( a.n, b.n, epsilon ) && + Numbers.equals( a.f, b.f, epsilon ); + } + + @Override + public int sizeof( Bound3i value ) + { + return 24; + } + + @Override + public int write( Bound3i value, ByteBuffer to ) + { + to.putInt( value.l ); + to.putInt( value.t ); + to.putInt( value.r ); + to.putInt( value.b ); + to.putInt( value.n ); + to.putInt( value.f ); + return 24; + } + + @Override + public Bound3i read( Bound3i out, ByteBuffer from ) + { + out.l = from.getInt(); + out.t = from.getInt(); + out.r = from.getInt(); + out.b = from.getInt(); + out.n = from.getInt(); + out.f = from.getInt(); + return out; + } + + @Override + public Bound3i read( Bound3i out, InputModel input ) + { + out.l = input.readInt( "l" ); + out.t = input.readInt( "t" ); + out.r = input.readInt( "r" ); + out.b = input.readInt( "b" ); + out.n = input.readInt( "n" ); + out.f = input.readInt( "f" ); + return out; + } + + @Override + public void write( Bound3i value, OutputModel output ) + { + output.write( "l", value.l ); + output.write( "t", value.t ); + output.write( "r", value.r ); + output.write( "b", value.b ); + output.write( "n", value.n ); + output.write( "f", value.f ); + } + + @Override + public int getComponents() + { + return 6; + } + + @Override + public float getComponent( Bound3i value, int index ) + { + switch (index) { + case 0: return value.l; + case 1: return value.t; + case 2: return value.r; + case 3: return value.b; + case 4: return value.n; + case 5: return value.f; + } + + return 0; + } + + @Override + public Bound3i setComponent( Bound3i value, int index, float component ) + { + switch (index) { + case 0: + value.l = (int)component; + break; + case 1: + value.t = (int)component; + break; + case 2: + value.r = (int)component; + break; + case 3: + value.b = (int)component; + break; + case 4: + value.n = (int)component; + break; + case 5: + value.f = (int)component; + break; + } + return value; + } + + @Override + public Bound3i mods(Bound3i out, Bound3i value, float divisor) + { + out.l = value.l % (int)divisor; + out.t = value.t % (int)divisor; + out.r = value.r % (int)divisor; + out.b = value.b % (int)divisor; + out.n = value.n % (int)divisor; + out.f = value.f % (int)divisor; + return out; + } + + @Override + public Bound3i mod(Bound3i out, Bound3i value, Bound3i divisor) + { + out.l = value.l % divisor.l; + out.t = value.t % divisor.t; + out.r = value.r % divisor.r; + out.b = value.b % divisor.b; + out.n = value.n % divisor.n; + out.f = value.f % divisor.f; + return out; + } + + @Override + public Bound3i truncates(Bound3i out, Bound3i value, float divisor) + { + out.l = value.l - value.l % (int)divisor; + out.t = value.t - value.t % (int)divisor; + out.r = value.r - value.r % (int)divisor; + out.b = value.b - value.b % (int)divisor; + out.n = value.n - value.n % (int)divisor; + out.f = value.f - value.f % (int)divisor; + return out; + } + + @Override + public Bound3i truncate(Bound3i out, Bound3i value, Bound3i divisor) + { + out.l = value.l - value.l % divisor.l; + out.t = value.t - value.t % divisor.t; + out.r = value.r - value.r % divisor.r; + out.b = value.b - value.b % divisor.b; + out.n = value.n - value.n % divisor.n; + out.f = value.f - value.f % divisor.f; + return out; + } + + @Override + public Bound3i clamp(Bound3i out, Bound3i min, Bound3i max) + { + out.l = Numbers.clamp( out.l, min.l, max.l ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + out.n = Numbers.clamp( out.n, min.n, max.n ); + out.f = Numbers.clamp( out.f, min.f, max.f ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorCamera2.java b/src/com/axe/math/calc/CalculatorCamera2.java new file mode 100644 index 0000000..f5793fd --- /dev/null +++ b/src/com/axe/math/calc/CalculatorCamera2.java @@ -0,0 +1,85 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe2d.Camera2; + +public class CalculatorCamera2 extends CalculatorGeneric +{ + + public static final CalculatorCamera2 INSTANCE = new CalculatorCamera2(); + + public CalculatorCamera2() + { + super(6); + } + + @Override + protected Object getValue(Camera2 out, int index) + { + switch (index) { + case 0: return out.roll; + case 1: return out.center; + case 2: return out.scale; + case 3: return out.size; + case 4: return out.near; + case 5: return out.far; + } + return null; + } + + @Override + protected Calculator getCalculator(Camera2 out, int index) + { + switch (index) { + case 0: return CalculatorScalarf.INSTANCE; + case 1: return CalculatorVec2f.INSTANCE; + case 2: return CalculatorVec2f.INSTANCE; + case 3: return CalculatorVec2f.INSTANCE; + case 4: return CalculatorScalarf.INSTANCE; + case 5: return CalculatorScalarf.INSTANCE; + } + return null; + } + + @Override + protected Camera2 instantiate() + { + return new Camera2(); + } + + @Override + public boolean isValue(Object value) + { + return value instanceof Camera2; + } + + @Override + public float distanceSq(Camera2 a, Camera2 b) + { + return a.center.distanceSq( b.center ); + } + + @Override + public Camera2 read(Camera2 out, InputModel input) + { + super.read( out, input ); + + out.update(); + + return out; + } + + @Override + public Camera2 read(Camera2 out, ByteBuffer from) + { + super.read( out, from ); + + out.update(); + + return out; + } + + +} diff --git a/src/com/axe/math/calc/CalculatorCamera3.java b/src/com/axe/math/calc/CalculatorCamera3.java new file mode 100644 index 0000000..4d4917b --- /dev/null +++ b/src/com/axe/math/calc/CalculatorCamera3.java @@ -0,0 +1,88 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe3d.Camera3; + +public class CalculatorCamera3 extends CalculatorGeneric +{ + + public static final CalculatorCamera3 INSTANCE = new CalculatorCamera3(); + + public CalculatorCamera3() + { + super(8); + } + + @Override + protected Object getValue(Camera3 out, int index) + { + switch (index) { + case 0: return out.yaw; + case 1: return out.pitch; + case 2: return out.roll; + case 3: return out.distance; + case 4: return out.focus; + case 5: return out.fov; + case 6: return out.near; + case 7: return out.far; + } + return null; + } + + @Override + protected Calculator getCalculator(Camera3 out, int index) + { + switch (index) { + case 0: return CalculatorScalarf.INSTANCE; + case 1: return CalculatorScalarf.INSTANCE; + case 2: return CalculatorScalarf.INSTANCE; + case 3: return CalculatorScalarf.INSTANCE; + case 4: return CalculatorVec3f.INSTANCE; + case 5: return CalculatorScalarf.INSTANCE; + case 6: return CalculatorScalarf.INSTANCE; + case 7: return CalculatorScalarf.INSTANCE; + } + return null; + } + + @Override + protected Camera3 instantiate() + { + return new Camera3(); + } + + @Override + public boolean isValue(Object value) + { + return value instanceof Camera3; + } + + @Override + public float distanceSq(Camera3 a, Camera3 b) + { + return a.position.distanceSq( b.position ); + } + + @Override + public Camera3 read(Camera3 out, InputModel input) + { + super.read( out, input ); + + out.update(); + + return out; + } + + @Override + public Camera3 read(Camera3 out, ByteBuffer from) + { + super.read( out, from ); + + out.update(); + + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorColor.java b/src/com/axe/math/calc/CalculatorColor.java new file mode 100644 index 0000000..9949c43 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorColor.java @@ -0,0 +1,337 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.color.Color; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +public final class CalculatorColor extends AbstractCalculator +{ + + public static final CalculatorColor INSTANCE = new CalculatorColor(); + + @Override + public Color copy(Color out, Color source) + { + out.r = source.r; + out.g = source.g; + out.b = source.b; + out.a = source.a; + return out; + } + + @Override + public Color parse(Object input, Color defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Color( floats[0], floats[1], floats[2], floats[3] ); + } + + if (input instanceof Color) + { + return (Color)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Color instantiate() + { + return new Color(); + } + + @Override + public Color clear(Color out, float component) + { + out.r = out.g = out.b = out.a = component; + return out; + } + + @Override + public Color unary(Color out, Color value, Unary unaryOperation) + { + out.r = unaryOperation.unary( value.r ); + out.g = unaryOperation.unary( value.g ); + out.b = unaryOperation.unary( value.b ); + out.a = unaryOperation.unary( value.a ); + return out; + } + + @Override + public Color binary(Color out, Color a, Color b, Binary binaryOperation) + { + out.r = binaryOperation.binary( a.r, b.r ); + out.g = binaryOperation.binary( a.g, b.g ); + out.b = binaryOperation.binary( a.b, b.b ); + out.a = binaryOperation.binary( a.b, b.a ); + return out; + } + + @Override + public Color adds(Color out, Color augend, Color addend, float scale) + { + out.r = augend.r + addend.r * scale; + out.g = augend.g + addend.g * scale; + out.b = augend.b + addend.b * scale; + out.a = augend.a + addend.a * scale; + return out; + } + + @Override + public Color mul( Color out, Color value, Color scale ) + { + out.r = value.r * scale.r; + out.g = value.g * scale.g; + out.b = value.b * scale.b; + out.a = value.a * scale.a; + return out; + } + + @Override + public Color div( Color out, Color dividend, Color divisor ) + { + out.r = Numbers.divide( dividend.r, divisor.r ); + out.g = Numbers.divide( dividend.g, divisor.g ); + out.b = Numbers.divide( dividend.b, divisor.b ); + out.a = Numbers.divide( dividend.a, divisor.a ); + return out; + } + + @Override + public Color interpolate( Color out, Color start, Color end, float delta ) + { + out.r = (end.r - start.r) * delta + start.r; + out.g = (end.g - start.g) * delta + start.g; + out.b = (end.b - start.b) * delta + start.b; + out.a = (end.a - start.a) * delta + start.a; + return out; + } + + @Override + public Color contain( Color out, Color min, Color max ) + { + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.g = Numbers.clamp( out.g, min.g, max.g ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + out.a = Numbers.clamp( out.a, min.a, max.a ); + return out; + } + + @Override + public boolean contains( Color point, Color min, Color max ) + { + return point.r >= min.r && point.r <= max.r && + point.g >= min.g && point.g <= max.g && + point.b >= min.b && point.b <= max.b && + point.a >= min.a && point.a <= max.a; + } + + @Override + public Color random( Color out, Color min, Color max, Binary randomizer ) + { + out.r = randomizer.binary( min.r, max.r ); + out.g = randomizer.binary( min.g, max.g ); + out.b = randomizer.binary( min.b, max.b ); + out.a = randomizer.binary( min.a, max.a ); + return out; + } + + @Override + public float dot( Color a, Color b ) + { + return a.r * b.r + a.g * b.g + a.b * b.b + a.a * b.a; + } + + @Override + public float distance( Color a, Color b ) + { + return Math.abs(a.r - b.r) + + Math.abs(a.g - b.g) + + Math.abs(a.b - b.b) + + Math.abs(a.a - b.a); + } + + @Override + public float distanceSq( Color a, Color b ) + { + float d = this.distance( a, b ); + + return d * d; + } + + @Override + public float lengthSq( Color value ) + { + return value.r * value.r + value.g * value.g + value.b * value.b + value.a * value.a; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Color; + } + + @Override + public boolean isFinite( Color value ) + { + return Float.isFinite( value.r ) && + Float.isFinite( value.g ) && + Float.isFinite( value.b ) && + Float.isFinite( value.a ); + } + + @Override + public boolean isEqual( Color a, Color b, float epsilon ) + { + return Numbers.equals( a.r, b.r, epsilon ) && + Numbers.equals( a.g, b.g, epsilon ) && + Numbers.equals( a.b, b.b, epsilon ) && + Numbers.equals( a.a, b.a, epsilon ); + } + + @Override + public int sizeof( Color value ) + { + return 16; + } + + @Override + public int write( Color value, ByteBuffer to ) + { + to.putFloat( value.r ); + to.putFloat( value.g ); + to.putFloat( value.b ); + to.putFloat( value.a ); + return 16; + } + + @Override + public Color read( Color out, ByteBuffer from ) + { + out.r = from.getFloat(); + out.g = from.getFloat(); + out.b = from.getFloat(); + out.a = from.getFloat(); + return out; + } + + @Override + public Color read( Color out, InputModel input ) + { + out.r = input.readFloat( "r" ); + out.g = input.readFloat( "g" ); + out.b = input.readFloat( "b" ); + out.a = input.readFloat( "a" ); + return out; + } + + @Override + public void write( Color value, OutputModel output ) + { + output.write( "r", value.r ); + output.write( "g", value.g ); + output.write( "b", value.b ); + output.write( "a", value.a ); + } + + @Override + public int getComponents() + { + return 4; + } + + @Override + public float getComponent( Color value, int index ) + { + switch (index) { + case 0: return value.r; + case 1: return value.g; + case 2: return value.b; + case 3: return value.a; + } + + return 0; + } + + @Override + public Color setComponent( Color value, int index, float component ) + { + switch (index) { + case 0: + value.r = component; + break; + case 1: + value.g = component; + break; + case 2: + value.b = component; + break; + case 3: + value.a = component; + break; + } + return value; + } + + @Override + public Color mods(Color out, Color value, float divisor) + { + out.r = value.r % divisor; + out.g = value.g % divisor; + out.b = value.b % divisor; + out.a = value.a % divisor; + return out; + } + + @Override + public Color mod(Color out, Color value, Color divisor) + { + out.r = value.r % divisor.r; + out.g = value.g % divisor.g; + out.b = value.b % divisor.b; + out.a = value.a % divisor.a; + return out; + } + + @Override + public Color truncates(Color out, Color value, float divisor) + { + out.r = value.r - value.r % (int)divisor; + out.g = value.g - value.g % (int)divisor; + out.b = value.b - value.b % (int)divisor; + out.a = value.a - value.a % (int)divisor; + return out; + } + + @Override + public Color truncate(Color out, Color value, Color divisor) + { + out.r = value.r - value.r % divisor.r; + out.g = value.g - value.g % divisor.g; + out.b = value.b - value.b % divisor.b; + out.a = value.a - value.a % divisor.a; + return out; + } + + @Override + public Color clamp(Color out, Color min, Color max) + { + out.r = Numbers.clamp( out.r, min.r, max.r ); + out.g = Numbers.clamp( out.g, min.g, max.g ); + out.b = Numbers.clamp( out.b, min.b, max.b ); + out.a = Numbers.clamp( out.a, min.a, max.a ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorCoord.java b/src/com/axe/math/calc/CalculatorCoord.java new file mode 100644 index 0000000..a66159e --- /dev/null +++ b/src/com/axe/math/calc/CalculatorCoord.java @@ -0,0 +1,279 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.gfx.Coord; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +public final class CalculatorCoord extends AbstractCalculator +{ + + public static final CalculatorCoord INSTANCE = new CalculatorCoord(); + + @Override + public Coord copy(Coord out, Coord source) + { + out.s = source.s; + out.t = source.t; + return out; + } + + @Override + public Coord parse(Object input, Coord defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Coord( floats[0], floats[1] ); + } + + if (input instanceof Coord) + { + return (Coord)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Coord instantiate() + { + return new Coord(); + } + + @Override + public Coord clear(Coord out, float component) + { + out.s = out.t = component; + return out; + } + + @Override + public Coord unary(Coord out, Coord value, Unary unaryOperation) + { + out.s = unaryOperation.unary( value.s ); + out.t = unaryOperation.unary( value.t ); + return out; + } + + @Override + public Coord binary(Coord out, Coord a, Coord b, Binary binaryOperation) + { + out.s = binaryOperation.binary( a.s, b.s ); + out.t = binaryOperation.binary( a.t, b.t ); + return out; + } + + @Override + public Coord adds(Coord out, Coord augend, Coord addend, float scale) + { + out.s = augend.s + addend.s * scale; + out.t = augend.t + addend.t * scale; + return out; + } + + @Override + public Coord mul( Coord out, Coord value, Coord scale ) + { + out.s = value.s * scale.s; + out.t = value.t * scale.t; + return out; + } + + @Override + public Coord div( Coord out, Coord dividend, Coord divisor ) + { + out.s = Numbers.divide( dividend.s, divisor.s ); + out.t = Numbers.divide( dividend.t, divisor.t ); + return out; + } + + @Override + public Coord interpolate( Coord out, Coord start, Coord end, float delta ) + { + out.s = (end.s - start.s) * delta + start.s; + out.t = (end.t - start.t) * delta + start.t; + return out; + } + + @Override + public Coord contain( Coord out, Coord min, Coord max ) + { + out.s = Numbers.clamp( out.s, min.s, max.s ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + return out; + } + + @Override + public boolean contains( Coord point, Coord min, Coord max ) + { + return point.s >= min.s && point.s <= max.s && + point.t >= min.t && point.t <= max.t; + } + + @Override + public Coord random( Coord out, Coord min, Coord max, Binary randomizer ) + { + out.s = randomizer.binary( min.s, max.s ); + out.t = randomizer.binary( min.t, max.t ); + return out; + } + + @Override + public float dot( Coord a, Coord b ) + { + return a.s * b.s + a.t * b.t; + } + + @Override + public float distanceSq( Coord a, Coord b ) + { + float dx = a.s - b.s; + float dy = a.t - b.t; + + return dx * dx + dy * dy; + } + + @Override + public float lengthSq( Coord value ) + { + return value.s * value.s + value.t * value.t; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Coord; + } + + @Override + public boolean isFinite( Coord value ) + { + return Float.isFinite( value.s ) && + Float.isFinite( value.t ); + } + + @Override + public boolean isEqual( Coord a, Coord b, float epsilon ) + { + return Numbers.equals( a.s, b.s, epsilon ) && + Numbers.equals( a.t, b.t, epsilon ); + } + + @Override + public int sizeof( Coord value ) + { + return 8; + } + + @Override + public int write( Coord value, ByteBuffer to ) + { + to.putFloat( value.s ); + to.putFloat( value.t ); + return 8; + } + + @Override + public Coord read( Coord out, ByteBuffer from ) + { + out.s = from.getFloat(); + out.t = from.getFloat(); + return out; + } + + @Override + public Coord read( Coord out, InputModel input ) + { + out.s = input.readFloat( "s" ); + out.t = input.readFloat( "t" ); + return out; + } + + @Override + public void write( Coord value, OutputModel output ) + { + output.write( "s", value.s ); + output.write( "t", value.t ); + } + + @Override + public int getComponents() + { + return 2; + } + + @Override + public float getComponent( Coord value, int index ) + { + switch (index) { + case 0: return value.s; + case 1: return value.t; + } + + return 0; + } + + @Override + public Coord setComponent( Coord value, int index, float component ) + { + switch (index) { + case 0: + value.s = component; + break; + case 1: + value.t = component; + break; + } + return value; + } + + @Override + public Coord mods(Coord out, Coord value, float divisor) + { + out.s = value.s % divisor; + out.t = value.t % divisor; + return out; + } + + @Override + public Coord mod(Coord out, Coord value, Coord divisor) + { + out.s = value.s % divisor.s; + out.t = value.t % divisor.t; + return out; + } + + @Override + public Coord truncates(Coord out, Coord value, float divisor) + { + out.s = value.s - value.s % divisor; + out.t = value.t - value.t % divisor; + return out; + } + + @Override + public Coord truncate(Coord out, Coord value, Coord divisor) + { + out.s = value.s - value.s % divisor.s; + out.t = value.t - value.t % divisor.t; + return out; + } + + @Override + public Coord clamp(Coord out, Coord min, Coord max) + { + out.s = Numbers.clamp( out.s, min.s, max.s ); + out.t = Numbers.clamp( out.t, min.t, max.t ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorGeneric.java b/src/com/axe/math/calc/CalculatorGeneric.java new file mode 100644 index 0000000..6e40489 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorGeneric.java @@ -0,0 +1,356 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + +public abstract class CalculatorGeneric extends AbstractCalculator +{ + + protected final int values; + protected final int size; + protected final int components; + protected final int[] componentToValue; + protected final int[] componentBase; + protected final Calculator[] calculators; + + public CalculatorGeneric(int valueCount) + { + Calculator[] calculators = new Calculator[ valueCount ]; + T test = instantiate(); + int size = 0; + int components = 0; + + for (int i = 0; i < valueCount; i++) + { + calculators[ i ] = getCalculator( test, i ); + size += calculators[ i ].sizeof( getValue( test, i ) ); + components += calculators[ i ].getComponents(); + } + + int[] componentToValue = new int[ components ]; + int[] componentBase = new int[ components ]; + int componentIndex = 0; + + for (int i = 0; i < valueCount; i++) + { + int valueComponents = calculators[ i ].getComponents(); + + for (int k = 0; k < valueComponents; k++) + { + componentToValue[ componentIndex ] = i; + componentBase[ componentIndex ] = k; + componentIndex++; + } + } + + this.values = valueCount; + this.size = size; + this.components = components; + this.calculators = calculators; + this.componentToValue = componentToValue; + this.componentBase = componentBase; + } + + protected abstract Object getValue(T out, int index); + + protected Calculator getCalculator(T out, int index) + { + return CalculatorRegistry.getFor( getValue( out, index ) ); + } + + @Override + public T copy(T out, T source) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].copy( getValue( out, i ), getValue( source, i ) ); + } + + return out; + } + + @Override + public T parse(Object input, T defaultValue) + { + return null; + } + + @Override + public T clear(T out, float component) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].clear( getValue( out, i ), component ); + } + + return out; + } + + @Override + public T unary(T out, T value, Unary unaryOoperation) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].unary( getValue( out, i ), getValue( value, i ), unaryOoperation ); + } + + return out; + } + + @Override + public T binary(T out, T a, T b, Binary binaryOperation) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].binary( getValue( out, i ), getValue( a, i ), getValue( b, i ), binaryOperation ); + } + + return out; + } + + @Override + public T adds(T out, T augend, T addend, float scale) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].adds( getValue( out, i ), getValue( augend, i ), getValue( addend, i ), scale ); + } + + return out; + } + + @Override + public T mul(T out, T value, T scale) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].mul( getValue( out, i ), getValue( value, i ), getValue( scale, i ) ); + } + + return out; + } + + @Override + public T div(T out, T dividend, T divisor) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].div( getValue( out, i ), getValue( dividend, i ), getValue( divisor, i ) ); + } + + return out; + } + + @Override + public T contain(T out, T min, T max) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].contain( getValue( out, i ), getValue( min, i ), getValue( max, i ) ); + } + + return out; + } + + @Override + public boolean contains(T point, T min, T max) + { + for (int i = 0; i < values; i++) + { + if ( !calculators[ i ].contains( getValue( point, i ), getValue( min, i ), getValue( max, i ) ) ) + { + return false; + } + } + + return true; + } + + @Override + public T random(T out, T min, T max, Binary randomizer) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].random( getValue( out, i ), getValue( min, i ), getValue( max, i ), randomizer ); + } + + return out; + } + + @Override + public float dot(T a, T b) + { + float dot = 0; + + for (int i = 0; i < values; i++) + { + dot += calculators[ i ].dot( getValue( a, i ), getValue( b, i ) ); + } + + return dot; + } + + @Override + public T mods(T out, T value, float divisor) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].mods( getValue( out, i ), getValue( value, i ), divisor ); + } + + return out; + } + + @Override + public T mod(T out, T value, T divisor) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].mod( getValue( out, i ), getValue( value, i ), getValue( divisor, i ) ); + } + + return out; + } + + @Override + public T truncates(T out, T value, float divisor) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].truncates( getValue( out, i ), getValue( value, i ), divisor ); + } + + return out; + } + + @Override + public T truncate(T out, T value, T divisor) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].truncate( getValue( out, i ), getValue( value, i ), getValue( divisor, i ) ); + } + + return out; + } + + @Override + public T clamp(T out, T min, T max) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].clamp( getValue( out, i ), getValue( min, i ), getValue( max, i ) ); + } + + return out; + } + + @Override + public boolean isValue(Object value) + { + return false; + } + + @Override + public boolean isFinite(T value) + { + for (int i = 0; i < values; i++) + { + if ( !calculators[ i ].isFinite( getValue( value, i ) ) ) + { + return false; + } + } + + return true; + } + + @Override + public boolean isEqual(T a, T b, float epsilon) + { + for (int i = 0; i < values; i++) + { + if ( !calculators[ i ].isEqual( getValue( a, i ), getValue( b, i ) ) ) + { + return false; + } + } + + return true; + } + + @Override + public int sizeof(T value) + { + return size; + } + + @Override + public int write(T value, ByteBuffer to) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].write( getValue( value, i ), to ); + } + + return size; + } + + @Override + public T read(T out, ByteBuffer from) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].read( getValue( out, i ), from ); + } + + return out; + } + + @Override + public void write(T value, OutputModel to) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].write( getValue( value, i ), to ); + } + } + + @Override + public T read(T out, InputModel from) + { + for (int i = 0; i < values; i++) + { + calculators[ i ].read( getValue( out, i ), from ); + } + + return out; + } + + @Override + public int getComponents() + { + return components; + } + + @Override + public float getComponent(T value, int index) + { + int i = componentToValue[ index ]; + + return calculators[ i ].getComponent( getValue( value, i ), index - componentBase[ i ] ); + } + + @Override + public T setComponent(T value, int index, float component) + { + int i = componentToValue[ index ]; + + calculators[ i ].setComponent( getValue( value, i ), index - componentBase[ i ], component ); + + return value; + } + +} diff --git a/src/com/axe/math/calc/CalculatorRangef.java b/src/com/axe/math/calc/CalculatorRangef.java new file mode 100644 index 0000000..708c2c0 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorRangef.java @@ -0,0 +1,284 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Rangef; + +public final class CalculatorRangef extends AbstractCalculator +{ + + public static final CalculatorRangef INSTANCE = new CalculatorRangef(); + + @Override + public Rangef copy(Rangef out, Rangef source) + { + out.min = source.min; + out.max = source.max; + return out; + } + + @Override + public Rangef parse(Object input, Rangef defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Rangef( floats[0], floats[1] ); + } + + if (input instanceof Rangef) + { + return (Rangef)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Rangef instantiate() + { + return new Rangef(); + } + + @Override + public Rangef clear(Rangef out, float component) + { + out.min = out.max = component; + return out; + } + + @Override + public Rangef unary(Rangef out, Rangef value, Unary unaryOperation) + { + out.min = unaryOperation.unary( value.min ); + out.max = unaryOperation.unary( value.max ); + return out; + } + + @Override + public Rangef binary(Rangef out, Rangef a, Rangef b, Binary binaryOperation) + { + out.min = binaryOperation.binary( a.min, b.min ); + out.max = binaryOperation.binary( a.max, b.max ); + return out; + } + + @Override + public Rangef adds(Rangef out, Rangef augend, Rangef addend, float scale) + { + out.min = augend.min + addend.min * scale; + out.max = augend.max + addend.max * scale; + return out; + } + + @Override + public Rangef mul( Rangef out, Rangef value, Rangef scale ) + { + out.min = value.min * scale.min; + out.max = value.max * scale.max; + return out; + } + + @Override + public Rangef div( Rangef out, Rangef dividend, Rangef divisor ) + { + out.min = Numbers.divide( dividend.min, divisor.min ); + out.max = Numbers.divide( dividend.max, divisor.max ); + return out; + } + + @Override + public Rangef interpolate( Rangef out, Rangef start, Rangef end, float delta ) + { + out.min = (end.min - start.min) * delta + start.min; + out.max = (end.max - start.max) * delta + start.max; + return out; + } + + @Override + public Rangef contain( Rangef out, Rangef min, Rangef max ) + { + out.min = Numbers.clamp( out.min, min.min, max.min ); + out.max = Numbers.clamp( out.max, min.max, max.max ); + return out; + } + + @Override + public boolean contains( Rangef point, Rangef min, Rangef max ) + { + return point.min >= min.min && point.min <= max.min && + point.max >= min.max && point.max <= max.max; + } + + @Override + public Rangef random( Rangef out, Rangef min, Rangef max, Binary randomizer ) + { + out.min = randomizer.binary( min.min, max.min ); + out.max = randomizer.binary( min.max, max.max ); + return out; + } + + @Override + public float dot( Rangef a, Rangef b ) + { + return a.min * b.min + a.max * b.max; + } + + @Override + public float distanceSq( Rangef a, Rangef b ) + { + float d = this.distance( a, b ); + + return d * d; + } + + @Override + public float distance( Rangef a, Rangef b ) + { + return Math.abs(a.min - b.min) + Math.abs(a.max - b.max); + } + + @Override + public float lengthSq( Rangef value ) + { + return value.min * value.min + value.max * value.max; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Rangef; + } + + @Override + public boolean isFinite( Rangef value ) + { + return Float.isFinite( value.min ) && + Float.isFinite( value.max ); + } + + @Override + public boolean isEqual( Rangef a, Rangef b, float epsilon ) + { + return Numbers.equals( a.min, b.min, epsilon ) && + Numbers.equals( a.max, b.max, epsilon ); + } + + @Override + public int sizeof( Rangef value ) + { + return 8; + } + + @Override + public int write( Rangef value, ByteBuffer to ) + { + to.putFloat( value.min ); + to.putFloat( value.max ); + return 8; + } + + @Override + public Rangef read( Rangef out, ByteBuffer from ) + { + out.min = from.getFloat(); + out.max = from.getFloat(); + return out; + } + + @Override + public Rangef read( Rangef out, InputModel input ) + { + out.min = input.readFloat( "min" ); + out.max = input.readFloat( "max" ); + return out; + } + + @Override + public void write( Rangef value, OutputModel output ) + { + output.write( "min", value.min ); + output.write( "max", value.max ); + } + + @Override + public int getComponents() + { + return 2; + } + + @Override + public float getComponent( Rangef value, int index ) + { + switch (index) { + case 0: return value.min; + case 1: return value.max; + } + + return 0; + } + + @Override + public Rangef setComponent( Rangef value, int index, float component ) + { + switch (index) { + case 0: + value.min = component; + break; + case 1: + value.max = component; + break; + } + return value; + } + + @Override + public Rangef mods(Rangef out, Rangef value, float divisor) + { + out.min = value.min % divisor; + out.max = value.max % divisor; + return out; + } + + @Override + public Rangef mod(Rangef out, Rangef value, Rangef divisor) + { + out.min = value.min % divisor.min; + out.max = value.max % divisor.max; + return out; + } + + @Override + public Rangef truncates(Rangef out, Rangef value, float divisor) + { + out.min = value.min - value.min % divisor; + out.max = value.max - value.max % divisor; + return out; + } + + @Override + public Rangef truncate(Rangef out, Rangef value, Rangef divisor) + { + out.min = value.min - value.min % divisor.min; + out.max = value.max - value.max % divisor.max; + return out; + } + + @Override + public Rangef clamp(Rangef out, Rangef min, Rangef max) + { + out.min = Numbers.clamp( out.min, min.min, max.min ); + out.max = Numbers.clamp( out.max, min.max, max.max ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorRangei.java b/src/com/axe/math/calc/CalculatorRangei.java new file mode 100644 index 0000000..d23179d --- /dev/null +++ b/src/com/axe/math/calc/CalculatorRangei.java @@ -0,0 +1,284 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Rangei; + +public final class CalculatorRangei extends AbstractCalculator +{ + + public static final CalculatorRangei INSTANCE = new CalculatorRangei(); + + @Override + public Rangei copy(Rangei out, Rangei source) + { + out.min = source.min; + out.max = source.max; + return out; + } + + @Override + public Rangei parse(Object input, Rangei defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof int[]) + { + int[] ints = (int[])input; + + return new Rangei( ints[0], ints[1] ); + } + + if (input instanceof Rangei) + { + return (Rangei)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Rangei instantiate() + { + return new Rangei(); + } + + @Override + public Rangei clear(Rangei out, float component) + { + out.min = out.max = (int)component; + return out; + } + + @Override + public Rangei unary(Rangei out, Rangei value, Unary unaryOperation) + { + out.min = (int)unaryOperation.unary( value.min ); + out.max = (int)unaryOperation.unary( value.max ); + return out; + } + + @Override + public Rangei binary(Rangei out, Rangei a, Rangei b, Binary binaryOperation) + { + out.min = (int)binaryOperation.binary( a.min, b.min ); + out.max = (int)binaryOperation.binary( a.max, b.max ); + return out; + } + + @Override + public Rangei adds(Rangei out, Rangei augend, Rangei addend, float scale) + { + out.min = (int)(augend.min + addend.min * scale); + out.max = (int)(augend.max + addend.max * scale); + return out; + } + + @Override + public Rangei mul( Rangei out, Rangei value, Rangei scale ) + { + out.min = value.min * scale.min; + out.max = value.max * scale.max; + return out; + } + + @Override + public Rangei div( Rangei out, Rangei dividend, Rangei divisor ) + { + out.min = Numbers.divide( dividend.min, divisor.min ); + out.max = Numbers.divide( dividend.max, divisor.max ); + return out; + } + + @Override + public Rangei interpolate( Rangei out, Rangei start, Rangei end, float delta ) + { + out.min = (int)((end.min - start.min) * delta + start.min); + out.max = (int)((end.max - start.max) * delta + start.max); + return out; + } + + @Override + public Rangei contain( Rangei out, Rangei min, Rangei max ) + { + out.min = Numbers.clamp( out.min, min.min, max.min ); + out.max = Numbers.clamp( out.max, min.max, max.max ); + return out; + } + + @Override + public boolean contains( Rangei point, Rangei min, Rangei max ) + { + return point.min >= min.min && point.min <= max.min && + point.max >= min.max && point.max <= max.max; + } + + @Override + public Rangei random( Rangei out, Rangei min, Rangei max, Binary randomizer ) + { + out.min = (int)randomizer.binary( min.min, max.min ); + out.max = (int)randomizer.binary( min.max, max.max ); + return out; + } + + @Override + public float dot( Rangei a, Rangei b ) + { + return a.min * b.min + a.max * b.max; + } + + @Override + public float distanceSq( Rangei a, Rangei b ) + { + float d = this.distance( a, b ); + + return d * d; + } + + @Override + public float distance( Rangei a, Rangei b ) + { + return Math.abs(a.min - b.min) + Math.abs(a.max - b.max); + } + + @Override + public float lengthSq( Rangei value ) + { + return value.min * value.min + value.max * value.max; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Rangei; + } + + @Override + public boolean isFinite( Rangei value ) + { + return Float.isFinite( value.min ) && + Float.isFinite( value.max ); + } + + @Override + public boolean isEqual( Rangei a, Rangei b, float epsilon ) + { + return Numbers.equals( a.min, b.min, epsilon ) && + Numbers.equals( a.max, b.max, epsilon ); + } + + @Override + public int sizeof( Rangei value ) + { + return 8; + } + + @Override + public int write( Rangei value, ByteBuffer to ) + { + to.putInt( value.min ); + to.putInt( value.max ); + return 8; + } + + @Override + public Rangei read( Rangei out, ByteBuffer from ) + { + out.min = from.getInt(); + out.max = from.getInt(); + return out; + } + + @Override + public Rangei read( Rangei out, InputModel input ) + { + out.min = input.readInt( "x" ); + out.max = input.readInt( "y" ); + return out; + } + + @Override + public void write( Rangei value, OutputModel output ) + { + output.write( "x", value.min ); + output.write( "y", value.max ); + } + + @Override + public int getComponents() + { + return 2; + } + + @Override + public float getComponent( Rangei value, int index ) + { + switch (index) { + case 0: return value.min; + case 1: return value.max; + } + + return 0; + } + + @Override + public Rangei setComponent( Rangei value, int index, float component ) + { + switch (index) { + case 0: + value.min = (int)component; + break; + case 1: + value.max = (int)component; + break; + } + return value; + } + + @Override + public Rangei mods(Rangei out, Rangei value, float divisor) + { + out.min = value.min % (int)divisor; + out.max = value.max % (int)divisor; + return out; + } + + @Override + public Rangei mod(Rangei out, Rangei value, Rangei divisor) + { + out.min = value.min % divisor.min; + out.max = value.max % divisor.max; + return out; + } + + @Override + public Rangei truncates(Rangei out, Rangei value, float divisor) + { + out.min = value.min - value.min % (int)divisor; + out.max = value.max - value.max % (int)divisor; + return out; + } + + @Override + public Rangei truncate(Rangei out, Rangei value, Rangei divisor) + { + out.min = value.min - value.min % divisor.min; + out.max = value.max - value.max % divisor.max; + return out; + } + + @Override + public Rangei clamp(Rangei out, Rangei min, Rangei max) + { + out.min = Numbers.clamp( out.min, min.min, max.min ); + out.max = Numbers.clamp( out.max, min.max, max.max ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorRect2f.java b/src/com/axe/math/calc/CalculatorRect2f.java new file mode 100644 index 0000000..ec86f8a --- /dev/null +++ b/src/com/axe/math/calc/CalculatorRect2f.java @@ -0,0 +1,331 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Rect2f; + +public final class CalculatorRect2f extends AbstractCalculator +{ + + public static final CalculatorRect2f INSTANCE = new CalculatorRect2f(); + + @Override + public Rect2f copy(Rect2f out, Rect2f source) + { + out.x = source.x; + out.y = source.y; + out.w = source.w; + out.h = source.h; + return out; + } + + @Override + public Rect2f parse(Object input, Rect2f defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Rect2f( floats[0], floats[1], floats[2], floats[3] ); + } + + if (input instanceof Rect2f) + { + return (Rect2f)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Rect2f instantiate() + { + return new Rect2f(); + } + + @Override + public Rect2f clear(Rect2f out, float component) + { + out.x = out.y = out.w = out.h = component; + return out; + } + + @Override + public Rect2f unary(Rect2f out, Rect2f value, Unary unaryOperation) + { + out.x = unaryOperation.unary( value.x ); + out.y = unaryOperation.unary( value.y ); + out.w = unaryOperation.unary( value.w ); + out.h = unaryOperation.unary( value.h ); + return out; + } + + @Override + public Rect2f binary(Rect2f out, Rect2f a, Rect2f b, Binary binaryOperation) + { + out.x = binaryOperation.binary( a.x, b.x ); + out.y = binaryOperation.binary( a.y, b.y ); + out.w = binaryOperation.binary( a.w, b.w ); + out.h = binaryOperation.binary( a.w, b.h ); + return out; + } + + @Override + public Rect2f adds(Rect2f out, Rect2f augend, Rect2f addend, float scale) + { + out.x = augend.x + addend.x * scale; + out.y = augend.y + addend.y * scale; + out.w = augend.w + addend.w * scale; + out.h = augend.h + addend.h * scale; + return out; + } + + @Override + public Rect2f mul( Rect2f out, Rect2f value, Rect2f scale ) + { + out.x = value.x * scale.x; + out.y = value.y * scale.y; + out.w = value.w * scale.w; + out.h = value.h * scale.h; + return out; + } + + @Override + public Rect2f div( Rect2f out, Rect2f dividend, Rect2f divisor ) + { + out.x = Numbers.divide( dividend.x, divisor.x ); + out.y = Numbers.divide( dividend.y, divisor.y ); + out.w = Numbers.divide( dividend.w, divisor.w ); + out.h = Numbers.divide( dividend.h, divisor.h ); + return out; + } + + @Override + public Rect2f interpolate( Rect2f out, Rect2f start, Rect2f end, float delta ) + { + out.x = (end.x - start.x) * delta + start.x; + out.y = (end.y - start.y) * delta + start.y; + out.w = (end.w - start.w) * delta + start.w; + out.h = (end.h - start.h) * delta + start.h; + return out; + } + + @Override + public Rect2f contain( Rect2f out, Rect2f min, Rect2f max ) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.w = Numbers.clamp( out.w, min.w, max.w ); + out.h = Numbers.clamp( out.h, min.h, max.h ); + return out; + } + + @Override + public boolean contains( Rect2f point, Rect2f min, Rect2f max ) + { + return point.x >= min.x && point.x <= max.x && + point.y >= min.y && point.y <= max.y && + point.w >= min.w && point.w <= max.w && + point.h >= min.h && point.h <= max.h; + } + + @Override + public Rect2f random( Rect2f out, Rect2f min, Rect2f max, Binary randomizer ) + { + out.x = randomizer.binary( min.x, max.x ); + out.y = randomizer.binary( min.y, max.y ); + out.w = randomizer.binary( min.w, max.w ); + out.h = randomizer.binary( min.h, max.h ); + return out; + } + + @Override + public float dot( Rect2f a, Rect2f b ) + { + return a.x * b.x + a.y * b.y + a.w * b.w + a.h * b.h; + } + + @Override + public float distanceSq( Rect2f a, Rect2f b ) + { + float dr = a.x - b.x; + float dg = a.y - b.y; + float db = a.w - b.w; + float da = a.h - b.h; + + return dr * dr + dg * dg + db * db + da * da; + } + + @Override + public float lengthSq( Rect2f value ) + { + return value.x * value.x + value.y * value.y + value.w * value.w + value.h * value.h; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Rect2f; + } + + @Override + public boolean isFinite( Rect2f value ) + { + return Float.isFinite( value.x ) && + Float.isFinite( value.y ) && + Float.isFinite( value.w ) && + Float.isFinite( value.h ); + } + + @Override + public boolean isEqual( Rect2f a, Rect2f b, float epsilon ) + { + return Numbers.equals( a.x, b.x, epsilon ) && + Numbers.equals( a.y, b.y, epsilon ) && + Numbers.equals( a.w, b.w, epsilon ) && + Numbers.equals( a.h, b.h, epsilon ); + } + + @Override + public int sizeof( Rect2f value ) + { + return 16; + } + + @Override + public int write( Rect2f value, ByteBuffer to ) + { + to.putFloat( value.x ); + to.putFloat( value.y ); + to.putFloat( value.w ); + to.putFloat( value.h ); + return 16; + } + + @Override + public Rect2f read( Rect2f out, ByteBuffer from ) + { + out.x = from.getFloat(); + out.y = from.getFloat(); + out.w = from.getFloat(); + out.h = from.getFloat(); + return out; + } + + @Override + public Rect2f read( Rect2f out, InputModel input ) + { + out.x = input.readFloat( "x" ); + out.y = input.readFloat( "y" ); + out.w = input.readFloat( "w" ); + out.h = input.readFloat( "h" ); + return out; + } + + @Override + public void write( Rect2f value, OutputModel output ) + { + output.write( "x", value.x ); + output.write( "y", value.y ); + output.write( "w", value.w ); + output.write( "h", value.h ); + } + + @Override + public int getComponents() + { + return 4; + } + + @Override + public float getComponent( Rect2f value, int index ) + { + switch (index) { + case 0: return value.x; + case 1: return value.y; + case 2: return value.w; + case 3: return value.h; + } + + return 0; + } + + @Override + public Rect2f setComponent( Rect2f value, int index, float component ) + { + switch (index) { + case 0: + value.x = component; + break; + case 1: + value.y = component; + break; + case 2: + value.w = component; + break; + case 3: + value.h = component; + break; + } + return value; + } + + @Override + public Rect2f mods(Rect2f out, Rect2f value, float divisor) + { + out.x = value.x % divisor; + out.y = value.y % divisor; + out.w = value.w % divisor; + out.h = value.h % divisor; + return out; + } + + @Override + public Rect2f mod(Rect2f out, Rect2f value, Rect2f divisor) + { + out.x = value.x % divisor.x; + out.y = value.y % divisor.y; + out.w = value.w % divisor.w; + out.h = value.h % divisor.h; + return out; + } + + @Override + public Rect2f truncates(Rect2f out, Rect2f value, float divisor) + { + out.x = value.x - value.x % divisor; + out.y = value.y - value.y % divisor; + out.w = value.w - value.w % divisor; + out.h = value.h - value.h % divisor; + return out; + } + + @Override + public Rect2f truncate(Rect2f out, Rect2f value, Rect2f divisor) + { + out.x = value.x - value.x % divisor.x; + out.y = value.y - value.y % divisor.y; + out.w = value.w - value.w % divisor.w; + out.h = value.h - value.h % divisor.h; + return out; + } + + @Override + public Rect2f clamp(Rect2f out, Rect2f min, Rect2f max) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.w = Numbers.clamp( out.w, min.w, max.w ); + out.h = Numbers.clamp( out.h, min.h, max.h ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorRect2i.java b/src/com/axe/math/calc/CalculatorRect2i.java new file mode 100644 index 0000000..8685d81 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorRect2i.java @@ -0,0 +1,331 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Rect2i; + +public final class CalculatorRect2i extends AbstractCalculator +{ + + public static final CalculatorRect2i INSTANCE = new CalculatorRect2i(); + + @Override + public Rect2i copy(Rect2i out, Rect2i source) + { + out.x = source.x; + out.y = source.y; + out.w = source.w; + out.h = source.h; + return out; + } + + @Override + public Rect2i parse(Object input, Rect2i defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof int[]) + { + int[] ints = (int[])input; + + return new Rect2i( ints[0], ints[1], ints[2], ints[3] ); + } + + if (input instanceof Rect2i) + { + return (Rect2i)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Rect2i instantiate() + { + return new Rect2i(); + } + + @Override + public Rect2i clear(Rect2i out, float component) + { + out.x = out.y = out.w = out.h = (int)component; + return out; + } + + @Override + public Rect2i unary(Rect2i out, Rect2i value, Unary unaryOperation) + { + out.x = (int)unaryOperation.unary( value.x ); + out.y = (int)unaryOperation.unary( value.y ); + out.w = (int)unaryOperation.unary( value.w ); + out.h = (int)unaryOperation.unary( value.h ); + return out; + } + + @Override + public Rect2i binary(Rect2i out, Rect2i a, Rect2i b, Binary binaryOperation) + { + out.x = (int)binaryOperation.binary( a.x, b.x ); + out.y = (int)binaryOperation.binary( a.y, b.y ); + out.w = (int)binaryOperation.binary( a.w, b.w ); + out.h = (int)binaryOperation.binary( a.w, b.h ); + return out; + } + + @Override + public Rect2i adds(Rect2i out, Rect2i augend, Rect2i addend, float scale) + { + out.x = (int)(augend.x + addend.x * scale); + out.y = (int)(augend.y + addend.y * scale); + out.w = (int)(augend.w + addend.w * scale); + out.h = (int)(augend.h + addend.h * scale); + return out; + } + + @Override + public Rect2i mul( Rect2i out, Rect2i value, Rect2i scale ) + { + out.x = value.x * scale.x; + out.y = value.y * scale.y; + out.w = value.w * scale.w; + out.h = value.h * scale.h; + return out; + } + + @Override + public Rect2i div( Rect2i out, Rect2i dividend, Rect2i divisor ) + { + out.x = Numbers.divide( dividend.x, divisor.x ); + out.y = Numbers.divide( dividend.y, divisor.y ); + out.w = Numbers.divide( dividend.w, divisor.w ); + out.h = Numbers.divide( dividend.h, divisor.h ); + return out; + } + + @Override + public Rect2i interpolate( Rect2i out, Rect2i start, Rect2i end, float delta ) + { + out.x = (int)((end.x - start.x) * delta + start.x); + out.y = (int)((end.y - start.y) * delta + start.y); + out.w = (int)((end.w - start.w) * delta + start.w); + out.h = (int)((end.h - start.h) * delta + start.h); + return out; + } + + @Override + public Rect2i contain( Rect2i out, Rect2i min, Rect2i max ) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.w = Numbers.clamp( out.w, min.w, max.w ); + out.h = Numbers.clamp( out.h, min.h, max.h ); + return out; + } + + @Override + public boolean contains( Rect2i point, Rect2i min, Rect2i max ) + { + return point.x >= min.x && point.x <= max.x && + point.y >= min.y && point.y <= max.y && + point.w >= min.w && point.w <= max.w && + point.h >= min.h && point.h <= max.h; + } + + @Override + public Rect2i random( Rect2i out, Rect2i min, Rect2i max, Binary randomizer ) + { + out.x = (int)randomizer.binary( min.x, max.x ); + out.y = (int)randomizer.binary( min.y, max.y ); + out.w = (int)randomizer.binary( min.w, max.w ); + out.h = (int)randomizer.binary( min.h, max.h ); + return out; + } + + @Override + public float dot( Rect2i a, Rect2i b ) + { + return a.x * b.x + a.y * b.y + a.w * b.w + a.h * b.h; + } + + @Override + public float distanceSq( Rect2i a, Rect2i b ) + { + float dr = a.x - b.x; + float dg = a.y - b.y; + float db = a.w - b.w; + float da = a.h - b.h; + + return dr * dr + dg * dg + db * db + da * da; + } + + @Override + public float lengthSq( Rect2i value ) + { + return value.x * value.x + value.y * value.y + value.w * value.w + value.h * value.h; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Rect2i; + } + + @Override + public boolean isFinite( Rect2i value ) + { + return Float.isFinite( value.x ) && + Float.isFinite( value.y ) && + Float.isFinite( value.w ) && + Float.isFinite( value.h ); + } + + @Override + public boolean isEqual( Rect2i a, Rect2i b, float epsilon ) + { + return Numbers.equals( a.x, b.x, epsilon ) && + Numbers.equals( a.y, b.y, epsilon ) && + Numbers.equals( a.w, b.w, epsilon ) && + Numbers.equals( a.h, b.h, epsilon ); + } + + @Override + public int sizeof( Rect2i value ) + { + return 16; + } + + @Override + public int write( Rect2i value, ByteBuffer to ) + { + to.putFloat( value.x ); + to.putFloat( value.y ); + to.putFloat( value.w ); + to.putFloat( value.h ); + return 16; + } + + @Override + public Rect2i read( Rect2i out, ByteBuffer from ) + { + out.x = from.getInt(); + out.y = from.getInt(); + out.w = from.getInt(); + out.h = from.getInt(); + return out; + } + + @Override + public Rect2i read( Rect2i out, InputModel input ) + { + out.x = input.readInt( "x" ); + out.y = input.readInt( "y" ); + out.w = input.readInt( "w" ); + out.h = input.readInt( "h" ); + return out; + } + + @Override + public void write( Rect2i value, OutputModel output ) + { + output.write( "x", value.x ); + output.write( "y", value.y ); + output.write( "w", value.w ); + output.write( "h", value.h ); + } + + @Override + public int getComponents() + { + return 4; + } + + @Override + public float getComponent( Rect2i value, int index ) + { + switch (index) { + case 0: return value.x; + case 1: return value.y; + case 2: return value.w; + case 3: return value.h; + } + + return 0; + } + + @Override + public Rect2i setComponent( Rect2i value, int index, float component ) + { + switch (index) { + case 0: + value.x = (int)component; + break; + case 1: + value.y = (int)component; + break; + case 2: + value.w = (int)component; + break; + case 3: + value.h = (int)component; + break; + } + return value; + } + + @Override + public Rect2i mods(Rect2i out, Rect2i value, float divisor) + { + out.x = value.x % (int)divisor; + out.y = value.y % (int)divisor; + out.w = value.w % (int)divisor; + out.h = value.h % (int)divisor; + return out; + } + + @Override + public Rect2i mod(Rect2i out, Rect2i value, Rect2i divisor) + { + out.x = value.x % divisor.x; + out.y = value.y % divisor.y; + out.w = value.w % divisor.w; + out.h = value.h % divisor.h; + return out; + } + + @Override + public Rect2i truncates(Rect2i out, Rect2i value, float divisor) + { + out.x = value.x - value.x % (int)divisor; + out.y = value.y - value.y % (int)divisor; + out.w = value.w - value.w % (int)divisor; + out.h = value.h - value.h % (int)divisor; + return out; + } + + @Override + public Rect2i truncate(Rect2i out, Rect2i value, Rect2i divisor) + { + out.x = value.x - value.x % divisor.x; + out.y = value.y - value.y % divisor.y; + out.w = value.w - value.w % divisor.w; + out.h = value.h - value.h % divisor.h; + return out; + } + + @Override + public Rect2i clamp(Rect2i out, Rect2i min, Rect2i max) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.w = Numbers.clamp( out.w, min.w, max.w ); + out.h = Numbers.clamp( out.h, min.h, max.h ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorRegistry.java b/src/com/axe/math/calc/CalculatorRegistry.java new file mode 100644 index 0000000..fb77157 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorRegistry.java @@ -0,0 +1,69 @@ +package com.axe.math.calc; + +import java.util.HashMap; +import java.util.Map; + +import com.axe.core.Attribute; +import com.axe.util.Array; + +public class CalculatorRegistry +{ + + private static Calculator[] calculators = {}; + private static Map calculatorMap = new HashMap<>(); + + public static Calculator getFor( T value ) + { + if ( value == null ) + { + throw new RuntimeException( "No calculator can be determined with null" ); + } + + if ( value instanceof Attribute ) + { + return ((Attribute)value).getCalculator(); + } + + Calculator calc = calculatorMap.get( value.getClass() ); + + if ( calc != null ) + { + return calc; + } + + for (int i = 0; i < calculators.length; i++) + { + calc = calculators[ i ]; + + if ( calc.isValue( value ) ) + { + return calc; + } + } + + throw new RuntimeException( "No calculator exists for the following value: " + value ); + } + + public static Calculator get( Object nameOrType ) + { + Calculator calc = calculatorMap.get( nameOrType ); + + if (calc != null) + { + return calc; + } + + throw new RuntimeException( "No calculator exists for the following type: " + nameOrType ); + } + + public static void register( Calculator calculator, Object ... namesOrTypes ) + { + for (int i = 0; i < namesOrTypes.length; i++) + { + calculatorMap.put( namesOrTypes[ i ], calculator ); + } + + calculators = Array.add( calculator, calculators ); + } + +} diff --git a/src/com/axe/math/calc/CalculatorScalarf.java b/src/com/axe/math/calc/CalculatorScalarf.java new file mode 100644 index 0000000..454c035 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorScalarf.java @@ -0,0 +1,255 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Scalarf; + +public final class CalculatorScalarf extends AbstractCalculator +{ + + public static final CalculatorScalarf INSTANCE = new CalculatorScalarf(); + + @Override + public Scalarf copy(Scalarf out, Scalarf source) + { + out.v = source.v; + return out; + } + + @Override + public Scalarf parse(Object input, Scalarf defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + return createUniform( ((float[])input)[0] ); + } + + if (input instanceof Scalarf) + { + return (Scalarf)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Scalarf instantiate() + { + return new Scalarf(); + } + + @Override + public Scalarf clear(Scalarf out, float component) + { + out.v = component; + return out; + } + + @Override + public Scalarf unary(Scalarf out, Scalarf value, Unary unaryOperation) + { + out.v = unaryOperation.unary( value.v ); + return out; + } + + @Override + public Scalarf binary(Scalarf out, Scalarf a, Scalarf b, Binary binaryOperation) + { + out.v = binaryOperation.binary( a.v, b.v ); + return out; + } + + @Override + public Scalarf adds(Scalarf out, Scalarf augend, Scalarf addend, float scale) + { + out.v = augend.v + addend.v * scale; + return out; + } + + @Override + public Scalarf mul( Scalarf out, Scalarf value, Scalarf scale ) + { + out.v = value.v * scale.v; + return out; + } + + @Override + public Scalarf div( Scalarf out, Scalarf dividend, Scalarf divisor ) + { + out.v = Numbers.divide( dividend.v, divisor.v ); + return out; + } + + @Override + public Scalarf interpolate( Scalarf out, Scalarf start, Scalarf end, float delta ) + { + out.v = (end.v - start.v) * delta + start.v; + return out; + } + + @Override + public Scalarf contain( Scalarf out, Scalarf min, Scalarf max ) + { + out.v = Numbers.clamp( out.v, min.v, max.v ); + return out; + } + + @Override + public boolean contains( Scalarf point, Scalarf min, Scalarf max ) + { + return point.v >= min.v && point.v <= max.v; + } + + @Override + public Scalarf random( Scalarf out, Scalarf min, Scalarf max, Binary randomizer ) + { + out.v = randomizer.binary( min.v, max.v ); + return out; + } + + @Override + public float dot( Scalarf a, Scalarf b ) + { + return a.v * b.v; + } + + @Override + public float distance( Scalarf a, Scalarf b ) + { + return Math.abs( a.v - b.v ); + } + + @Override + public float distanceSq( Scalarf a, Scalarf b ) + { + float dx = a.v - b.v; + + return dx * dx; + } + + @Override + public float length( Scalarf value ) + { + return Math.abs( value.v ); + } + + @Override + public float lengthSq( Scalarf value ) + { + return value.v * value.v; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Scalarf; + } + + @Override + public boolean isFinite( Scalarf value ) + { + return Float.isFinite( value.v ); + } + + @Override + public boolean isEqual( Scalarf a, Scalarf b, float epsilon ) + { + return Numbers.equals( a.v, b.v, epsilon ); + } + + @Override + public int sizeof( Scalarf value ) + { + return 4; + } + + @Override + public int write( Scalarf value, ByteBuffer to ) + { + to.putFloat( value.v ); + return 4; + } + + @Override + public Scalarf read( Scalarf out, ByteBuffer from ) + { + out.v = from.getFloat(); + return out; + } + + @Override + public Scalarf read( Scalarf out, InputModel input ) + { + out.v = input.readFloat( "v" ); + return out; + } + + @Override + public void write( Scalarf value, OutputModel output ) + { + output.write( "v", value.v ); + } + + @Override + public int getComponents() + { + return 1; + } + + @Override + public float getComponent( Scalarf value, int index ) + { + return value.v; + } + + @Override + public Scalarf setComponent( Scalarf value, int index, float component ) + { + value.v = component; + return value; + } + + @Override + public Scalarf mods(Scalarf out, Scalarf value, float divisor) + { + out.v = value.v % divisor; + return out; + } + + @Override + public Scalarf mod(Scalarf out, Scalarf value, Scalarf divisor) + { + out.v = value.v % divisor.v; + return out; + } + + @Override + public Scalarf truncates(Scalarf out, Scalarf value, float divisor) + { + out.v = value.v - value.v % divisor; + return out; + } + + @Override + public Scalarf truncate(Scalarf out, Scalarf value, Scalarf divisor) + { + out.v = value.v - value.v % divisor.v; + return out; + } + + @Override + public Scalarf clamp(Scalarf out, Scalarf min, Scalarf max) + { + out.v = Numbers.clamp( out.v, min.v, max.v ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorScalari.java b/src/com/axe/math/calc/CalculatorScalari.java new file mode 100644 index 0000000..5e79e88 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorScalari.java @@ -0,0 +1,255 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Scalari; + +public final class CalculatorScalari extends AbstractCalculator +{ + + public static final CalculatorScalari INSTANCE = new CalculatorScalari(); + + @Override + public Scalari copy(Scalari out, Scalari source) + { + out.v = source.v; + return out; + } + + @Override + public Scalari parse(Object input, Scalari defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + return createUniform( ((float[])input)[0] ); + } + + if (input instanceof Scalari) + { + return (Scalari)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Scalari instantiate() + { + return new Scalari(); + } + + @Override + public Scalari clear(Scalari out, float component) + { + out.v = (int)component; + return out; + } + + @Override + public Scalari unary(Scalari out, Scalari value, Unary unaryOperation) + { + out.v = (int)unaryOperation.unary( value.v ); + return out; + } + + @Override + public Scalari binary(Scalari out, Scalari a, Scalari b, Binary binaryOperation) + { + out.v = (int)binaryOperation.binary( a.v, b.v ); + return out; + } + + @Override + public Scalari adds(Scalari out, Scalari augend, Scalari addend, float scale) + { + out.v = (int)(augend.v + addend.v * scale); + return out; + } + + @Override + public Scalari mul( Scalari out, Scalari value, Scalari scale ) + { + out.v = value.v * scale.v; + return out; + } + + @Override + public Scalari div( Scalari out, Scalari dividend, Scalari divisor ) + { + out.v = Numbers.divide( dividend.v, divisor.v ); + return out; + } + + @Override + public Scalari interpolate( Scalari out, Scalari start, Scalari end, float delta ) + { + out.v = (int)((end.v - start.v) * delta + start.v); + return out; + } + + @Override + public Scalari contain( Scalari out, Scalari min, Scalari max ) + { + out.v = Numbers.clamp( out.v, min.v, max.v ); + return out; + } + + @Override + public boolean contains( Scalari point, Scalari min, Scalari max ) + { + return point.v >= min.v && point.v <= max.v; + } + + @Override + public Scalari random( Scalari out, Scalari min, Scalari max, Binary randomizer ) + { + out.v = (int)randomizer.binary( min.v, max.v ); + return out; + } + + @Override + public float dot( Scalari a, Scalari b ) + { + return a.v * b.v; + } + + @Override + public float distance( Scalari a, Scalari b ) + { + return Math.abs( a.v - b.v ); + } + + @Override + public float distanceSq( Scalari a, Scalari b ) + { + float dx = a.v - b.v; + + return dx * dx; + } + + @Override + public float length( Scalari value ) + { + return Math.abs( value.v ); + } + + @Override + public float lengthSq( Scalari value ) + { + return value.v * value.v; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Scalari; + } + + @Override + public boolean isFinite( Scalari value ) + { + return Float.isFinite( value.v ); + } + + @Override + public boolean isEqual( Scalari a, Scalari b, float epsilon ) + { + return Numbers.equals( a.v, b.v, epsilon ); + } + + @Override + public int sizeof( Scalari value ) + { + return 4; + } + + @Override + public int write( Scalari value, ByteBuffer to ) + { + to.putInt( value.v ); + return 4; + } + + @Override + public Scalari read( Scalari out, ByteBuffer from ) + { + out.v = from.getInt(); + return out; + } + + @Override + public Scalari read( Scalari out, InputModel input ) + { + out.v = input.readInt( "v" ); + return out; + } + + @Override + public void write( Scalari value, OutputModel output ) + { + output.write( "v", value.v ); + } + + @Override + public int getComponents() + { + return 1; + } + + @Override + public float getComponent( Scalari value, int index ) + { + return value.v; + } + + @Override + public Scalari setComponent( Scalari value, int index, float component ) + { + value.v = (int)component; + return value; + } + + @Override + public Scalari mods(Scalari out, Scalari value, float divisor) + { + out.v = value.v % (int)divisor; + return out; + } + + @Override + public Scalari mod(Scalari out, Scalari value, Scalari divisor) + { + out.v = value.v % divisor.v; + return out; + } + + @Override + public Scalari truncates(Scalari out, Scalari value, float divisor) + { + out.v = value.v - value.v % (int)divisor; + return out; + } + + @Override + public Scalari truncate(Scalari out, Scalari value, Scalari divisor) + { + out.v = value.v - value.v % divisor.v; + return out; + } + + @Override + public Scalari clamp(Scalari out, Scalari min, Scalari max) + { + out.v = Numbers.clamp( out.v, min.v, max.v ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorTile.java b/src/com/axe/math/calc/CalculatorTile.java new file mode 100644 index 0000000..487df75 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorTile.java @@ -0,0 +1,358 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import org.magnos.asset.Assets; + +import com.axe.gfx.TextureInfo; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.tile.Tile; + +public final class CalculatorTile extends AbstractCalculator +{ + + public static final CalculatorTile INSTANCE = new CalculatorTile(); + + public static final String NO_VALUE = "NO_VALUE"; + + @Override + public Tile copy(Tile out, Tile source) + { + out.s0 = source.s0; + out.t0 = source.t0; + out.s1 = source.s1; + out.t1 = source.t1; + out.texture = source.texture; + return out; + } + + @Override + public Tile parse(Object input, Tile defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Tile( floats[0], floats[1], floats[2], floats[3] ); + } + + if (input instanceof Tile) + { + return (Tile)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Tile instantiate() + { + return new Tile(); + } + + @Override + public Tile clear(Tile out, float component) + { + out.s0 = out.t0 = out.s1 = out.t1 = component; + return out; + } + + @Override + public Tile unary(Tile out, Tile value, Unary unaryOperation) + { + out.s0 = unaryOperation.unary( value.s0 ); + out.t0 = unaryOperation.unary( value.t0 ); + out.s1 = unaryOperation.unary( value.s1 ); + out.t1 = unaryOperation.unary( value.t1 ); + return out; + } + + @Override + public Tile binary(Tile out, Tile a, Tile b, Binary binaryOperation) + { + out.s0 = binaryOperation.binary( a.s0, b.s0 ); + out.t0 = binaryOperation.binary( a.t0, b.t0 ); + out.s1 = binaryOperation.binary( a.s1, b.s1 ); + out.t1 = binaryOperation.binary( a.s1, b.t1 ); + return out; + } + + @Override + public Tile adds(Tile out, Tile augend, Tile addend, float scale) + { + out.s0 = augend.s0 + addend.s0 * scale; + out.t0 = augend.t0 + addend.t0 * scale; + out.s1 = augend.s1 + addend.s1 * scale; + out.t1 = augend.t1 + addend.t1 * scale; + return out; + } + + @Override + public Tile mul( Tile out, Tile value, Tile scale ) + { + out.s0 = value.s0 * scale.s0; + out.t0 = value.t0 * scale.t0; + out.s1 = value.s1 * scale.s1; + out.t1 = value.t1 * scale.t1; + return out; + } + + @Override + public Tile div( Tile out, Tile dividend, Tile divisor ) + { + out.s0 = Numbers.divide( dividend.s0, divisor.s0 ); + out.t0 = Numbers.divide( dividend.t0, divisor.t0 ); + out.s1 = Numbers.divide( dividend.s1, divisor.s1 ); + out.t1 = Numbers.divide( dividend.t1, divisor.t1 ); + return out; + } + + @Override + public Tile interpolate( Tile out, Tile start, Tile end, float delta ) + { + out.s0 = (end.s0 - start.s0) * delta + start.s0; + out.t0 = (end.t0 - start.t0) * delta + start.t0; + out.s1 = (end.s1 - start.s1) * delta + start.s1; + out.t1 = (end.t1 - start.t1) * delta + start.t1; + return out; + } + + @Override + public Tile contain( Tile out, Tile min, Tile max ) + { + out.s0 = Numbers.clamp( out.s0, min.s0, max.s0 ); + out.t0 = Numbers.clamp( out.t0, min.t0, max.t0 ); + out.s1 = Numbers.clamp( out.s1, min.s1, max.s1 ); + out.t1 = Numbers.clamp( out.t1, min.t1, max.t1 ); + return out; + } + + @Override + public boolean contains( Tile point, Tile min, Tile max ) + { + return point.s0 >= min.s0 && point.s0 <= max.s0 && + point.t0 >= min.t0 && point.t0 <= max.t0 && + point.s1 >= min.s1 && point.s1 <= max.s1 && + point.t1 >= min.t1 && point.t1 <= max.t1; + } + + @Override + public Tile random( Tile out, Tile min, Tile max, Binary randomizer ) + { + out.s0 = randomizer.binary( min.s0, max.s0 ); + out.t0 = randomizer.binary( min.t0, max.t0 ); + out.s1 = randomizer.binary( min.s1, max.s1 ); + out.t1 = randomizer.binary( min.t1, max.t1 ); + return out; + } + + @Override + public float dot( Tile a, Tile b ) + { + return a.s0 * b.s0 + a.t0 * b.t0 + a.s1 * b.s1 + a.t1 * b.t1; + } + + @Override + public float distanceSq( Tile a, Tile b ) + { + return 1; + } + + @Override + public float distance( Tile a, Tile b ) + { + return 1; + } + + @Override + public float lengthSq( Tile value ) + { + return value.s0 * value.s0 + value.t0 * value.t0 + value.s1 * value.s1 + value.t1 * value.t1; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Tile; + } + + @Override + public boolean isFinite( Tile value ) + { + return Float.isFinite( value.s0 ) && + Float.isFinite( value.t0 ) && + Float.isFinite( value.s1 ) && + Float.isFinite( value.t1 ); + } + + @Override + public boolean isEqual( Tile a, Tile b, float epsilon ) + { + return Numbers.equals( a.s0, b.s0, epsilon ) && + Numbers.equals( a.t0, b.t0, epsilon ) && + Numbers.equals( a.s1, b.s1, epsilon ) && + Numbers.equals( a.t1, b.t1, epsilon ); + } + + @Override + public int sizeof( Tile value ) + { + return 16; + } + + @Override + public int write( Tile value, ByteBuffer to ) + { + to.putFloat( value.s0 ); + to.putFloat( value.t0 ); + to.putFloat( value.s1 ); + to.putFloat( value.t1 ); + return 16; + } + + @Override + public Tile read( Tile out, ByteBuffer from ) + { + out.s0 = from.getFloat(); + out.t0 = from.getFloat(); + out.s1 = from.getFloat(); + out.t1 = from.getFloat(); + return out; + } + + @Override + public Tile read( Tile out, InputModel input ) + { + out.s0 = input.readFloat( "s0" ); + out.t0 = input.readFloat( "t0" ); + out.s1 = input.readFloat( "s1" ); + out.t1 = input.readFloat( "t1" ); + + String texturePath = input.readString( "texture", NO_VALUE ); + + if (texturePath != NO_VALUE) + { + out.texture = Assets.load( texturePath ); + } + else + { + InputModel textureModel = input.readModel( "texture" ); + String textureName = textureModel.readString( "ref" ); + TextureInfo textureInfo = textureModel.readModel( "info", new TextureInfo() ); + out.texture = Assets.load( textureName, textureInfo ); + } + + return out; + } + + @Override + public void write( Tile value, OutputModel output ) + { + output.write( "s0", value.s0 ); + output.write( "t0", value.t0 ); + output.write( "s1", value.s1 ); + output.write( "t1", value.t1 ); + + TextureInfo textureInfo = value.texture.getInfo(); + OutputModel textureModel = output.writeModel( "texture" ); + textureModel.write( "ref", textureInfo.getPath() ); + textureModel.writeModel( "info", textureInfo ); + } + + @Override + public int getComponents() + { + return 4; + } + + @Override + public float getComponent( Tile value, int index ) + { + switch (index) { + case 0: return value.s0; + case 1: return value.t0; + case 2: return value.s1; + case 3: return value.t1; + } + + return 0; + } + + @Override + public Tile setComponent( Tile value, int index, float component ) + { + switch (index) { + case 0: + value.s0 = component; + break; + case 1: + value.t0 = component; + break; + case 2: + value.s1 = component; + break; + case 3: + value.t1 = component; + break; + } + return value; + } + + @Override + public Tile mods(Tile out, Tile value, float divisor) + { + out.s0 = value.s0 % divisor; + out.t0 = value.t0 % divisor; + out.s1 = value.s1 % divisor; + out.t1 = value.t1 % divisor; + return out; + } + + @Override + public Tile mod(Tile out, Tile value, Tile divisor) + { + out.s0 = value.s0 % divisor.s0; + out.t0 = value.t0 % divisor.t0; + out.s1 = value.s1 % divisor.s1; + out.t1 = value.t1 % divisor.t1; + return out; + } + + @Override + public Tile truncates(Tile out, Tile value, float divisor) + { + out.s0 = value.s0 - value.s0 % divisor; + out.t0 = value.t0 - value.t0 % divisor; + out.s1 = value.s1 - value.s1 % divisor; + out.t1 = value.t1 - value.t1 % divisor; + return out; + } + + @Override + public Tile truncate(Tile out, Tile value, Tile divisor) + { + out.s0 = value.s0 - value.s0 % divisor.s0; + out.t0 = value.t0 - value.t0 % divisor.t0; + out.s1 = value.s1 - value.s1 % divisor.s1; + out.t1 = value.t1 - value.t1 % divisor.t1; + return out; + } + + @Override + public Tile clamp(Tile out, Tile min, Tile max) + { + out.s0 = Numbers.clamp( out.s0, min.s0, max.s0 ); + out.t0 = Numbers.clamp( out.t0, min.t0, max.t0 ); + out.s1 = Numbers.clamp( out.s1, min.s1, max.s1 ); + out.t1 = Numbers.clamp( out.t1, min.t1, max.t1 ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorVec2f.java b/src/com/axe/math/calc/CalculatorVec2f.java new file mode 100644 index 0000000..a052ae3 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorVec2f.java @@ -0,0 +1,285 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Vec2f; + +public final class CalculatorVec2f extends AbstractCalculator +{ + + public static final CalculatorVec2f INSTANCE = new CalculatorVec2f(); + + @Override + public Vec2f copy(Vec2f out, Vec2f source) + { + out.x = source.x; + out.y = source.y; + return out; + } + + @Override + public Vec2f parse(Object input, Vec2f defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Vec2f( floats[0], floats[1] ); + } + + if (input instanceof Vec2f) + { + return (Vec2f)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Vec2f instantiate() + { + return new Vec2f(); + } + + @Override + public Vec2f clear(Vec2f out, float component) + { + out.x = out.y = component; + return out; + } + + @Override + public Vec2f unary(Vec2f out, Vec2f value, Unary unaryOperation) + { + out.x = unaryOperation.unary( value.x ); + out.y = unaryOperation.unary( value.y ); + return out; + } + + @Override + public Vec2f binary(Vec2f out, Vec2f a, Vec2f b, Binary binaryOperation) + { + out.x = binaryOperation.binary( a.x, b.x ); + out.y = binaryOperation.binary( a.y, b.y ); + return out; + } + + @Override + public Vec2f adds(Vec2f out, Vec2f augend, Vec2f addend, float scale) + { + out.x = augend.x + addend.x * scale; + out.y = augend.y + addend.y * scale; + return out; + } + + @Override + public Vec2f mul( Vec2f out, Vec2f value, Vec2f scale ) + { + out.x = value.x * scale.x; + out.y = value.y * scale.y; + return out; + } + + @Override + public Vec2f div( Vec2f out, Vec2f dividend, Vec2f divisor ) + { + out.x = Numbers.divide( dividend.x, divisor.x ); + out.y = Numbers.divide( dividend.y, divisor.y ); + return out; + } + + @Override + public Vec2f interpolate( Vec2f out, Vec2f start, Vec2f end, float delta ) + { + out.x = (end.x - start.x) * delta + start.x; + out.y = (end.y - start.y) * delta + start.y; + return out; + } + + @Override + public boolean isParallel( Vec2f a, Vec2f b, float epsilon ) + { + return Math.abs( (a.y * b.x) - (a.x * b.y) ) < epsilon; + } + + @Override + public Vec2f contain( Vec2f out, Vec2f min, Vec2f max ) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + return out; + } + + @Override + public boolean contains( Vec2f point, Vec2f min, Vec2f max ) + { + return point.x >= min.x && point.x <= max.x && + point.y >= min.y && point.y <= max.y; + } + + @Override + public Vec2f random( Vec2f out, Vec2f min, Vec2f max, Binary randomizer ) + { + out.x = randomizer.binary( min.x, max.x ); + out.y = randomizer.binary( min.y, max.y ); + return out; + } + + @Override + public float dot( Vec2f a, Vec2f b ) + { + return a.x * b.x + a.y * b.y; + } + + @Override + public float distanceSq( Vec2f a, Vec2f b ) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + + return dx * dx + dy * dy; + } + + @Override + public float lengthSq( Vec2f value ) + { + return value.x * value.x + value.y * value.y; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Vec2f; + } + + @Override + public boolean isFinite( Vec2f value ) + { + return Float.isFinite( value.x ) && + Float.isFinite( value.y ); + } + + @Override + public boolean isEqual( Vec2f a, Vec2f b, float epsilon ) + { + return Numbers.equals( a.x, b.x, epsilon ) && + Numbers.equals( a.y, b.y, epsilon ); + } + + @Override + public int sizeof( Vec2f value ) + { + return 8; + } + + @Override + public int write( Vec2f value, ByteBuffer to ) + { + to.putFloat( value.x ); + to.putFloat( value.y ); + return 8; + } + + @Override + public Vec2f read( Vec2f out, ByteBuffer from ) + { + out.x = from.getFloat(); + out.y = from.getFloat(); + return out; + } + + @Override + public Vec2f read( Vec2f out, InputModel input ) + { + out.x = input.readFloat( "x" ); + out.y = input.readFloat( "y" ); + return out; + } + + @Override + public void write( Vec2f value, OutputModel output ) + { + output.write( "x", value.x ); + output.write( "y", value.y ); + } + + @Override + public int getComponents() + { + return 2; + } + + @Override + public float getComponent( Vec2f value, int index ) + { + switch (index) { + case 0: return value.x; + case 1: return value.y; + } + + return 0; + } + + @Override + public Vec2f setComponent( Vec2f value, int index, float component ) + { + switch (index) { + case 0: + value.x = component; + break; + case 1: + value.y = component; + break; + } + return value; + } + + @Override + public Vec2f mods(Vec2f out, Vec2f value, float divisor) + { + out.x = value.x % divisor; + out.y = value.y % divisor; + return out; + } + + @Override + public Vec2f mod(Vec2f out, Vec2f value, Vec2f divisor) + { + out.x = value.x % divisor.x; + out.y = value.y % divisor.y; + return out; + } + + @Override + public Vec2f truncates(Vec2f out, Vec2f value, float divisor) + { + out.x = value.x - value.x % divisor; + out.y = value.y - value.y % divisor; + return out; + } + + @Override + public Vec2f truncate(Vec2f out, Vec2f value, Vec2f divisor) + { + out.x = value.x - value.x % divisor.x; + out.y = value.y - value.y % divisor.y; + return out; + } + + @Override + public Vec2f clamp(Vec2f out, Vec2f min, Vec2f max) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorVec2i.java b/src/com/axe/math/calc/CalculatorVec2i.java new file mode 100644 index 0000000..4dbd419 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorVec2i.java @@ -0,0 +1,279 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Vec2i; + +public final class CalculatorVec2i extends AbstractCalculator +{ + + public static final CalculatorVec2i INSTANCE = new CalculatorVec2i(); + + @Override + public Vec2i copy(Vec2i out, Vec2i source) + { + out.x = source.x; + out.y = source.y; + return out; + } + + @Override + public Vec2i parse(Object input, Vec2i defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof int[]) + { + int[] ints = (int[])input; + + return new Vec2i( ints[0], ints[1] ); + } + + if (input instanceof Vec2i) + { + return (Vec2i)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Vec2i instantiate() + { + return new Vec2i(); + } + + @Override + public Vec2i clear(Vec2i out, float component) + { + out.x = out.y = (int)component; + return out; + } + + @Override + public Vec2i unary(Vec2i out, Vec2i value, Unary unaryOperation) + { + out.x = (int)unaryOperation.unary( value.x ); + out.y = (int)unaryOperation.unary( value.y ); + return out; + } + + @Override + public Vec2i binary(Vec2i out, Vec2i a, Vec2i b, Binary binaryOperation) + { + out.x = (int)binaryOperation.binary( a.x, b.x ); + out.y = (int)binaryOperation.binary( a.y, b.y ); + return out; + } + + @Override + public Vec2i adds(Vec2i out, Vec2i augend, Vec2i addend, float scale) + { + out.x = (int)(augend.x + addend.x * scale); + out.y = (int)(augend.y + addend.y * scale); + return out; + } + + @Override + public Vec2i mul( Vec2i out, Vec2i value, Vec2i scale ) + { + out.x = value.x * scale.x; + out.y = value.y * scale.y; + return out; + } + + @Override + public Vec2i div( Vec2i out, Vec2i dividend, Vec2i divisor ) + { + out.x = Numbers.divide( dividend.x, divisor.x ); + out.y = Numbers.divide( dividend.y, divisor.y ); + return out; + } + + @Override + public Vec2i interpolate( Vec2i out, Vec2i start, Vec2i end, float delta ) + { + out.x = (int)((end.x - start.x) * delta + start.x); + out.y = (int)((end.y - start.y) * delta + start.y); + return out; + } + + @Override + public Vec2i contain( Vec2i out, Vec2i min, Vec2i max ) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + return out; + } + + @Override + public boolean contains( Vec2i point, Vec2i min, Vec2i max ) + { + return point.x >= min.x && point.x <= max.x && + point.y >= min.y && point.y <= max.y; + } + + @Override + public Vec2i random( Vec2i out, Vec2i min, Vec2i max, Binary randomizer ) + { + out.x = (int)randomizer.binary( min.x, max.x ); + out.y = (int)randomizer.binary( min.y, max.y ); + return out; + } + + @Override + public float dot( Vec2i a, Vec2i b ) + { + return a.x * b.x + a.y * b.y; + } + + @Override + public float distanceSq( Vec2i a, Vec2i b ) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + + return dx * dx + dy * dy; + } + + @Override + public float lengthSq( Vec2i value ) + { + return value.x * value.x + value.y * value.y; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Vec2i; + } + + @Override + public boolean isFinite( Vec2i value ) + { + return Float.isFinite( value.x ) && + Float.isFinite( value.y ); + } + + @Override + public boolean isEqual( Vec2i a, Vec2i b, float epsilon ) + { + return Numbers.equals( a.x, b.x, epsilon ) && + Numbers.equals( a.y, b.y, epsilon ); + } + + @Override + public int sizeof( Vec2i value ) + { + return 8; + } + + @Override + public int write( Vec2i value, ByteBuffer to ) + { + to.putInt( value.x ); + to.putInt( value.y ); + return 8; + } + + @Override + public Vec2i read( Vec2i out, ByteBuffer from ) + { + out.x = from.getInt(); + out.y = from.getInt(); + return out; + } + + @Override + public Vec2i read( Vec2i out, InputModel input ) + { + out.x = input.readInt( "x" ); + out.y = input.readInt( "y" ); + return out; + } + + @Override + public void write( Vec2i value, OutputModel output ) + { + output.write( "x", value.x ); + output.write( "y", value.y ); + } + + @Override + public int getComponents() + { + return 2; + } + + @Override + public float getComponent( Vec2i value, int index ) + { + switch (index) { + case 0: return value.x; + case 1: return value.y; + } + + return 0; + } + + @Override + public Vec2i setComponent( Vec2i value, int index, float component ) + { + switch (index) { + case 0: + value.x = (int)component; + break; + case 1: + value.y = (int)component; + break; + } + return value; + } + + @Override + public Vec2i mods(Vec2i out, Vec2i value, float divisor) + { + out.x = value.x % (int)divisor; + out.y = value.y % (int)divisor; + return out; + } + + @Override + public Vec2i mod(Vec2i out, Vec2i value, Vec2i divisor) + { + out.x = value.x % divisor.x; + out.y = value.y % divisor.y; + return out; + } + + @Override + public Vec2i truncates(Vec2i out, Vec2i value, float divisor) + { + out.x = value.x - value.x % (int)divisor; + out.y = value.y - value.y % (int)divisor; + return out; + } + + @Override + public Vec2i truncate(Vec2i out, Vec2i value, Vec2i divisor) + { + out.x = value.x - value.x % divisor.x; + out.y = value.y - value.y % divisor.y; + return out; + } + + @Override + public Vec2i clamp(Vec2i out, Vec2i min, Vec2i max) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorVec3f.java b/src/com/axe/math/calc/CalculatorVec3f.java new file mode 100644 index 0000000..8bc3795 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorVec3f.java @@ -0,0 +1,305 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Vec3f; + +public final class CalculatorVec3f extends AbstractCalculator +{ + + public static final CalculatorVec3f INSTANCE = new CalculatorVec3f(); + + @Override + public Vec3f copy(Vec3f out, Vec3f source) + { + out.x = source.x; + out.y = source.y; + out.z = source.z; + return out; + } + + @Override + public Vec3f parse(Object input, Vec3f defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof float[]) + { + float[] floats = (float[])input; + + return new Vec3f( floats[0], floats[1], floats[2] ); + } + + if (input instanceof Vec3f) + { + return (Vec3f)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Vec3f instantiate() + { + return new Vec3f(); + } + + @Override + public Vec3f clear(Vec3f out, float component) + { + out.x = out.y = out.z = component; + return out; + } + + @Override + public Vec3f unary(Vec3f out, Vec3f value, Unary unaryOperation) + { + out.x = unaryOperation.unary( value.x ); + out.y = unaryOperation.unary( value.y ); + out.z = unaryOperation.unary( value.z ); + return out; + } + + @Override + public Vec3f binary(Vec3f out, Vec3f a, Vec3f b, Binary binaryOperation) + { + out.x = binaryOperation.binary( a.x, b.x ); + out.y = binaryOperation.binary( a.y, b.y ); + out.z = binaryOperation.binary( a.z, b.z ); + return out; + } + + @Override + public Vec3f adds(Vec3f out, Vec3f augend, Vec3f addend, float scale) + { + out.x = augend.x + addend.x * scale; + out.y = augend.y + addend.y * scale; + out.z = augend.z + addend.z * scale; + return out; + } + + @Override + public Vec3f mul( Vec3f out, Vec3f value, Vec3f scale ) + { + out.x = value.x * scale.x; + out.y = value.y * scale.y; + out.z = value.z * scale.z; + return out; + } + + @Override + public Vec3f div( Vec3f out, Vec3f dividend, Vec3f divisor ) + { + out.x = Numbers.divide( dividend.x, divisor.x ); + out.y = Numbers.divide( dividend.y, divisor.y ); + out.z = Numbers.divide( dividend.z, divisor.z ); + return out; + } + + @Override + public Vec3f interpolate( Vec3f out, Vec3f start, Vec3f end, float delta ) + { + out.x = (end.x - start.x) * delta + start.x; + out.y = (end.y - start.y) * delta + start.y; + out.z = (end.z - start.z) * delta + start.z; + return out; + } + + @Override + public Vec3f contain( Vec3f out, Vec3f min, Vec3f max ) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.z = Numbers.clamp( out.z, min.z, max.z ); + return out; + } + + @Override + public boolean contains( Vec3f point, Vec3f min, Vec3f max ) + { + return point.x >= min.x && point.x <= max.x && + point.y >= min.y && point.y <= max.y && + point.z >= min.z && point.z <= max.z; + } + + @Override + public Vec3f random( Vec3f out, Vec3f min, Vec3f max, Binary randomizer ) + { + out.x = randomizer.binary( min.x, max.x ); + out.y = randomizer.binary( min.y, max.y ); + out.z = randomizer.binary( min.z, max.z ); + return out; + } + + @Override + public float dot( Vec3f a, Vec3f b ) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + @Override + public float distanceSq( Vec3f a, Vec3f b ) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + float dz = a.z - b.z; + + return dx * dx + dy * dy + dz * dz; + } + + @Override + public float lengthSq( Vec3f value ) + { + return value.x * value.x + value.y * value.y + value.z * value.z; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Vec3f; + } + + @Override + public boolean isFinite( Vec3f value ) + { + return Float.isFinite( value.x ) && + Float.isFinite( value.y ) && + Float.isFinite( value.z ); + } + + @Override + public boolean isEqual( Vec3f a, Vec3f b, float epsilon ) + { + return Numbers.equals( a.x, b.x, epsilon ) && + Numbers.equals( a.y, b.y, epsilon ) && + Numbers.equals( a.z, b.z, epsilon ); + } + + @Override + public int sizeof( Vec3f value ) + { + return 12; + } + + @Override + public int write( Vec3f value, ByteBuffer to ) + { + to.putFloat( value.x ); + to.putFloat( value.y ); + to.putFloat( value.z ); + return 12; + } + + @Override + public Vec3f read( Vec3f out, ByteBuffer from ) + { + out.x = from.getFloat(); + out.y = from.getFloat(); + out.z = from.getFloat(); + return out; + } + + @Override + public Vec3f read( Vec3f out, InputModel input ) + { + out.x = input.readFloat( "x" ); + out.y = input.readFloat( "y" ); + out.z = input.readFloat( "z" ); + return out; + } + + @Override + public void write( Vec3f value, OutputModel output ) + { + output.write( "x", value.x ); + output.write( "y", value.y ); + output.write( "z", value.z ); + } + + @Override + public int getComponents() + { + return 3; + } + + @Override + public float getComponent( Vec3f value, int index ) + { + switch (index) { + case 0: return value.x; + case 1: return value.y; + case 2: return value.z; + } + + return 0; + } + + @Override + public Vec3f setComponent( Vec3f value, int index, float component ) + { + switch (index) { + case 0: + value.x = component; + break; + case 1: + value.y = component; + break; + case 2: + value.z = component; + break; + } + return value; + } + + @Override + public Vec3f mods(Vec3f out, Vec3f value, float divisor) + { + out.x = value.x % divisor; + out.y = value.y % divisor; + out.z = value.z % divisor; + return out; + } + + @Override + public Vec3f mod(Vec3f out, Vec3f value, Vec3f divisor) + { + out.x = value.x % divisor.x; + out.y = value.y % divisor.y; + out.z = value.z % divisor.z; + return out; + } + + @Override + public Vec3f truncates(Vec3f out, Vec3f value, float divisor) + { + out.x = value.x - value.x % divisor; + out.y = value.y - value.y % divisor; + out.z = value.z - value.z % divisor; + return out; + } + + @Override + public Vec3f truncate(Vec3f out, Vec3f value, Vec3f divisor) + { + out.x = value.x - value.x % divisor.x; + out.y = value.y - value.y % divisor.y; + out.z = value.z - value.z % divisor.z; + return out; + } + + @Override + public Vec3f clamp(Vec3f out, Vec3f min, Vec3f max) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.z = Numbers.clamp( out.z, min.z, max.z ); + return out; + } + +} diff --git a/src/com/axe/math/calc/CalculatorVec3i.java b/src/com/axe/math/calc/CalculatorVec3i.java new file mode 100644 index 0000000..9037785 --- /dev/null +++ b/src/com/axe/math/calc/CalculatorVec3i.java @@ -0,0 +1,305 @@ +package com.axe.math.calc; + +import java.nio.ByteBuffer; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; +import com.axe.math.Vec3i; + +public final class CalculatorVec3i extends AbstractCalculator +{ + + public static final CalculatorVec3i INSTANCE = new CalculatorVec3i(); + + @Override + public Vec3i copy(Vec3i out, Vec3i source) + { + out.x = source.x; + out.y = source.y; + out.z = source.z; + return out; + } + + @Override + public Vec3i parse(Object input, Vec3i defaultValue) + { + if (input instanceof Float) + { + return this.createUniform( (float)input ); + } + + if (input instanceof int[]) + { + int[] ints = (int[])input; + + return new Vec3i( ints[0], ints[1], ints[2] ); + } + + if (input instanceof Vec3i) + { + return (Vec3i)input; + } + + return defaultValue != null ? clone( defaultValue ) : null; + } + + @Override + public Vec3i instantiate() + { + return new Vec3i(); + } + + @Override + public Vec3i clear(Vec3i out, float component) + { + out.x = out.y = out.z = (int)component; + return out; + } + + @Override + public Vec3i unary(Vec3i out, Vec3i value, Unary unaryOperation) + { + out.x = (int)unaryOperation.unary( value.x ); + out.y = (int)unaryOperation.unary( value.y ); + out.z = (int)unaryOperation.unary( value.z ); + return out; + } + + @Override + public Vec3i binary(Vec3i out, Vec3i a, Vec3i b, Binary binaryOperation) + { + out.x = (int)binaryOperation.binary( a.x, b.x ); + out.y = (int)binaryOperation.binary( a.y, b.y ); + out.z = (int)binaryOperation.binary( a.z, b.z ); + return out; + } + + @Override + public Vec3i adds(Vec3i out, Vec3i augend, Vec3i addend, float scale) + { + out.x = (int)(augend.x + addend.x * scale); + out.y = (int)(augend.y + addend.y * scale); + out.z = (int)(augend.z + addend.z * scale); + return out; + } + + @Override + public Vec3i mul( Vec3i out, Vec3i value, Vec3i scale ) + { + out.x = value.x * scale.x; + out.y = value.y * scale.y; + out.z = value.z * scale.z; + return out; + } + + @Override + public Vec3i div( Vec3i out, Vec3i dividend, Vec3i divisor ) + { + out.x = Numbers.divide( dividend.x, divisor.x ); + out.y = Numbers.divide( dividend.y, divisor.y ); + out.z = Numbers.divide( dividend.z, divisor.z ); + return out; + } + + @Override + public Vec3i interpolate( Vec3i out, Vec3i start, Vec3i end, float delta ) + { + out.x = (int)((end.x - start.x) * delta + start.x); + out.y = (int)((end.y - start.y) * delta + start.y); + out.z = (int)((end.z - start.z) * delta + start.z); + return out; + } + + @Override + public Vec3i contain( Vec3i out, Vec3i min, Vec3i max ) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.z = Numbers.clamp( out.z, min.z, max.z ); + return out; + } + + @Override + public boolean contains( Vec3i point, Vec3i min, Vec3i max ) + { + return point.x >= min.x && point.x <= max.x && + point.y >= min.y && point.y <= max.y && + point.z >= min.z && point.z <= max.z; + } + + @Override + public Vec3i random( Vec3i out, Vec3i min, Vec3i max, Binary randomizer ) + { + out.x = (int)randomizer.binary( min.x, max.x ); + out.y = (int)randomizer.binary( min.y, max.y ); + out.z = (int)randomizer.binary( min.z, max.z ); + return out; + } + + @Override + public float dot( Vec3i a, Vec3i b ) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + @Override + public float distanceSq( Vec3i a, Vec3i b ) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + float dz = a.z - b.z; + + return dx * dx + dy * dy + dz * dz; + } + + @Override + public float lengthSq( Vec3i value ) + { + return value.x * value.x + value.y * value.y + value.z * value.z; + } + + @Override + public boolean isValue( Object value ) + { + return value instanceof Vec3i; + } + + @Override + public boolean isFinite( Vec3i value ) + { + return Float.isFinite( value.x ) && + Float.isFinite( value.y ) && + Float.isFinite( value.z ); + } + + @Override + public boolean isEqual( Vec3i a, Vec3i b, float epsilon ) + { + return Numbers.equals( a.x, b.x, epsilon ) && + Numbers.equals( a.y, b.y, epsilon ) && + Numbers.equals( a.z, b.z, epsilon ); + } + + @Override + public int sizeof( Vec3i value ) + { + return 12; + } + + @Override + public int write( Vec3i value, ByteBuffer to ) + { + to.putFloat( value.x ); + to.putFloat( value.y ); + to.putFloat( value.z ); + return 12; + } + + @Override + public Vec3i read( Vec3i out, ByteBuffer from ) + { + out.x = from.getInt(); + out.y = from.getInt(); + out.z = from.getInt(); + return out; + } + + @Override + public Vec3i read( Vec3i out, InputModel input ) + { + out.x = input.readInt( "x" ); + out.y = input.readInt( "y" ); + out.z = input.readInt( "z" ); + return out; + } + + @Override + public void write( Vec3i value, OutputModel output ) + { + output.write( "x", value.x ); + output.write( "y", value.y ); + output.write( "z", value.z ); + } + + @Override + public int getComponents() + { + return 3; + } + + @Override + public float getComponent( Vec3i value, int index ) + { + switch (index) { + case 0: return value.x; + case 1: return value.y; + case 2: return value.z; + } + + return 0; + } + + @Override + public Vec3i setComponent( Vec3i value, int index, float component ) + { + switch (index) { + case 0: + value.x = (int)component; + break; + case 1: + value.y = (int)component; + break; + case 2: + value.z = (int)component; + break; + } + return value; + } + + @Override + public Vec3i mods(Vec3i out, Vec3i value, float divisor) + { + out.x = value.x % (int)divisor; + out.y = value.y % (int)divisor; + out.z = value.z % (int)divisor; + return out; + } + + @Override + public Vec3i mod(Vec3i out, Vec3i value, Vec3i divisor) + { + out.x = value.x % divisor.x; + out.y = value.y % divisor.y; + out.z = value.z % divisor.z; + return out; + } + + @Override + public Vec3i truncates(Vec3i out, Vec3i value, float divisor) + { + out.x = value.x - value.x % (int)divisor; + out.y = value.y - value.y % (int)divisor; + out.z = value.z - value.z % (int)divisor; + return out; + } + + @Override + public Vec3i truncate(Vec3i out, Vec3i value, Vec3i divisor) + { + out.x = value.x - value.x % divisor.x; + out.y = value.y - value.y % divisor.y; + out.z = value.z - value.z % divisor.z; + return out; + } + + @Override + public Vec3i clamp(Vec3i out, Vec3i min, Vec3i max) + { + out.x = Numbers.clamp( out.x, min.x, max.x ); + out.y = Numbers.clamp( out.y, min.y, max.y ); + out.z = Numbers.clamp( out.z, min.z, max.z ); + return out; + } + +} diff --git a/src/com/axe/math/calc/Unary.java b/src/com/axe/math/calc/Unary.java new file mode 100644 index 0000000..d89f661 --- /dev/null +++ b/src/com/axe/math/calc/Unary.java @@ -0,0 +1,6 @@ +package com.axe.math.calc; + +public interface Unary +{ + public float unary(float value); +} diff --git a/src/com/axe/mem/Memory.java b/src/com/axe/mem/Memory.java new file mode 100755 index 0000000..f3b40ed --- /dev/null +++ b/src/com/axe/mem/Memory.java @@ -0,0 +1,10 @@ +package com.axe.mem; + + +public interface Memory +{ + public T alloc(); + public void free(T item); + public int size(); + public int capacity(); +} diff --git a/src/com/axe/mem/MemoryHeap.java b/src/com/axe/mem/MemoryHeap.java new file mode 100755 index 0000000..2441fec --- /dev/null +++ b/src/com/axe/mem/MemoryHeap.java @@ -0,0 +1,39 @@ +package com.axe.mem; + +import com.axe.core.Factory; + + +public class MemoryHeap implements Memory +{ + + private Factory factory; + + public MemoryHeap( Factory factory ) + { + this.factory = factory; + } + + @Override + public T alloc() + { + return factory.create(); + } + + @Override + public void free( T item ) + { + } + + @Override + public int size() + { + return 0; + } + + @Override + public int capacity() + { + return 0; + } + +} diff --git a/src/com/axe/mem/MemoryPool.java b/src/com/axe/mem/MemoryPool.java new file mode 100755 index 0000000..83c52ab --- /dev/null +++ b/src/com/axe/mem/MemoryPool.java @@ -0,0 +1,72 @@ +package com.axe.mem; + +import java.util.Arrays; + +import com.axe.core.Factory; +import com.axe.util.Array; + + +public class MemoryPool implements Memory +{ + + private Factory factory; + private T[] pool; + private int size; + private int maximum; + private int increase; + + public MemoryPool( T[] pool, Factory factory, int maximum, int increase, boolean fill ) + { + this.pool = pool; + this.factory = factory; + this.maximum = maximum; + this.increase = increase; + + if ( fill ) { + Array.allocate( pool, factory ); + } + } + + public MemoryPool( int poolSize, Factory factory, int maximum, int increase, boolean fill, T ... poolBase ) + { + this.pool = Arrays.copyOf( poolBase, poolSize ); + this.factory = factory; + this.maximum = maximum; + this.increase = increase; + + if ( fill ) { + Array.allocate( pool, factory ); + } + } + + + @Override + public T alloc() + { + return (size == 0 ? factory.create() : pool[--size]); + } + + @Override + public void free( T item ) + { + if (size < pool.length) { + pool[size++] = item; + } else if (size < maximum) { + pool = Arrays.copyOf( pool, size + increase ); + pool[size++] = item; + } + } + + @Override + public int size() + { + return size; + } + + @Override + public int capacity() + { + return maximum; + } + +} diff --git a/src/com/axe/monitor/Monitor.java b/src/com/axe/monitor/Monitor.java new file mode 100644 index 0000000..f1a6ef8 --- /dev/null +++ b/src/com/axe/monitor/Monitor.java @@ -0,0 +1,24 @@ +package com.axe.monitor; + +import com.axe.core.HasData; + +public interface Monitor extends HasData +{ + + public int getWidth(); + + public int getHeight(); + + public int getRefreshRate(); + + public boolean isPrimary(); + + public int getX(); + + public int getY(); + + public String getName(); + + public boolean isValid(); + +} diff --git a/src/com/axe/monitor/MonitorEvent.java b/src/com/axe/monitor/MonitorEvent.java new file mode 100644 index 0000000..eec3b7f --- /dev/null +++ b/src/com/axe/monitor/MonitorEvent.java @@ -0,0 +1,14 @@ +package com.axe.monitor; + +import com.axe.event.Event; + +public class MonitorEvent +{ + + public static final Event Connected = + new Event<>(0, MonitorEventListener.class); + + public static final Event Disconnected = + new Event<>(1, MonitorEventListener.class); + +} diff --git a/src/com/axe/monitor/MonitorEventListener.java b/src/com/axe/monitor/MonitorEventListener.java new file mode 100644 index 0000000..ab7e3d4 --- /dev/null +++ b/src/com/axe/monitor/MonitorEventListener.java @@ -0,0 +1,8 @@ +package com.axe.monitor; + +public interface MonitorEventListener +{ + + public void onEvent(Monitor monitor); + +} diff --git a/src/com/axe/monitor/MonitorSystem.java b/src/com/axe/monitor/MonitorSystem.java new file mode 100644 index 0000000..5f279c1 --- /dev/null +++ b/src/com/axe/monitor/MonitorSystem.java @@ -0,0 +1,12 @@ +package com.axe.monitor; + +import com.axe.event.HasEvents; + +public interface MonitorSystem extends HasEvents +{ + + public Monitor getPrimary(); + + public Monitor[] getMonitors(); + +} diff --git a/src/com/axe/net/BroadcastMessageHandler.java b/src/com/axe/net/BroadcastMessageHandler.java new file mode 100755 index 0000000..94f3104 --- /dev/null +++ b/src/com/axe/net/BroadcastMessageHandler.java @@ -0,0 +1,29 @@ +package com.axe.net; + + +public class BroadcastMessageHandler extends PermanentMessageHandler +{ + + public final boolean echo; + + public BroadcastMessageHandler(boolean echo) + { + this.echo = echo; + } + + @Override + public void handle( Client client, M message, Packet packet ) + { + client.server.broadcast( message, echo ? null : client ); + } + + @SuppressWarnings ({ "rawtypes", "unchecked" } ) + public static void addToRegistry(boolean echo, Class ... messageTypes) + { + for (Class mt : messageTypes) + { + MessageRegistry.addHandler( mt, new BroadcastMessageHandler( echo ) ); + } + } + +} diff --git a/src/com/axe/net/Client.java b/src/com/axe/net/Client.java new file mode 100755 index 0000000..d100189 --- /dev/null +++ b/src/com/axe/net/Client.java @@ -0,0 +1,424 @@ + +package com.axe.net; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.ArrayDeque; +import java.util.Queue; + + +public class Client +{ + + private static final int HEADER_SIZE = 28; + private static final int HEADER_PACKET_SIZE_OFFSET = 24; + private static final int MESSAGE_ID_SIZE = 4; + + public Queue out = new ArrayDeque(); + public Queue in = new ArrayDeque(); + + public String host; + public int port; + public long updateRateMillis = 50; + public int bufferSize = 3800; + public boolean receivesBroadcasts; + public int magicNumber; + + public Server server; + public Object attachment; + + public boolean closed; + public int messagesSent; + public int packetsRead; + public int packetIndex; + public int lastSentPacketSize; + public int lastReceivedPacketSize; + public long lastReceivedPacketTime; + public long pingTime; + public SocketAddress address; + public long lastUpdate; + public boolean readyToSend; + public SocketChannel socket; + public ByteBuffer bufferIn; + public ArrayDeque bufferOutQueue; + public ArrayDeque bufferOutPool; + public Packet packet; + + public long durationRead; + public long durationUpdate; + public long durationWrite; + public long durationSend; + + public Client() + { + } + + public Client( String host, int port, int magicNumber ) + { + this.host = host; + this.port = port; + this.magicNumber = magicNumber; + } + + public Client( SocketChannel socket, Server server, int magicNumber ) + { + this.socket = socket; + this.server = server; + this.magicNumber = magicNumber; + } + + public void init() throws Exception + { + assert bufferSize != 0 : "Buffer size must be non-zero"; + + lastUpdate = System.currentTimeMillis(); + + if (socket == null) + { + assert host != null : "Host must be non-null"; + assert port != 0 : "Port must be non-zero"; + + address = new InetSocketAddress( host, port ); + socket = SocketChannel.open(); + socket.configureBlocking( false ); + socket.connect( address ); + } + else + { + socket.configureBlocking( false ); + address = socket.getRemoteAddress(); + } + + bufferIn = ByteBuffer.allocateDirect( bufferSize ); + bufferOutQueue = new ArrayDeque(); + bufferOutPool = new ArrayDeque(); + + packet = new Packet(); + } + + public void close() + { + if (closed) + { + return; + } + + try + { + closed = true; + + if (socket.isOpen()) + { + socket.close(); + } + } + catch (Exception e) + { + System.err.println( "Error closing socket" ); + + e.printStackTrace(); + } + } + + public boolean isClosed() + { + return closed; + } + + public void update() throws Exception + { + if (closed) + { + return; + } + + long startTime = System.nanoTime(); + long currentTime = System.currentTimeMillis(); + long elapsedTime = currentTime - lastUpdate; + + readyToSend = (elapsedTime >= updateRateMillis); + + if (readyToSend) + { + while (elapsedTime >= updateRateMillis) + { + lastUpdate += updateRateMillis; + elapsedTime -= updateRateMillis; + } + + assert lastUpdate <= currentTime : "Client.lastUpdate was over adjusted."; + } + + if (socket.finishConnect()) + { + write(); + } + + durationUpdate = System.nanoTime() - startTime; + } + + private void write() throws Exception + { + if (closed) + { + return; + } + + long startTime = System.nanoTime(); + + assert bufferOutQueue != null : "Client.bufferOutQueue must be non-null, try calling Client.init() first."; + assert bufferOutPool != null : "Client.bufferOutPool must be non-null, try calling Client.init() first."; + assert socket != null : "Client.socket must be non-null, try calling Client.init() first."; + assert socket.isOpen() : "Socket must be open to write to."; + + while (!bufferOutQueue.isEmpty()) + { + ByteBuffer next = bufferOutQueue.peek(); + + socket.write( next ); + + if (next.hasRemaining()) + { + break; + } + + bufferOutQueue.poll(); + bufferOutPool.push( next ); + } + + durationWrite = System.nanoTime() - startTime; + } + + public void sendAllNow() throws Exception + { + if (closed) + { + return; + } + + while (!out.isEmpty()) + { + send(); + } + + while (!bufferOutQueue.isEmpty()) + { + write(); + } + } + + public void send() throws Exception + { + if (closed) + { + return; + } + + if (!socket.finishConnect()) + { + return; + } + + long startTime = System.nanoTime(); + + ByteBuffer bufferOut = bufferOutPool.isEmpty() ? ByteBuffer.allocateDirect( bufferSize ) : bufferOutPool.pop(); + + bufferOut.clear(); + bufferOut.putInt( magicNumber ); + bufferOut.putInt( packetIndex ); + bufferOut.putLong( System.nanoTime() ); + bufferOut.putLong( lastReceivedPacketTime ); + bufferOut.putInt( 0 ); + + messagesSent = 0; + + while (!out.isEmpty() && tryPut( out.peek(), bufferOut )) + { + out.poll(); + + messagesSent++; + } + + bufferOut.flip(); + + if (messagesSent > 0) + { + lastSentPacketSize = bufferOut.limit() - HEADER_SIZE; + + bufferOut.putInt( HEADER_PACKET_SIZE_OFFSET, lastSentPacketSize ); + + bufferOutQueue.offer( bufferOut ); + + packetIndex++; + + write(); + } + else + { + bufferOutPool.push( bufferOut ); + } + + durationSend = System.nanoTime() - startTime; + } + + private boolean tryPut( Object dataObject, ByteBuffer dest ) + { + M data = (M)dataObject; + Message message = MessageRegistry.getMessage( data ); + int sizeOf = message.sizeOf( data ); + + if (dest.remaining() < sizeOf + MESSAGE_ID_SIZE) + { + return false; + } + + dest.putInt( message.id() ); + message.write( dest, data ); + + return true; + } + + public void read() throws Exception + { + if (closed) + { + return; + } + + if (!socket.finishConnect()) + { + return; + } + + long startTime = System.nanoTime(); + + assert socket.isOpen() : "Socket must be open to read from."; + + in.clear(); + + packetsRead = 0; + + int read = socket.read( bufferIn ); + + if (read == -1) + { + close(); + + return; + } + + bufferIn.flip(); + + while (readBuffer()) + { + packetsRead++; + } + + durationRead = System.nanoTime() - startTime; + } + + private boolean readBuffer() + { + if (bufferIn.remaining() < HEADER_SIZE) + { + unflip( bufferIn ); + + return false; + } + + packet.messages.clear(); + packet.ids.clear(); + + int packetMagicNumber = bufferIn.getInt(); + + if (packetMagicNumber != magicNumber) + { + close(); + + return false; + } + + packet.packetIndex = bufferIn.getInt(); + packet.packetTime = bufferIn.getLong(); + packet.receivedTime = bufferIn.getLong(); + packet.packetSize = bufferIn.getInt(); + + assert packet.packetIndex >= 0 : "Packet index must be >= 0."; + assert packet.packetTime > 0 : "Packet time must be > 0."; + assert packet.packetSize > 0 : "Packet size must be > 0."; + assert packet.packetSize < bufferSize : "Packet size must be < " + bufferSize + "."; + + if (bufferIn.remaining() < packet.packetSize) + { + unflip( bufferIn ); + + return false; + } + + pingTime = System.nanoTime() - packet.receivedTime; + + int messageCount = 0; + int finalPosition = bufferIn.position() + packet.packetSize; + + while (bufferIn.position() < finalPosition) + { + int messageId = bufferIn.getInt(); + + assert messageId > 0 : "Message IDs must be > 0."; + + Message message = MessageRegistry.getMessage( messageId ); + + assert message != null : "Message for id " + messageId + " is null."; + + Object data = message.read( bufferIn ); + + if (data != null) + { + in.add( data ); + packet.messages.add( data ); + packet.ids.add( messageId ); + messageCount++; + } + } + + assert bufferIn.position() == finalPosition : "Buffer position did not match expected packet length."; + + int remaining = bufferIn.remaining(); + bufferIn.compact(); + bufferIn.position( 0 ); + bufferIn.limit( remaining ); + + for (int i = 0; i < messageCount; i++) + { + int messageId = packet.ids.get( i ); + Object data = packet.messages.get( i ); + + MessageRegistry.handleMessage( this, data, messageId, packet ); + } + + lastReceivedPacketSize = packet.packetSize; + lastReceivedPacketTime = packet.packetTime; + + return true; + } + + private void unflip( ByteBuffer bb ) + { + int readMax = bb.limit(); + bb.limit( bb.capacity() ); + bb.position( readMax ); + } + + public T attachment() + { + return (T)attachment; + } + + public T attachment( Class type ) + { + return (T)attachment; + } + +} diff --git a/src/com/axe/net/Message.java b/src/com/axe/net/Message.java new file mode 100755 index 0000000..f144a7b --- /dev/null +++ b/src/com/axe/net/Message.java @@ -0,0 +1,20 @@ + +package com.axe.net; + +import java.nio.ByteBuffer; + + +public interface Message +{ + + public M read( ByteBuffer in ); + + public void write( ByteBuffer out, M m ); + + public int sizeOf( M m ); + + public int id(); + + public Class type(); + +} diff --git a/src/com/axe/net/MessageHandler.java b/src/com/axe/net/MessageHandler.java new file mode 100755 index 0000000..8de86c5 --- /dev/null +++ b/src/com/axe/net/MessageHandler.java @@ -0,0 +1,11 @@ + +package com.axe.net; + +public interface MessageHandler +{ + + public void handle( Client client, M message, Packet packet ); + + public boolean isExpired(); + +} diff --git a/src/com/axe/net/MessageRegistry.java b/src/com/axe/net/MessageRegistry.java new file mode 100755 index 0000000..694123c --- /dev/null +++ b/src/com/axe/net/MessageRegistry.java @@ -0,0 +1,113 @@ + +package com.axe.net; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + + +public class MessageRegistry +{ + + private static Map, Message> messageTypeMap = new HashMap, Message>(); + private static Map> messageIdMap = new HashMap>(); + private static Map>> handlerIdMap = new HashMap>>(); + private static Map, Set>> handlerTypeMap = new HashMap, Set>>(); + + public static Message getMessage( M o ) + { + assert o != null : "You cannot send a null object as a message."; + + Message message = (Message)messageTypeMap.get( o.getClass() ); + + assert message != null : "The object '" + o + "' has no associated message."; + + return message; + } + + public static Message getMessage( int id ) + { + Message message = messageIdMap.get( id ); + + assert message != null : "The id " + id + " has no associated message."; + + return message; + } + + public static void addMessage( Message message ) + { + assert message != null : "You cannot add a null message."; + assert message.type() != null : "The message type cannot be null for message " + message.getClass() + " with id " + message.id(); + + messageTypeMap.put( message.type(), message ); + messageIdMap.put( message.id(), message ); + } + + public static void addHandler( int id, Class type, MessageHandler handler ) + { + if (messageIdMap.containsKey( id )) + { + addToMapSet( id, handler, handlerIdMap ); + } + + if (type != null && messageTypeMap.containsKey( type )) + { + addToMapSet( type, handler, handlerTypeMap ); + } + } + + public static void addHandler( int id, MessageHandler handler) + { + addToMapSet( id, handler, handlerIdMap ); + } + + public static void addHandler( Class type, MessageHandler handler) + { + addToMapSet( type, handler, handlerTypeMap ); + } + + private static void addToMapSet( K key, V value, Map> target ) + { + Set valueSet = target.get( key ); + + if (valueSet == null) + { + valueSet = new HashSet(); + + target.put( key, valueSet ); + } + + valueSet.add( value ); + } + + public static void handleMessage( Client client, M message, int messageId, Packet packet ) + { + handleMessageWithSet( client, message, packet, handlerIdMap.get( messageId ) ); + handleMessageWithSet( client, message, packet, handlerTypeMap.get( message.getClass() ) ); + } + + private static void handleMessageWithSet( Client client, M message, Packet packet, Set> handlerSet ) + { + if (handlerSet != null) + { + Iterator> iter = handlerSet.iterator(); + + while (iter.hasNext()) + { + MessageHandler handler = (MessageHandler)iter.next(); + + if (handler.isExpired()) + { + iter.remove(); + } + else + { + handler.handle( client, message, packet ); + } + } + } + } + +} diff --git a/src/com/axe/net/Packet.java b/src/com/axe/net/Packet.java new file mode 100755 index 0000000..763d6e1 --- /dev/null +++ b/src/com/axe/net/Packet.java @@ -0,0 +1,16 @@ +package com.axe.net; + +import java.util.ArrayList; +import java.util.List; + +public class Packet +{ + + public List messages = new ArrayList(); + public List ids = new ArrayList(); + public long packetTime; + public int packetIndex; + public int packetSize; + public long receivedTime; + +} diff --git a/src/com/axe/net/PermanentMessageHandler.java b/src/com/axe/net/PermanentMessageHandler.java new file mode 100755 index 0000000..4e26aef --- /dev/null +++ b/src/com/axe/net/PermanentMessageHandler.java @@ -0,0 +1,12 @@ + +package com.axe.net; + +public abstract class PermanentMessageHandler implements MessageHandler +{ + + public boolean isExpired() + { + return false; + } + +} diff --git a/src/com/axe/net/ReflectionMessage.java b/src/com/axe/net/ReflectionMessage.java new file mode 100755 index 0000000..62572f6 --- /dev/null +++ b/src/com/axe/net/ReflectionMessage.java @@ -0,0 +1,238 @@ + +package com.axe.net; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.UUID; + +import com.axe.core.Bufferable; +import com.axe.util.Reflections; + + +public class ReflectionMessage implements Message +{ + + public final int id; + public final Class type; + public final Field[] fields; + + public ReflectionMessage(int id, Class type) + { + this.id = id; + this.type = type; + this.fields = Reflections.getFieldArray( type, Modifier.STATIC | Modifier.TRANSIENT ); + } + + @Override + public M read( ByteBuffer in ) + { + try + { + M data = type.newInstance(); + + for (int i = 0; i < fields.length; i++) + { + Field f = fields[i]; + + f.set( data, read( in, f.getType() ) ); + } + + return data; + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static Object read( ByteBuffer in, Class fieldType ) throws Exception + { + if (fieldType == byte.class) return in.get(); + if (fieldType == Byte.class) return readBoolean( in ) ? null : in.get(); + if (fieldType == short.class) return in.getShort(); + if (fieldType == Short.class) return readBoolean( in ) ? null : in.getShort(); + if (fieldType == int.class) return in.getInt(); + if (fieldType == Integer.class) return readBoolean( in ) ? null : in.getInt(); + if (fieldType == long.class) return in.getLong(); + if (fieldType == Long.class) return readBoolean( in ) ? null : in.getLong(); + if (fieldType == float.class) return in.getFloat(); + if (fieldType == Float.class) return readBoolean( in ) ? null : in.getFloat(); + if (fieldType == double.class) return in.getDouble(); + if (fieldType == Double.class) return readBoolean( in ) ? null : in.getDouble(); + if (fieldType == boolean.class) return readBoolean( in ); + if (fieldType == Boolean.class) return readBoolean( in ) ? null : readBoolean( in ); + if (fieldType == char.class) return in.getChar(); + if (fieldType == Character.class) return readBoolean( in ) ? null : in.getChar(); + if (fieldType == UUID.class) return readBoolean( in ) ? null : new UUID( in.getLong(), in.getLong() ); + if (fieldType.isEnum()) return readBoolean( in ) ? null : fieldType.getEnumConstants()[in.get() & 0xFF]; + + if (fieldType == String.class) + { + if (readBoolean( in )) + { + return null; + } + + int stringLength = in.getShort() & 0xFFFF; + byte[] stringBytes = new byte[stringLength]; + in.get( stringBytes ); + return new String( stringBytes ); + } + + if (Bufferable.class.isAssignableFrom( fieldType )) + { + if (readBoolean( in )) + { + return null; + } + + Bufferable b = (Bufferable)fieldType.newInstance(); + b.read( in ); + return b; + } + + throw new RuntimeException( "No reading logic for type " + fieldType ); + } + + public static boolean readBoolean( ByteBuffer in ) + { + return (in.get() != 0 ? true : false); + } + + @Override + public void write( ByteBuffer out, M m ) + { + try + { + for (int i = 0; i < fields.length; i++) + { + Field f = fields[i]; + + write( out, f.getType(), f.get( m ) ); + } + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static void write( ByteBuffer out, Class fieldType, Object fieldValue ) throws Exception + { + if (fieldType == byte.class) { out.put( (Byte)fieldValue ); } + else if (fieldType == Byte.class) { if (writeNonNull( out, fieldValue )) out.put( (Byte)fieldValue ); } + else if (fieldType == short.class) { out.putShort( (Short)fieldValue ); } + else if (fieldType == Short.class) { if (writeNonNull( out, fieldValue )) out.putShort( (Short)fieldValue ); } + else if (fieldType == int.class) { out.putInt( (Integer)fieldValue ); } + else if (fieldType == Integer.class) { if (writeNonNull( out, fieldValue )) out.putInt( (Integer)fieldValue ); } + else if (fieldType == long.class) { out.putLong( (Long)fieldValue ); } + else if (fieldType == Long.class) { if (writeNonNull( out, fieldValue )) out.putLong( (Long)fieldValue ); } + else if (fieldType == float.class) { out.putFloat( (Float)fieldValue ); } + else if (fieldType == Float.class) { if (writeNonNull( out, fieldValue )) out.putFloat( (Float)fieldValue ); } + else if (fieldType == double.class) { out.putDouble( (Double)fieldValue ); } + else if (fieldType == Double.class) { if (writeNonNull( out, fieldValue )) out.putDouble( (Double)fieldValue ); } + else if (fieldType == boolean.class) { out.put( (Boolean)fieldValue ? (byte)1 : (byte)0 ); } + else if (fieldType == Boolean.class) { if (writeNonNull( out, fieldValue )) out.put( (Boolean)fieldValue ? (byte)1 : (byte)0 ); } + else if (fieldType == char.class) { out.putChar( (Character)fieldValue ); } + else if (fieldType == Character.class) { if (writeNonNull( out, fieldValue )) out.putChar( (Character)fieldValue ); } + else if (fieldType == UUID.class) { if (writeNonNull( out, fieldValue )) { out.putLong( ((UUID)fieldValue).getMostSignificantBits() ); out.putLong( ((UUID)fieldValue).getLeastSignificantBits() ); } } + else if (fieldType.isEnum()) { if (writeNonNull( out, fieldValue )) out.put( (byte)((Enum)fieldValue).ordinal() ); } + + else if (fieldType == String.class) + { + if (writeNonNull( out, fieldValue )) + { + String s = (String)fieldValue; + out.putShort( (short)s.length() ); + out.put( s.getBytes() ); + } + } + + else if (Bufferable.class.isAssignableFrom( fieldType )) + { + if (writeNonNull(out, fieldValue)) + { + ((Bufferable)fieldValue).write( out ); + } + } + + else + { + throw new RuntimeException( "No writing logic for type " + fieldType ); + } + } + + public static boolean writeNonNull(ByteBuffer out, Object o) + { + out.put( (byte)(o == null ? 1 : 0) ); + + return (o != null); + } + + @Override + public int sizeOf( M m ) + { + int size = 0; + + try + { + for (int i = 0; i < fields.length; i++) + { + Field f = fields[i]; + + size += sizeOf( f.getType(), f.get( m ) ); + } + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + + return size; + } + + public static int sizeOf( Class fieldType, Object fieldValue ) throws Exception + { + if (fieldType == byte.class) return 1; + if (fieldType == Byte.class) return sizeOfNullable(fieldValue, 1); + if (fieldType == short.class) return 2; + if (fieldType == Short.class) return sizeOfNullable(fieldValue, 2); + if (fieldType == int.class) return 4; + if (fieldType == Integer.class) return sizeOfNullable(fieldValue, 4); + if (fieldType == long.class) return 8; + if (fieldType == Long.class) return sizeOfNullable(fieldValue, 8); + if (fieldType == float.class) return 4; + if (fieldType == Float.class) return sizeOfNullable(fieldValue, 4); + if (fieldType == double.class) return 8; + if (fieldType == Double.class) return sizeOfNullable(fieldValue, 8); + if (fieldType == boolean.class) return 1; + if (fieldType == Boolean.class) return sizeOfNullable(fieldValue, 1); + if (fieldType == char.class) return 2; + if (fieldType == Character.class) return sizeOfNullable(fieldValue, 2); + if (fieldType == UUID.class) return sizeOfNullable(fieldValue, 16); + if (fieldType.isEnum()) return sizeOfNullable(fieldValue, 1); + if (fieldType == String.class) return fieldValue == null ? 1 : 3 + ((String)fieldValue).length(); + if (Bufferable.class.isAssignableFrom( fieldType )) return fieldValue == null ? 1 : 1 + ((Bufferable)fieldValue).bytes(); + + throw new RuntimeException( "No sizeOf logic for type " + fieldType ); + } + + public static int sizeOfNullable(Object value, int normalSize) + { + return (value == null ? 1 : 1 + normalSize); + } + + @Override + public int id() + { + return id; + } + + @Override + public Class type() + { + return type; + } + +} diff --git a/src/com/axe/net/ReflectionMessageHandler.java b/src/com/axe/net/ReflectionMessageHandler.java new file mode 100755 index 0000000..c0fd2af --- /dev/null +++ b/src/com/axe/net/ReflectionMessageHandler.java @@ -0,0 +1,129 @@ + +package com.axe.net; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + + +public class ReflectionMessageHandler extends PermanentMessageHandler +{ + + public final Object notify; + public final Class messageType; + public final Method notifyMethod; + public final Type type; + + public ReflectionMessageHandler( Class messageType, String methodName, Object notify ) + { + this.notify = notify; + this.messageType = messageType; + this.type = findType( notify, methodName, messageType ); + this.notifyMethod = type.getMethod( notify, methodName, messageType ); + } + + @Override + public void handle( Client client, M message, Packet packet ) + { + try + { + type.invoke( notify, notifyMethod, client, message, packet ); + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static Type findType( Object o, String methodName, Class messageType ) + { + for (Type t : Type.values()) + { + if (t.getMethod( o, methodName, messageType ) != null) + { + return t; + } + } + + return null; + } + + // Normal Order is: Client, Message, Packet + public enum Type + { + MESSAGE( 1, -1, 0, -1 ), + CLIENT_MESSAGE( 2, 0, 1, -1 ), + MESSAGE_CLIENT( 2, 1, 0, -1 ), + MESSAGE_PACKET( 2, -1, 0, 1 ), + PACKET_MESSAGE( 2, -1, 1, 0 ), + CLIENT_MESSAGE_PACKET( 3, 0, 1, 2 ), + CLIENT_PACKET_MESSAGE( 3, 0, 2, 1 ), + MESSAGE_CLIENT_PACKET( 3, 1, 0, 2 ), + MESSAGE_PACKET_CLIENT( 3, 2, 0, 1 ), + PACKET_MESSAGE_CLIENT( 3, 2, 1, 0 ), + PACKET_CLIENT_MESSAGE( 3, 1, 2, 0 ); + + public final int parameterCount; + public final int clientIndex; + public final int messageIndex; + public final int packetIndex; + + private Type( int parameterCount, int clientIndex, int messageIndex, int packetIndex ) + { + this.parameterCount = parameterCount; + this.clientIndex = clientIndex; + this.messageIndex = messageIndex; + this.packetIndex = packetIndex; + } + + public Method getMethod( Object obj, String methodName, Class messageType ) + { + try + { + return obj.getClass().getMethod( methodName, getParameterTypes( messageType ) ); + } + catch (Exception e) + { + return null; + } + } + + public Class[] getParameterTypes( Class messageType ) + { + return build( Client.class, messageType, Packet.class ); + } + + public void invoke( Object obj, Method method, Client client, Object message, Packet packet ) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException + { + method.invoke( obj, build( client, message, packet ) ); + } + + public T[] build( T client, T message, T packet, T... out ) + { + out = Arrays.copyOf( out, parameterCount ); + if (clientIndex != -1) + { + out[clientIndex] = client; + } + if (messageIndex != -1) + { + out[messageIndex] = message; + } + if (packetIndex != -1) + { + out[packetIndex] = packet; + } + return out; + } + } + + @SuppressWarnings ("rawtypes" ) + public static void addToRegistry( Object notify, String methodName, Class... messageTypes ) + { + for (Class mt : messageTypes) + { + MessageRegistry.addHandler( mt, new ReflectionMessageHandler( mt, methodName, notify ) ); + } + } + +} diff --git a/src/com/axe/net/SerializedMessage.java b/src/com/axe/net/SerializedMessage.java new file mode 100755 index 0000000..b0280cb --- /dev/null +++ b/src/com/axe/net/SerializedMessage.java @@ -0,0 +1,132 @@ + +package com.axe.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + + +public class SerializedMessage implements Message +{ + + public int id; + public Class type; + public M lastSized; + public int lastSizeOf; + public ByteBuffer lastSizedBuffer = ByteBuffer.allocate( 8192 ); + + public SerializedMessage( int id, Class type ) + { + this.id = id; + this.type = type; + } + + @Override + public M read( ByteBuffer in ) + { + ByteBufferInputStream bbis = new ByteBufferInputStream( in ); + + try + { + ObjectInputStream ois = new ObjectInputStream( bbis ); + M result = (M)ois.readObject(); + ois.close(); + + return result; + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + @Override + public void write( ByteBuffer out, M m ) + { + if (m != lastSized) + { + sizeOf( m ); + } + + out.put( lastSizedBuffer ); + lastSizedBuffer.position( 0 ); + } + + @Override + public int sizeOf( M m ) + { + if (m == lastSized) + { + return lastSizeOf; + } + + lastSizedBuffer.clear(); + + ByteBufferOutputStream bbos = new ByteBufferOutputStream( lastSizedBuffer ); + + try + { + ObjectOutputStream oos = new ObjectOutputStream( bbos ); + oos.writeObject( m ); + oos.close(); + lastSizedBuffer.flip(); + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + + lastSized = m; + lastSizeOf = lastSizedBuffer.limit(); + + return lastSizeOf; + } + + @Override + public int id() + { + return id; + } + + @Override + public Class type() + { + return type; + } + + private class ByteBufferOutputStream extends OutputStream + { + + public final ByteBuffer buffer; + + public ByteBufferOutputStream( ByteBuffer buffer ) + { + this.buffer = buffer; + } + + public void write( int b ) throws IOException + { + buffer.put( (byte)b ); + } + } + + private class ByteBufferInputStream extends InputStream + { + + public final ByteBuffer buffer; + + public ByteBufferInputStream( ByteBuffer buffer ) + { + this.buffer = buffer; + } + + public int read() throws IOException + { + return !buffer.hasRemaining() ? -1 : buffer.get() & 0xFF; + } + } + +} diff --git a/src/com/axe/net/Server.java b/src/com/axe/net/Server.java new file mode 100755 index 0000000..3e276e4 --- /dev/null +++ b/src/com/axe/net/Server.java @@ -0,0 +1,188 @@ +package com.axe.net; + +import java.net.InetSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + + +public class Server +{ + + public String host; + public int port; + public int magicNumber; + public ServerSocketChannel socket; + public Set clients; + public final ServerListener listener; + + public Server(String host, int port, int magicNumber, ServerListener listener) + { + this.host = host; + this.port = port; + this.magicNumber = magicNumber; + this.listener = listener; + } + + public void broadcast( Object message, Client exceptFor ) + { + for (Client c : clients) + { + if (c.receivesBroadcasts && c != exceptFor) + { + c.out.add( message ); + } + } + } + + public void init() throws Exception + { + socket = ServerSocketChannel.open(); + socket.bind( new InetSocketAddress( port ) ); + socket.configureBlocking( false ); + + clients = new HashSet(); + + listener.onInit( this ); + } + + public void update() throws Exception + { + listener.onUpdateBegin( this ); + + boolean sleep = true; + + for (;;) + { + try + { + SocketChannel clientSocket = socket.accept(); + + if (clientSocket == null) + { + break; + } + + sleep = false; + + initializeClient( new Client( clientSocket, this, magicNumber ) ); + } + catch (Exception e) + { + listener.onAcceptError( this, e ); + + break; + } + } + + Iterator iter = clients.iterator(); + + while (iter.hasNext()) + { + Client c = iter.next(); + + if (!updateClient( c )) + { + iter.remove(); + } + else if (c.packetsRead > 0 || c.messagesSent > 0) + { + sleep = false; + } + } + + listener.onUpdateEnd( this ); + + if (sleep) + { + try + { + Thread.sleep( 1 ); + } + catch (Exception e) + { + // catch some sleep for not doing anything! + } + } + } + + private void initializeClient(Client c) + { + try + { + c.init(); + + listener.onClientConnect( this, c ); + + clients.add( c ); + } + catch (Exception e) + { + listener.onClientFailedConnect( this, c, e ); + + System.err.println( "Error initializing accepted client" ); + } + } + + private boolean updateClient(Client c) + { + if (!c.socket.isOpen()) + { + return false; + } + + if ( c.isClosed() ) + { + listener.onClientClose( this, c, null ); + + return false; + } + + boolean success = true; + + try + { + c.read(); + c.update(); + + listener.onClientUpdate( this, c ); + + if (c.readyToSend) + { + c.send(); + } + } + catch (Exception e) + { + success = false; + + System.err.println( "Error with client, closing connection and removing them." ); + + listener.onClientClose( this, c, e ); + + c.close(); + } + + return success; + } + + public void close() + { + for (Client c : clients) + { + c.close(); + } + + try + { + socket.close(); + } + catch (Exception e) + { + // swallow + } + } + +} diff --git a/src/com/axe/net/ServerListener.java b/src/com/axe/net/ServerListener.java new file mode 100755 index 0000000..281b2ba --- /dev/null +++ b/src/com/axe/net/ServerListener.java @@ -0,0 +1,14 @@ +package com.axe.net; + + +public interface ServerListener +{ + public void onInit(Server server); + public void onUpdateBegin(Server server); + public void onUpdateEnd(Server server); + public void onClientConnect(Server server, Client c); + public void onClientFailedConnect(Server server, Client c, Exception e); + public void onClientUpdate(Server server, Client c); + public void onClientClose(Server server, Client c, Exception e); + public void onAcceptError(Server server, Exception e); +} diff --git a/src/com/axe/net/Service.java b/src/com/axe/net/Service.java new file mode 100755 index 0000000..17227b1 --- /dev/null +++ b/src/com/axe/net/Service.java @@ -0,0 +1,80 @@ + +package com.axe.net; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.magnos.reflect.ReflectFactory; + + +public class Service implements InvocationHandler +{ + + public static Service create(Class serviceClass) + { + return new Service( serviceClass ); + } + + private Client client; + private Server server; + + private final T service; + private final ServiceInterface serviceInterface; + + public Service(Class serviceClass) + { + this.serviceInterface = serviceClass.getAnnotation( ServiceInterface.class ); + this.service = (T)Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {serviceClass}, this ); + } + + public T get() + { + return service; + } + + @Override + public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable + { + ServiceCall call = serviceInterface.call().newInstance(); + call.method = ReflectFactory.addMethod( method ); + call.arguments = args; + call.methodId = method.getAnnotation( ServiceMethod.class ).id(); + + if (server != null) + { + for (Client c : server.clients) + { + if (c != client && c.receivesBroadcasts) + { + c.out.add( call ); + } + } + } + else + { + client.out.add( call ); + } + + return null; + } + + public void setTarget( Client client ) + { + this.server = null; + this.client = client; + } + + public void setTarget( Server server ) + { + this.server = server; + this.client = null; + } + + public void setTarget( Server server, Client skip ) + { + this.server = server; + this.client = skip; + } + +} diff --git a/src/com/axe/net/ServiceBase.java b/src/com/axe/net/ServiceBase.java new file mode 100755 index 0000000..ee8909d --- /dev/null +++ b/src/com/axe/net/ServiceBase.java @@ -0,0 +1,6 @@ +package com.axe.net; + +public interface ServiceBase +{ + public void setCallSource(Client client, Packet packet); +} diff --git a/src/com/axe/net/ServiceCall.java b/src/com/axe/net/ServiceCall.java new file mode 100755 index 0000000..a9e81a9 --- /dev/null +++ b/src/com/axe/net/ServiceCall.java @@ -0,0 +1,12 @@ +package com.axe.net; + +import org.magnos.reflect.impl.ReflectMethod; + +public class ServiceCall +{ + + public int methodId; + public ReflectMethod method; + public Object[] arguments; + +} diff --git a/src/com/axe/net/ServiceInterface.java b/src/com/axe/net/ServiceInterface.java new file mode 100755 index 0000000..786cff3 --- /dev/null +++ b/src/com/axe/net/ServiceInterface.java @@ -0,0 +1,14 @@ +package com.axe.net; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ServiceInterface +{ + public int id(); + public Class call(); +} diff --git a/src/com/axe/net/ServiceMessage.java b/src/com/axe/net/ServiceMessage.java new file mode 100755 index 0000000..46d2c3e --- /dev/null +++ b/src/com/axe/net/ServiceMessage.java @@ -0,0 +1,78 @@ + +package com.axe.net; + +import java.lang.reflect.Method; +import java.nio.ByteBuffer; + +import org.magnos.reflect.ReflectFactory; +import org.magnos.reflect.impl.ReflectMethod; + +import com.axe.core.Factory; +import com.axe.util.ReflectionFactory; + + +public class ServiceMessage implements Message +{ + + public int id; + public Class service; + public ServiceInterface serviceInterface; + public ReflectMethod[] serviceMethods; + public Factory factory; + + public ServiceMessage( Class service ) + { + this.service = service; + this.serviceInterface = service.getAnnotation( ServiceInterface.class ); + this.serviceMethods = new ReflectMethod[256]; + this.factory = ReflectionFactory.fromClass( serviceInterface.call() ); + + for (Method m : service.getDeclaredMethods()) + { + ServiceMethod sm = m.getAnnotation( ServiceMethod.class ); + + if (sm != null) + { + serviceMethods[sm.id()] = ReflectFactory.addMethod( m ); + } + } + } + + @Override + public ServiceCall read( ByteBuffer in ) + { + ServiceCall call = factory.create(); + call.methodId = in.get() & 0xFF; + call.method = serviceMethods[ call.methodId ]; + call.arguments = call.method.get( in ); + + return call; + } + + @Override + public void write( ByteBuffer out, ServiceCall m ) + { + out.put( (byte)m.methodId ); + + m.method.put( out, m.arguments ); + } + + @Override + public int sizeOf( ServiceCall m ) + { + return m.method.sizeOf( m.arguments ) + 1; + } + + @Override + public int id() + { + return serviceInterface.id(); + } + + @Override + public Class type() + { + return (Class)serviceInterface.call(); + } + +} diff --git a/src/com/axe/net/ServiceMessageHandler.java b/src/com/axe/net/ServiceMessageHandler.java new file mode 100755 index 0000000..908f00b --- /dev/null +++ b/src/com/axe/net/ServiceMessageHandler.java @@ -0,0 +1,43 @@ +package com.axe.net; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ServiceMessageHandler implements MessageHandler +{ + + public ServiceBase service; + public AtomicBoolean expired = new AtomicBoolean( false ); + + public ServiceMessageHandler(ServiceBase service) + { + this.service = service; + } + + public ServiceMessageHandler(ServiceBase service, AtomicBoolean expired) + { + this.service = service; + this.expired = expired; + } + + @Override + public void handle( Client client, ServiceCall m, Packet packet ) + { + service.setCallSource( client, packet ); + + try + { + m.method.getMethod().invoke( service, m.arguments ); + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + @Override + public boolean isExpired() + { + return expired.get(); + } + +} diff --git a/src/com/axe/net/ServiceMethod.java b/src/com/axe/net/ServiceMethod.java new file mode 100755 index 0000000..093ead7 --- /dev/null +++ b/src/com/axe/net/ServiceMethod.java @@ -0,0 +1,17 @@ +package com.axe.net; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ServiceMethod +{ + public int id(); + public boolean reliable() default true; + public boolean ordered() default false; + public int retryCount() default 0; + public int channel() default 0; +} diff --git a/src/com/axe/net/reflect/Reflect.java b/src/com/axe/net/reflect/Reflect.java new file mode 100755 index 0000000..1cd2be1 --- /dev/null +++ b/src/com/axe/net/reflect/Reflect.java @@ -0,0 +1,12 @@ +package com.axe.net.reflect; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; + +public interface Reflect +{ + public Object read(ByteBuffer in); + public void set(Object obj, Field field, ByteBuffer in) throws IllegalAccessException; + public void write(Object obj, ByteBuffer out); + public int sizeOf(Object obj); +} diff --git a/src/com/axe/net/reflect/ReflectInt.java b/src/com/axe/net/reflect/ReflectInt.java new file mode 100755 index 0000000..32718da --- /dev/null +++ b/src/com/axe/net/reflect/ReflectInt.java @@ -0,0 +1,59 @@ + +package com.axe.net.reflect; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; + + +public class ReflectInt implements Reflect +{ + + public static final int MORE = 0x80; + public static final int SIGN = 0x40; + public static final int SIGN_SHIFT = 0x40; + public static final int SIGNED_MASK = 0x3F; + public static final int MASK = 0x7F; + + @Override + public Object read( ByteBuffer in ) + { + return get( in ); + } + + @Override + public void set( Object obj, Field field, ByteBuffer in ) throws IllegalAccessException + { + field.setFloat( obj, get( in ) ); + } + + @Override + public int sizeOf( Object obj ) + { + return 0; + } + + @Override + public void write( Object obj, ByteBuffer out ) + { + + } + + public static int get( ByteBuffer in ) + { + int a = in.get() & 0xFF; + int sign = ((((a & SIGN) >> SIGN_SHIFT) & 1) << 1) - 1; + int av = (a & SIGNED_MASK); + + if ((a & MORE) == 0) { + return av * sign; + } + + return 0; + } + + public static void put( ByteBuffer out, int v) + { + + } + +} diff --git a/src/com/axe/node/AbstractNode.java b/src/com/axe/node/AbstractNode.java new file mode 100644 index 0000000..1d7fa3b --- /dev/null +++ b/src/com/axe/node/AbstractNode.java @@ -0,0 +1,22 @@ +package com.axe.node; + +import com.axe.math.Vec; + +public abstract class AbstractNode> implements Node +{ + + public Node parent; + + @Override + public Node getParent() + { + return parent; + } + + @Override + public void setParent(Node parent) + { + this.parent = parent; + } + +} diff --git a/src/com/axe/node/MatrixNode.java b/src/com/axe/node/MatrixNode.java new file mode 100644 index 0000000..d4483de --- /dev/null +++ b/src/com/axe/node/MatrixNode.java @@ -0,0 +1,26 @@ +package com.axe.node; + +import com.axe.View; +import com.axe.game.GameState; +import com.axe.math.Matrix; +import com.axe.math.Vec; + +public class MatrixNode> extends AbstractNode +{ + + public Matrix localMatrix; + public Matrix matrix; + + public MatrixNode(Matrix localMatrix) + { + this.localMatrix = localMatrix; + this.matrix = localMatrix.create(); + } + + @Override + public void update(GameState state, View firstView) + { + + } + +} diff --git a/src/com/axe/node/Node.java b/src/com/axe/node/Node.java new file mode 100644 index 0000000..54c8e70 --- /dev/null +++ b/src/com/axe/node/Node.java @@ -0,0 +1,191 @@ +package com.axe.node; + +import com.axe.View; +import com.axe.collect.List; +import com.axe.game.GameState; +import com.axe.math.Matrix; +import com.axe.math.Scalarf; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; + +/** + * The life-cycle of a node: + * + * setParent is called when added to node list + * activate is called at game start OR before the first draw (for nodes added after the game has started) + * update node if not expired + * if node expired, remove from parent node list + * draw node if not expired + * + * @author phil + * + * @param + */ +public interface Node> +{ + + public Node getParent(); + + public void setParent( Node parent ); + + default public Matrix getLocalMatrix() + { + return null; + } + + default public Matrix getMatrix() + { + return null; + } + + default public Matrix getParentMatrix() + { + Node parent = getParent(); + + if ( parent != null ) + { + return parent.getMatrix() != null ? parent.getMatrix() : parent.getParentMatrix(); + } + + return null; + } + + default public void updateLocalMatrix() + { + + } + + default public void updateMatrix() + { + updateLocalMatrix(); + + Matrix local = getLocalMatrix(); + Matrix global = getMatrix(); + + if (local != null && global != null) + { + Matrix parent = getParentMatrix(); + + if (parent != null) + { + global.set( parent ); + global.multiplyi( local ); + } + else + { + global.set( local ); + } + } + } + + default public boolean isActivated() + { + return true; + } + + default public void activate() + { + + } + + public void update(GameState state, View firstView); + + default public void draw(GameState state, View view) + { + + } + + default public boolean getBounds(GameState state, V min, V max) + { + final Calculator calc = min.getCalculator(); + V center = calc.create(); + Scalarf radius = new Scalarf(); + boolean exists = false; + + if (getSphere(state, center, radius)) + { + min = calc.clear( min, -radius.v ); + max = calc.clear( max, +radius.v ); + + min = calc.addi( min, center ); + max = calc.addi( max, center ); + + exists = true; + } + + calc.recycle( center ); + + return exists; + } + + default public boolean getSphere(GameState state, V center, Scalarf radius) + { + final Calculator calc = center.getCalculator(); + + V min = calc.create(); + V max = calc.create(); + boolean exists = false; + + if (getBounds(state, min, max)) + { + center = calc.interpolate( center, min, max, 0.5f ); + radius.v = calc.distance( min, max ) * 0.5f; + exists = true; + } + + calc.recycle( min ); + calc.recycle( max ); + + return exists; + } + + default public boolean isExpired() + { + return false; + } + + default public void expire() + { + + } + + default public void destroy() + { + + } + + public static > void activateNode(Node node) + { + if (!node.isExpired() && !node.isActivated()) + { + node.activate(); + } + } + + public static > void drawNode(Node node, GameState state, View view) + { + if (!node.isExpired()) + { + if (!node.isActivated()) + { + node.activate(); + } + + node.draw(state, view); + } + } + + public static > void removeNode(Node parent, Node child) + { + if (child.getParent() == parent) + { + child.setParent( null ); + } + } + + public static , N extends Node> void updateMatrices(List nodes) + { + nodes.forAll( n -> n.updateMatrix() ); + } + +} \ No newline at end of file diff --git a/src/com/axe/node/NodeList.java b/src/com/axe/node/NodeList.java new file mode 100644 index 0000000..0031338 --- /dev/null +++ b/src/com/axe/node/NodeList.java @@ -0,0 +1,228 @@ +package com.axe.node; + +import java.util.Comparator; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import com.axe.View; +import com.axe.collect.List; +import com.axe.collect.ListArray; +import com.axe.collect.ListItem; +import com.axe.collect.ListLinked; +import com.axe.collect.ListSorted; +import com.axe.game.GameState; +import com.axe.math.Matrix; +import com.axe.math.Scalarf; +import com.axe.math.Vec; + + +public class NodeList, N extends Node> extends AbstractNode implements List +{ + + public static , T extends Node> NodeList create(List nodes) + { + return new NodeList( nodes ); + } + + public static , T extends Node> NodeList linked() + { + return new NodeList( ListLinked.create() ); + } + + public static , T extends Node> NodeList array(T ... nodes) + { + return new NodeList( ListArray.create(nodes) ); + } + + public static , T extends Node> NodeList sorted(Comparator comparator, T ... nodes) + { + return new NodeList( ListSorted.create(comparator, nodes) ); + } + + public final List nodes; + public boolean hasSphere; + public boolean hasBounds; + public boolean activated; + public Matrix localMatrix; + public Matrix matrix; + + protected NodeList(List nodes) + { + this.nodes = nodes; + this.activated = false; + } + + public NodeList setBoundary(boolean hasSphere, boolean hasBounds) + { + this.hasSphere = hasSphere; + this.hasBounds = hasBounds; + + return this; + } + + public void setLocalMatrix(Matrix matrix) + { + this.localMatrix = matrix; + this.matrix = matrix != null ? matrix.create() : null; + this.updateMatrix(); + + Node.updateMatrices( this ); + } + + @Override + public boolean isActivated() + { + return activated; + } + + @Override + public void activate() + { + nodes.forAll( Node::activateNode ); + activated = true; + } + + @Override + public void update(GameState state, View firstView) + { + nodes.forEach((n, item) -> + { + n.update( state, firstView ); + + if (n.isExpired()) + { + item.remove( n ); + + n.destroy(); + } + + if (!item.isLinked()) + { + Node.removeNode( this, n ); + } + + return true; + }); + + nodes.update(); + } + + @Override + public void draw(GameState state, View view) + { + if ( view.getCamera().intersects( state, this ) ) + { + nodes.forAll((n) -> Node.drawNode(n, state, view)); + } + } + + @Override + public boolean getBounds(GameState state, V min, V max) + { + if ( !hasBounds ) + { + return false; + } + + return Nodes.getBounds( state, nodes, min, max ); + } + + @Override + public boolean getSphere(GameState state, V center, Scalarf radius) + { + if ( !hasSphere ) + { + return false; + } + + return Nodes.getSphere( state, nodes, center, radius ); + } + + @Override + public ListItem add(N value) + { + value.setParent( this ); + + return nodes.add( value ); + } + + @Override + public void add(N[] values) + { + for (int i = 0; i < values.length; i++) + { + values[ i ].setParent( this ); + } + + nodes.add( values ); + } + + @Override + public ListItem find(N value, BiPredicate equals) + { + return nodes.find( value, equals ); + } + + @Override + public boolean remove(N value) + { + boolean removed = nodes.remove( value ); + + if (removed) + { + Node.removeNode( this, value ); + } + + return removed; + } + + @Override + public void clear() + { + nodes.clear(); + } + + @Override + public int size() + { + return nodes.size(); + } + + @Override + public void forAll(Consumer consume) + { + nodes.forAll( consume ); + } + + @Override + public int forEach(Predicate predicate) + { + return nodes.forEach( predicate ); + } + + @Override + public int forEach(BiPredicate predicate) + { + return nodes.forEach( predicate ); + } + + @Override + public void where(Predicate predicate, Consumer consumer) + { + nodes.where( predicate, consumer ); + } + + @Override + public int count(Predicate predicate) + { + return nodes.count( predicate ); + } + + @Override + public N first(Predicate predicate) + { + return nodes.first( predicate ); + } + +} \ No newline at end of file diff --git a/src/com/axe/node/Nodes.java b/src/com/axe/node/Nodes.java new file mode 100644 index 0000000..b83b615 --- /dev/null +++ b/src/com/axe/node/Nodes.java @@ -0,0 +1,411 @@ +package com.axe.node; + +import java.util.Arrays; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import com.axe.View; +import com.axe.collect.List; +import com.axe.collect.ListItem; +import com.axe.collect.ListItemIgnored; +import com.axe.game.GameState; +import com.axe.math.Matrix; +import com.axe.math.Scalarf; +import com.axe.math.Scalari; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; +import com.axe.util.Array; +import com.axe.util.Ref; + +public class Nodes, N extends Node> extends RenderedNode implements List +{ + + public static int DEFAULT_CAPACITY = 32; + public static int DEFAULT_INCREASE = 16; + public static boolean DEFAULT_EXPIRE = false; + + public N[] nodes; + public int size; + public int increase; + public boolean hasSphere; + public boolean hasBounds; + public boolean expireOnEmpty; + public boolean activated; + public Matrix localMatrix; + public Matrix matrix; + + public Nodes(N ... initial) + { + this( DEFAULT_CAPACITY, DEFAULT_INCREASE, DEFAULT_EXPIRE, initial ); + } + + public Nodes(int capacity, N ... initial) + { + this( capacity, DEFAULT_INCREASE, DEFAULT_EXPIRE, initial ); + } + + public Nodes(int capacity, boolean expireOnEmpty, N ... initial) + { + this( capacity, DEFAULT_INCREASE, expireOnEmpty, initial ); + } + + public Nodes(boolean expireOnEmpty, N ... initial) + { + this( DEFAULT_CAPACITY, DEFAULT_INCREASE, expireOnEmpty, initial ); + } + + public Nodes(int capacity, int increase, boolean expireOnEmpty, N ... initial) + { + this.nodes = Arrays.copyOf( initial, Math.max( capacity, initial.length ) ); + this.size = initial.length; + this.increase = increase; + this.expireOnEmpty = expireOnEmpty; + this.activated = false; + } + + public Nodes setBoundary(boolean hasSphere, boolean hasBounds) + { + this.hasSphere = hasSphere; + this.hasBounds = hasBounds; + + return this; + } + + public void setLocalMatrix(Matrix matrix) + { + this.localMatrix = matrix; + this.matrix = matrix != null ? matrix.create() : null; + this.updateMatrix(); + + Node.updateMatrices( this ); + } + + @Override + public boolean isActivated() + { + return activated; + } + + @Override + public void activate() + { + for (int i = 0; i < size; i++) + { + Node.activateNode( nodes[ i ] ); + } + + activated = true; + } + + @Override + public void update(GameState state, View firstView) + { + int live = 0; + + for (int i = 0; i < size; i++) + { + N node = nodes[ i ]; + + node.update( state, firstView ); + + if ( node.isExpired() ) + { + destroyNode( node ); + } + else + { + nodes[ live++ ] = node; + } + } + + while (live < size) + { + nodes[ live++ ] = null; + } + + size = live; + } + + protected void destroyNode(N node) + { + node.destroy(); + + Node.removeNode( this, node ); + } + + @Override + public void draw(GameState state, View view) + { + for (int i = 0; i < size; i++) + { + Node.drawNode( nodes[ i ], state, view ); + } + } + + @Override + public boolean getBounds(GameState state, V min, V max) + { + if ( !hasBounds ) + { + return false; + } + + return Nodes.getBounds( state, this, min, max ); + } + + @Override + public boolean getSphere(GameState state, V center, Scalarf radius) + { + if ( !hasSphere ) + { + return false; + } + + return Nodes.getSphere( state, this, center, radius ); + } + + @Override + public boolean isExpired() + { + if ( !expireOnEmpty ) + { + return false; + } + + for (int i = 0; i < size; i++) + { + if ( !nodes[ i ].isExpired() ) + { + return false; + } + } + + return true; + } + + @Override + public void expire() + { + for (int i = 0; i < size; i++) + { + nodes[ i ].expire(); + } + } + + @Override + public void destroy() + { + clear( true ); + } + + @Override + public ListItem add(N value) + { + nodes = Array.put( nodes, size++, value, increase ); + value.setParent( this ); + + return ListItemIgnored.INSTANCE; + } + + @Override + public ListItem find(N value, BiPredicate equals) + { + return null; + } + + @Override + public void clear() + { + clear( false ); + } + + public void clear(boolean destroy) + { + while (--size >= 0) + { + if ( destroy ) + { + destroyNode( nodes[ size ] ); + } + + nodes[ size ] = null; + } + + size = 0; + } + + @Override + public int size() + { + return size; + } + + @Override + public int forEach(Predicate predicate) + { + int iterated = 0; + + while (iterated < size) + { + N node = nodes[ iterated++ ]; + + if ( !predicate.test( node ) ) + { + break; + } + } + + return iterated; + } + + @Override + public void forAll(Consumer consumer) + { + for (int i = 0; i < size; i++) + { + consumer.accept( nodes[ i ] ); + } + } + + @Override + public int forEach(BiPredicate predicate) + { + int iterated = 0; + + while (iterated < size) + { + N node = nodes[ iterated++ ]; + + if ( !predicate.test( node, ListItemIgnored.INSTANCE ) ) + { + break; + } + } + + return iterated; + } + + public static , N extends Node> boolean getSphere(GameState state, List nodes, V center, Scalarf radius) + { + final V centerRef = center; + final Calculator calc = center.getCalculator(); + + // Set center and radius to the first node which has a bounding sphere. + int searched = nodes.forEach((n) -> + { + return !n.getSphere(state, centerRef, radius); + }); + + // If the forEach returned the number of nodes, none of the nodes have a bounding sphere. + if ( searched >= nodes.size() ) + { + return false; + } + + // Calculate the sphere with the farthest boundary from the first sphere. + N far1 = getFurthestCenter( state, nodes, center ); + + Scalarf radius1 = radius.clone(); + V center1 = calc.clone( center ); + far1.getSphere( state, center1, radius1 ); + + // Calculate the sphere with the farthest boundary from the second sphere. + N for2 = getFurthestCenter( state, nodes, center1 ); + + Scalarf radius2 = radius.clone(); + V center2 = calc.clone( center ); + for2.getSphere( state, center2, radius2 ); + + // The center of the bounding sphere is the center of the two previous spheres + // while taking into account their radius. + float distance = calc.distance( center1, center2 ); + float total = distance + radius1.v + radius2.v; + float delta1 = radius1.v / total; + float delta2 = (radius1.v + distance) / total; + float relative1 = delta2 - delta1; + float relative2 = 0.5f - delta1; + float delta = relative2 / relative1; + + center = calc.interpolate( center, center1, center2, delta ); + radius.v = Math.max( distance * 0.5f, Math.max( radius1.v, radius2.v ) ); + + final V centerNewRef = center; + + // The bounding sphere is then expanded to fit all nodes. + nodes.forAll((n) -> + { + if (n.getSphere( state, center1, radius1 )) + { + float ndist = calc.distance( center1, centerNewRef ) + radius1.v; + + radius.v = Math.max( radius.v, ndist ); + } + }); + + calc.recycle( center1 ); + calc.recycle( center2 ); + + return false; + } + + public static , N extends Node> N getFurthestCenter(GameState state, List nodes, V center) + { + final Calculator calc = center.getCalculator(); + final Ref furthest = new Ref<>(); + final V tempCenter = calc.clone( center ); + final Scalarf tempRadius = new Scalarf(); + final Scalarf max = new Scalarf(); + + nodes.forAll((n) -> + { + if (n.getSphere(state, tempCenter, tempRadius)) + { + float distance = calc.distance( center, tempCenter ) + tempRadius.v; + + if ( furthest.value == null || distance > max.v ) + { + furthest.value = n; + max.v = distance; + } + } + }); + + calc.recycle( tempCenter ); + + return furthest.value; + } + + public static , N extends Node> boolean getBounds(GameState state, List nodes, V min, V max) + { + final Calculator calc = min.getCalculator(); + Scalari set = new Scalari(); + V minTemp = calc.clone( min ); + V maxTemp = calc.clone( max ); + + nodes.forAll((n) -> + { + if (n.getBounds(state, minTemp, maxTemp)) + { + if (set.v == 0) + { + calc.copy( min, minTemp ); + calc.copy( max, maxTemp ); + } + else + { + calc.binary( min, min, minTemp, Math::min ); + calc.binary( max, max, maxTemp, Math::max ); + } + + set.v++; + } + }); + + calc.recycle( minTemp ); + calc.recycle( maxTemp ); + + return set.v > 0; + } + +} \ No newline at end of file diff --git a/src/com/axe/node/RenderedNode.java b/src/com/axe/node/RenderedNode.java new file mode 100644 index 0000000..b9f88ce --- /dev/null +++ b/src/com/axe/node/RenderedNode.java @@ -0,0 +1,89 @@ +package com.axe.node; + +import com.axe.Axe; +import com.axe.View; +import com.axe.game.GameState; +import com.axe.math.Vec; +import com.axe.render.Renderer; + +public class RenderedNode> extends AbstractNode +{ + + public Renderer> renderer; + + public RenderedNode() + { + } + + public RenderedNode(int id) + { + this.setRenderer( id ); + } + + @Override + public boolean isActivated() + { + return renderer == null || renderer.isActivated( this ); + } + + @Override + public void activate() + { + renderer.activate( this ); + } + + @Override + public void update(GameState state, View firstView) + { + if (renderer != null) + { + renderer.update( this ); + } + } + + @Override + public void draw(GameState state, View view) + { + if (renderer != null) + { + renderer.begin( this, state, view ); + + indraw( state, view ); + + renderer.end( this, state, view ); + } + } + + protected void indraw(GameState state, View view) + { + + } + + public void setRenderer(int id) + { + destroyRenderer(); + + renderer = Axe.renderers.get( id ); + + if (renderer != null) + { + renderer = renderer.create( this ); + } + } + + @Override + public void destroy() + { + destroyRenderer(); + } + + protected void destroyRenderer() + { + if (renderer != null) + { + renderer.destroy( this ); + renderer = null; + } + } + +} diff --git a/src/com/axe/node/RenderersNode.java b/src/com/axe/node/RenderersNode.java new file mode 100644 index 0000000..2e654d0 --- /dev/null +++ b/src/com/axe/node/RenderersNode.java @@ -0,0 +1,140 @@ +package com.axe.node; + +import java.util.Arrays; + +import com.axe.Axe; +import com.axe.View; +import com.axe.game.GameState; +import com.axe.math.Scalarf; +import com.axe.math.Vec; +import com.axe.render.Renderer; + +public class RenderersNode> extends AbstractNode +{ + + public Node parent; + public Renderer>[] renderers; + public Node model; + public boolean callUpdate; + + public RenderersNode(Node model, boolean callUpdate, int ... ids) + { + this.model = model; + this.callUpdate = callUpdate; + this.renderers = new Renderer[ ids.length ]; + + for (int i = 0; i < ids.length; i++) + { + Renderer> template = Axe.renderers.get( ids[ i ] ); + + this.renderers[ i ] = template.create( model ); + } + } + + @Override + public boolean isActivated() + { + if (!model.isActivated()) + { + return false; + } + + for (int i = 0; i < renderers.length; i++) + { + if (!renderers[i].isActivated(this)) + { + return false; + } + } + + return true; + } + + @Override + public void activate() + { + if (!model.isActivated()) + { + model.activate(); + } + + for (int i = 0; i < renderers.length; i++) + { + if (!renderers[i].isActivated(this)) + { + renderers[i].activate(this); + } + } + } + + @Override + public void update(GameState state, View firstView) + { + model.update( state, firstView ); + + if ( callUpdate && !model.isExpired() ) + { + for (int i = 0; i < renderers.length; i++) + { + renderers[ i ].update( model ); + } + } + } + + @Override + public void draw(GameState state, View view) + { + if ( view.getCamera().intersects( state, this ) ) + { + for (int i = 0; i < renderers.length; i++) + { + renderers[ i ].begin( model, state, view ); + } + + Node.drawNode( model, state, view ); + + for (int i = renderers.length - 1; i >= 0; i--) + { + renderers[ i ].end( model, state, view ); + } + } + } + + @Override + public boolean getBounds(GameState state, V min, V max) + { + return model.getBounds( state, min, max ); + } + + @Override + public boolean getSphere(GameState state, V center, Scalarf radius) + { + return model.getSphere( state, center, radius ); + } + + @Override + public void expire() + { + model.expire(); + } + + @Override + public boolean isExpired() + { + return model.isExpired(); + } + + @Override + public void destroy() + { + for (int i = 0; i < renderers.length; i++) + { + renderers[ i ].destroy( model ); + } + + renderers = Arrays.copyOf( renderers, 0 ); + + model.destroy(); + } + +} diff --git a/src/com/axe/node/UpdateNode.java b/src/com/axe/node/UpdateNode.java new file mode 100644 index 0000000..890a05e --- /dev/null +++ b/src/com/axe/node/UpdateNode.java @@ -0,0 +1,139 @@ +package com.axe.node; + +import com.axe.View; +import com.axe.game.GameState; +import com.axe.math.Scalarf; +import com.axe.math.Vec; + +public class UpdateNode> extends AbstractNode +{ + + public static float DEFAULT_RATE = 0; + public static boolean DEFAULT_EXACT = false; + public static boolean DEFAULT_ENABLED = true; + public static float DEFAULT_TIME_SCALE = 1.0f; + + public Node child; + public float rate; + public boolean exact; + public boolean enabled; + public float timeScale; + + protected float time; + + public UpdateNode(Node child) + { + this( child, DEFAULT_RATE, DEFAULT_EXACT, DEFAULT_ENABLED, DEFAULT_TIME_SCALE ); + } + + public UpdateNode(Node child, float timeScale) + { + this( child, DEFAULT_RATE, DEFAULT_EXACT, DEFAULT_ENABLED, timeScale ); + } + + public UpdateNode(Node child, float rate, boolean exact) + { + this( child, rate, exact, DEFAULT_ENABLED, DEFAULT_TIME_SCALE ); + } + + public UpdateNode(Node child, float rate, boolean exact, boolean enabled, float timeScale) + { + this.child = child; + this.rate = rate; + this.exact = exact; + this.enabled = enabled; + this.timeScale = timeScale; + } + + @Override + public boolean isActivated() + { + return child.isActivated(); + } + + @Override + public void activate() + { + child.activate(); + } + + @Override + public void update(GameState state, View firstView) + { + if ( !enabled ) + { + return; + } + + int updates = 1; + + if (rate > 0) + { + time += state.seconds; + + updates = (int)(time / rate); + + time -= updates * rate; + + if (!exact && updates > 0) + { + updates = 1; + } + } + + long nanos = state.nanos; + float scale = timeScale; + + if (scale != 1.0f) + { + state.setElapsed( (long)(nanos * timeScale) ); + } + + while (--updates >= 0) + { + child.update( state, firstView ); + } + + if (scale != 1.0f) + { + state.setElapsed( nanos ); + } + } + + @Override + public void draw(GameState state, View view) + { + Node.drawNode( child, state, view ); + } + + @Override + public boolean getBounds(GameState state, V min, V max) + { + return child.getBounds( state, min, max ); + } + + @Override + public boolean getSphere(GameState state, V center, Scalarf radius) + { + return child.getSphere( state, center, radius ); + } + + @Override + public void expire() + { + child.expire(); + } + + @Override + public boolean isExpired() + { + return child.isExpired(); + } + + @Override + public void destroy() + { + child.destroy(); + } + +} diff --git a/src/com/axe/node/state/StateManagerNode.java b/src/com/axe/node/state/StateManagerNode.java new file mode 100644 index 0000000..c9a57a8 --- /dev/null +++ b/src/com/axe/node/state/StateManagerNode.java @@ -0,0 +1,160 @@ +package com.axe.node.state; + +import com.axe.View; +import com.axe.game.GameState; +import com.axe.math.Vec; +import com.axe.node.Node; + +public class StateManagerNode, S extends Enum> implements Node +{ + + public Node parent; + public S currentState; + public S nextState; + public StateNode[] states; + public StateManagerState managerState; + + public StateManagerNode(Class stateEnum) + { + this.states = new StateNode[ stateEnum.getEnumConstants().length ]; + this.managerState = StateManagerState.Inactive; + } + + @Override + public Node getParent() + { + return parent; + } + + @Override + public void setParent(Node parent) + { + this.parent = parent; + } + + @Override + public boolean isActivated() + { + StateNode current = get( currentState ); + + return current == null || current.isActivated(); + } + + @Override + public void activate() + { + StateNode current = get( currentState ); + + if ( !current.isActivated() ) + { + current.activate(); + } + } + + @Override + public void draw(GameState state, View view) + { + StateNode current = get( currentState ); + + if (current != null) + { + Node.drawNode( current, state, view ); + } + } + + @Override + public void update(GameState state, View firstView) + { + StateNode current = get( currentState ); + + switch (managerState) + { + case Inactive: + break; + + case Entering: + current.entering(state, firstView, this); + break; + + case Exiting: + current.exiting(state, firstView, this); + break; + + case Active: + current.update(state, firstView); + break; + } + } + + public StateNode add(S state, StateNode node) + { + states[ state.ordinal() ] = node; + + node.manager = this; + + return node; + } + + public StateNode get(S state) + { + return state == null ? null : states[ state.ordinal() ]; + } + + public void set(S state) + { + StateNode next = get( state ); + StateNode current = get( currentState ); + + if ( next != null && nextState != state && currentState != state ) + { + if (current == null) + { + currentState = state; + managerState = StateManagerState.Entering; + next.onEntering(); + next.listeners( StateNodeEvent.Entering ).onState( next ); + } + else + { + if (managerState != StateManagerState.Active) + { + current.onCancel(); + } + + nextState = state; + managerState = StateManagerState.Exiting; + current.onExiting(); + current.listeners( StateNodeEvent.Exiting ).onState( current ); + } + } + } + + public void proceed() + { + StateNode current = get( currentState ); + StateNode next = get( nextState ); + + switch (managerState) + { + case Exiting: + current.onExit(); + current.listeners( StateNodeEvent.Exit ).onState( current ); + managerState = StateManagerState.Entering; + currentState = nextState; + nextState = null; + next.onEntering(); + next.listeners( StateNodeEvent.Entering ).onState( next ); + break; + + case Entering: + managerState = StateManagerState.Active; + current.onEnter(); + current.listeners( StateNodeEvent.Enter ).onState( current ); + break; + + default: + break; + } + } + +} diff --git a/src/com/axe/node/state/StateManagerState.java b/src/com/axe/node/state/StateManagerState.java new file mode 100644 index 0000000..2d6c06c --- /dev/null +++ b/src/com/axe/node/state/StateManagerState.java @@ -0,0 +1,9 @@ +package com.axe.node.state; + +public enum StateManagerState +{ + Inactive, + Entering, + Exiting, + Active +} diff --git a/src/com/axe/node/state/StateNode.java b/src/com/axe/node/state/StateNode.java new file mode 100644 index 0000000..a6228ec --- /dev/null +++ b/src/com/axe/node/state/StateNode.java @@ -0,0 +1,102 @@ +package com.axe.node.state; + +import com.axe.View; +import com.axe.event.EventMap; +import com.axe.event.HasEvents; +import com.axe.game.GameState; +import com.axe.math.Vec; +import com.axe.node.Node; + +public class StateNode, S extends Enum> implements Node, HasEvents +{ + + public Node child; + public EventMap events; + public StateManagerNode manager; + + public StateNode(Node child) + { + this.child = child; + this.events = new EventMap(); + } + + @Override + public Node getParent() + { + return manager; + } + + @Override + public void setParent(Node parent) + { + this.manager = (StateManagerNode)parent; + } + + @Override + public void update(GameState state, View firstView) + { + if (child != null && !child.isExpired()) + { + child.update( state, firstView ); + } + + updating( state, firstView, manager ); + } + + @Override + public void draw(GameState state, View view) + { + if (child != null) + { + Node.drawNode( child, state, view ); + } + } + + public void updating(GameState state, View view, StateManagerNode manager) + { + + } + + public void onExit() + { + + } + + public void onExiting() + { + + } + + public void exiting(GameState state, View view, StateManagerNode manager) + { + manager.proceed(); + } + + public void onEnter() + { + + } + + public void onEntering() + { + + } + + public void onCancel() + { + + } + + public void entering(GameState state, View view, StateManagerNode manager) + { + manager.proceed(); + } + + @Override + public EventMap getEvents() + { + return events; + } + + +} diff --git a/src/com/axe/node/state/StateNodeEvent.java b/src/com/axe/node/state/StateNodeEvent.java new file mode 100644 index 0000000..e6b3b9b --- /dev/null +++ b/src/com/axe/node/state/StateNodeEvent.java @@ -0,0 +1,20 @@ +package com.axe.node.state; + +import com.axe.event.Event; + +public class StateNodeEvent +{ + + public static final Event Exiting = + new Event<>( 0, StateNodeListener.class ); + + public static final Event Exit = + new Event<>( 1, StateNodeListener.class ); + + public static final Event Entering = + new Event<>( 2, StateNodeListener.class ); + + public static final Event Enter = + new Event<>( 3, StateNodeListener.class ); + +} diff --git a/src/com/axe/node/state/StateNodeListener.java b/src/com/axe/node/state/StateNodeListener.java new file mode 100644 index 0000000..fcb179f --- /dev/null +++ b/src/com/axe/node/state/StateNodeListener.java @@ -0,0 +1,6 @@ +package com.axe.node.state; + +public interface StateNodeListener +{ + public void onState(StateNode node); +} diff --git a/src/com/axe/noise/Noise.java b/src/com/axe/noise/Noise.java new file mode 100755 index 0000000..1351462 --- /dev/null +++ b/src/com/axe/noise/Noise.java @@ -0,0 +1,94 @@ + +package com.axe.noise; + +public abstract class Noise +{ + + protected final int perm[] = new int[512]; + protected double offsetX; + protected double offsetY; + protected double offsetZ; + + public static int floor( double x ) + { + return x >= 0 ? (int)x : (int)x - 1; + } + + protected static double fade( double x ) + { + return x * x * x * (x * (x * 6 - 15) + 10); + } + + protected static double lerp( double x, double y, double z ) + { + return y + x * (z - y); + } + + protected static double grad( int hash, double x, double y, double z ) + { + hash &= 15; + double u = hash < 8 ? x : y; + double v = hash < 4 ? y : hash == 12 || hash == 14 ? x : z; + return ((hash & 1) == 0 ? u : -u) + ((hash & 2) == 0 ? v : -v); + } + + public double noise( double x ) + { + return noise( x, 0, 0 ); + } + + public double noise( double x, double y ) + { + return noise( x, y, 0 ); + } + + public abstract double noise( double x, double y, double z ); + + public double noise( double x, int octaves, double frequency, double amplitude ) + { + return noise( x, 0, 0, octaves, frequency, amplitude ); + } + + public double noise( double x, int octaves, double frequency, double amplitude, boolean normalized ) + { + return noise( x, 0, 0, octaves, frequency, amplitude, normalized ); + } + + public double noise( double x, double y, int octaves, double frequency, double amplitude ) + { + return noise( x, y, 0, octaves, frequency, amplitude ); + } + + public double noise( double x, double y, int octaves, double frequency, double amplitude, boolean normalized ) + { + return noise( x, y, 0, octaves, frequency, amplitude, normalized ); + } + + public double noise( double x, double y, double z, int octaves, double frequency, double amplitude ) + { + return noise( x, y, z, octaves, frequency, amplitude, false ); + } + + public double noise( double x, double y, double z, int octaves, double frequency, double amplitude, boolean normalized ) + { + double result = 0; + double amp = 1; + double freq = 1; + double max = 0; + + for (int i = 0; i < octaves; i++) + { + result += noise( x * freq, y * freq, z * freq ) * amp; + max += amp; + freq *= frequency; + amp *= amplitude; + } + + if (normalized) + { + result /= max; + } + + return result; + } +} diff --git a/src/com/axe/noise/NoiseGenerator.java b/src/com/axe/noise/NoiseGenerator.java new file mode 100755 index 0000000..8e92659 --- /dev/null +++ b/src/com/axe/noise/NoiseGenerator.java @@ -0,0 +1,113 @@ +package com.axe.noise; + +import java.lang.reflect.Constructor; + +import com.axe.io.DataModel; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + +public class NoiseGenerator implements DataModel +{ + + public Noise noise; + public long seed; + public double scaleX; + public double scaleY; + public double scaleZ; + public int octaves; + public double frequency; + public double amplitude; + public boolean normalize; + + public NoiseGenerator() + { + } + + public NoiseGenerator( double scaleX, double scaleY, double scaleZ, int octaves, double frequency, double amplitude, boolean normalize ) + { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.scaleZ = scaleZ; + this.octaves = octaves; + this.frequency = frequency; + this.amplitude = amplitude; + this.normalize = normalize; + } + + private double scale( double min, double max, double n ) + { + return (n + 1) * 0.5f * (max - min) + min; + } + + public double noise(double min, double max, double x) + { + return scale( min, max, noise.noise( x * scaleX, octaves, frequency, amplitude, normalize ) ); + } + + public double noise(double min, double max, double x, double y) + { + return scale( min, max, noise.noise( x * scaleX, y * scaleY, octaves, frequency, amplitude, normalize ) ); + } + + public double noise(double min, double max, double x, double y, double z) + { + return scale( min, max, noise.noise( x * scaleX, y * scaleY, z * scaleZ, octaves, frequency, amplitude, normalize ) ); + } + + public float noisef(float min, float max, float x) + { + return (float)noise( min, max, x ); + } + + public float noisef(float min, float max, float x, float y) + { + return (float)noise( min, max, x, y ); + } + + public float noisef(float min, float max, float x, float y, float z) + { + return (float)noise( min, max, x, y, z ); + } + + @Override + public void read( InputModel input ) + { + seed = input.readLong( "seed", seed ); + scaleX = input.readDouble( "scale-x", scaleX ); + scaleY = input.readDouble( "scale-y", scaleY ); + scaleZ = input.readDouble( "scale-z", scaleZ ); + octaves = input.readInt( "octaves", octaves ); + frequency = input.readDouble( "frequency", frequency ); + amplitude = input.readDouble( "amplitude", amplitude ); + normalize = input.readBoolean( "normalize", normalize ); + + try + { + Class noiseClass = input.readClass( "noise" ); + Constructor noiseConstructor = noiseClass.getConstructor( long.class ); + noise = noiseConstructor.newInstance( seed ); + } + catch (Exception e) + { + if ( noise == null ) + { + noise = SimplexNoise.getInstance(); + } + } + } + + @Override + public void write( OutputModel output ) + { + output.write( "seed", seed ); + output.write( "scale-x", scaleX ); + output.write( "scale-y", scaleY ); + output.write( "scale-z", scaleZ ); + output.write( "octaves", octaves ); + output.write( "frequency", frequency ); + output.write( "amplitude", amplitude ); + output.write( "normalize", normalize ); + output.writeInstance( "noise", noise ); + } + +} diff --git a/src/com/axe/noise/PerlinNoise.java b/src/com/axe/noise/PerlinNoise.java new file mode 100755 index 0000000..55c4f8d --- /dev/null +++ b/src/com/axe/noise/PerlinNoise.java @@ -0,0 +1,149 @@ + +package com.axe.noise; + +import java.util.Random; + + +public class PerlinNoise extends Noise +{ + + protected static final int grad3[][] = { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 }, + { 1, 0, 1 }, { -1, 0, 1 }, { 1, 0, -1 }, { -1, 0, -1 }, + { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 }, { 0, -1, -1 } }; + + private static final PerlinNoise instance = new PerlinNoise(); + + public PerlinNoise() + { + int p[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, + 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, + 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, + 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, + 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, + 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, + 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, + 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, + 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, + 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, + 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, + 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, + 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, + 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, + 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, + 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, + 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, + 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 }; + + for (int i = 0; i < 512; i++) + { + perm[i] = p[i & 255]; + } + } + + public PerlinNoise( long seed ) + { + this( new Random( seed ) ); + } + + public PerlinNoise( Random rand ) + { + offsetX = rand.nextDouble() * 256; + offsetY = rand.nextDouble() * 256; + offsetZ = rand.nextDouble() * 256; + + for (int i = 0; i < 256; i++) + { + perm[i] = rand.nextInt( 256 ); + } + + for (int i = 0; i < 256; i++) + { + int pos = rand.nextInt( 256 - i ) + i; + int old = perm[i]; + + perm[i] = perm[pos]; + perm[pos] = old; + perm[i + 256] = perm[i]; + } + } + + public static double getNoise( double x ) + { + return instance.noise( x ); + } + + public static double getNoise( double x, double y ) + { + return instance.noise( x, y ); + } + + public static double getNoise( double x, double y, double z ) + { + return instance.noise( x, y, z ); + } + + public static PerlinNoise getInstance() + { + return instance; + } + + @Override + public double noise( double x, double y, double z ) + { + x += offsetX; + y += offsetY; + z += offsetZ; + + int floorX = floor( x ); + int floorY = floor( y ); + int floorZ = floor( z ); + + // Find unit cube containing the point + int X = floorX & 255; + int Y = floorY & 255; + int Z = floorZ & 255; + + // Get relative xyz coordinates of the point within the cube + x -= floorX; + y -= floorY; + z -= floorZ; + + // Compute fade curves for xyz + double fX = fade( x ); + double fY = fade( y ); + double fZ = fade( z ); + + // Hash coordinates of the cube corners + int A = perm[X] + Y; + int AA = perm[A] + Z; + int AB = perm[A + 1] + Z; + int B = perm[X + 1] + Y; + int BA = perm[B] + Z; + int BB = perm[B + 1] + Z; + + return lerp( fZ, lerp( fY, lerp( fX, grad( perm[AA], x, y, z ), + grad( perm[BA], x - 1, y, z ) ), + lerp( fX, grad( perm[AB], x, y - 1, z ), + grad( perm[BB], x - 1, y - 1, z ) ) ), + lerp( fY, lerp( fX, grad( perm[AA + 1], x, y, z - 1 ), + grad( perm[BA + 1], x - 1, y, z - 1 ) ), + lerp( fX, grad( perm[AB + 1], x, y - 1, z - 1 ), + grad( perm[BB + 1], x - 1, y - 1, z - 1 ) ) ) ); + } + + public static double getNoise( double x, int octaves, double frequency, double amplitude ) + { + return instance.noise( x, octaves, frequency, amplitude ); + } + + public static double getNoise( double x, double y, int octaves, double frequency, double amplitude ) + { + return instance.noise( x, y, octaves, frequency, amplitude ); + } + + public static double getNoise( double x, double y, double z, int octaves, double frequency, double amplitude ) + { + return instance.noise( x, y, z, octaves, frequency, amplitude ); + } + +} diff --git a/src/com/axe/noise/SimplexNoise.java b/src/com/axe/noise/SimplexNoise.java new file mode 100755 index 0000000..27e54ee --- /dev/null +++ b/src/com/axe/noise/SimplexNoise.java @@ -0,0 +1,526 @@ + +package com.axe.noise; + +import java.util.Random; + +public class SimplexNoise extends PerlinNoise +{ + + protected static final double SQRT_3 = Math.sqrt( 3 ); + protected static final double SQRT_5 = Math.sqrt( 5 ); + protected static final double F2 = 0.5 * (SQRT_3 - 1); + protected static final double G2 = (3 - SQRT_3) / 6; + protected static final double G22 = G2 * 2.0 - 1; + protected static final double F3 = 1.0 / 3.0; + protected static final double G3 = 1.0 / 6.0; + protected static final double F4 = (SQRT_5 - 1.0) / 4.0; + protected static final double G4 = (5.0 - SQRT_5) / 20.0; + protected static final double G42 = G4 * 2.0; + protected static final double G43 = G4 * 3.0; + protected static final double G44 = G4 * 4.0 - 1.0; + protected static final int grad4[][] = { { 0, 1, 1, 1 }, { 0, 1, 1, -1 }, { 0, 1, -1, 1 }, { 0, 1, -1, -1 }, + { 0, -1, 1, 1 }, { 0, -1, 1, -1 }, { 0, -1, -1, 1 }, { 0, -1, -1, -1 }, + { 1, 0, 1, 1 }, { 1, 0, 1, -1 }, { 1, 0, -1, 1 }, { 1, 0, -1, -1 }, + { -1, 0, 1, 1 }, { -1, 0, 1, -1 }, { -1, 0, -1, 1 }, { -1, 0, -1, -1 }, + { 1, 1, 0, 1 }, { 1, 1, 0, -1 }, { 1, -1, 0, 1 }, { 1, -1, 0, -1 }, + { -1, 1, 0, 1 }, { -1, 1, 0, -1 }, { -1, -1, 0, 1 }, { -1, -1, 0, -1 }, + { 1, 1, 1, 0 }, { 1, 1, -1, 0 }, { 1, -1, 1, 0 }, { 1, -1, -1, 0 }, + { -1, 1, 1, 0 }, { -1, 1, -1, 0 }, { -1, -1, 1, 0 }, { -1, -1, -1, 0 } }; + protected static final int simplex[][] = { + { 0, 1, 2, 3 }, { 0, 1, 3, 2 }, { 0, 0, 0, 0 }, { 0, 2, 3, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 2, 3, 0 }, + { 0, 2, 1, 3 }, { 0, 0, 0, 0 }, { 0, 3, 1, 2 }, { 0, 3, 2, 1 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 1, 3, 2, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 1, 2, 0, 3 }, { 0, 0, 0, 0 }, { 1, 3, 0, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 3, 0, 1 }, { 2, 3, 1, 0 }, + { 1, 0, 2, 3 }, { 1, 0, 3, 2 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 2, 0, 3, 1 }, { 0, 0, 0, 0 }, { 2, 1, 3, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, + { 2, 0, 1, 3 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 0, 1, 2 }, { 3, 0, 2, 1 }, { 0, 0, 0, 0 }, { 3, 1, 2, 0 }, + { 2, 1, 0, 3 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 3, 1, 0, 2 }, { 0, 0, 0, 0 }, { 3, 2, 0, 1 }, { 3, 2, 1, 0 } }; + protected static double offsetW; + private static final SimplexNoise instance = new SimplexNoise(); + + public SimplexNoise() + { + super(); + } + + public SimplexNoise( long seed ) + { + this( new Random( seed ) ); + } + + public SimplexNoise( Random rand ) + { + super( rand ); + offsetW = rand.nextDouble() * 256; + } + + protected static double dot( int g[], double x, double y ) + { + return g[0] * x + g[1] * y; + } + + protected static double dot( int g[], double x, double y, double z ) + { + return g[0] * x + g[1] * y + g[2] * z; + } + + protected static double dot( int g[], double x, double y, double z, double w ) + { + return g[0] * x + g[1] * y + g[2] * z + g[3] * w; + } + + public static double getNoise( double xin ) + { + return instance.noise( xin ); + } + + public static double getNoise( double xin, double yin ) + { + return instance.noise( xin, yin ); + } + + public static double getNoise( double xin, double yin, double zin ) + { + return instance.noise( xin, yin, zin ); + } + + public static double getNoise( double x, double y, double z, double w ) + { + return instance.noise( x, y, z, w ); + } + + @Override + public double noise( double xin, double yin, double zin ) + { + xin += offsetX; + yin += offsetY; + zin += offsetZ; + + double n0, n1, n2, n3; // Noise contributions from the four corners + + // Skew the input space to determine which simplex cell we're in + double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for + // 3D + int i = floor( xin + s ); + int j = floor( yin + s ); + int k = floor( zin + s ); + double t = (i + j + k) * G3; + double X0 = i - t; // Unskew the cell origin back to (x,y,z) space + double Y0 = j - t; + double Z0 = k - t; + double x0 = xin - X0; // The x,y,z distances from the cell origin + double y0 = yin - Y0; + double z0 = zin - Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if (x0 >= y0) + { + if (y0 >= z0) + { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } // X Y Z order + else if (x0 >= z0) + { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } // X Z Y order + else + { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } // Z X Y order + } + else + { // x0 y0) + { + i1 = 1; + j1 = 0; + } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else + { + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed + // coords + double y1 = y0 - j1 + G2; + double x2 = x0 + G22; // Offsets for last corner in (x,y) unskewed coords + double y2 = y0 + G22; + + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 255; + int jj = j & 255; + int gi0 = perm[ii + perm[jj]] % 12; + int gi1 = perm[ii + i1 + perm[jj + j1]] % 12; + int gi2 = perm[ii + 1 + perm[jj + 1]] % 12; + + // Calculate the contribution from the three corners + double t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 < 0) + { + n0 = 0.0; + } + else + { + t0 *= t0; + n0 = t0 * t0 * dot( grad3[gi0], x0, y0 ); // (x,y) of grad3 used for 2D + // gradient + } + + double t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 < 0) + { + n1 = 0.0; + } + else + { + t1 *= t1; + n1 = t1 * t1 * dot( grad3[gi1], x1, y1 ); + } + + double t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 < 0) + { + n2 = 0.0; + } + else + { + t2 *= t2; + n2 = t2 * t2 * dot( grad3[gi2], x2, y2 ); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); + } + + public double noise( double x, double y, double z, double w ) + { + x += offsetX; + y += offsetY; + z += offsetZ; + w += offsetW; + + double n0, n1, n2, n3, n4; // Noise contributions from the five corners + + // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're + // in + double s = (x + y + z + w) * F4; // Factor for 4D skewing + int i = floor( x + s ); + int j = floor( y + s ); + int k = floor( z + s ); + int l = floor( w + s ); + + double t = (i + j + k + l) * G4; // Factor for 4D unskewing + double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space + double Y0 = j - t; + double Z0 = k - t; + double W0 = l - t; + double x0 = x - X0; // The x,y,z,w distances from the cell origin + double y0 = y - Y0; + double z0 = z - Z0; + double w0 = w - W0; + + // For the 4D case, the simplex is a 4D shape I won't even try to + // describe. + // To find out which of the 24 possible simplices we're in, we need to + // determine the magnitude ordering of x0, y0, z0 and w0. + // The method below is a good way of finding the ordering of x,y,z,w and + // then find the correct traversal order for the simplex we’re in. + // First, six pair-wise comparisons are performed between each possible + // pair + // of the four coordinates, and the results are used to add up binary bits + // for an integer index. + int c1 = (x0 > y0) ? 32 : 0; + int c2 = (x0 > z0) ? 16 : 0; + int c3 = (y0 > z0) ? 8 : 0; + int c4 = (x0 > w0) ? 4 : 0; + int c5 = (y0 > w0) ? 2 : 0; + int c6 = (z0 > w0) ? 1 : 0; + int c = c1 + c2 + c3 + c4 + c5 + c6; + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; + j1 = simplex[c][1] >= 3 ? 1 : 0; + k1 = simplex[c][2] >= 3 ? 1 : 0; + l1 = simplex[c][3] >= 3 ? 1 : 0; + + // The number 2 in the "simplex" array is at the second largest + // coordinate. + i2 = simplex[c][0] >= 2 ? 1 : 0; + j2 = simplex[c][1] >= 2 ? 1 : 0; + k2 = simplex[c][2] >= 2 ? 1 : 0; + l2 = simplex[c][3] >= 2 ? 1 : 0; + + // The number 1 in the "simplex" array is at the second smallest + // coordinate. + i3 = simplex[c][0] >= 1 ? 1 : 0; + j3 = simplex[c][1] >= 1 ? 1 : 0; + k3 = simplex[c][2] >= 1 ? 1 : 0; + l3 = simplex[c][3] >= 1 ? 1 : 0; + + // The fifth corner has all coordinate offsets = 1, so no need to look + // that up. + + double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + double y1 = y0 - j1 + G4; + double z1 = z0 - k1 + G4; + double w1 = w0 - l1 + G4; + + double x2 = x0 - i2 + G42; // Offsets for third corner in (x,y,z,w) coords + double y2 = y0 - j2 + G42; + double z2 = z0 - k2 + G42; + double w2 = w0 - l2 + G42; + + double x3 = x0 - i3 + G43; // Offsets for fourth corner in (x,y,z,w) + // coords + double y3 = y0 - j3 + G43; + double z3 = z0 - k3 + G43; + double w3 = w0 - l3 + G43; + + double x4 = x0 + G44; // Offsets for last corner in (x,y,z,w) coords + double y4 = y0 + G44; + double z4 = z0 + G44; + double w4 = w0 + G44; + + // Work out the hashed gradient indices of the five simplex corners + int ii = i & 255; + int jj = j & 255; + int kk = k & 255; + int ll = l & 255; + + int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32; + int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32; + int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32; + int gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32; + int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32; + + // Calculate the contribution from the five corners + double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0; + if (t0 < 0) + { + n0 = 0.0; + } + else + { + t0 *= t0; + n0 = t0 * t0 * dot( grad4[gi0], x0, y0, z0, w0 ); + } + + double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1; + if (t1 < 0) + { + n1 = 0.0; + } + else + { + t1 *= t1; + n1 = t1 * t1 * dot( grad4[gi1], x1, y1, z1, w1 ); + } + + double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2; + if (t2 < 0) + { + n2 = 0.0; + } + else + { + t2 *= t2; + n2 = t2 * t2 * dot( grad4[gi2], x2, y2, z2, w2 ); + } + + double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3; + if (t3 < 0) + { + n3 = 0.0; + } + else + { + t3 *= t3; + n3 = t3 * t3 * dot( grad4[gi3], x3, y3, z3, w3 ); + } + + double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4; + if (t4 < 0) + { + n4 = 0.0; + } + else + { + t4 *= t4; + n4 = t4 * t4 * dot( grad4[gi4], x4, y4, z4, w4 ); + } + + // Sum up and scale the result to cover the range [-1,1] + return 27.0 * (n0 + n1 + n2 + n3 + n4); + } + + public static SimplexNoise getInstance() + { + return instance; + } +} diff --git a/src/com/axe/path/AbstractPath.java b/src/com/axe/path/AbstractPath.java new file mode 100644 index 0000000..148e7a7 --- /dev/null +++ b/src/com/axe/path/AbstractPath.java @@ -0,0 +1,43 @@ +package com.axe.path; + +import com.axe.math.calc.Calculator; +import com.axe.math.calc.CalculatorRegistry; + +public abstract class AbstractPath implements Path +{ + + public Calculator calc; + + public AbstractPath() + { + + } + + public AbstractPath(T value) + { + this.initialize( value ); + } + + public AbstractPath( Calculator calc ) + { + this.calc = calc; + } + + protected void initialize(T value) + { + this.calc = CalculatorRegistry.getFor( value ); + } + + @Override + public T get(float delta) + { + return set( getCalculator().create(), delta ); + } + + @Override + public Calculator getCalculator() + { + return calc; + } + +} diff --git a/src/com/axe/path/AdditivePath.java b/src/com/axe/path/AdditivePath.java new file mode 100755 index 0000000..e4fa52a --- /dev/null +++ b/src/com/axe/path/AdditivePath.java @@ -0,0 +1,74 @@ + +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class AdditivePath extends AbstractPath +{ + + public Path path0; + public Path path1; + public T temp; + + public AdditivePath() + { + } + + public AdditivePath( Path path0, Path path1 ) + { + super( path0.getCalculator() ); + + this.path0 = path0; + this.path1 = path1; + this.temp = calc.create(); + } + + @Override + public void read( InputModel input ) + { + // TODO calc + temp = input.readModel( "temp", "temp-type", true ); + path0 = input.readModel( "path0", "path0-type", true ); + path1 = input.readModel( "path1", "path1-type", true ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModel( "path0", path0, "path0-type" ); + output.writeModel( "path1", path1, "path1-type" ); + output.writeModel( "temp", temp, "temp-type", getCalculator() ); + } + + @Override + public T set( T subject, float delta ) + { + calc.copy( temp, path0.set( subject, delta ) ); + calc.addi( temp, path1.set( subject, delta ) ); + calc.copy( subject, temp ); + + return subject; + } + + @Override + public int getPointCount() + { + return path0.getPointCount() + path1.getPointCount(); + } + + @Override + public T getPoint( int index ) + { + int offset = path0.getPointCount(); + + if (index < offset) + { + return path0.getPoint( index ); + } + + return path1.getPoint( index - offset ); + } + +} diff --git a/src/com/axe/path/BasisSplinePath.java b/src/com/axe/path/BasisSplinePath.java new file mode 100755 index 0000000..824915d --- /dev/null +++ b/src/com/axe/path/BasisSplinePath.java @@ -0,0 +1,72 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + +//TODO fix BasisSplinePath where +// P(t) = Ti * Mbs * Gbs +// Ti = [(t - ti)^3, (t - ti)^2, (t - ti), 1] +// Gbs = [P(i-3), P(i-2), P(i-1), P(i)] +// Mbs = BasisSplinePath.MATRIX * BasisSplinePath.WEIGHT +public class BasisSplinePath extends AbstractPath +{ + + public static final float WEIGHT = 1.0f / 6.0f; + public static final float[][] MATRIX = { + {-1, 3,-3, 1}, + { 3,-6, 3, 0}, + {-3, 0, 3, 0}, + { 1, 4, 1, 0} + }; + + public T[] points; + + public BasisSplinePath() + { + } + + public BasisSplinePath( Alternative ... points ) + { + this( PathUtility.alternatives( points ) ); + } + + public BasisSplinePath( T ... points ) + { + super( points[0] ); + + this.points = points; + } + + @Override + public T set( T subject, float delta ) + { + return calc.parametricCubicCurve( subject, delta, points, MATRIX, WEIGHT ); + } + + @Override + public int getPointCount() + { + return points.length; + } + + @Override + public T getPoint( int index ) + { + return points[index]; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "points", "point-type" ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelArray( "points", points, "point-type", calc ); + } + +} diff --git a/src/com/axe/path/BezierPath.java b/src/com/axe/path/BezierPath.java new file mode 100755 index 0000000..4e3ecb6 --- /dev/null +++ b/src/com/axe/path/BezierPath.java @@ -0,0 +1,105 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +public class BezierPath extends AbstractPath +{ + + private T[] points; + private T temp; + private int[] weights; + private float[] inverses; + + public BezierPath() + { + } + + public BezierPath( Alternative ... points ) + { + this( PathUtility.alternatives( points ) ); + } + + public BezierPath( T ... points ) + { + super( points[0] ); + + this.points = points; + this.weights = computeWeights( points.length ); + this.inverses = new float[ points.length ]; + this.temp = calc.create(); + } + + private int[] computeWeights( int n ) + { + int[] w = new int[ n-- ]; + + for (int i = 0; i <= n; i++) + { + w[i] = Numbers.choose( n, i ); + } + + return w; + } + + @Override + public T set(T subject, float delta) + { + final int n = points.length; + float x = 1; + + inverses[n - 1] = 1; + + for (int i = n - 2; i >= 0; i--) + { + inverses[i] = inverses[i + 1] * (1 - delta); + } + + calc.clear( temp, 0 ); + + for (int i = 0; i < n; i++) + { + calc.addsi( temp, points[i], weights[i] * inverses[i] * x ); + + x *= delta; + } + + calc.copy( subject, temp ); + + return subject; + } + + public int getPointCount() + { + return points.length; + } + + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "point", "point-type" ); + weights = computeWeights( points.length ); + temp = calc.create(); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelArray( "point", points, "point-type", calc ); + } + + +} diff --git a/src/com/axe/path/CatmullRomPath.java b/src/com/axe/path/CatmullRomPath.java new file mode 100755 index 0000000..55f4c52 --- /dev/null +++ b/src/com/axe/path/CatmullRomPath.java @@ -0,0 +1,68 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class CatmullRomPath extends AbstractPath +{ + + public static final float WEIGHT = 0.5f; + public static final float[][] MATRIX = { + { 0, 2, 0, 0}, + {-1, 0, 1, 0}, + { 2,-5, 4,-1}, + {-1, 3,-3, 1} + }; + + public T[] points; + + public CatmullRomPath() + { + } + + public CatmullRomPath( Alternative ... points ) + { + this( PathUtility.alternatives( points ) ); + } + + public CatmullRomPath(T ... points) + { + super( points[0] ); + + this.points = points; + } + + @Override + public T set( T subject, float delta ) + { + return calc.parametricCubicCurve( subject, delta, points, MATRIX, WEIGHT ); + } + + @Override + public int getPointCount() + { + return points.length; + } + + @Override + public T getPoint( int index ) + { + return points[index]; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "points", "point-type" ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelArray( "points", points, "point-type", calc ); + } + +} diff --git a/src/com/axe/path/ChaikinPath.java b/src/com/axe/path/ChaikinPath.java new file mode 100755 index 0000000..30e0570 --- /dev/null +++ b/src/com/axe/path/ChaikinPath.java @@ -0,0 +1,120 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +/** + * Chaikin?s algorithm + * + * @author Philip Diffenderfer + * + * @param + */ +public class ChaikinPath extends AbstractPath +{ + + protected T[] points; + protected T temp0; + protected T temp1; + protected T temp2; + protected T temp3; + protected T temp4; + protected int depth; + protected boolean loops; + protected float midpoint; + + public ChaikinPath() + { + } + + public ChaikinPath( int depth, Alternative ... points ) + { + this( depth, PathUtility.alternatives( points ) ); + } + + public ChaikinPath( int depth, T ... points ) + { + super( points[0] ); + + this.depth = depth; + this.points = points; + this.temp0 = calc.create(); + this.temp1 = calc.create(); + this.temp2 = calc.create(); + this.temp3 = calc.create(); + this.temp4 = calc.create(); + } + + @Override + public T set(T subject, float delta) + { + final int n = points.length - 1; + final float a = delta * n; + final int i = Numbers.clamp( (int)a, 0, n - 1 ); + float d = a - i; + final int s = (d < 0.5f ? i - 1 : i ); + + calc.copy( temp0, points[ s ] ); + calc.copy( temp2, points[ s + 1 ] ); + calc.copy( temp4, points[ s + 2 ] ); + + int k = depth; + + while (--k >= 0) + { + calc.interpolate( temp1, temp0, temp2, 0.5f ); + calc.interpolate( temp3, temp2, temp4, 0.5f ); + } + + calc.interpolate( subject, temp1, temp3, d ); + + return subject; + } + + public int getActualIndex( int index ) + { + final int n = points.length; + + return ( loops ? (index + n) % n : Numbers.clamp( index, 0, n - 1 ) ); + } + + @Override + public int getPointCount() + { + return points.length; + } + + @Override + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + depth = input.readInt( "depth" ); + points = input.readModelArray( "point", "point-type" ); + temp0 = calc.create(); + temp1 = calc.create(); + temp2 = calc.create(); + temp3 = calc.create(); + temp4 = calc.create(); + } + + @Override + public void write( OutputModel output ) + { + output.write( "depth", depth ); + output.writeModelArray( "point", points, "point-type", calc ); + } + +} diff --git a/src/com/axe/path/ComboPath.java b/src/com/axe/path/ComboPath.java new file mode 100755 index 0000000..fab72d7 --- /dev/null +++ b/src/com/axe/path/ComboPath.java @@ -0,0 +1,127 @@ +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.calc.Calculator; + + +public class ComboPath extends AbstractPath +{ + + private Path[] paths; + private float[] times; + + public ComboPath( Path[] paths, int points ) + { + this( paths, getPathTimes( paths, points ) ); + } + + public ComboPath( Path[] paths, float[] times ) + { + super( paths[0].getCalculator() ); + + this.paths = paths; + this.times = times; + } + + @Override + public T set( T subject, float delta ) + { + if (delta <= 0) + { + paths[0].set( subject, 0 ); + } + else if (delta >= 1) + { + paths[ paths.length - 1 ].set( subject, 1 ); + } + else + { + int i = paths.length - 1; + while (times[i] > delta) --i; + float q = (delta - times[i]) / (times[i + 1] - times[i]); + paths[i].set( subject, q ); + } + + return subject; + } + + public int getPointCount() + { + return -1; + } + + public T getPoint(int index) + { + return null; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + paths = input.readModelArray( "path", "path-type" ); + times = new float[ paths.length + 1 ]; + + int i = 0; + + times[0] = 0; + for (InputModel c : input) + { + times[++i] = c.readFloat( "time" ); + } + } + + @Override + public void write( OutputModel output ) + { + OutputModel[] models = output.writeModelArray( "path", paths, "path-type" ); + + for (int i = 1; i <= paths.length; i++) + { + models[i].write( "time", times[i] ); + } + } + + public static float[] getPathTimes(Path[] paths, int points) + { + int n = paths.length; + float[] times = new float[ n + 1 ]; + + times[0] = 0; + for (int k = 1; k <= n; k++) + { + times[k] = times[k - 1] + getPathLength( paths[k - 1], points ); + } + + float scale = 1.0f / times[n]; + + times[n] = 1.0f; + for (int k = 1; k < n; k++) + { + times[k] *= scale; + } + + return times; + } + + public static float getPathLength(Path path, int points) + { + Calculator calc = path.getCalculator(); + T start = calc.create(); + T end = calc.create(); + float length = 0; + + path.set( start, 0 ); + + for (int i = 1; i <= points; i++) + { + path.set( end, (float)i / points ); + length += calc.distance( end, start ); + start = end; + } + + return length; + } + +} diff --git a/src/com/axe/path/CompiledPath.java b/src/com/axe/path/CompiledPath.java new file mode 100755 index 0000000..9cfbdf6 --- /dev/null +++ b/src/com/axe/path/CompiledPath.java @@ -0,0 +1,33 @@ +package com.axe.path; + + +public class CompiledPath extends JumpPath +{ + + public CompiledPath() + { + } + + public CompiledPath( Path path, T[] allocated ) + { + super( compile( path, allocated ) ); + } + + public CompiledPath( Path path, int pointCount ) + { + super( compile( path, path.getCalculator().createArray( pointCount ) ) ); + } + + public static T[] compile( Path path, T[] allocated) + { + int n = allocated.length - 1; + + for ( int i = 0; i <= n; i++ ) + { + path.set( allocated[i], (float)i / n ); + } + + return allocated; + } + +} diff --git a/src/com/axe/path/CubicPath.java b/src/com/axe/path/CubicPath.java new file mode 100755 index 0000000..81acbdd --- /dev/null +++ b/src/com/axe/path/CubicPath.java @@ -0,0 +1,100 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + +public class CubicPath extends AbstractPath +{ + + private T p0; + private T p1; + private T p2; + private T p3; + private T temp; + + public CubicPath() + { + } + + public CubicPath(Alternative p0, Alternative p1, Alternative p2, Alternative p3) + { + this( p0.alternative(), p1.alternative(), p2.alternative(), p3.alternative() ); + } + + public CubicPath(T p0, T p1, T p2, T p3) + { + super( p0 ); + + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.temp = calc.create(); + } + + @Override + public T set(T subject, float d1) + { + float d2 = d1 * d1; + float d3 = d1 * d2; + float i1 = 1 - d1; + float i2 = i1 * i1; + float i3 = i1 * i2; + + calc.copy( temp, p0 ); + calc.scalei( temp, i3 ); + calc.addsi( temp, p1, 3 * i2 * d1 ); + calc.addsi( temp, p2, 3 * i1 * d2 ); + calc.addsi( temp, p3, d3 ); + calc.copy( subject, temp ); + + return subject; + } + + public int getPointCount() + { + return 4; + } + + public T getPoint(int index) + { + switch(index) { + case 0: return p0; + case 1: return p1; + case 2: return p2; + case 3: return p3; + } + return null; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + // Attribute factory = input.readInstance( "type" ); + + p0 = calc.create(); + p1 = calc.create(); + p2 = calc.create(); + p3 = calc.create(); + temp = calc.create(); + + input.readModel( "p0", p0, calc ); + input.readModel( "p1", p1, calc ); + input.readModel( "p2", p2, calc ); + input.readModel( "p3", p3, calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeInstance( "type", p0 ); + + output.writeModel( "p0", p0, calc ); + output.writeModel( "p1", p1, calc ); + output.writeModel( "p2", p2, calc ); + output.writeModel( "p3", p3, calc ); + } + +} diff --git a/src/com/axe/path/DurationPath.java b/src/com/axe/path/DurationPath.java new file mode 100755 index 0000000..f22ce99 --- /dev/null +++ b/src/com/axe/path/DurationPath.java @@ -0,0 +1,172 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.util.Array; + + +public class DurationPath extends AbstractPath +{ + + private static final float[] NO_DURATIONS = {}; + + protected float[] durations; + protected T[] points; + protected float durationTotal; + + public DurationPath() + { + } + + public DurationPath( T initialPoint ) + { + this( toArray( initialPoint ), NO_DURATIONS ); + } + + public DurationPath( Alternative[] points, float[] durations ) + { + this( PathUtility.alternatives( points ), durations ); + } + + public DurationPath( T[] points, float[] durations ) + { + super( points[0] ); + + this.points = points; + this.durations = durations; + this.updateDuration(); + } + + public void setPoints( T[] points ) + { + this.points = points; + } + + public void setDurations( float[] durations ) + { + this.durations = durations; + this.updateDuration(); + } + + public DurationPath withPoints( T ... points ) + { + this.points = points; + + return this; + } + + public DurationPath withDurations( float ... durations ) + { + this.durations = durations; + this.updateDuration(); + + return this; + } + + public DurationPath addPoint( T point, float duration ) + { + points = Array.add( point, points ); + durations = Array.add( duration, durations ); + durationTotal += duration; + + return this; + } + + public float updateDuration() + { + durationTotal = 0; + + for ( int i = 0; i < durations.length; i++ ) + { + durationTotal += durations[ i ]; + } + + return durationTotal; + } + + @Override + public T set( T subject, float delta ) + { + if (delta <= 0) + { + subject = calc.copy( subject, points[0] ); + } + else if (delta >= 1) + { + subject = calc.copy( subject, points[points.length - 1] ); + } + else + { + int i = 0; + float d = delta * durationTotal; + + while ( d > durations[i] ) { + d -= durations[i++]; + } + + float q = d / durations[i]; + + subject = calc.interpolate( subject, points[i], points[i + 1], q ); + } + + return subject; + } + + public int getPointCount() + { + return points.length; + } + + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + public float[] durations() + { + return durations; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + + points = input.readModelArray( "point", "point-type" ); + durations = new float[ points.length - 1 ]; + + InputModel[] pointModels = input.readModelArray( "point" ); + + for (int i = 0; i < pointModels.length - 1; i++ ) + { + InputModel c = pointModels[ i ]; + + durations[i] = c.readFloat( "duration" ); + } + + updateDuration(); + } + + @Override + public void write( OutputModel output ) + { + OutputModel[] models = output.writeModelArray( "point", points, "point-type", calc ); + + for (int i = 0; i < durations.length; i++) + { + models[i].write( "duration", durations[i] ); + } + } + + private static T[] toArray( T ... attributes ) + { + return attributes; + } + +} diff --git a/src/com/axe/path/EasedPath.java b/src/com/axe/path/EasedPath.java new file mode 100755 index 0000000..b644454 --- /dev/null +++ b/src/com/axe/path/EasedPath.java @@ -0,0 +1,220 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.easing.Easing; +import com.axe.easing.EasingMethod; +import com.axe.easing.EasingType; +import com.axe.easing.Easings; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.util.Array; + + +public class EasedPath extends AbstractPath +{ + + protected float[] durations; + protected Easing[] easings; + protected T[] points; + protected float durationTotal; + + public EasedPath() + { + } + + public EasedPath( Alternative[] points, float[] durations, Easing[] easings ) + { + this( PathUtility.alternatives( points ), durations, easings ); + } + + public EasedPath( T[] points, float[] durations, Easing[] easings ) + { + super( points[0] ); + + this.points = points; + this.durations = durations; + this.easings = easings; + this.updateDuration(); + } + + public void setPoints( T[] points ) + { + this.points = points; + } + + public void setDurations( float[] durations ) + { + this.durations = durations; + this.updateDuration(); + } + + public void setEasings( Easing[] easings ) + { + this.easings = easings; + } + + public EasedPath withPoints( T ... points ) + { + this.points = points; + + return this; + } + + public EasedPath withDurations( float ... durations ) + { + this.durations = durations; + this.updateDuration(); + + return this; + } + + public EasedPath withEasings( Easing ... easings ) + { + this.easings = easings; + + return this; + } + + public EasedPath addPoint( T point, float duration, EasingType type, EasingMethod method, float scale ) + { + return addPoint( point, duration, new Easing( type, method, scale ) ); + } + + public EasedPath addPoint( T point, float duration, EasingType type, EasingMethod method ) + { + return addPoint( point, duration, new Easing( type, method ) ); + } + + public EasedPath addPoint( T point, float duration, EasingMethod method ) + { + return addPoint( point, duration, new Easing( Easings.In, method ) ); + } + + public EasedPath addPoint( T point, float duration, Easing easing ) + { + points = Array.add( point, points ); + durations = Array.add( duration, durations ); + easings = Array.add( easing, easings ); + durationTotal += duration; + + return this; + } + + public float updateDuration() + { + durationTotal = 0; + + for ( int i = 0; i < durations.length; i++ ) + { + durationTotal += durations[ i ]; + } + + return durationTotal; + } + + @Override + public T set( T subject, float delta ) + { + if (delta <= 0) + { + subject = calc.copy( subject, points[0] ); + } + else if (delta >= 1) + { + subject = calc.copy( subject, points[points.length - 1] ); + } + else + { + int i = 0; + float d = delta * durationTotal; + + while ( d > durations[i] ) { + d -= durations[i++]; + } + + float q = easings[i].delta( d / durations[i] ); + + subject = calc.interpolate( subject, points[i], points[i + 1], q ); + } + + return subject; + } + + public int getPointCount() + { + return points.length; + } + + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + public float totalDuration() + { + float total = 0; + + for (float d : durations) + { + total+=d; + } + + return total; + } + + public float[] durations() + { + return durations; + } + + public Easing[] easings() + { + return easings; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "point", "point-type" ); + durations = new float[ points.length - 1 ]; + easings = new Easing[ points.length - 1 ]; + + InputModel[] pointModels = input.readModelArray( "point" ); + + for (int i = 0; i < pointModels.length - 1; i++) + { + InputModel c = pointModels[ i ]; + + durations[i] = c.readFloat( "duration" ); + + EasingType type = c.readInstance( "easing-type" ); + EasingMethod method = c.readInstance( "easing-method" ); + float scale = c.readFloat( "easing-scale" ); + + easings[i] = new Easing( type, method, scale ); + } + + updateDuration(); + } + + @Override + public void write( OutputModel output ) + { + OutputModel[] models = output.writeModelArray( "point", points, "point-type", calc ); + + for (int i = 0; i < durations.length; i++) + { + models[i].write( "duration", durations[i] ); + models[i].writeInstance( "easing-type", easings[i].type() ); + models[i].writeInstance( "easing-method", easings[i].method() ); + models[i].write( "easing-scale", easings[i].scale() ); + } + } + +} diff --git a/src/com/axe/path/HermitePath.java b/src/com/axe/path/HermitePath.java new file mode 100755 index 0000000..cd733d1 --- /dev/null +++ b/src/com/axe/path/HermitePath.java @@ -0,0 +1,95 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class HermitePath extends AbstractPath +{ + + public T start; + public T startTangent; + public T end; + public T endTangent; + + public HermitePath() + { + } + + public HermitePath(Alternative start, Alternative startTangent, Alternative end, Alternative endTangent) + { + this( start.alternative(), startTangent.alternative(), end.alternative(), endTangent.alternative() ); + } + + public HermitePath(T start, T startTangent, T end, T endTangent) + { + super( start ); + + this.start = start; + this.startTangent = startTangent; + this.end = end; + this.endTangent = endTangent; + } + + @Override + public T set( T subject, float d ) + { + float d2 = d * d; + float d3 = d2 * d; + + subject = calc.clear( subject, 0 ); + subject = calc.addsi( subject, start, 2 * d3 - 3 * d2 + 1 ); + subject = calc.addsi( subject, end, -2 * d3 + 3 * d2 ); + subject = calc.addsi( subject, startTangent, d3 - 2 * d2 + d ); + subject = calc.addsi( subject, endTangent, d3 - d2 ); + + return subject; + } + + @Override + public int getPointCount() + { + return 4; + } + + @Override + public T getPoint( int index ) + { + switch(index) { + case 0: return start; + case 1: return startTangent; + case 2: return end; + case 3: return endTangent; + } + return null; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + // Attribute factory = input.readInstance( "type" ); + + start = calc.create(); + startTangent = calc.create(); + end = calc.create(); + endTangent = calc.create(); + + input.readModel( "start", start, calc ); + input.readModel( "start-tangent", startTangent, calc ); + input.readModel( "end", end, calc ); + input.readModel( "end-tangent", endTangent, calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeInstance( "type", start ); + output.writeModel( "start", start, calc ); + output.writeModel( "start-tangent", startTangent, calc ); + output.writeModel( "end", end, calc ); + output.writeModel( "end-tangent", endTangent, calc ); + } + +} diff --git a/src/com/axe/path/IntegralPath.java b/src/com/axe/path/IntegralPath.java new file mode 100755 index 0000000..ed14f73 --- /dev/null +++ b/src/com/axe/path/IntegralPath.java @@ -0,0 +1,89 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +/** + * delta + * | + * 0.0 0.25 0.5 0.75 V 1.0 + * |----------|----------|----------|----------| (5) + * 0 1 2 3 4 + * + * @param + */ +public class IntegralPath extends AbstractPath +{ + + private T[] points; + + public IntegralPath() + { + } + + public IntegralPath( Alternative ... points ) + { + this( PathUtility.alternatives( points ) ); + } + + public IntegralPath(T ... points ) + { + super( points[0] ); + + this.points = points; + } + + @Override + public T set(T subject, float delta) + { + if (delta <= 0) + { + subject = calc.copy( subject, points[0] ); + } + else if (delta >= 1) + { + subject = calc.copy( subject, points[points.length - 1] ); + } + else + { + float a = delta * (points.length - 1); + int index = Numbers.clamp( (int)a, 0, points.length - 2 ); + + subject = calc.interpolate( subject, points[index], points[index + 1], a - index ); + } + + return subject; + } + + public int getPointCount() + { + return points.length; + } + + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "point", "point-type" ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelArray( "point", points, "point-type", calc ); + } + + +} diff --git a/src/com/axe/path/JumpPath.java b/src/com/axe/path/JumpPath.java new file mode 100755 index 0000000..5e486f7 --- /dev/null +++ b/src/com/axe/path/JumpPath.java @@ -0,0 +1,79 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +/** + * delta + * | + * 0.0 0.25 0.5 0.75 V 1.0 + * |----------|----------|----------|---------- (4) + * 0 1 2 3 + * |________ |________ |________ |__________ + * 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 + * + * @param + */ +public class JumpPath extends AbstractPath +{ + + protected T[] points; + + public JumpPath() + { + } + + public JumpPath( Alternative ... points ) + { + this( PathUtility.alternatives( points ) ); + } + + public JumpPath( T ... jumps ) + { + super( jumps[0] ); + + this.points = jumps; + } + + @Override + public T set(T subject, float delta) + { + float a = delta * points.length; + int index = Numbers.clamp( (int)a, 0, points.length - 1 ); + + calc.copy( subject, points[ index ] ); + + return subject; + } + + public int getPointCount() + { + return points.length; + } + + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "point", "point-type" ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelArray( "point", points, "point-type", calc ); + } + +} diff --git a/src/com/axe/path/KramerPath.java b/src/com/axe/path/KramerPath.java new file mode 100755 index 0000000..392c172 --- /dev/null +++ b/src/com/axe/path/KramerPath.java @@ -0,0 +1,267 @@ +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +/** + * http://www.reddit.com/r/gamedev/comments/1ei88i/very_fast_2d_interpolation/ + * + * @author Philip Diffenderfer + * + * @param + */ +public class KramerPath extends AbstractPath +{ + + public static final float DEFAULT_LOOSENESS = 0.0575f; + + protected T[] points; + protected T temp0; + protected T temp1; + protected int depth; + protected float looseness; + protected boolean loops; + protected float roughness; + + public KramerPath() + { + } + + // TODO alternatives + + public KramerPath( int depth, boolean loops, T ... points ) + { + this( depth, loops, DEFAULT_LOOSENESS, 0.0f, points ); + } + + public KramerPath( int depth, boolean loops, float looseness, T ... points ) + { + this( depth, loops, looseness, 0.0f, points ); + } + + public KramerPath( float roughness, boolean loops, T ... points ) + { + this( 0, loops, DEFAULT_LOOSENESS, roughness, points ); + } + + public KramerPath( float roughness, boolean loops, float looseness, T ... points ) + { + this( 0, loops, looseness, roughness, points ); + } + + protected KramerPath( int depth, boolean loops, float looseness, float roughness, T ... points ) + { + super( points[0] ); + + this.depth = depth; + this.loops = loops; + this.looseness = looseness; + this.roughness = roughness; + this.points = points; + this.temp0 = calc.create(); + this.temp1 = calc.create(); + } + + @Override + public T set(T subject, float delta) + { + final int n = points.length; + final float a = delta * n; + final int i = Numbers.clamp( (int)a, 0, n - 1 ); + float d = a - i; + + if (depth != 0) + { + subject = getPointWithExactDepth( i, d, subject ); + } + else + { + subject = getPointWithRoughness( i, d, subject ); + } + + return subject; + } + + public T getPointWithExactDepth( int i, float d, T subject ) + { + // v0 and v5 are used to calculate the next v1 or v4, at the next level. + T v0 = points[ getActualIndex( i - 2 ) ]; + T v1 = points[ getActualIndex( i - 1 ) ]; + T v2 = points[ getActualIndex( i ) ]; + T v3 = points[ getActualIndex( i + 1 ) ]; + T v4 = points[ getActualIndex( i + 2 ) ]; + T v5 = points[ getActualIndex( i + 3 ) ]; + + int k = depth; + + while (--k >= 0) + { + // Get mid point + T mid = getPoint( v1, v2, v3, v4 ); + + // If the desired point is closer to v2... + if (d < 0.5f) + { + // shift all surrounding points one-level closer to v2 + if (k == 0) + { + v3 = mid; + } + else + { + T newEnd = v1; + v5 = v4; + v4 = v3; + v3 = mid; + v1 = getPoint( v0, v1, v2, v3 ); + v0 = newEnd; + } + // adjust d so it's between 0.0 an 1.0 + d = d * 2.0f; + } + // else, the desired point is closer to v3... + else + { + // shift all surrounding points one-level closer to v3 + if (k == 0) + { + v2 = mid; + } + else + { + T newEnd = v4; + v0 = v1; + v1 = v2; + v2 = mid; + v4 = getPoint( v2, v3, v4, v5 ); + v5 = newEnd; + } + // adjust d so it's between 0.0 an 1.0 + d = (d - 0.5f) * 2.0f; + } + } + + // subject = (v3 - v2) * d + v2 + return calc.interpolate( subject, v2, v3, d ); + } + + public T getPointWithRoughness( int i, float d, T subject ) + { + // v0 and v5 are used to calculate the next v1 or v4, at the next level. + T v0 = points[ getActualIndex( i - 2 ) ]; + T v1 = points[ getActualIndex( i - 1 ) ]; + T v2 = points[ getActualIndex( i ) ]; + T v3 = points[ getActualIndex( i + 1 ) ]; + T v4 = points[ getActualIndex( i + 2 ) ]; + T v5 = points[ getActualIndex( i + 3 ) ]; + + for (;;) + { + // Get mid point + T mid = getPoint( v1, v2, v3, v4 ); + + // if distance from mid to (v2->v3) is <= roughness, break + // calculate the distance between all three points to form a triangle, + // with the distances determine the perimeter then area. Use the + // area = 0.5 * b * h formula to calculate height. + float height = calc.distanceFrom( v2, v3, mid ); + + if (height <= roughness) + { + break; + } + + // If the desired point is closer to v2... + if (d < 0.5f) + { + // shift all surrounding points one-level closer to v2 + T newEnd = v1; + v5 = v4; + v4 = v3; + v3 = mid; + v1 = getPoint( v0, v1, v2, v3 ); + v0 = newEnd; + // adjust d so it's between 0.0 an 1.0 + d = d * 2.0f; + } + // else, the desired point is closer to v3... + else + { + // shift all surrounding points one-level closer to v3 + T newEnd = v4; + v0 = v1; + v1 = v2; + v2 = mid; + v4 = getPoint( v2, v3, v4, v5 ); + v5 = newEnd; + // adjust d so it's between 0.0 an 1.0 + d = (d - 0.5f) * 2.0f; + } + } + + // subject = (v3 - v2) * d + v2 + return calc.interpolate( subject, v2, v3, d ); + } + + public T getPoint( T v1, T v2, T v3, T v4 ) + { + // p = (0.5f + looseness) * (v2 + v3) - looseness * (v1 + v4) + + temp0 = calc.copy( temp0, v2 ); + temp0 = calc.addi( temp0, v3 ); + temp0 = calc.scalei( temp0, 0.5f + looseness ); + + temp1 = calc.copy( temp1, v1 ); + temp1 = calc.addi( temp1, v4 ); + + // TODO recycle + return calc.addsn( temp0, temp1, -looseness ); + } + + public int getActualIndex( int index ) + { + final int n = points.length; + + return ( loops ? (index + n) % n : Numbers.clamp( index, 0, n - 1 ) ); + } + + @Override + public int getPointCount() + { + return points.length; + } + + @Override + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + depth = input.readInt( "depth" ); + loops = input.readBoolean( "loops" ); + looseness = input.readFloat( "looseness" ); + points = input.readModelArray( "point", "point-type" ); + temp0 = calc.create(); + temp1 = calc.create(); + } + + @Override + public void write( OutputModel output ) + { + output.write( "depth", depth ); + output.write( "loops", loops ); + output.write( "looseness", looseness ); + output.writeModelArray( "point", points, "point-type", calc ); + } + +} diff --git a/src/com/axe/path/LinearPath.java b/src/com/axe/path/LinearPath.java new file mode 100755 index 0000000..913032b --- /dev/null +++ b/src/com/axe/path/LinearPath.java @@ -0,0 +1,48 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.math.calc.Calculator; +import com.axe.math.calc.CalculatorRegistry; + +public class LinearPath extends TimedPath +{ + + public LinearPath() + { + } + + public LinearPath( Alternative ... points ) + { + this( PathUtility.alternatives( points ) ); + } + + public LinearPath( T ... points) + { + super( points, getTimes( points ) ); + } + + public static float[] getTimes(T[] points) + { + final Calculator calc = CalculatorRegistry.getFor( points[0] ); + int n = points.length; + float[] distances = new float[n--]; + + distances[0] = 0; + for (int i = 1; i <= n; i++) + { + distances[i] = distances[i - 1] + calc.distance( points[i - 1], points[i] ); + } + + float length = 1f / distances[ n ]; + + for (int i = 1; i < n; i++) + { + distances[i] *= length; + } + + distances[n] = 1f; + + return distances; + } + +} diff --git a/src/com/axe/path/ParametricCubicPath.java b/src/com/axe/path/ParametricCubicPath.java new file mode 100755 index 0000000..1e7e24a --- /dev/null +++ b/src/com/axe/path/ParametricCubicPath.java @@ -0,0 +1,102 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class ParametricCubicPath extends AbstractPath +{ + + public float[][] matrix; + public float weight; + public T[] points; + + public ParametricCubicPath() + { + } + + public ParametricCubicPath( float weight, float[][] matrix, Alternative ... points ) + { + this( weight, matrix, PathUtility.alternatives( points ) ); + } + + public ParametricCubicPath(float weight, float[][] matrix, T ... points) + { + super( points[0] ); + + this.weight = weight; + this.matrix = matrix; + this.points = points; + } + + @Override + public T set( T subject, float delta ) + { + return calc.parametricCubicCurve( subject, delta, points, matrix, delta ); + } + + @Override + public int getPointCount() + { + return points.length; + } + + @Override + public T getPoint( int index ) + { + return points[index]; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + weight = input.readFloat( "weight" ); + points = input.readModelArray( "points", "point-type" ); + matrix = getMatrixFromString( input.readString( "matrix" ) ); + } + + @Override + public void write( OutputModel output ) + { + output.write( "weight", weight ); + output.writeModelArray( "points", points, "point-type", calc ); + output.write( "matrix", getStringFromMatrix( matrix ) ); + } + + public static float[][] getMatrixFromString(String x) + { + String[] parts = x.split( "," ); + + float[][] m = new float[4][4]; + + for (int i = 0; i < 16; i++) + { + m[i / 4][i % 4] = Float.parseFloat( parts[i] ); + } + + return m; + } + + public static String getStringFromMatrix(float[][] matrix) + { + StringBuilder s = new StringBuilder( 64 ); + + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 4; x++) + { + if (s.length() > 0) + { + s.append( ',' ); + } + + s.append( matrix[y][x] ); + } + } + + return s.toString(); + } + +} diff --git a/src/com/axe/path/Path.java b/src/com/axe/path/Path.java new file mode 100755 index 0000000..684489b --- /dev/null +++ b/src/com/axe/path/Path.java @@ -0,0 +1,19 @@ +package com.axe.path; + +import com.axe.io.DataModel; +import com.axe.math.calc.Calculator; + +public interface Path extends DataModel +{ + + public T set(T subject, float delta); + + public T get(float delta); + + public int getPointCount(); + + public T getPoint(int index); + + public Calculator getCalculator(); + +} diff --git a/src/com/axe/path/PathUtility.java b/src/com/axe/path/PathUtility.java new file mode 100755 index 0000000..f8202f1 --- /dev/null +++ b/src/com/axe/path/PathUtility.java @@ -0,0 +1,24 @@ +package com.axe.path; + +import java.util.Arrays; + +import com.axe.core.Alternative; + + +public class PathUtility +{ + + + public static T[] alternatives(Alternative[] alts, T ... empty) + { + T[] attrs = Arrays.copyOf( empty, alts.length ); + + for (int i = 0; i < alts.length; i++) + { + attrs[ i ] = alts[ i ].alternative(); + } + + return attrs; + } + +} diff --git a/src/com/axe/path/PointPath.java b/src/com/axe/path/PointPath.java new file mode 100755 index 0000000..c3a0f16 --- /dev/null +++ b/src/com/axe/path/PointPath.java @@ -0,0 +1,53 @@ +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class PointPath extends AbstractPath +{ + + public T value; + + public PointPath(T value) + { + super( value ); + + this.value = value; + } + + @Override + public T set( T subject, float delta ) + { + return calc.copy( subject, value ); + } + + public int getPointCount() + { + return 1; + } + + public T getPoint(int index) + { + return (index == 0 ? value : null); + } + + public T get(int index) + { + return (index == 0 ? value : null ); + } + + @Override + public void read( InputModel input ) + { + // TODO calc + input.readModel( "value", value, calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModel( "value", value, calc ); + } + +} diff --git a/src/com/axe/path/QuadraticCornerPath.java b/src/com/axe/path/QuadraticCornerPath.java new file mode 100755 index 0000000..90389cc --- /dev/null +++ b/src/com/axe/path/QuadraticCornerPath.java @@ -0,0 +1,125 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Numbers; + +/** + * + * @author Philip Diffenderfer + * + * @param + */ +public class QuadraticCornerPath extends AbstractPath +{ + + protected T[] points; + protected T temp0; + protected T temp1; + protected boolean loops; + protected float midpoint; + + public QuadraticCornerPath() + { + } + + public QuadraticCornerPath( float midpoint, boolean loops, Alternative ... points ) + { + this( midpoint, loops, PathUtility.alternatives( points ) ); + } + + public QuadraticCornerPath( float midpoint, boolean loops, T ... points ) + { + super( points[0] ); + + this.midpoint = midpoint; + this.loops = loops; + this.points = points; + this.temp0 = calc.create(); + this.temp1 = calc.create(); + } + + @Override + public T set(T subject, float delta) + { + final float negmidpoint = 1.0f - midpoint; + final float halfmidpoint = midpoint * 0.5f; + final int n = points.length - (loops ? 0 : 1); + final float a = delta * n; + final int i = Numbers.clamp( (int)a, 0, n - 1 ); + float d = a - i; + + T p0 = points[ getActualIndex( i - 1 ) ]; + T p1 = points[ getActualIndex( i ) ]; + T p2 = points[ getActualIndex( i + 1 ) ]; + T p3 = points[ getActualIndex( i + 2 ) ]; + + if ( d < midpoint ) + { + d = (d / midpoint); + + temp0 = calc.interpolate( temp0, p0, p1, d * halfmidpoint + negmidpoint + halfmidpoint ); + temp1 = calc.interpolate( temp1, p1, p2, d * halfmidpoint + halfmidpoint ); + + p1 = temp0; + p2 = temp1; + d = d * 0.5f + 0.5f; + } + else if ( d > negmidpoint ) + { + d = (d - negmidpoint) / midpoint; + + temp0 = calc.interpolate( temp0, p1, p2, d * halfmidpoint + negmidpoint ); + temp1 = calc.interpolate( temp1, p2, p3, d * halfmidpoint ); + + p1 = temp0; + p2 = temp1; + d = d * 0.5f; + } + + subject = calc.interpolate( subject, p1, p2, d ); + + return subject; + } + + public int getActualIndex( int index ) + { + final int n = points.length; + + return ( loops ? (index + n) % n : Numbers.clamp( index, 0, n - 1 ) ); + } + + @Override + public int getPointCount() + { + return points.length; + } + + @Override + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "point", "point-type" ); + temp0 = calc.create(); + temp1 = calc.create(); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelArray( "point", points, "point-type", calc ); + } + +} diff --git a/src/com/axe/path/QuadraticPath.java b/src/com/axe/path/QuadraticPath.java new file mode 100755 index 0000000..10907a9 --- /dev/null +++ b/src/com/axe/path/QuadraticPath.java @@ -0,0 +1,92 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + +public class QuadraticPath extends AbstractPath +{ + + public T p0; + public T p1; + public T p2; + private T temp; + + public QuadraticPath() + { + } + + public QuadraticPath( Alternative p0, Alternative p1, Alternative p2 ) + { + this( p0.alternative(), p1.alternative(), p2.alternative() ); + } + + public QuadraticPath(T p0, T p1, T p2) + { + super( p0 ); + + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.temp = calc.create(); + } + + + @Override + public T set(T subject, float d1) + { + float d2 = d1 * d1; + float i1 = 1 - d1; + float i2 = i1 * i1; + + temp = calc.scale( temp, p0, i2 ); + temp = calc.addsi( temp, p1, 2 * i1 * d1 ); + temp = calc.addsi( temp, p2, d2 ); + + subject = calc.copy(subject, temp ); + + return subject; + } + + public int getPointCount() + { + return 3; + } + + public T getPoint(int index) + { + switch(index) { + case 0: return p0; + case 1: return p1; + case 2: return p2; + } + return null; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + // Attribute factory = input.readInstance( "type" ); + + p0 = calc.create(); + p1 = calc.create(); + p2 = calc.create(); + temp = calc.create(); + + input.readModel( "p0", p0, calc ); + input.readModel( "p1", p1, calc ); + input.readModel( "p2", p2, calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeInstance( "type", p0 ); + + output.writeModel( "p0", p0, calc ); + output.writeModel( "p1", p1, calc ); + output.writeModel( "p2", p2, calc ); + } + +} diff --git a/src/com/axe/path/ScaledPath.java b/src/com/axe/path/ScaledPath.java new file mode 100755 index 0000000..6e10c48 --- /dev/null +++ b/src/com/axe/path/ScaledPath.java @@ -0,0 +1,59 @@ +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class ScaledPath extends AbstractPath +{ + + public T scale; + public Path path; + + public ScaledPath() + { + } + + public ScaledPath(T scale, Path path) + { + super( scale ); + + this.scale = scale; + this.path = path; + } + + @Override + public void read( InputModel input ) + { + path = input.readModel( "path", "path-type", true ); + scale = input.readModel( "scale", "scale-type", true ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModel( "path", path, "path-type" ); + output.writeModel( "scale", scale, "scale-type", calc ); + } + + @Override + public T set( T subject, float delta ) + { + subject = path.set( subject, delta ); + + return calc.muli( subject, scale ); + } + + @Override + public int getPointCount() + { + return path.getPointCount(); + } + + @Override + public T getPoint( int index ) + { + return path.getPoint( index ); + } + +} diff --git a/src/com/axe/path/SpringPath.java b/src/com/axe/path/SpringPath.java new file mode 100755 index 0000000..07a4cdc --- /dev/null +++ b/src/com/axe/path/SpringPath.java @@ -0,0 +1,84 @@ +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; +import com.axe.spring.LinearSpring; + + +public class SpringPath, T> extends LinearSpring implements Path +{ + + private Path path; + + public SpringPath() + { + } + + public SpringPath(Path path, T position, T damping, T stiffness) + { + super( position, damping, stiffness ); + + this.path = path; + } + + public SpringPath(Path path, T position, T rest, T damping, T stiffness) + { + super( position, rest, damping, stiffness ); + + this.path = path; + } + + @Override + public T set( T subject, float delta ) + { + final Calculator calc = path.getCalculator(); + + subject = path.set( subject, delta ); + subject = calc.addi( subject, position() ); + + return subject; + } + + @Override + public T get( float delta ) + { + return set( getCalculator().create(), delta ); + } + + @Override + public int getPointCount() + { + return path.getPointCount(); + } + + @Override + public T getPoint(int index) + { + return path.getPoint( index ); + } + + @Override + public Calculator getCalculator() + { + return path.getCalculator(); + } + + @Override + public void read( InputModel input ) + { + super.read( input ); + + path = input.readModel( "path", "path-type", true ); + } + + @Override + public void write( OutputModel output ) + { + super.write( output ); + + output.writeModel( "path", path, "path-type" ); + } + +} diff --git a/src/com/axe/path/SubPath.java b/src/com/axe/path/SubPath.java new file mode 100755 index 0000000..b3dbc21 --- /dev/null +++ b/src/com/axe/path/SubPath.java @@ -0,0 +1,60 @@ +package com.axe.path; + +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Scalarf; + + +public class SubPath extends AbstractPath +{ + + public final Scalarf start = new Scalarf(); + public final Scalarf end = new Scalarf(); + public Path path; + + public SubPath( float start, float end, Path path ) + { + super( path.getCalculator() ); + + this.start.set( start ); + this.end.set( end ); + this.path = path; + } + + @Override + public T set( T subject, float delta ) + { + subject = path.set( subject, (end.v - start.v) * delta + start.v ); + + return subject; + } + + @Override + public int getPointCount() + { + return path.getPointCount(); + } + + @Override + public T getPoint( int index ) + { + return path.getPoint( index ); + } + + @Override + public void read( InputModel input ) + { + start.v = input.readFloat( "start" ); + end.v = input.readFloat( "end" ); + path = input.readModel( "path", "path-class" ); + } + + @Override + public void write( OutputModel output ) + { + output.write( "start", start.v ); + output.write( "end", end.v ); + output.writeModel( "path", "path-class", path ); + } + +} diff --git a/src/com/axe/path/TimedPath.java b/src/com/axe/path/TimedPath.java new file mode 100755 index 0000000..85426b8 --- /dev/null +++ b/src/com/axe/path/TimedPath.java @@ -0,0 +1,105 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class TimedPath extends AbstractPath +{ + + protected float[] times; + protected T[] points; + + public TimedPath() + { + } + + public TimedPath( Alternative[] points, float[] times ) + { + this( PathUtility.alternatives( points ), times ); + } + + public TimedPath( T[] points, float[] times ) + { + super( points[0] ); + + this.points = points; + this.times = times; + } + + protected void setPoints( T[] points ) + { + this.points = points; + } + + protected void setTimes( float[] times ) + { + this.times = times; + } + + @Override + public T set( T subject, float delta ) + { + if (delta <= 0) + { + subject = calc.copy( subject, points[0] ); + } + else if (delta >= 1) + { + subject = calc.copy( subject, points[points.length - 1] ); + } + else + { + int i = points.length - 1; + while (times[i] > delta) --i; + float q = (delta - times[i]) / (times[i + 1] - times[i]); + + subject = calc.interpolate( subject, points[i], points[i + 1], q ); + } + + return subject; + } + + public int getPointCount() + { + return points.length; + } + + public T getPoint(int index) + { + return points[ index ]; + } + + public T[] points() + { + return points; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + points = input.readModelArray( "point", "point-type" ); + times = new float[ points.length ]; + + int i = 0; + + for (InputModel c : input) + { + times[i++] = c.readFloat( "time" ); + } + } + + @Override + public void write( OutputModel output ) + { + OutputModel[] models = output.writeModelArray( "point", points, "point-type", calc ); + + for (int i = 0; i < points.length; i++) + { + models[i].write( "time", times[i] ); + } + } + +} diff --git a/src/com/axe/path/Tween.java b/src/com/axe/path/Tween.java new file mode 100755 index 0000000..ff4c070 --- /dev/null +++ b/src/com/axe/path/Tween.java @@ -0,0 +1,72 @@ +package com.axe.path; + +import com.axe.core.Alternative; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; + + +public class Tween extends AbstractPath +{ + + public T start; + public T end; + + public Tween() + { + } + + public Tween(Alternative start, Alternative end) + { + this( start.alternative(), end.alternative() ); + } + + public Tween(T start, T end) + { + super( start ); + + this.start = start; + this.end = end; + } + + @Override + public T set(T subject, float delta) + { + return calc.interpolate( subject, start, end, delta ); + } + + @Override + public int getPointCount() + { + return 2; + } + + @Override + public T getPoint(int index) + { + switch(index) { + case 0: return start; + case 1: return end; + } + return null; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + // Attribute factory = input.readInstance( "type" ); + + input.readModel( "start", start = calc.create(), calc ); + input.readModel( "end", end = calc.create(), calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeInstance( "type", start ); + + output.writeModel( "start", start, calc ); + output.writeModel( "end", end, calc ); + } + +} \ No newline at end of file diff --git a/src/com/axe/path/UniformPath.java b/src/com/axe/path/UniformPath.java new file mode 100755 index 0000000..e15f964 --- /dev/null +++ b/src/com/axe/path/UniformPath.java @@ -0,0 +1,40 @@ +package com.axe.path; + +import com.axe.math.calc.Calculator; + +public class UniformPath extends TimedPath +{ + + public UniformPath( Path path ) + { + this( path, path.getPointCount() ); + } + + public UniformPath( Path path, int attributeCount ) + { + setPoints( getPoints( path, attributeCount ) ); + + setTimes( LinearPath.getTimes( points ) ); + } + + public static T[] getPoints( Path path, int attributeCount ) + { + final Calculator calc = path.getCalculator(); + T[] points = calc.createArray( attributeCount ); + + if (path.getPointCount() == attributeCount) + { + for (int i = 0; i < attributeCount; i++) + { + points[i] = calc.clone( path.getPoint( i ) ); + } + } + else + { + points = CompiledPath.compile( path, points ); + } + + return points; + } + +} diff --git a/src/com/axe/plot/Plot.java b/src/com/axe/plot/Plot.java new file mode 100755 index 0000000..879c655 --- /dev/null +++ b/src/com/axe/plot/Plot.java @@ -0,0 +1,8 @@ +package com.axe.plot; + +public interface Plot { + public boolean next(); + public void reset(); + public void end(); + public T get(); +} diff --git a/src/com/axe/plot/PlotCell2f.java b/src/com/axe/plot/PlotCell2f.java new file mode 100755 index 0000000..ae80ba9 --- /dev/null +++ b/src/com/axe/plot/PlotCell2f.java @@ -0,0 +1,151 @@ +package com.axe.plot; + +import com.axe.math.Vec2f; +import com.axe.math.Vec2i; + +public class PlotCell2f implements Plot +{ + + private final Vec2f size = new Vec2f(); + private final Vec2f off = new Vec2f(); + private final Vec2f pos = new Vec2f(); + private final Vec2f dir = new Vec2f(); + + private final Vec2i index = new Vec2i(); + + private final Vec2f delta = new Vec2f(); + private final Vec2i sign = new Vec2i(); + private final Vec2f max = new Vec2f(); + + private int limit; + private int plotted; + + public PlotCell2f(float offx, float offy, float width, float height) + { + off.set( offx, offy ); + size.set( width, height ); + } + + public void plot(Vec2f position, Vec2f direction, int cells) + { + limit = cells; + + pos.set( position ); + dir.normal( direction ); + + delta.set( size ); + delta.div( dir ); + + sign.x = (dir.x > 0) ? 1 : (dir.x < 0 ? -1 : 0); + sign.y = (dir.y > 0) ? 1 : (dir.y < 0 ? -1 : 0); + + reset(); + } + + @Override + public boolean next() + { + if (plotted++ > 0) + { + float mx = sign.x * max.x; + float my = sign.y * max.y; + + if (mx < my) + { + max.x += delta.x; + index.x += sign.x; + } + else + { + max.y += delta.y; + index.y += sign.y; + } + } + return (plotted <= limit); + } + + @Override + public void reset() + { + plotted = 0; + + index.x = (int)Math.floor((pos.x - off.x) / size.x); + index.y = (int)Math.floor((pos.y - off.y) / size.y); + + float ax = index.x * size.x + off.x; + float ay = index.y * size.y + off.y; + + max.x = (sign.x > 0) ? ax + size.x - pos.x : pos.x - ax; + max.y = (sign.y > 0) ? ay + size.y - pos.y : pos.y - ay; + max.div( dir ); + } + + @Override + public void end() + { + plotted = limit + 1; + } + + @Override + public Vec2i get() + { + return index; + } + + public Vec2f actual(Vec2f out) { + out.x = index.x * size.x + off.x; + out.y = index.y * size.y + off.y; + return out; + } + + public Vec2f actual() { + return actual(new Vec2f()); + } + + public Vec2f size() { + return size; + } + + public void size(float w, float h) { + size.set(w, h); + } + + public Vec2f offset() { + return off; + } + + public void offset(float x, float y) { + off.set(x, y); + } + + public Vec2f position() { + return pos; + } + + public Vec2f direction() { + return dir; + } + + public Vec2i sign() { + return sign; + } + + public Vec2f delta() { + return delta; + } + + public Vec2f max() { + return max; + } + + public int limit() { + return limit; + } + + public int plotted() { + return plotted; + } + + + +} diff --git a/src/com/axe/plot/PlotCell3f.java b/src/com/axe/plot/PlotCell3f.java new file mode 100755 index 0000000..c8e7ea8 --- /dev/null +++ b/src/com/axe/plot/PlotCell3f.java @@ -0,0 +1,162 @@ +package com.axe.plot; + +import com.axe.math.Vec3f; +import com.axe.math.Vec3i; + +public class PlotCell3f implements Plot +{ + + private final Vec3f size = new Vec3f(); + private final Vec3f off = new Vec3f(); + private final Vec3f pos = new Vec3f(); + private final Vec3f dir = new Vec3f(); + + private final Vec3i index = new Vec3i(); + + private final Vec3f delta = new Vec3f(); + private final Vec3i sign = new Vec3i(); + private final Vec3f max = new Vec3f(); + + private int limit; + private int plotted; + + public PlotCell3f(float offx, float offy, float offz, float width, float height, float depth) + { + off.set( offx, offy, offz ); + size.set( width, height, depth ); + } + + public void plot(Vec3f position, Vec3f direction, int cells) + { + limit = cells; + + pos.set( position ); + dir.normal( direction ); + + delta.set( size ); + delta.div( dir ); + + sign.x = (dir.x > 0) ? 1 : (dir.x < 0 ? -1 : 0); + sign.y = (dir.y > 0) ? 1 : (dir.y < 0 ? -1 : 0); + sign.z = (dir.z > 0) ? 1 : (dir.z < 0 ? -1 : 0); + + reset(); + } + + @Override + public boolean next() + { + if (plotted++ > 0) + { + float mx = sign.x * max.x; + float my = sign.y * max.y; + float mz = sign.z * max.z; + + if (mx < my && mx < mz) + { + max.x += delta.x; + index.x += sign.x; + } + else if (mz < my && mz < mx) + { + max.z += delta.z; + index.z += sign.z; + } + else + { + max.y += delta.y; + index.y += sign.y; + } + } + return (plotted <= limit); + } + + @Override + public void reset() + { + plotted = 0; + + index.x = (int)Math.floor((pos.x - off.x) / size.x); + index.y = (int)Math.floor((pos.y - off.y) / size.y); + index.z = (int)Math.floor((pos.z - off.z) / size.z); + + float ax = index.x * size.x + off.x; + float ay = index.y * size.y + off.y; + float az = index.z * size.z + off.z; + + max.x = (sign.x > 0) ? ax + size.x - pos.x : pos.x - ax; + max.y = (sign.y > 0) ? ay + size.y - pos.y : pos.y - ay; + max.z = (sign.z > 0) ? az + size.z - pos.z : pos.z - az; + max.div( dir ); + } + + @Override + public void end() + { + plotted = limit + 1; + } + + @Override + public Vec3i get() + { + return index; + } + + public Vec3f actual(Vec3f out) { + out.x = index.x * size.x + off.x; + out.y = index.y * size.y + off.y; + out.z = index.z * size.z + off.z; + return out; + } + + public Vec3f actual() { + return actual( new Vec3f() ); + } + + public Vec3f size() { + return size; + } + + public void size(float w, float h, float d) { + size.set(w, h, d); + } + + public Vec3f offset() { + return off; + } + + public void offset(float x, float y, float z) { + off.set(x, y, z); + } + + public Vec3f position() { + return pos; + } + + public Vec3f direction() { + return dir; + } + + public Vec3i sign() { + return sign; + } + + public Vec3f delta() { + return delta; + } + + public Vec3f max() { + return max; + } + + public int limit() { + return limit; + } + + public int plotted() { + return plotted; + } + + + +} diff --git a/src/com/axe/render/Renderer.java b/src/com/axe/render/Renderer.java new file mode 100644 index 0000000..d0389c3 --- /dev/null +++ b/src/com/axe/render/Renderer.java @@ -0,0 +1,43 @@ +package com.axe.render; + +import com.axe.View; +import com.axe.game.GameState; + +public interface Renderer +{ + + public static final int NONE = -1; + + default public Renderer create(T entity) + { + return this; + } + + default public boolean isActivated(T entity) + { + return true; + } + + default public void activate(T entity) + { + + } + + public void begin(T entity, GameState state, View view); + + default public void end(T entity, GameState state, View view) + { + // nothing + } + + default public void update(T entity) + { + // nothing + } + + default public void destroy(T entity) + { + // nothing + } + +} \ No newline at end of file diff --git a/src/com/axe/render/RendererHandle.java b/src/com/axe/render/RendererHandle.java new file mode 100755 index 0000000..b226758 --- /dev/null +++ b/src/com/axe/render/RendererHandle.java @@ -0,0 +1,97 @@ + +package com.axe.render; + +import com.axe.Axe; +import com.axe.View; +import com.axe.game.GameState; + +public class RendererHandle +{ + + public Renderer renderer; + public final T entity; + + public RendererHandle( T entity, int view ) + { + this.entity = entity; + this.set( view ); + } + + public RendererHandle( T entity ) + { + this.entity = entity; + } + + public void set( int view ) + { + Renderer renderer = Axe.renderers.get( view ); + + set( renderer ); + } + + public void refresh() + { + set( renderer ); + } + + public void set( Renderer newRenderer ) + { + if (renderer != null) + { + renderer.destroy( entity ); + renderer = null; + } + + if (newRenderer != null) + { + renderer = newRenderer.create( entity ); + } + } + + public void begin( GameState state, View view ) + { + if (renderer != null) + { + renderer.begin( entity, state, view ); + } + } + + public void end( GameState state, View view ) + { + if (renderer != null) + { + renderer.end( entity, state, view ); + } + } + + public void draw( GameState state, View view ) + { + if (renderer != null) + { + renderer.begin( entity, state, view ); + renderer.end( entity, state, view ); + } + } + + public void destroy() + { + if (renderer != null) + { + renderer.destroy( entity ); + renderer = null; + } + } + + public boolean hasRenderer() + { + return renderer != null; + } + + public static > R take( T entity, int view ) + { + R renderer = Axe.renderers.get( view ); + + return (renderer == null ? null : (R)renderer.create( entity )); + } + +} diff --git a/src/com/axe/resource/Resource.java b/src/com/axe/resource/Resource.java new file mode 100644 index 0000000..7e6f4fd --- /dev/null +++ b/src/com/axe/resource/Resource.java @@ -0,0 +1,13 @@ +package com.axe.resource; + + +public interface Resource +{ + + public void destroy(); + + public boolean isActivated(); + + public void activate(); + +} diff --git a/src/com/axe/resource/ResourceBundle.java b/src/com/axe/resource/ResourceBundle.java new file mode 100644 index 0000000..983eba3 --- /dev/null +++ b/src/com/axe/resource/ResourceBundle.java @@ -0,0 +1,12 @@ +package com.axe.resource; + +import org.magnos.asset.FutureAssetBundle; + +public class ResourceBundle extends FutureAssetBundle +{ + + private static final long serialVersionUID = 1L; + + + +} diff --git a/src/com/axe/resource/ResourceFutureAsset.java b/src/com/axe/resource/ResourceFutureAsset.java new file mode 100644 index 0000000..dfe2d8b --- /dev/null +++ b/src/com/axe/resource/ResourceFutureAsset.java @@ -0,0 +1,29 @@ +package com.axe.resource; + +import org.magnos.asset.AssetInfo; +import org.magnos.asset.FutureAssetFactory; +import org.magnos.asset.base.BaseFutureAsset; + +public class ResourceFutureAsset extends BaseFutureAsset +{ + + public static final FutureAssetFactory FACTORY = + (assetInfo) -> new ResourceFutureAsset( assetInfo ); + + public ResourceFutureAsset(AssetInfo info) + { + super(info); + } + + @Override + public void loaded() + { + Resource resource = get(); + + if (resource != null && !resource.isActivated()) + { + resource.activate(); + } + } + +} diff --git a/src/com/axe/spring/AbstractSpring.java b/src/com/axe/spring/AbstractSpring.java new file mode 100644 index 0000000..d440c84 --- /dev/null +++ b/src/com/axe/spring/AbstractSpring.java @@ -0,0 +1,66 @@ +package com.axe.spring; + +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; +import com.axe.math.calc.CalculatorRegistry; +import com.axe.node.Node; + +public abstract class AbstractSpring, T> implements Spring +{ + + public Node parent; + protected Calculator calc; + protected T rest; + protected T position; + protected T velocity; + + public AbstractSpring() + { + + } + + public AbstractSpring(T rest, T position, T velocity) + { + this.calc = CalculatorRegistry.getFor( rest ); + this.rest = rest; + this.position = position; + this.velocity = velocity; + } + + @Override + public Node getParent() + { + return parent; + } + + @Override + public void setParent(Node parent) + { + this.parent = parent; + } + + @Override + public T rest() + { + return rest; + } + + @Override + public T velocity() + { + return velocity; + } + + @Override + public T position() + { + return position; + } + + @Override + public Calculator getCalculator() + { + return calc; + } + +} diff --git a/src/com/axe/spring/BoxSpring.java b/src/com/axe/spring/BoxSpring.java new file mode 100755 index 0000000..1a1ad50 --- /dev/null +++ b/src/com/axe/spring/BoxSpring.java @@ -0,0 +1,70 @@ +package com.axe.spring; + +import com.axe.View; +import com.axe.game.GameState; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; + +public class BoxSpring, T> extends AbstractSpring +{ + + protected T constant; + protected T acceleration; + + public BoxSpring() + { + } + + public BoxSpring(T position, T constant) + { + this( position, Calculator.cloneLike( position ), constant ); + } + + public BoxSpring(T position, T rest, T constant) + { + super( rest, position, Calculator.createLike( position ) ); + + this.constant = constant; + this.acceleration = calc.create(); + } + + @Override + public void update( GameState state, View view ) + { + // acceleration = (position - rest) * -constant + // position += velocity * dt + // velocity += acceleration * dt + final float elapsed = state.seconds; + + acceleration = calc.sub( acceleration, rest, position ); + acceleration = calc.muli( acceleration, constant ); + + position = calc.addsi( position, velocity, elapsed ); + velocity = calc.addsi( velocity, acceleration, elapsed ); + } + + public T constant() + { + return constant; + } + + public T acceleration() + { + return acceleration; + } + + @Override + public void read( InputModel input ) + { + + } + + @Override + public void write( OutputModel output ) + { + + } + +} diff --git a/src/com/axe/spring/DistanceSpring.java b/src/com/axe/spring/DistanceSpring.java new file mode 100755 index 0000000..cb72cf6 --- /dev/null +++ b/src/com/axe/spring/DistanceSpring.java @@ -0,0 +1,114 @@ +package com.axe.spring; + +import com.axe.View; +import com.axe.game.GameState; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Scalarf; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; + + +public class DistanceSpring, T> extends AbstractSpring +{ + + protected Scalarf distance; + protected Scalarf stiffness; + protected Scalarf damping; + + protected T temp; + + public DistanceSpring() + { + } + + public DistanceSpring(T position, Scalarf distance, Scalarf damping, Scalarf stiffness) + { + this( position, Calculator.cloneLike( position ), distance, damping, stiffness ); + } + + public DistanceSpring(T position, T rest, Scalarf distance, Scalarf damping, Scalarf stiffness) + { + super( rest, position, Calculator.createLike( position ) ); + + this.distance = distance; + this.damping = damping; + this.stiffness = stiffness; + this.temp = calc.create(); + } + + @Override + public void update( GameState gameState, View view ) + { + // d = DISTANCE( position, rest ) + // velocity += ((position - rest) / d * stiffness * |distance - d| - (damping * velocity)) * elapsed.seconds; + // position += velocity * elapsed.seconds; + + final float elapsed = gameState.seconds; + float d = calc.distance( position, rest ); + + temp = calc.sub( temp, position, rest ); + + if ( d != 0 ) + { + temp = calc.scalei( temp, 1f / d ); + temp = calc.scalei( temp, (d - distance.v) * stiffness.v ); + } + + temp = calc.addsi( temp, velocity, -damping.v ); + + velocity = calc.addsi( velocity, temp, elapsed ); + position = calc.addsi( position, velocity, elapsed ); + } + + public Scalarf stiffness() + { + return stiffness; + } + + public Scalarf damping() + { + return damping; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + // Attribute factory = input.readInstance( "type" ); + + position = calc.create(); + velocity = calc.create(); + rest = calc.create(); + temp = calc.create(); + + if ( stiffness == null ) + { + stiffness = new Scalarf(); + } + + if ( damping == null ) + { + damping = new Scalarf(); + } + + input.readModel( "stiffness", stiffness ); + input.readModel( "damping", damping ); + input.readModel( "rest", rest, calc ); + input.readModel( "velocity", velocity, calc ); + input.readModel( "position", position, calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeInstance( "type", position ); + + output.writeModel( "stiffness",stiffness ); + output.writeModel( "damping", damping ); + output.writeModel( "rest", rest, calc ); + output.writeModel( "velocity", velocity, calc ); + output.writeModel( "position", position, calc ); + } + +} diff --git a/src/com/axe/spring/LinearSpring.java b/src/com/axe/spring/LinearSpring.java new file mode 100755 index 0000000..eb35f2f --- /dev/null +++ b/src/com/axe/spring/LinearSpring.java @@ -0,0 +1,110 @@ +package com.axe.spring; + +import com.axe.View; +import com.axe.game.GameState; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; +import com.axe.math.calc.CalculatorRegistry; + + +public class LinearSpring, T> extends AbstractSpring +{ + + protected T stiffness; + protected T damping; + + protected T temp0; + protected T temp1; + + public LinearSpring() + { + } + + public LinearSpring(T position, T damping, T stiffness) + { + this( position, Calculator.cloneLike( position ), damping, stiffness ); + } + + public LinearSpring(T position, T rest, T damping, T stiffness) + { + super( rest, position, Calculator.createLike( position ) ); + + this.damping = damping; + this.stiffness = stiffness; + this.temp0 = calc.create(); + this.temp1 = calc.create(); + } + + @Override + public void update( GameState gameState, View view ) + { + // velocity += ((stiffness * (position - rest)) - (damping * velocity)) * elapsed.seconds; + // position += velocity * elapsed.seconds; + final float elapsed = gameState.seconds; + + temp0 = calc.sub( temp0, position, rest ); + temp0 = calc.muli( temp0, stiffness ); + + temp1 = calc.mul( temp1, damping, velocity ); + temp0 = calc.subi( temp0, temp1 ); + + velocity = calc.addsi( velocity, temp0, elapsed ); + position = calc.addsi( position, velocity, elapsed ); + } + + public T stiffness() + { + return stiffness; + } + + public T damping() + { + return damping; + } + + @Override + public void read( InputModel input ) + { + // TODO calc + // Attribute factory = input.readInstance( "type" ); + + position = calc.create(); + velocity = calc.create(); + stiffness = calc.create(); + damping = calc.create(); + rest = calc.create(); + temp0 = calc.create(); + temp1 = calc.create(); + + input.readModel( "stiffness", stiffness, calc ); + input.readModel( "damping", damping, calc ); + input.readModel( "rest", rest, calc ); + input.readModel( "velocity", velocity, calc ); + input.readModel( "position", position, calc ); + } + + @Override + public void write( OutputModel output ) + { + output.writeInstance( "type", position ); + + output.writeModel( "stiffness",stiffness, calc ); + output.writeModel( "damping", damping, calc ); + output.writeModel( "rest", rest, calc ); + output.writeModel( "velocity", velocity, calc ); + output.writeModel( "position", position, calc ); + } + + protected static T clone(T a) + { + return CalculatorRegistry.getFor( a ).clone( a ); + } + + protected static T create(T a) + { + return CalculatorRegistry.getFor( a ).create(); + } + +} diff --git a/src/com/axe/spring/Spring.java b/src/com/axe/spring/Spring.java new file mode 100755 index 0000000..dd44bbc --- /dev/null +++ b/src/com/axe/spring/Spring.java @@ -0,0 +1,14 @@ +package com.axe.spring; + +import com.axe.io.DataModel; +import com.axe.math.Vec; +import com.axe.math.calc.Calculator; +import com.axe.node.Node; + +public interface Spring, T> extends Node, DataModel +{ + public T rest(); + public T velocity(); + public T position(); + public Calculator getCalculator(); +} diff --git a/src/com/axe/sprite/PhysicsSprite.java b/src/com/axe/sprite/PhysicsSprite.java new file mode 100644 index 0000000..83638f0 --- /dev/null +++ b/src/com/axe/sprite/PhysicsSprite.java @@ -0,0 +1,63 @@ +package com.axe.sprite; + +import com.axe.View; +import com.axe.core.Factory; +import com.axe.game.GameState; +import com.axe.integrate.Integrator; +import com.axe.integrate.IntegratorEuler; +import com.axe.integrate.IntegratorVerlet; +import com.axe.math.Vec; + +public class PhysicsSprite> extends Sprite +{ + + public final V velocity; + public final V acceleration; + public Integrator integrator; + + public PhysicsSprite(int id, Factory factory) + { + super(id, factory); + + this.velocity = factory.create(); + this.acceleration = factory.create(); + this.setIntegratorEuler(); + } + + @Override + public void update(GameState state, View firstView) + { + integrator.update( state ); + + super.update( state, firstView ); + } + + public PhysicsSprite setVelocity(V velocity) + { + this.velocity.set( velocity ); + + return this; + } + + public PhysicsSprite setAcceleration(V acceleration) + { + this.acceleration.set( acceleration ); + + return this; + } + + public PhysicsSprite setIntegratorEuler() + { + this.integrator = new IntegratorEuler( position, velocity, acceleration ); + + return this; + } + + public PhysicsSprite setIntegratorVerlet() + { + this.integrator = new IntegratorVerlet( position, acceleration ); + + return this; + } + +} diff --git a/src/com/axe/sprite/SimpleSprite.java b/src/com/axe/sprite/SimpleSprite.java new file mode 100644 index 0000000..98b020d --- /dev/null +++ b/src/com/axe/sprite/SimpleSprite.java @@ -0,0 +1,107 @@ +package com.axe.sprite; + +import com.axe.core.Factory; +import com.axe.gfx.Texture; +import com.axe.math.Vec2f; +import com.axe.math.Vec; +import com.axe.node.RenderedNode; +import com.axe.tile.Tile; + +public class SimpleSprite> extends RenderedNode +{ + + public final V position; + public final Vec2f size; + public final Tile tile; + + protected boolean expired; + + public SimpleSprite(int id, Factory factory) + { + super( id ); + + this.position = factory.create(); + this.size = new Vec2f(); + this.tile = new Tile(); + } + + @Override + public boolean isActivated() + { + assert tile.texture != null; + + return super.isActivated() && tile.texture.isActivated(); + } + + @Override + public void activate() + { + if (!super.isActivated()) + { + super.activate(); + } + + if (!tile.texture.isActivated()) + { + tile.texture.activate(); + } + } + + @Override + public boolean isExpired() + { + return expired; + } + + @Override + public void expire() + { + expired = true; + } + + public SimpleSprite setPosition(V position) + { + this.position.set( position ); + + return this; + } + + public SimpleSprite setSize(Vec2f size) + { + this.size.set( size ); + + return this; + } + + public SimpleSprite setSize(float x, float y) + { + this.size.set( x, y ); + + return this; + } + + public SimpleSprite setTile(Tile tile) + { + this.tile.set( tile ); + + if ( this.size.isZero() ) + { + this.size.x = this.tile.texture.getImageWidth(); + this.size.y = this.tile.texture.getImageHeight(); + } + + return this; + } + + public SimpleSprite setTexture(Texture texture) + { + return setTile( texture.tile() ); + } + + @Override + public int hashCode() + { + return position.hashCode() ^ size.hashCode() ^ tile.hashCode(); + } + +} \ No newline at end of file diff --git a/src/com/axe/sprite/Sprite.java b/src/com/axe/sprite/Sprite.java new file mode 100644 index 0000000..bbe4e29 --- /dev/null +++ b/src/com/axe/sprite/Sprite.java @@ -0,0 +1,108 @@ +package com.axe.sprite; + +import com.axe.color.Color; +import com.axe.color.ColorInterface; +import com.axe.core.Factory; +import com.axe.gfx.Blend; +import com.axe.math.Scalarf; +import com.axe.math.Vec; +import com.axe.math.Vec2f; + +public class Sprite> extends SimpleSprite +{ + + public final Vec2f scale; + public final Vec2f anchor; + public final Color color; + public final Scalarf rotation; + public Blend blend; + + public Sprite(int id, Factory factory) + { + super( id, factory ); + + this.scale = new Vec2f( 1f ); + this.anchor = new Vec2f( 0.5f ); + this.color = new Color(); + this.rotation = new Scalarf(); + } + + public Sprite setScale(Vec2f scale) + { + this.scale.set( scale ); + + return this; + } + + public Sprite setScale(float scale) + { + this.scale.set( scale ); + + return this; + } + + public Sprite setScale(float x, float y) + { + this.scale.set( x, y ); + + return this; + } + + public Sprite setAnchor(Vec2f anchor) + { + this.anchor.set( anchor ); + + return this; + } + + public Sprite setAnchor(float anchor) + { + this.anchor.set( anchor ); + + return this; + } + + public Sprite setAnchor(float x, float y) + { + this.anchor.set( x, y ); + + return this; + } + + public Sprite setRotation(Scalarf rotation) + { + this.rotation.set( rotation ); + + return this; + } + + public Sprite setRotation(float rotation) + { + this.rotation.set( rotation ); + + return this; + } + + public Sprite setColor(ColorInterface color) + { + this.color.set( color ); + + return this; + } + + public Sprite setBlend(Blend blend) + { + this.blend = blend; + + return this; + } + + @Override + public int hashCode() + { + return position.hashCode() ^ size.hashCode() ^ tile.hashCode() ^ + scale.hashCode() ^ anchor.hashCode() ^ color.hashCode() ^ + rotation.hashCode() ^ (blend != null ? blend.hashCode() : 0); + } + +} diff --git a/src/com/axe/tile/Tile.java b/src/com/axe/tile/Tile.java new file mode 100755 index 0000000..291dc58 --- /dev/null +++ b/src/com/axe/tile/Tile.java @@ -0,0 +1,186 @@ +package com.axe.tile; + +import com.axe.core.Attribute; +import com.axe.gfx.Coord; +import com.axe.gfx.Texture; +import com.axe.math.Bound2i; +import com.axe.math.Vec2f; +import com.axe.math.Vec2i; +import com.axe.math.calc.CalculatorTile; + +public class Tile implements Attribute +{ + + public Texture texture; + public float s0, s1, t0, t1; + + public Tile() + { + } + + public Tile(Texture texture, float s0, float t0, float s1, float t1) + { + this.set( texture, s0, s1, t0, t1 ); + } + + public Tile(float s0, float t0, float s1, float t1) + { + this.set( null, s0, s1, t0, t1 ); + } + + public void set(float s0, float s1, float t0, float t1) + { + this.s0 = s0; + this.s1 = s1; + this.t0 = t0; + this.t1 = t1; + } + + public Tile set(Texture texture, float s0, float s1, float t0, float t1) + { + this.texture = texture; + this.set( s0, s1, t0, t1 ); + + return this; + } + + public void bind() + { + texture.bind(); + } + + public void shift(Coord src) + { + shift(src.s, src.t); + } + + public void shift(float s, float t) + { + s0 += s; + t0 += t; + s1 += s; + t1 += t; + } + + public void get(Coord[] srcs) + { + srcs[0].set(s0, t0); + srcs[1].set(s0, t1); + srcs[2].set(s1, t1); + srcs[3].set(s1, t0); + } + + public float cs() + { + return (s0 + s1) * 0.5f; + } + + public float ct() + { + return (t0 + t1) * 0.5f; + } + + public float ds(float d) + { + return (s1 - s0) * d + s0; + } + + public float dt(float d) + { + return (t1 - t0) * d + t0; + } + + public float sspan() + { + return (s1 - s0); + } + + public float tspan() + { + return (t1 - t0); + } + + public Vec2f getSizef() + { + return getSizef( new Vec2f() ); + } + + public Vec2f getSizef( Vec2f size ) + { + size.x = getWidthf(); + size.y = getHeightf(); + return size; + } + + public float getWidthf() + { + return (texture.width() + 1) * (s1 - s0); + } + + public float getHeightf() + { + return (texture.height() + 1) * (t1 - t0); + } + + public Vec2i getSizei() + { + return getSizei( new Vec2i() ); + } + + public Vec2i getSizei( Vec2i size ) + { + size.x = (int)getWidthf(); + size.y = (int)getHeightf(); + return size; + } + + public int getWidthi() + { + return (int)getWidthf(); + } + + public int getHeighti() + { + return (int)getHeightf(); + } + + public Bound2i getBounds() + { + return texture.bound(this); + } + + public void flips() + { + float s = s0; s0 = s1; s1 = s; + } + + public void flipt() + { + float t = t0; t0 = t1; t1 = t; + } + + @Override + public Tile get() + { + return this; + } + + @Override + public Tile clone() + { + return new Tile(texture, s0, t0, s1, t1); + } + + @Override + public CalculatorTile getCalculator() + { + return CalculatorTile.INSTANCE; + } + + @Override + public int hashCode() + { + return (int)(s0 + t0 * 73 + s1 * 5404 + t1 * 397336 + (texture != null ? texture.hashCode() : 0) * 29210829); + } + +} diff --git a/src/com/axe/tile/TileFactory.java b/src/com/axe/tile/TileFactory.java new file mode 100755 index 0000000..45e2936 --- /dev/null +++ b/src/com/axe/tile/TileFactory.java @@ -0,0 +1,9 @@ +package com.axe.tile; + +import com.axe.gfx.Texture; +import com.axe.io.DataModel; + +public interface TileFactory extends DataModel +{ + public Tile[] create(Texture ... textures); +} diff --git a/src/com/axe/tile/TileSet.java b/src/com/axe/tile/TileSet.java new file mode 100755 index 0000000..3c5a21d --- /dev/null +++ b/src/com/axe/tile/TileSet.java @@ -0,0 +1,73 @@ +package com.axe.tile; + +import java.util.Arrays; + +import com.axe.gfx.Texture; +import com.axe.tile.factory.TileFactoryGrid; +import com.axe.tile.factory.TileFactoryTexture; + + +public class TileSet +{ + + private int tileCount; + private Tile[] tiles; + + public TileSet(int capacity) + { + tiles = new Tile[capacity]; + } + + public void add(Tile[] t) + { + for (int i = 0; i < t.length; i++) { + tiles[tileCount++] = t[i]; + } + } + + public void add(Tile[] t, int start, int end ) + { + int dir = Integer.signum( end - start ); + + for (int i = start; i != end; i += dir) + { + tiles[ tileCount++ ] = t[ i ]; + } + + tiles[ tileCount++ ] = t[ end ]; + } + + public void add(Tile[] t, int ... indices) + { + for (int i = 0; i < indices.length; i++) { + tiles[tileCount++] = t[indices[i]]; + } + } + + public void add(Texture tex, int x, int y, int columns, int frames, int frameWidth, int frameHeight) + { + add( new TileFactoryGrid(x, y, columns, frames, frameWidth, frameHeight).create(new Texture[] {tex}) ); + } + + public void add(Texture ... texs) + { + add( new TileFactoryTexture().create(texs) ); + } + + public Tile get(int index) + { + return tiles[index]; + } + + public int size() + { + return tileCount; + } + + public Tile[] tiles() + { + return (tileCount == tiles.length ? + tiles : Arrays.copyOf( tiles, tileCount )); + } + +} diff --git a/src/com/axe/tile/TileSource.java b/src/com/axe/tile/TileSource.java new file mode 100755 index 0000000..afcc173 --- /dev/null +++ b/src/com/axe/tile/TileSource.java @@ -0,0 +1,6 @@ +package com.axe.tile; + +public interface TileSource { + public Tile get(int index); + public int size(); +} diff --git a/src/com/axe/tile/factory/TileFactoryFixed.java b/src/com/axe/tile/factory/TileFactoryFixed.java new file mode 100755 index 0000000..01ee72b --- /dev/null +++ b/src/com/axe/tile/factory/TileFactoryFixed.java @@ -0,0 +1,169 @@ +package com.axe.tile.factory; + +import java.util.ArrayList; +import java.util.List; + +import com.axe.gfx.Texture; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Vec2i; +import com.axe.tile.Tile; +import com.axe.tile.TileFactory; + + +/** + * TODO + * + * @author Philip Diffenderfer + * + */ +public class TileFactoryFixed implements TileFactory +{ + + /** + * The width of a tile on the texture in pixels. + */ + public int frameWidth; + + /** + * The height of a tile on the texture in pixels. + */ + public int frameHeight; + + // The list of points that make up the offsets of the created frames. + private List offsets = new ArrayList(); + + /** + * Instantiates a new TileFactoryFixed. + */ + public TileFactoryFixed() + { + + } + + /** + * Instantiates a new TileFactoryFixed. + * + * @param frameWidth + * The width of a tile on the texture in pixels. + * @param frameHeight + * The height of a tile on the texture in pixels. + */ + public TileFactoryFixed(int frameWidth, int frameHeight) + { + set(frameWidth, frameHeight); + } + + /** + * Sets the factories parameters. + * + * @param frameWidth + * The width of a tile on the texture in pixels. + * @param frameHeight + * The height of a tile on the texture in pixels. + */ + public void set(int frameWidth, int frameHeight) + { + this.frameWidth = frameWidth; + this.frameHeight = frameHeight; + } + + /** + * Creates a new array of tiles given the source texture. If the number of + * offsets or the frame dimensions is less then one then null is returned. + * The tiles returned will all have the same dimensions but different + * locations according the the offsets given (in the same order added). + * For example: + *
+	 * TileFactory f = new TileFactorySizeFixed();
+	 * f.frameWidth = 4;
+	 * f.frameHeight = 3;
+	 * f.addFrame(0, 0);
+	 * f.addFrame(6, 0);
+	 * f.addFrame(2, 5);
+	 * f.addFrame(5, 3);
+	 * 
+	 * Texture:
+	 * x--------------x
+	 * |+--+  +--+    |
+	 * ||0 |  |1 |    |
+	 * |+--+  +--+    |
+	 * |     +--+     |
+	 * |     |3 |     |
+	 * |  +--+--+     |
+	 * |  |2 |        |
+	 * |  +--+        |
+	 * x--------------x
+	 * 
+	 * 
+ */ + public Tile[] create(Texture ... textures) + { + // If the number of offsets or the frame dimensions is less then 1 + // then this factory does not have sufficient information. + if (offsets.size() < 1 || frameWidth < 1 || frameHeight < 1) + return null; + + int frames = offsets.size(); + + // A tile for each offset + Tile[] tiles = new Tile[frames]; + + // Start at the 0th frame. + int frame = 0; + Vec2i offset; + + while (frame < frames) + { + offset = offsets.get(frame); + // Create the tile based on the current offset + tiles[frame] = textures[0].tile(offset.x, offset.y, frameWidth, frameHeight); + + // Increment the number of frames. + frame++; + } + + return tiles; + } + + /** + * Clears all existing frame offsets from the factory. + */ + public void clear() + { + offsets.clear(); + } + + /** + * Adds the offset of the frame to the factory list. + */ + public void addFrame(Vec2i offset) + { + offsets.add(offset); + } + + /** + * Adds the offset of the frame to the factory list. + */ + public void addFrame(int x, int y) + { + offsets.add(new Vec2i(x, y)); + } + + @Override + public void read( InputModel input ) + { + frameWidth = input.readInt( "frame-width" ); + frameHeight = input.readInt( "frame-height" ); + offsets = input.readModelList( "offset", Vec2i.class ); + } + + @Override + public void write( OutputModel output ) + { + output.write( "frame-width", frameWidth ); + output.write( "frame-height", frameHeight ); + output.writeModelList( "offset", offsets ); + } + +} diff --git a/src/com/axe/tile/factory/TileFactoryGrid.java b/src/com/axe/tile/factory/TileFactoryGrid.java new file mode 100755 index 0000000..144656d --- /dev/null +++ b/src/com/axe/tile/factory/TileFactoryGrid.java @@ -0,0 +1,231 @@ +package com.axe.tile.factory; + +import com.axe.gfx.Texture; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.tile.Tile; +import com.axe.tile.TileFactory; + +/** + * A factory that takes a texture and generates an array of immutable tiles + * taken from the texture in a grid-like fashion. + * + * @author Philip Diffenderfer + * + */ +public class TileFactoryGrid implements TileFactory +{ + + /** + * The offset (on the x-axis) of the grid on the texture in pixels + */ + public int x; + + /** + * The offset (on the y-axis) of the grid on the texture in pixels + */ + public int y; + + /** + * The number of columns on the grid. + */ + public int columns; + + /** + * The total number of frames on the grid. + */ + public int frames; + + /** + * The width of a tile on the grid in pixels. + */ + public int frameWidth; + + /** + * The height of a tile on the grid in pixels. + */ + public int frameHeight; + + /** + * Whether the tiles generated should lie on the pixel centers (false) or + * exactly on the pixel edge (true). + */ + public boolean exact = false; + + /** + * Instantiates a new TileFactoryGrid. + */ + public TileFactoryGrid() + { + + } + + /** + * Instantiates a new TileFactoryGrid. + * + * @param x + * The offset (on the x-axis) of the grid on the texture in pixels + * @param y + * The offset (on the y-axis) of the grid on the texture in pixels + * @param columns + * The number of columns on the grid. + * @param frames + * The total number of frames on the grid. + * @param frameWidth + * The width of a tile on the grid in pixels. + * @param frameHeight + * The height of a tile on the grid in pixels. + */ + public TileFactoryGrid(int columns, int frames, int frameWidth, int frameHeight) + { + set(0, 0, columns, frames, frameWidth, frameHeight); + } + + /** + * Instantiates a new TileFactoryGrid. + * + * @param x + * The offset (on the x-axis) of the grid on the texture in pixels + * @param y + * The offset (on the y-axis) of the grid on the texture in pixels + * @param columns + * The number of columns on the grid. + * @param frames + * The total number of frames on the grid. + * @param frameWidth + * The width of a tile on the grid in pixels. + * @param frameHeight + * The height of a tile on the grid in pixels. + */ + public TileFactoryGrid(int x, int y, int columns, int frames, int frameWidth, int frameHeight) + { + set(x, y, columns, frames, frameWidth, frameHeight); + } + + /** + * Sets the factories parameters. + * + * @param x + * The offset (on the x-axis) of the grid on the texture in pixels + * @param y + * The offset (on the y-axis) of the grid on the texture in pixels + * @param columns + * The number of columns on the grid. + * @param frames + * The total number of frames on the grid. + * @param frameWidth + * The width of a tile on the grid in pixels. + * @param frameHeight + * The height of a tile on the grid in pixels. + */ + public void set(int x, int y, int columns, int frames, int frameWidth, int frameHeight) + { + this.x = x; + this.y = y; + this.columns = columns; + this.frames = frames; + this.frameWidth = frameWidth; + this.frameHeight = frameHeight; + } + + /** + * Creates a new array of tiles given the source texture. If the number of + * columns, frames, or the frame dimensions are less then one then null is + * returned. The array of tiles returned will be translated from the grid + * where the upper left frame of the grid is the first tile and following + * tiles are the following frames in the row, until a row is exhausted in + * which case the row below is deemed the next row for traversal, until all + * frames have been created. For example: + *
+	 * TileFactory f = new TileFactoryGrid();
+	 * f.x = 5;
+	 * f.y = 2;
+	 * f.frames = 13;
+	 * f.columns = 5;
+	 * f.frameWidth = 4;
+	 * f.frameHeight = 3;
+	 * 
+	 * Texture:
+	 * x-----------------------x
+	 * |                       |
+	 * |                       |
+	 * |     +--+--+--+--+--+  |
+	 * |     |0 |1 |2 |3 |4 |  |
+	 * |     +--+--+--+--+--+  |
+	 * |     |5 |6 |7 |8 |9 |  |
+	 * |     +--+--+--+--+--+  |
+	 * |     |10|11|12|        |
+	 * |     +--+--+--+        |
+	 * x-----------------------x
+	 * 
+ */ + @Override + public Tile[] create(Texture... textures) + { + // If the number of columns, frames, or frame dimensions is less then 1 + // then this factory does not have sufficient information. + if (columns < 1 || frames < 1 || frameWidth < 1 || frameHeight < 1) + return null; + + // A tile for each frame + Tile[] tiles = new Tile[frames]; + + // Calculate the right side of the grid in pixels. + final int gridRight = (frameWidth * columns) + x; + + // Start at the 0th frame and with the given offset. + int frame = 0; + int frameX = x; + int frameY = y; + + while (frame < frames) + { + // Create the tile based on the current frame + if (exact) + { + tiles[frame++] = textures[0].tileExact(frameX, frameY, frameWidth, frameHeight); + } + else + { + tiles[frame++] = textures[0].tile(frameX, frameY, frameWidth, frameHeight); + } + + // Move the x-coordinate of the frame over by the width of a frame. + frameX += frameWidth; + + // If the x-coordinate of the next frame is at the grid's right + // coordinate then restart the x-coordinate and move down to the + // next row. + if (frameX == gridRight) + { + frameX = x; + frameY += frameHeight; + } + } + + return tiles; + } + + @Override + public void read( InputModel input ) + { + x = input.readInt( "offset-x" ); + y = input.readInt( "offset-y" ); + columns = input.readInt( "columns" ); + frames = input.readInt( "frames" ); + frameWidth = input.readInt( "frame-width" ); + frameHeight = input.readInt( "frame-height" ); + } + + @Override + public void write( OutputModel output ) + { + output.write( "offset-x", x ); + output.write( "offset-y", y ); + output.write( "columns", columns ); + output.write( "frames", frames ); + output.write( "frame-width", frameWidth ); + output.write( "frame-height", frameHeight ); + } + +} diff --git a/src/com/axe/tile/factory/TileFactoryStatic.java b/src/com/axe/tile/factory/TileFactoryStatic.java new file mode 100755 index 0000000..87c0013 --- /dev/null +++ b/src/com/axe/tile/factory/TileFactoryStatic.java @@ -0,0 +1,132 @@ +package com.axe.tile.factory; + +import com.axe.gfx.Texture; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.tile.Tile; +import com.axe.tile.TileFactory; + +/** + * TODO + * + * @author Philip Diffenderfer + * + */ +public class TileFactoryStatic implements TileFactory +{ + + /** + * The offset (on the x-axis) of the single tile on the texture in pixels + */ + public int x; + + /** + * The offset (on the y-axis) of the single tile on the texture in pixels + */ + public int y; + + /** + * The width of a tile on the texture in pixels. + */ + public int width; + + /** + * The height of a tile on the texture in pixels. + */ + public int height; + + + /** + * Instantiates a new TileFactoryStatic. + */ + public TileFactoryStatic() + { + + } + + /** + * Instantiates a new TileFactoryStatic. + * + * @param x + * The offset (on the x-axis) of the grid on the texture in pixels + * @param y + * The offset (on the y-axis) of the grid on the texture in pixels + * @param frameWidth + * The width of a tile on the grid in pixels. + * @param frameHeight + * The height of a tile on the grid in pixels. + */ + public TileFactoryStatic(int x, int y, int frameWidth, int frameHeight) + { + set(x, y, frameWidth, frameHeight); + } + + /** + * Sets the factories parameters. + * + * @param x + * The offset (on the x-axis) of the grid on the texture in pixels + * @param y + * The offset (on the y-axis) of the grid on the texture in pixels + * @param frameWidth + * The width of a tile on the grid in pixels. + * @param frameHeight + * The height of a tile on the grid in pixels. + */ + public void set(int x, int y, int frameWidth, int frameHeight) + { + this.x = x; + this.y = y; + this.width = frameWidth; + this.height = frameHeight; + } + + /** + * Creates a new array with a single tile given the source texture. If the + * frame dimensions are less then one then null is returned. For example: + *
+	 * TileFactory f = new TileFactoryStatic();
+	 * f.x = 4;
+	 * f.y = 1;
+	 * f.width = 4;
+	 * f.height = 3;
+	 * 
+	 * Texture:
+	 * x---------------x
+	 * |               |
+	 * |    +--+       |
+	 * |    |0 |       |
+	 * |    +--+       |
+	 * |               |
+	 * x---------------x
+	 * 
+ */ + public Tile[] create(Texture ... textures) + { + // If the frame dimensions are less then 1 then this factory does not + // have sufficient information. + if (width < 1 || height < 1) + return null; + + return new Tile[] { textures[0].tile(x, y, width, height) }; + } + + @Override + public void read( InputModel input ) + { + x = input.readInt( "x" ); + y = input.readInt( "y" ); + width = input.readInt( "width" ); + height = input.readInt( "height" ); + } + + @Override + public void write( OutputModel output ) + { + output.write( "x", x ); + output.write( "y", y ); + output.write( "width", width ); + output.write( "height", height ); + } + +} diff --git a/src/com/axe/tile/factory/TileFactoryTexture.java b/src/com/axe/tile/factory/TileFactoryTexture.java new file mode 100755 index 0000000..2a3342e --- /dev/null +++ b/src/com/axe/tile/factory/TileFactoryTexture.java @@ -0,0 +1,47 @@ +package com.axe.tile.factory; + +import com.axe.gfx.Texture; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.tile.Tile; +import com.axe.tile.TileFactory; + +/** + * TODO + * + * @author Philip Diffenderfer + * + */ +public class TileFactoryTexture implements TileFactory +{ + + /** + * TODO + */ + public Tile[] create(Texture... textures) + { + int count = textures.length; + + Tile[] tiles = new Tile[count]; + + for (int i = 0; i < count; i++) + { + tiles[i] = textures[i].tile(); + } + + return tiles; + } + + @Override + public void read( InputModel input ) + { + + } + + @Override + public void write( OutputModel output ) + { + + } + +} diff --git a/src/com/axe/tile/factory/TileFactoryVariable.java b/src/com/axe/tile/factory/TileFactoryVariable.java new file mode 100755 index 0000000..a566e64 --- /dev/null +++ b/src/com/axe/tile/factory/TileFactoryVariable.java @@ -0,0 +1,129 @@ +package com.axe.tile.factory; + +import java.util.ArrayList; +import java.util.List; + +import com.axe.gfx.Texture; +import com.axe.io.InputModel; +import com.axe.io.OutputModel; +import com.axe.math.Rect2i; +import com.axe.math.Vec2i; +import com.axe.tile.Tile; +import com.axe.tile.TileFactory; + + + +/** + * TODO + * + * @author Philip Diffenderfer + * + */ +public class TileFactoryVariable implements TileFactory +{ + + // The list of frames. + private List frames = new ArrayList(); + + /** + * Creates a new array of tiles given the source texture. If the number of + * offsets or the frame dimensions is less then one then null is returned. + * The tiles returned will all have the same dimensions but different + * locations according the the offsets given (in the same order added). + * For example: + *
+	 * TileFactory f = new TileFactorySizeVariable();
+	 * f.addFrame(0, 0, 3, 3);
+	 * f.addFrame(6, 0, 6, 3);
+	 * f.addFrame(2, 5, 4, 3);
+	 * f.addFrame(5, 3, 4, 4);
+	 * 
+	 * Texture:
+	 * x--------------x
+	 * |+-+   +----+  |
+	 * ||0|   |1   |  |
+	 * |+-+   +----+  |
+	 * |     +--+     |
+	 * |     |3 |     |
+	 * |  +--+  |     |
+	 * |  |2 +--+     |
+	 * |  +--+        |
+	 * x--------------x
+	 * 
+	 * 
+ */ + public Tile[] create(Texture ... textures) + { + // If the number of offsets or the frame dimensions is less then 1 + // then this factory does not have sufficient information. + if (frames.size() < 1) + return null; + + int frameCount = frames.size(); + + // A tile for each offset + Tile[] tiles = new Tile[frameCount]; + + // Start at the 0th frame. + int frame = 0; + Rect2i f; + + while (frame < frameCount) + { + f = frames.get( frame ); + + // Create the tile based on the current offset + tiles[frame] = textures[0].tile(f.x, f.y, f.x, f.y); + + // Increment the number of frames. + frame++; + } + + return tiles; + } + + /** + * Clears all existing frame offsets from the factory. + */ + public void clear() + { + frames.clear(); + } + + /** + * Adds the offset of the frame to the factory list. + */ + public void addFrame(Vec2i offset, Vec2i size) + { + frames.add( new Rect2i(offset, size) ); + } + + /** + * Adds the offset of the frame to the factory list. + */ + public void addFrame(Vec2i offset, int width, int height) + { + frames.add( new Rect2i( offset.x, offset.y, width, height ) ); + } + + /** + * Adds the offset of the frame to the factory list. + */ + public void addFrame(int x, int y, int width, int height) + { + frames.add( new Rect2i( x, y, width, height) ); + } + + @Override + public void read( InputModel input ) + { + frames = input.readModelList( "frame", Rect2i.class ); + } + + @Override + public void write( OutputModel output ) + { + output.writeModelList( "frame", frames ); + } + +} diff --git a/src/com/axe/ui/Anchor.java b/src/com/axe/ui/Anchor.java new file mode 100644 index 0000000..602dccf --- /dev/null +++ b/src/com/axe/ui/Anchor.java @@ -0,0 +1,22 @@ +package com.axe.ui; + +public class Anchor { + public float base; + public float anchor; + + public void set(float base, float anchor) { + this.base = base; + this.anchor = anchor; + } + + public void set(float value, boolean relative) { + this.base = relative ? 0 : value; + this.anchor = relative ? value : 0; + } + + public float get(float total) + { + return base + total * anchor; + } + +} \ No newline at end of file diff --git a/src/com/axe/ui/Placement.java b/src/com/axe/ui/Placement.java new file mode 100644 index 0000000..e5e1c3f --- /dev/null +++ b/src/com/axe/ui/Placement.java @@ -0,0 +1,174 @@ +package com.axe.ui; + +import com.axe.math.Bound2f; +import com.axe.math.Bound2i; + +public class Placement +{ + public Anchor left = new Anchor(); + public Anchor right = new Anchor(); + public Anchor top = new Anchor(); + public Anchor bottom = new Anchor(); + + public Placement() + { + maximize(); + } + + public void maximize() + { + relative(0, 0, 1, 1); + } + + public void center(float width, float height) + { + attach(0.5f, 0.5f, width, height); + } + + public void attach(float dx, float dy, float width, float height) + { + left.set(width * -dx, dx); + right.set(width * (1 - dx), dx); + bottom.set(height * -dy, dy); + top.set(height * (1 - dy), dy); + } + + public void relative(float leftAnchor, float topAnchor, float rightAnchor, float bottomAnchor) + { + left.set(0, leftAnchor); + top.set(0, topAnchor); + right.set(0, rightAnchor); + bottom.set(0, bottomAnchor); + } + + public Bound2f getBounds(float parentWidth, float parentHeight, Bound2f out) + { + out.l = left.get(parentWidth); + out.t = top.get(parentHeight); + out.r = right.get(parentWidth); + out.b = bottom.get(parentHeight); + return out; + } + + public Bound2i getBounds(float parentWidth, float parentHeight, Bound2i out) + { + out.l = (int)left.get(parentWidth); + out.t = (int)top.get(parentHeight); + out.r = (int)right.get(parentWidth); + out.b = (int)bottom.get(parentHeight); + return out; + } + + public boolean contains(float x, float y, float parentWidth, float parentHeight) + { + return !( + x < left.get( parentWidth ) || + y > top.get( parentHeight ) || + x > right.get( parentWidth ) || + y < bottom.get( parentHeight ) + ); + } + + public float getWidth(float parentWidth) + { + return getWidth(parentWidth, 0); + } + + public float getWidth(float parentWidth, float minWidth) + { + return Math.max(minWidth, right.get(parentWidth) - left.get(parentWidth)); + } + + public float getHeight(float parentHeight) + { + return getHeight(parentHeight, 0); + } + + public float getHeight(float parentHeight, float minHeight) + { + return Math.max(minHeight, top.get(parentHeight) - bottom.get(parentHeight)); + } + + + public int getWidth(int parentWidth) + { + return getWidth(parentWidth, 0); + } + + public int getWidth(int parentWidth, int minWidth) + { + return (int)Math.max(minWidth, right.get(parentWidth) - left.get(parentWidth)); + } + + public int getHeight(int parentHeight) + { + return getHeight(parentHeight, 0); + } + + public int getHeight(int parentHeight,int minHeight) + { + return (int)Math.max(minHeight, top.get(parentHeight) - bottom.get(parentHeight)); + } + + public float getLeft(float parentWidth) + { + return left.get(parentWidth); + } + + public int getLeft(int parentWidth) + { + return (int)left.get(parentWidth); + } + + public float getTop(float parentHeight) + { + return top.get(parentHeight); + } + + public int getTop(int parentHeight) + { + return (int)top.get(parentHeight); + } + + public float getRight(float parentWidth) + { + return right.get(parentWidth); + } + + public int getRight(int parentWidth) + { + return (int)right.get(parentWidth); + } + + public float getBottom(float parentHeight) + { + return bottom.get(parentHeight); + } + + public int getBottom(int parentHeight) + { + return (int)bottom.get(parentHeight); + } + + public static Placement maximized() + { + Placement p = new Placement(); + p.maximize(); + return p; + } + + public static Placement centered(float width, float height) + { + Placement p = new Placement(); + p.center(width, height); + return p; + } + + public static Placement attached(float dx, float dy, float width, float height) + { + Placement p = new Placement(); + p.attach(dx, dy, width, height); + return p; + } + +} \ No newline at end of file diff --git a/src/com/axe/util/Array.java b/src/com/axe/util/Array.java new file mode 100755 index 0000000..7954257 --- /dev/null +++ b/src/com/axe/util/Array.java @@ -0,0 +1,332 @@ +package com.axe.util; + +import java.util.Arrays; +import java.util.Collection; + +import com.axe.core.Attribute; +import com.axe.core.Factory; +import com.axe.mem.Memory; + +public class Array +{ + + public static > T[] clone( T[] template ) + { + T[] cloneArray = Arrays.copyOf( template, template.length ); + + for ( int i = 0; i < template.length; i++ ) + { + cloneArray[i] = template[i].clone(); + } + + return cloneArray; + } + + public static T[] fromCollection( Collection items ) + { + return items.toArray( (T[])new Object[ items.size()] ); + } + + public static T[] add(T e, T[] elements) + { + int size = elements.length; + elements = Arrays.copyOf(elements, size + 1); + elements[size] = e; + return elements; + } + + public static T[] add(T[] base, T ... adding) + { + T[] result = Arrays.copyOf( base, base.length + adding.length ); + System.arraycopy( adding, 0, result, base.length, adding.length ); + return result; + } + + public static float[] add(float e, float[] elements) + { + int size = elements.length; + elements = Arrays.copyOf(elements, size + 1); + elements[size] = e; + return elements; + } + + public static int[] add(int e, int[] elements) + { + int size = elements.length; + elements = Arrays.copyOf(elements, size + 1); + elements[size] = e; + return elements; + } + + public static boolean[] add(boolean e, boolean[] elements) + { + int size = elements.length; + elements = Arrays.copyOf(elements, size + 1); + elements[size] = e; + return elements; + } + + public static void insert( int index, T e, T[] elements ) + { + System.arraycopy( elements, index, elements, index + 1, elements.length - index ); + + elements[ index ] = e; + } + + public static T remove( int index, T[] elements ) + { + T e = elements[ index ]; + + System.arraycopy( elements, index - 1, elements, index, elements.length - index ); + + return e; + } + + public static int indexOf(T e, T[] elements) + { + for (int i = 0; i < elements.length; i++) { + if (elements[i] == e) { + return i; + } + } + return -1; + } + + public static T[] remove(T e, T[] elements) + { + int i = indexOf(e, elements); + if (i >= 0) { + int size = elements.length - 1; + elements[i] = elements[size]; + elements = Arrays.copyOf(elements, size); + } + return elements; + } + + public static > T[] copy(T[] in, T[] out) + { + for (int i = 0; i < out.length; i++) + { + if (out[i] == null) + { + out[i] = in[i].clone(); + } + else + { + out[i].set( in[i] ); + } + } + + return out; + } + + public static T[] select( T[] elements, int ... indices ) + { + T[] copy = Arrays.copyOf( elements, indices.length ); + + for (int i = 0; i < indices.length; i++) + { + copy[i] = elements[ indices[i] ]; + } + + return copy; + } + + public static > T[] select( T[] elements, E ... enumConstants ) + { + T[] copy = Arrays.copyOf( elements, enumConstants.length ); + + for (int i = 0; i < enumConstants.length; i++) + { + copy[i] = elements[ enumConstants[i].ordinal() ]; + } + + return copy; + } + + public static T[] selectRanges( T[] elements, int ... indices ) + { + int total = 0, current = 0; + int[] dir = new int[ indices.length >> 1 ]; + + for ( int i = 0; i < indices.length; i+=2 ) + { + int k = i >> 1; + int d = indices[ i + 1 ] - indices[ i ]; + dir[ k ] = Integer.signum( d ); + total += d; + } + + T[] copy = Arrays.copyOf( elements, total ); + + for ( int i = 0; i < indices.length; i+=2 ) + { + for (int k = indices[i]; k != indices[i + 1]; k += dir[ i >> 1 ] ) + { + copy[ current++ ] = elements[ k ]; + } + copy[ current++ ] = elements[ indices[i + 1] ]; + } + + return copy; + } + + public static T[] allocate( T[] array, ArrayAllocator allocator ) + { + for (int i = 0; i < array.length; i++) + { + array[i] = allocator.allocate( i ); + } + return array; + } + + public static T[] allocate( int size, Attribute template ) + { + return allocate( (T[])new Object[size], template ); + } + + public static T[] allocate( T[] array, Attribute template ) + { + for (int i = 0; i < array.length; i++) + { + array[i] = template.clone(); + } + + return array; + } + + public static T[] allocate( int size, Factory factory ) + { + return allocate( (T[])new Object[size], factory ); + } + + public static T[] allocate( T[] array, Factory factory ) + { + for (int i = 0; i < array.length; i++) + { + array[i] = factory.create(); + } + + return array; + } + + public static T[] allocate( int size, Memory memory ) + { + return allocate( (T[])new Object[size], memory ); + } + + public static T[] allocate( T[] array, Memory memory ) + { + for (int i = 0; i < array.length; i++) + { + array[i] = memory.alloc(); + } + + return array; + } + + public static T[] place(int relativeIndex, T value, T[] array) + { + if (relativeIndex < 0) + { + array = Arrays.copyOf( array, array.length - relativeIndex ); + System.arraycopy( array, 0, array, -relativeIndex, array.length + relativeIndex ); + Arrays.fill( array, 0, -relativeIndex - 1, null ); + array[0] = value; + } + else if (relativeIndex >= array.length) + { + array = Arrays.copyOf( array, relativeIndex + 1 ); + array[relativeIndex] = value; + } + else + { + array[relativeIndex] = value; + } + + return array; + } + + public static T[] trim(T[] array) + { + int tn = trailingNulls( array ); + + if (tn > 0) { + array = Arrays.copyOf( array, array.length - tn ); + } + + int ln = leadingNulls( array ); + + if (ln > 0) { + int z = array.length - ln; + System.arraycopy( array, ln, array, 0, z ); + array = Arrays.copyOf( array, z ); + } + + return array; + } + + public static int leadingNulls(T[] input) + { + int nullCount = 0; + + while (nullCount < input.length && input[nullCount] == null) + { + nullCount++; + } + + return nullCount; + } + + public static int trailingNulls(T[] input) + { + int n = input.length; + int nullCount = 0; + + while (nullCount < input.length && input[--n] == null) + { + nullCount++; + } + + return nullCount; + } + + public static T[] resize( T[] array ) + { + return resize( array, 1 ); + } + + public static T[] resize( T[] array, int minimumLength ) + { + int length = Math.max( minimumLength, array.length + (array.length >> 1) ); + + return Arrays.copyOf( array, length ); + } + + public static T[] duplify( T[] array ) + { + return duplify( array, 1 ); + } + + public static T[] duplify( T[] array, int minimumLength ) + { + int length = Math.max( minimumLength, array.length << 1 ); + + return Arrays.copyOf( array, length ); + } + + public static T[] put( T[] array, int i, T item, int increaseBy ) + { + if (i >= array.length) + { + int newCapacity = Math.min( i + 1, array.length + increaseBy ); + + array = Arrays.copyOf( array, newCapacity ); + } + + array[ i ] = item; + + return array; + } + +} diff --git a/src/com/axe/util/ArrayAllocator.java b/src/com/axe/util/ArrayAllocator.java new file mode 100755 index 0000000..95f9427 --- /dev/null +++ b/src/com/axe/util/ArrayAllocator.java @@ -0,0 +1,7 @@ +package com.axe.util; + + +public interface ArrayAllocator +{ + public T allocate( int index ); +} diff --git a/src/com/axe/util/AttributeHistory.java b/src/com/axe/util/AttributeHistory.java new file mode 100755 index 0000000..3303af9 --- /dev/null +++ b/src/com/axe/util/AttributeHistory.java @@ -0,0 +1,293 @@ + +package com.axe.util; + +import com.axe.core.Attribute; +import com.axe.easing.Easing; +import com.axe.easing.Easings; +import com.axe.math.calc.Calculator; + + +/** + * A circular queue of attributes added at a specific time. New attributes can + * be added given a time, where time is expected to be non-zero, increasing, and + * will never overflow. An attribute value can be calculated any point in time + * within the history using an interpolation function with the Easing class (by + * default {@link Easings#Default}). Attributes can be inserted at a specific + * time however this is not recommended. + * + * @author Philip Diffenderfer + * + * @param + * The attribute type. + */ +public class AttributeHistory +{ + + private final Calculator calculator; + private final int capacity; + private T[] history; + private long[] times; + private int index; + + public AttributeHistory( Calculator calculator, int historySize, T ... initial ) + { + this.calculator = calculator; + this.capacity = historySize; + this.history = calculator.createArray( historySize, initial ); + this.times = new long[ capacity ]; + } + + public void clear( long time, T value ) + { + for (int i = 0; i < capacity; i++) + { + calculator.copy( history[i], value ); + times[i] = time; + } + } + + public void insertOrAdd( long time, T value ) + { + if (!insert( time, value )) + { + add( time, value ); + } + } + + public boolean insert( long time, T value ) + { + int j = getBeforeIndex( time ); + + if (j == -1) + { + return false; + } + + int k = previous( index ); + int e = index; + + while (k != j) + { + calculator.copy( history[k], history[e] ); + times[e] = times[k]; + + e = k; + k = previous( k ); + } + + calculator.copy( history[e], value ); + times[e] = time; + + return true; + } + + public void add( long time, T value ) + { + calculator.copy( history[index], value ); + times[index] = time; + + index = next( index ); + } + + public T get( long time, T attribute ) + { + return get( time, attribute, Easings.Default ); + } + + public T get( long time, T attribute, Easing easing ) + { + int j = getBeforeIndex( time ); + + if (j == -1) + { + return null; + } + + int i = next( j ); + + float d0 = (float)(time - times[j]) / (float)(times[i] - times[j]); + float d1 = easing.delta( d0 ); + + calculator.interpolate( attribute, history[j], history[i], d1 ); + + return attribute; + } + + public T getNearest( long time, Attribute attribute ) + { + int nearest = getNearestIndex( time ); + + if (nearest == -1) + { + return null; + } + + attribute.set( history[nearest] ); + + return attribute.get(); + } + + public long getNearestTime( long time, long empty ) + { + int nearest = getNearestIndex( time ); + + if (nearest == -1) + { + return empty; + } + + return times[nearest]; + } + + public boolean exists( long time ) + { + return (time >= firstTime() && time <= lastTime()); + } + + public int capacity() + { + return capacity; + } + + public int size() + { + int size = 0; + + for (int i = 0; i < capacity; i++) + { + if (times[i] != 0) + { + size++; + } + } + + return size; + } + + public long firstTime() + { + return times[index]; + } + + public T first() + { + return history[index]; + } + + public long lastTime() + { + return times[previous( index )]; + } + + public T last() + { + return history[previous( index )]; + } + + public long minTime() + { + return times[minIndex()]; + } + + public T min() + { + return history[minIndex()]; + } + + public long maxTime() + { + return times[maxIndex()]; + } + + public T max() + { + return history[maxIndex()]; + } + + private int getBeforeIndex( long time ) + { + final int last = previous( index ); + int i = index; + int j = index; + + while (i != last && time < times[i]) + { + j = i; + i = next( i ); + } + + if (i == j || time > times[i] || time < times[j]) + { + return -1; + } + + return j; + } + + private int getNearestIndex( long time ) + { + long nearest = Long.MAX_VALUE; + int nearestIndex = -1; + + for (int i = 0; i < capacity; i++) + { + if (times[i] != 0) + { + long dif = Math.abs( time - times[i] ); + + if (dif < nearest) + { + nearest = dif; + nearestIndex = i; + } + } + } + + return nearestIndex; + } + + private int minIndex() + { + long min = Long.MAX_VALUE; + int minIndex = -1; + + for (int i = 0; i < capacity; i++) + { + if (times[i] < min) + { + min = times[i]; + minIndex = i; + } + } + + return minIndex; + } + + private int maxIndex() + { + long max = Long.MIN_VALUE; + int maxIndex = -1; + + for (int i = 0; i < capacity; i++) + { + if (times[i] > max) + { + max = times[i]; + maxIndex = i; + } + } + + return maxIndex; + } + + private final int previous( int i ) + { + return (i + capacity - 1) % capacity; + } + + private final int next( int i ) + { + return (i + 1) % capacity; + } + +} diff --git a/src/com/axe/util/AttributeUtility.java b/src/com/axe/util/AttributeUtility.java new file mode 100755 index 0000000..d5d55cf --- /dev/null +++ b/src/com/axe/util/AttributeUtility.java @@ -0,0 +1,99 @@ +package com.axe.util; + +import com.axe.core.Attribute; +import com.axe.math.Numbers; +import com.axe.math.Vec2f; +import com.axe.math.Vec3f; + + +public class AttributeUtility +{ + + public static > A bilinear( Vec2f delta, A tl, A tr, A br, A bl, A temp, A out ) + { + return bilinear( delta.x, delta.y, tl, tr, br, bl, temp, out ); + } + + public static > A bilinear( Vec2f delta, A[] corners, A temp, A out ) + { + return bilinear( delta.x, delta.y, corners[0], corners[1], corners[2], corners[3], temp, out ); + } + + public static > A bilinear( float x, float y, A[] corners, A temp, A out ) + { + return bilinear( x, y, corners[0], corners[1], corners[2], corners[3], temp, out ); + } + + public static > A bilinear( float x, float y, A tl, A tr, A br, A bl, A temp, A out ) + { + temp.interpolate( tl, tr, x ); + out.interpolate( bl, br, x ); + out.interpolate( temp, out, y ); + + return out; + } + + public static > A trilinear( Vec3f delta, A ftl, A ftr, A fbl, A fbr, A btl, A btr, A bbl, A bbr, A temp0, A temp1, A temp2, A out ) + { + return trilinear( delta.x, delta.y, delta.z, ftl, ftr, fbl, fbr, btl, btr, bbl, bbr, temp0, temp1, temp2, out ); + } + + public static > A trilinear( Vec3f delta, A[] corners, A[] temp3, A out ) + { + return trilinear( delta.x, delta.y, delta.z, corners[0], corners[1], corners[2], corners[3], corners[4], corners[5], corners[6], corners[7], temp3[0], temp3[1], temp3[2], out ); + } + + public static > A trilinear( float x, float y, float z, A[] corners, A[] temp3, A out ) + { + return trilinear( x, y, z, corners[0], corners[1], corners[2], corners[3], corners[4], corners[5], corners[6], corners[7], temp3[0], temp3[1], temp3[2], out ); + } + + public static > A trilinear( float x, float y, float z, A ftl, A ftr, A fbl, A fbr, A btl, A btr, A bbl, A bbr, A temp0, A temp1, A temp2, A out ) + { + temp0.interpolate( fbl, bbl, z ); + temp1.interpolate( fbr, bbr, z ); + temp2.interpolate( ftl, btl, z ); + out.interpolate( ftr, btr, z ); + temp0.interpolate( temp0, temp1, x ); + temp2.interpolate( temp2, out, x ); + out.interpolate( temp0, temp2, y ); + + return out; + } + + public static > float getDistanceFromLine(T point, T start, T end) + { + float lineLength = start.distance( end ); + float startToPoint = point.distance( start ); + float endToPoint = point.distance( end ); + + return getTriangleHeight( lineLength, startToPoint, endToPoint ); + } + + public static > float getDistanceFromLine(T point, T start, T end, V temp) + { + temp.set( start ); + float lineLength = temp.distance( end ); + float startToPoint = temp.distance( point ); + temp.set( end ); + float endToPoint = temp.distance( point ); + + return getTriangleHeight( lineLength, startToPoint, endToPoint ); + } + + public static float getTriangleHeight(float base, float side1, float side2) + { + float p = (base + side1 + side2) * 0.5f; + float area = Numbers.sqrt( p * (p - base) * (p - side1) * (p - side2) ); + float height = area * 2.0f / base; + + return height; + } + + public static void euler(Attribute position, Attribute velocity, T acceleration, float time) + { + velocity.adds( acceleration, time ); + position.adds( velocity.get(), time ); + } + +} diff --git a/src/com/axe/util/Bitset.java b/src/com/axe/util/Bitset.java new file mode 100755 index 0000000..a64f632 --- /dev/null +++ b/src/com/axe/util/Bitset.java @@ -0,0 +1,215 @@ +package com.axe.util; + +import com.axe.core.Match; + +public class Bitset +{ + public long x = 0; + + public void clear() + { + x = 0; + } + + public void setIndex(int i, boolean on) + { + if (on) + { + x |= (1L << i); + } + else + { + x &= ~(1L << i); + } + } + + public > void set(E enumConstant, boolean on) + { + if (on) + { + x |= (1L << enumConstant.ordinal()); + } + else + { + x &= ~(1L << enumConstant.ordinal()); + } + } + + public void add(long y) + { + x |= y; + } + + public void addIf(long y, boolean condition) + { + if (condition) + { + x |= y; + } + } + + public > void add(E enumConstant) + { + x |= (1L << enumConstant.ordinal()); + } + + public > void addIf(E enumConstant, boolean condition) + { + if (condition) + { + x |= (1L << enumConstant.ordinal()); + } + } + + public void addIndex(int i) + { + x |= (1L << i); + } + + public void addIndexIf(int i, boolean condition) + { + if (condition) + { + x |= (1L << i); + } + } + + public void remove(long y) + { + x &= ~y; + } + + public void removeIf(long y, boolean condition) + { + if (condition) + { + x &= ~y; + } + } + + public > void remove(E enumConstant) + { + x &= ~(1L << enumConstant.ordinal()); + } + + public > void removeIf(E enumConstant, boolean condition) + { + if (condition) + { + x &= ~(1L << enumConstant.ordinal()); + } + } + + public void removeIndex(int i) + { + x &= ~(1L << i); + } + + public void removeIndexIf(int i, boolean condition) + { + if (condition) + { + x &= ~(1L << i); + } + } + + public void toggle(long y) + { + x ^= y; + } + + public void toggleIf(long y, boolean condition) + { + if (condition) + { + x ^= y; + } + } + + public > void toggle(E enumConstant) + { + x ^= (1L << enumConstant.ordinal()); + } + + public > void toggleIf(E enumConstant, boolean condition) + { + if (condition) + { + x ^= (1L << enumConstant.ordinal()); + } + } + + public void toggleIndex(int i) + { + x ^= (1L << i); + } + + public void toggleIndexIf(int i, boolean condition) + { + if (condition) + { + x ^= (1L << i); + } + } + + public boolean hasIndex(int i, Match match) + { + return has(1L << i, match); + } + + public boolean matchesIndex(int i) + { + return has(1L << i, Match.All); + } + + public boolean existsIndex(int i) + { + return has(1L << i, Match.AnyOf); + } + + public boolean equalsIndex(int i) + { + return has(1L << i, Match.Exact); + } + + public > boolean has(E enumConstant, Match match) + { + return match.isMatch(x, 1L << enumConstant.ordinal()); + } + + public > boolean matches(E enumConstant) + { + return has(1L << enumConstant.ordinal(), Match.All); + } + + public > boolean exists(E enumConstant) + { + return has(1L << enumConstant.ordinal(), Match.AnyOf); + } + + public > boolean equals(E enumConstant) + { + return has(1L << enumConstant.ordinal(), Match.Exact); + } + + public boolean has(long y, Match match) + { + return match.isMatch(x, y); + } + + public boolean matches(long y) + { + return has(y, Match.All); + } + + public boolean exists(long y) + { + return has(y, Match.AnyOf); + } + + public boolean equals(long y) + { + return has(y, Match.Exact); + } + +} \ No newline at end of file diff --git a/src/com/axe/util/Buffers.java b/src/com/axe/util/Buffers.java new file mode 100755 index 0000000..2d9eca7 --- /dev/null +++ b/src/com/axe/util/Buffers.java @@ -0,0 +1,63 @@ +package com.axe.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +public class Buffers +{ + + public static ShortBuffer shorts(int count) + { + return bytes(count << 1).asShortBuffer(); + } + + public static ShortBuffer shorts(short ... values) + { + return (ShortBuffer)shorts(values.length).put(values).flip(); + } + + public static IntBuffer ints(int count) + { + return bytes(count << 2).asIntBuffer(); + } + + public static IntBuffer ints(int ... values) + { + return (IntBuffer)ints(values.length).put(values).flip(); + } + + public static ByteBuffer bytes(int count) + { + return ByteBuffer.allocateDirect(count).order(ByteOrder.nativeOrder()); + } + + public static ByteBuffer bytes(byte[] values) + { + return (ByteBuffer)bytes(values.length).put(values, 0, values.length).flip(); + } + + public static FloatBuffer floats(int count) + { + return bytes(count << 2).asFloatBuffer(); + } + + public static FloatBuffer floats(float ... values) + { + return (FloatBuffer)floats(values.length).put(values).flip(); + } + + public static DoubleBuffer doubles(int count) + { + return bytes(count << 3).asDoubleBuffer(); + } + + public static DoubleBuffer doubles(double ... values) + { + return (DoubleBuffer)doubles(values.length).put(values).flip(); + } + +} \ No newline at end of file diff --git a/src/com/axe/util/DefaultLogger.java b/src/com/axe/util/DefaultLogger.java new file mode 100644 index 0000000..664359a --- /dev/null +++ b/src/com/axe/util/DefaultLogger.java @@ -0,0 +1,53 @@ +package com.axe.util; + +import java.io.PrintStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DefaultLogger implements Logger +{ + + private static final DateFormat dateFormat = new SimpleDateFormat("dd/MM/YYYY HH:mm:ss"); + + public PrintStream out; + + public DefaultLogger() + { + this( System.out ); + } + + public DefaultLogger(PrintStream out) + { + this.out = out; + } + + @Override + public void log(Object message, Object... arguments) + { + out.print( "[" ); + out.print( dateFormat.format(new Date()) ); + out.print( "] " ); + + if (message instanceof Exception) + { + ((Exception)message).printStackTrace(); + } + else if (message instanceof String) + { + if (arguments.length == 0) + { + out.println( message ); + } + else + { + out.format( message + "\n", arguments ); + } + } + else + { + out.println( message ); + } + } + +} diff --git a/src/com/axe/util/EnumIntBiMap.java b/src/com/axe/util/EnumIntBiMap.java new file mode 100644 index 0000000..30fc3f1 --- /dev/null +++ b/src/com/axe/util/EnumIntBiMap.java @@ -0,0 +1,43 @@ +package com.axe.util; + +import java.lang.reflect.Array; + +public class EnumIntBiMap> +{ + + private E[] enums; + private int[] ints; + + public EnumIntBiMap(Class enumClass, int maxInteger) + { + enums = (E[])Array.newInstance( enumClass, maxInteger + 1 ); + ints = new int[ enumClass.getEnumConstants().length ]; + } + + public void add(E enumeration, int integer) + { + enums[ integer ] = enumeration; + ints[ enumeration.ordinal() ] = integer; + } + + public int get(E enumeration) + { + return ints[ enumeration.ordinal() ]; + } + + public E get(int integer) + { + return enums[ integer ]; + } + + public boolean has(E enumeration) + { + return ints[ enumeration.ordinal() ] != 0; + } + + public boolean has(int integer) + { + return enums[ integer ] != null; + } + +} diff --git a/src/com/axe/util/EnumIntMap.java b/src/com/axe/util/EnumIntMap.java new file mode 100644 index 0000000..ca6f8fb --- /dev/null +++ b/src/com/axe/util/EnumIntMap.java @@ -0,0 +1,29 @@ +package com.axe.util; + + +public class EnumIntMap> +{ + + private int[] ints; + + public EnumIntMap(Class enumClass) + { + ints = new int[ enumClass.getEnumConstants().length ]; + } + + public void add(E enumeration, int integer) + { + ints[ enumeration.ordinal() ] = integer; + } + + public int get(E enumeration) + { + return ints[ enumeration.ordinal() ]; + } + + public boolean has(E enumeration) + { + return ints[ enumeration.ordinal() ] != 0; + } + +} diff --git a/src/com/axe/util/IntMap.java b/src/com/axe/util/IntMap.java new file mode 100755 index 0000000..0651f79 --- /dev/null +++ b/src/com/axe/util/IntMap.java @@ -0,0 +1,142 @@ +package com.axe.util; + + +public class IntMap +{ + + public static int DEFAULT_TABLE_SIZE = 16; + public static int DEFAULT_INVALID_VALUE = -1; + + private int size; + private final int invalidValue; + private final int mod; + private final Entry[] entries; + + public IntMap() + { + this( DEFAULT_TABLE_SIZE, DEFAULT_INVALID_VALUE ); + } + + public IntMap( int invalidValue ) + { + this( DEFAULT_TABLE_SIZE, invalidValue ); + } + + public IntMap( int tableSize, int invalidValue ) + { + this.mod = computeTableSize( tableSize ); + this.entries = (Entry[])new Object[ mod + 1 ]; + this.invalidValue = invalidValue; + } + + private int computeTableSize( int requested ) + { + return (Integer.highestOneBit( requested - 1 ) << 1) - 1; + } + + public int get( K key ) + { + int hash = key.hashCode() & mod; + + Entry e = entries[ hash ]; + + while ( e != null ) { + if ( e.key == key || e.key.equals( key ) ) { + return e.value; + } + e = e.next; + } + + return invalidValue; + } + + public boolean exists( K key ) + { + int hash = key.hashCode() & mod; + + Entry e = entries[ hash ]; + + while ( e != null ) { + if ( e.key == key || e.key.equals( key ) ) { + return true; + } + e = e.next; + } + + return false; + } + + public int put( K key, int value ) + { + int hash = key.hashCode() & mod; + + Entry e = entries[ hash ]; + + while ( e != null ) { + if ( e.key == key || e.key.equals( key ) ) { + int previous = e.value; + e.value = value; + return previous; + } + e = e.next; + } + + entries[ hash ] = new Entry( key, value, entries[ hash ] ); + size++; + + return invalidValue; + } + + public void add( K key, int value ) + { + int hash = key.hashCode() & mod; + + entries[ hash ] = new Entry( key, value, entries[ hash ] ); + size++; + } + + public int remove( K key ) + { + int hash = key.hashCode() & mod; + + Entry p = null; + Entry e = entries[ hash ]; + + while ( e != null ) { + if ( e.key == key || e.key.equals( key ) ) { + if ( p == null ) { + entries[ hash ] = e.next; + } + else { + p.next = e.next; + } + size--; + return e.value; + } + p = e; + e = e.next; + } + + return invalidValue; + } + + public int size() + { + return size; + } + + private class Entry + { + private K key; + private int value; + private Entry next; + + public Entry( K key, int value, Entry next ) + { + this.key = key; + this.value = value; + this.next = next; + } + } + +} diff --git a/src/com/axe/util/IntStack.java b/src/com/axe/util/IntStack.java new file mode 100755 index 0000000..7b908e4 --- /dev/null +++ b/src/com/axe/util/IntStack.java @@ -0,0 +1,70 @@ +package com.axe.util; + +import java.util.Arrays; + + +public class IntStack +{ + + public static final int DEFAULT_INITIAL_CAPACITY = 16; + + private int size; + private int[] stack; + + public IntStack( ) + { + this( DEFAULT_INITIAL_CAPACITY ); + } + + public IntStack( int initialCapacity ) + { + this.size = 0; + this.stack = new int[ initialCapacity ]; + } + + public int peek() + { + return peek( -1 ); + } + + public int peek( int returnOnEmpty ) + { + return ( size == 0 ? returnOnEmpty : stack[ size - 1 ] ); + } + + public int pop() + { + return pop( -1 ); + } + + public int pop( int returnOnEmpty ) + { + return ( size == 0 ? returnOnEmpty : stack[ --size ] ); + } + + public void push( int x ) + { + if ( size == stack.length ) + { + stack = Arrays.copyOf( stack, size << 1 ); + } + + stack[ size++ ] = x; + } + + public int get( int i ) + { + return stack[ i ]; + } + + public boolean hasInts() + { + return ( size > 0 ); + } + + public int size() + { + return size; + } + +} diff --git a/src/com/axe/util/Logger.java b/src/com/axe/util/Logger.java new file mode 100644 index 0000000..8295714 --- /dev/null +++ b/src/com/axe/util/Logger.java @@ -0,0 +1,9 @@ +package com.axe.util; + +@FunctionalInterface +public interface Logger +{ + + public void log(Object message, Object ... arguments); + +} diff --git a/src/com/axe/util/Ref.java b/src/com/axe/util/Ref.java new file mode 100644 index 0000000..5e78815 --- /dev/null +++ b/src/com/axe/util/Ref.java @@ -0,0 +1,24 @@ +package com.axe.util; + +public class Ref +{ + + public T value; + + public Ref() + { + } + + public Ref(T value) + { + this.value = value; + } + + public Ref set(T value) + { + this.value = value; + + return this; + } + +} diff --git a/src/com/axe/util/ReflectionFactory.java b/src/com/axe/util/ReflectionFactory.java new file mode 100755 index 0000000..364f047 --- /dev/null +++ b/src/com/axe/util/ReflectionFactory.java @@ -0,0 +1,52 @@ +package com.axe.util; + +import com.axe.core.Factory; +import com.axe.io.DataRegistry; + + +public class ReflectionFactory implements Factory +{ + + public final Class clazz; + + private ReflectionFactory( Class clazz ) + { + this.clazz = clazz; + } + + @Override + public T create() + { + try + { + return clazz.newInstance(); + } + catch (Exception e) + { + return null; + } + } + + public static ReflectionFactory fromClass( Class clazz ) + { + return new ReflectionFactory( clazz ); + } + + public static ReflectionFactory fromName( String className ) + { + try + { + return new ReflectionFactory( (Class)Class.forName( className ) ); + } + catch (Exception e) + { + return null; + } + } + + public static ReflectionFactory fromRegistry( String name ) + { + return new ReflectionFactory( (Class)DataRegistry.getClass( name ) ); + } + +} diff --git a/src/com/axe/util/Reflections.java b/src/com/axe/util/Reflections.java new file mode 100755 index 0000000..5815144 --- /dev/null +++ b/src/com/axe/util/Reflections.java @@ -0,0 +1,150 @@ + +package com.axe.util; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +public class Reflections +{ + + public static T[] getStaticFieldArray( Class type, boolean exact, Class from ) + { + List fieldList = getStaticFieldCollection( type, exact, from, new ArrayList() ); + + return fieldList.toArray( (T[])Array.newInstance( type, fieldList.size() ) ); + } + + public static > C getStaticFieldCollection( Class type, boolean exact, Class from, C destination ) + { + try + { + List fieldList = getStaticFields( type, exact, from ); + + for (Field f : fieldList) + { + destination.add( (T)f.get( null ) ); + } + + return destination; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static > M getStaticFieldMap( Class type, boolean exact, Class from, M destination ) + { + try + { + List fieldList = getStaticFields( type, exact, from ); + + for (Field f : fieldList) + { + destination.put( f.getName(), (T)f.get( null ) ); + } + + return destination; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static > M getStaticFieldMapInversed( Class type, boolean exact, Class from, M destination ) + { + try + { + List fieldList = getStaticFields( type, exact, from ); + + for (Field f : fieldList) + { + destination.put( (T)f.get( null ), f.getName() ); + } + + return destination; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException( e ); + } + } + + public static List getStaticFields( Class type, boolean exact, Class from ) + { + List fieldList = new ArrayList(); + Field[] fieldArray = from.getFields(); + + for (Field f : fieldArray) + { + if ((f.getModifiers() & Modifier.STATIC) == 0) + { + continue; + } + + if (exact) + { + if (type != f.getType()) + { + continue; + } + } + else + { + if (!type.isAssignableFrom( f.getType() )) + { + continue; + } + } + + fieldList.add( f ); + } + + return fieldList; + } + + public static List getFields( Class from, int avoidModifiers ) + { + List fieldList = new ArrayList(); + Field[] fieldArray = from.getFields(); + + for (Field f : fieldArray) + { + if (avoidModifiers != 0 && (f.getModifiers() & avoidModifiers) != 0) + { + continue; + } + + fieldList.add( f ); + } + + return fieldList; + } + + public static Field[] getFieldArray( Class from, int avoidModifiers ) + { + List fields = getFields( from, avoidModifiers ); + + return fields.toArray( new Field[ fields.size() ] ); + } + +} diff --git a/src/com/axe/util/Registry.java b/src/com/axe/util/Registry.java new file mode 100644 index 0000000..a794a44 --- /dev/null +++ b/src/com/axe/util/Registry.java @@ -0,0 +1,36 @@ + +package com.axe.util; + +import java.util.Arrays; + +public class Registry +{ + private int identifier; + private T[] items; + + public Registry( T... items ) + { + this.items = items; + } + + public int create() + { + return identifier++; + } + + public void register( int identifier, T item ) + { + if (identifier >= items.length) + { + items = Arrays.copyOf( items, identifier + 1 ); + } + + items[identifier] = item; + } + + public X get( int identifier ) + { + return (identifier >= items.length || identifier < 0 ? null : (X)items[identifier]); + } + +} diff --git a/src/com/axe/window/AbstractWindow.java b/src/com/axe/window/AbstractWindow.java new file mode 100644 index 0000000..64c6652 --- /dev/null +++ b/src/com/axe/window/AbstractWindow.java @@ -0,0 +1,280 @@ +package com.axe.window; + +import com.axe.View; +import com.axe.collect.ListArray; +import com.axe.collect.ListItem; +import com.axe.color.Color; +import com.axe.color.ColorInterface; +import com.axe.color.Colors; +import com.axe.event.EventMap; +import com.axe.input.MouseState; +import com.axe.math.Vec; +import com.axe.monitor.Monitor; +import com.axe.ui.Placement; + +public abstract class AbstractWindow implements Window +{ + + public ListItem listItem; + public EventMap events; + public ListArray views; + public View hoverView; + public View focusView; + public int width; + public int height; + public String title; + public boolean fullscreen; + public int frameRate; + public boolean vsync; + public Placement placement; + public Monitor monitor; + public boolean resizable; + public Color backgroundColor; + public MouseState mouse; + public Object data; + + public AbstractWindow() + { + this.events = new EventMap(); + this.views = ListArray.compacted(); + this.placement = new Placement(); + this.mouse = new MouseState(); + this.backgroundColor = Colors.Black.mutable(); + this.title = "Axengine Game"; + } + + protected abstract boolean isInitialized(); + protected abstract void applyTitle(String title); + protected abstract void applyFullscreen(boolean fullscreen); + protected abstract void applyVsync(boolean vsync); + protected abstract void applyFrameRate(int frameRate); + protected abstract void applyResizable(boolean resizable); + protected abstract void applyPlacement(Placement placement, Monitor monitor); + + @Override + public void updateMouse() + { + mouse.updateInside( width, height ); + + if (mouse.insideChange) + { + listeners( mouse.inside ? WindowEvent.MouseEnter : WindowEvent.MouseLeave ).onEvent( this ); + } + } + + @Override + public EventMap getEvents() + { + return events; + } + + @Override + public void setListItem(ListItem listItem) + { + this.listItem = listItem; + } + + @Override + public ListArray getViews() + { + return views; + } + + @Override + public MouseState getMouse() + { + return mouse; + } + + @Override + public T getData() + { + return (T) data; + } + + @Override + public void setData(Object data) + { + this.data = data; + } + + @Override + public , W extends View> W getHoverView() + { + return (W)hoverView; + } + + @Override + public , W extends View> W getFocusView() + { + return (W)focusView; + } + + @Override + public void setTitle(String title) + { + if (this.title != title) + { + this.title = title; + + if (isInitialized()) + { + applyTitle(title); + } + } + } + + @Override + public String getTitle() + { + return title; + } + + @Override + public void setFullscreen(boolean fullscreen) + { + if (this.fullscreen != fullscreen) + { + this.fullscreen = fullscreen; + + if (isInitialized()) + { + applyFullscreen( fullscreen ); + } + } + } + + @Override + public boolean isFullscreen() + { + return fullscreen; + } + + @Override + public Color getBackground() + { + return backgroundColor; + } + + @Override + public void setBackground(ColorInterface backgroundColor) + { + this.backgroundColor.set( backgroundColor ); + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public int getHeight() + { + return height; + } + + @Override + public int getFrameWidth() + { + return width; + } + + @Override + public int getFrameHeight() + { + return height; + } + + @Override + public void setPlacement( Placement placement, Monitor monitor ) + { + this.placement = placement; + this.monitor = monitor; + + if (isInitialized()) + { + applyPlacement(placement, monitor); + } + } + + @Override + public Monitor getMonitor() + { + return monitor; + } + + @Override + public Placement getPlacement() + { + return placement; + } + + @Override + public void setVsync(boolean vsync) + { + if (this.vsync != vsync) + { + this.vsync = vsync; + + if (isInitialized()) + { + applyVsync( vsync ); + } + } + } + + @Override + public boolean isVsync() + { + return vsync; + } + + @Override + public int getFrameRate() + { + return frameRate; + } + + @Override + public void setFrameRate(int frameRate) + { + if (this.frameRate != frameRate) + { + this.frameRate = frameRate; + + if (isInitialized()) + { + applyFrameRate(frameRate); + } + } + } + + @Override + public void setResizable(boolean resizable) + { + if (this.resizable != resizable) + { + this.resizable = resizable; + + if (isInitialized()) + { + applyResizable(resizable); + } + } + } + + @Override + public boolean isResizable() + { + return resizable; + } + + @Override + public float getResolution() + { + return (float)getFrameWidth() / getWidth(); + } + + +} diff --git a/src/com/axe/window/Window.java b/src/com/axe/window/Window.java new file mode 100644 index 0000000..41adb1a --- /dev/null +++ b/src/com/axe/window/Window.java @@ -0,0 +1,103 @@ +package com.axe.window; + +import com.axe.Axe; +import com.axe.Scene; +import com.axe.View; +import com.axe.collect.HasListItem; +import com.axe.collect.ListArray; +import com.axe.color.Color; +import com.axe.color.ColorInterface; +import com.axe.core.HasData; +import com.axe.event.HasEvents; +import com.axe.game.Game; +import com.axe.game.GameState; +import com.axe.input.MouseState; +import com.axe.math.Vec; +import com.axe.monitor.Monitor; +import com.axe.ui.Placement; + +public interface Window extends HasEvents, HasListItem, HasData +{ + + public Game getGame(); + + public void update(); + + public void draw(GameState state); + + public boolean isFocused(); + + public boolean isHidden(); + + public boolean isCloseRequest(); + + public MouseState getMouse(); + + public void updateMouse(); + + public ListArray getViews(); + + public , W extends View> W newView(Scene scene); + + public , W extends View> W getHoverView(); + + public , W extends View> W getFocusView(); + + public void setTitle(String title); + + public String getTitle(); + + public void setFullscreen(boolean fullscreen); + + public boolean isFullscreen(); + + public Color getBackground(); + + public void setBackground(ColorInterface backgroundColor); + + public int getWidth(); + + public int getHeight(); + + public int getFrameWidth(); + + public int getFrameHeight(); + + public void setVsync(boolean vsync); + + public boolean isVsync(); + + public void setResizable(boolean resizable); + + public boolean isResizable(); + + default public void setPlacement( Placement placement ) + { + setPlacement( placement, Axe.monitors.getPrimary() ); + } + + public void setPlacement( Placement placement, Monitor monitor ); + + public Monitor getMonitor(); + + public Placement getPlacement(); + + public int getFrameRate(); + + public void setFrameRate(int frameRate); + + public float getResolution(); + + public void close(); + + public void hide(); + + public void show(); + + public void minimize(); + + public void maximize(); + + public void focus(); + +} \ No newline at end of file diff --git a/src/com/axe/window/WindowEvent.java b/src/com/axe/window/WindowEvent.java new file mode 100644 index 0000000..3c7ad7c --- /dev/null +++ b/src/com/axe/window/WindowEvent.java @@ -0,0 +1,41 @@ +package com.axe.window; + +import com.axe.event.Event; + +public class WindowEvent +{ + + public static final Event Resize = + new Event<>(0, WindowResizeEventListener.class); + + public static final Event Focus = + new Event<>(1, WindowEventListener.class); + + public static final Event Blur = + new Event<>(2, WindowEventListener.class); + + public static final Event CloseRequest = + new Event<>(3, WindowEventListener.class); + + public static final Event DrawStart = + new Event<>(4, WindowEventListener.class); + + public static final Event DrawEnd = + new Event<>(5, WindowEventListener.class); + + public static final Event Minimized = + new Event<>(6, WindowEventListener.class); + + public static final Event Restored = + new Event<>(7, WindowEventListener.class); + + public static final Event Maximized = + new Event<>(8, WindowEventListener.class); + + public static final Event MouseLeave = + new Event<>(9, WindowEventListener.class); + + public static final Event MouseEnter = + new Event<>(10, WindowEventListener.class); + +} \ No newline at end of file diff --git a/src/com/axe/window/WindowEventListener.java b/src/com/axe/window/WindowEventListener.java new file mode 100644 index 0000000..663cc59 --- /dev/null +++ b/src/com/axe/window/WindowEventListener.java @@ -0,0 +1,8 @@ +package com.axe.window; + +public interface WindowEventListener +{ + + public void onEvent(Window window); + +} \ No newline at end of file diff --git a/src/com/axe/window/WindowResizeEventListener.java b/src/com/axe/window/WindowResizeEventListener.java new file mode 100644 index 0000000..8f43733 --- /dev/null +++ b/src/com/axe/window/WindowResizeEventListener.java @@ -0,0 +1,8 @@ +package com.axe.window; + +public interface WindowResizeEventListener +{ + + public void onResize(Window window, int width, int height, int previousWidth, int previousHeight); + +} \ No newline at end of file diff --git a/src/com/axe2d/Camera2.java b/src/com/axe2d/Camera2.java new file mode 100644 index 0000000..42e7fcc --- /dev/null +++ b/src/com/axe2d/Camera2.java @@ -0,0 +1,85 @@ +package com.axe2d; + +import com.axe.AbstractCamera; +import com.axe.core.Attribute; +import com.axe.game.GameState; +import com.axe.math.Bound2f; +import com.axe.math.Scalarf; +import com.axe.math.Vec2f; +import com.axe.math.calc.CalculatorCamera2; +import com.axe.node.Node; + +public class Camera2 extends AbstractCamera implements Attribute +{ + + private static final Vec2f MIN = new Vec2f(); + private static final Vec2f MAX = new Vec2f(); + private static final Bound2f BOUNDS = new Bound2f(); + + public static final int SIZE = 28; + + public final Scalarf roll = new Scalarf(0f); + public final Vec2f center = new Vec2f(); + public final Vec2f scale = new Vec2f(1f, 1f); + public final Vec2f size = new Vec2f(); + public final Scalarf near = new Scalarf(0); + public final Scalarf far = new Scalarf(1); + public final Vec2f halfSizeScaled = new Vec2f(); // + public final Vec2f up = new Vec2f(); // + public final Vec2f right = new Vec2f(); // + public final Bound2f bounds = new Bound2f(); // + + // sceneBounds, scaleMax, scaleMin, sizeMax, sizeMin + // offset (from center) - for shake spring + // + + @Override + public void update() + { + float cosr = roll.cos(); + float sinr = roll.sin(); + + right.set(cosr, sinr); + up.set(-sinr, cosr); + bounds.quad(center.x, center.y, size.x, size.y, cosr, sinr); + + halfSizeScaled.set( size );; + halfSizeScaled.mul( scale ); + halfSizeScaled.scale( 0.5f ); + } + + @Override + public boolean intersects(GameState state, Node node) + { + if (node.getBounds(state, MIN, MAX)) + { + BOUNDS.set( MIN, MAX ); + + if ( !BOUNDS.intersects( bounds ) ) + { + return false; + } + } + + return true; + } + + @Override + public Camera2 get() + { + return this; + } + + @Override + public CalculatorCamera2 getCalculator() + { + return CalculatorCamera2.INSTANCE; + } + + @Override + public Camera2 clone() + { + return copy( create() ); + } + +} diff --git a/src/com/axe2d/Scene2.java b/src/com/axe2d/Scene2.java new file mode 100644 index 0000000..284bc7c --- /dev/null +++ b/src/com/axe2d/Scene2.java @@ -0,0 +1,9 @@ +package com.axe2d; + +import com.axe.AbstractScene; +import com.axe.math.Vec2f; + +public class Scene2 extends AbstractScene +{ + +} diff --git a/src/com/axe2d/View2.java b/src/com/axe2d/View2.java new file mode 100644 index 0000000..1b04bb5 --- /dev/null +++ b/src/com/axe2d/View2.java @@ -0,0 +1,167 @@ +package com.axe2d; + +import com.axe.AbstractView; +import com.axe.Camera; +import com.axe.Scene; +import com.axe.gfx.ProjectionOutside; +import com.axe.math.Bound2i; +import com.axe.math.Numbers; +import com.axe.math.Scalarf; +import com.axe.math.Vec2f; +import com.axe.math.Vec2i; +import com.axe.window.Window; +import com.axe2d.math.Matrix2; + +public abstract class View2 extends AbstractView +{ + + protected static final Bound2i BOUNDS = new Bound2i(); + protected static final Vec2f HALF = new Vec2f( 0.5f ); + + public Scene2 scene; + public Camera2 camera; + public Matrix2 projection; + public Matrix2 view; + public Matrix2 combined; + public Matrix2 inverted; + + public View2(Window window, Scene2 scene) + { + super( window ); + + this.scene = scene; + this.projection = new Matrix2(); + this.view = new Matrix2(); + this.combined = new Matrix2(); + this.inverted = new Matrix2(); + this.camera = new Camera2(); + this.camera.size.set( + this.placement.getWidth( window.getWidth() ), + this.placement.getHeight( window.getHeight() ) + ); + } + + @Override + public Matrix2 getProjectionMatrix() + { + return projection; + } + + @Override + public Matrix2 getViewMatrix() + { + return view; + } + + @Override + public Matrix2 getCombinedMatrix() + { + return combined; + } + + @Override + public void update() + { + final Vec2f size = camera.size; + final Vec2f halfSizeScaled = camera.halfSizeScaled; + final Vec2f scale = camera.scale; + final Vec2f center = camera.center; + final Scalarf roll = camera.roll; + + projection.ortho( 0, 0, size.x, size.y ); + + view.identity(); + view.translatei( halfSizeScaled ); + view.rotatei( roll.v ); + view.scalei( scale ); + view.translatei( -center.x, -center.y ); + + combined.set( projection ); + combined.multiplyi( view ); + combined.invert( inverted ); + } + + + @Override + public Vec2f project(Vec2i mouse, ProjectionOutside outside, Vec2f out) + { + final int windowWidth = window.getWidth(); + final int windowHeight = window.getHeight(); + final Bound2i view = placement.getBounds( windowWidth, windowHeight, BOUNDS ); + + out.x = (float)(mouse.x - view.l) / view.width(); + out.y = (float)((windowHeight - mouse.y) - view.b) / view.height(); + + if (!inProjection(out, outside)) + { + return null; + } + + out.sub( HALF ); + out.div( HALF ); + out.mul( camera.halfSizeScaled ); + out.rotate( camera.right ); + out.add( camera.center ); + + return out; + } + + @Override + public Vec2f unproject(Vec2f point, ProjectionOutside outside, Vec2f out) + { + final int windowWidth = window.getWidth(); + final int windowHeight = window.getHeight(); + final Bound2i view = placement.getBounds( windowWidth, windowHeight, BOUNDS ); + + out.set( point ); + out.sub( camera.center ); + out.rotater( camera.right ); + out.div( camera.halfSizeScaled ); + out.mul( HALF ); + out.add( HALF ); + + if (!inProjection(out, outside)) + { + return null; + } + + out.x = ((out.x * view.width()) + view.l); + out.y = windowHeight - (out.y * view.height() + view.b); + + return out; + } + + protected boolean inProjection(Vec2f out, ProjectionOutside outside) + { + if (out.x < 0 || out.y < 0 || out.x >= 1 || out.y >= 1) + { + switch (outside) { + case Ignore: + return false; + case Clamp: + out.x = Numbers.clamp( out.x, 0, 1 ); + out.y = Numbers.clamp( out.y, 0, 1 ); + break; + case Relative: + // No action necessary + break; + } + } + + return true; + } + + @Override + public > S getScene() + { + return (S)scene; + } + + @Override + public > C getCamera() + { + return (C)camera; + } + + +} diff --git a/src/com/axe2d/math/Matrix2.java b/src/com/axe2d/math/Matrix2.java new file mode 100644 index 0000000..5d04afb --- /dev/null +++ b/src/com/axe2d/math/Matrix2.java @@ -0,0 +1,547 @@ +package com.axe2d.math; + +import com.axe.math.Matrix; +import com.axe.math.Numbers; +import com.axe.math.Vec2f; +import com.axe.math.Vec3f; + +public class Matrix2 implements Matrix +{ + public static final int VALUES = 9; + + public static final int M00 = 0; + public static final int M01 = 3; + public static final int M02 = 6; + public static final int M10 = 1; + public static final int M11 = 4; + public static final int M12 = 7; + public static final int M20 = 2; + public static final int M21 = 5; + public static final int M22 = 8; + + private static float[] temp0 = new float[VALUES]; + private static float[] temp1 = new float[VALUES]; + + public float[] value = new float[VALUES]; + + public Matrix2() + { + this.identity(); + } + + public Matrix2(float[] value) + { + this.set(value); + } + + @Override + public Vec2f createVector() + { + return new Vec2f(); + } + + @Override + public Matrix2 identity() + { + setIdentity( this.value ); + return this; + } + + @Override + public float determinant() + { + float[] m = this.value; + + return m[M00] * m[M11] * m[M22] + + m[M01] * m[M12] * m[M20] + + m[M02] * m[M10] * m[M21] - + m[M00] * m[M12] * m[M21] - + m[M01] * m[M10] * m[M22] - + m[M02] * m[M11] * m[M20]; + } + + @Override + public Matrix2 ortho( Vec2f min, Vec2f max ) + { + return ortho( min.x, max.y, max.x, min.y ); + } + + public Matrix2 ortho(float left, float top, float right, float bottom) + { + float[] m = this.value; + float dx = (right - left); + float dy = (top - bottom); + float xo = 2 / dx; + float yo = 2 / dy; + float tx = -(right + left) / dx; + float ty = -(top + bottom) / dy; + + m[M00] = xo; + m[M10] = 0; + m[M20] = 0; + + m[M01] = 0; + m[M11] = yo; + m[M21] = 0; + + m[M02] = tx; + m[M12] = ty; + m[M22] = 1; + + return this; + } + + @Override + public Vec2f transform(Vec2f point, Vec2f out) + { + return transform( point.x, point.y, out ); + } + + public Vec2f transform(float x, float y, Vec2f out) + { + float[] m = this.value; + float invw = 1f / (m[M20] * x + m[M21] * y + m[M22]); + + out.x = (m[M00] * x + m[M01] * y + m[M02]) * invw; + out.y = (m[M10] * x + m[M11] * y + m[M12]) * invw; + + return out; + } + + @Override + public Vec2f transform(Vec2f point) + { + return transform( point, new Vec2f() ); + } + + @Override + public Vec2f transformVector(Vec2f vector, Vec2f out) + { + return transformVector( vector.x, vector.y, out ); + } + + public Vec2f transformVector(float x, float y, Vec2f out) + { + float[] m = this.value; + + out.x = m[M00] * x + m[M01] * y; + out.y = m[M10] * x + m[M11] * y; + + return out; + } + + @Override + public Vec2f transformVector(Vec2f vector) + { + return transformVector( vector, new Vec2f() ); + } + + @Override + public Matrix translate(Vec2f by, Matrix out) + { + return translate( by.x, by.y, out ); + } + + public Matrix translate(float x, float y, Matrix out) + { + return out.set( multiply( this.value, getTranslation( x, y, temp0 ) ) ); + } + + public Matrix translatei(float x, float y) + { + return set( multiply( this.value, getTranslation( x, y, temp0 ) ) ); + } + + @Override + public Matrix pretranslate(Vec2f by) + { + return pretranslate( by.x, by.y ); + } + + public Matrix pretranslate(float x, float y) + { + float[] m = this.value; + m[M02] += x; + m[M12] += y; + return this; + } + + @Override + public Vec2f getTranslation(Vec2f out) + { + float[] m = this.value; + out.x = m[M02]; + out.y = m[M12]; + return out; + } + + @Override + public Matrix setTranslation(Vec2f by) + { + getTranslation( by.x, by.y, this.value ); + return this; + } + + @Override + public Matrix scale(Vec2f by, Matrix out) + { + return scale( by.x, by.y, out ); + } + + @Override + public Matrix scale(float by, Matrix out) + { + return scale( by, by, out ); + } + + public Matrix scale(float x, float y, Matrix out) + { + return out.set( multiply( this.value, getScaling( x, y, temp0 ) ) ); + } + + public Matrix scalei(float x, float y) + { + return set( multiply( this.value, getScaling( x, y, temp0 ) ) ); + } + + @Override + public Matrix prescale(Vec2f by) + { + return prescale( by.x, by.y ); + } + + @Override + public Matrix prescale(float by) + { + return prescale( by, by ); + } + + public Matrix prescale(float x, float y) + { + float[] m = this.value; + m[M00] *= x; + m[M11] *= y; + return this; + } + + @Override + public Vec2f getScaling(Vec2f out) + { + float[] m = this.value; + out.x = (float)Math.sqrt(m[M00] * m[M00] + m[M01] * m[M01]); + out.y = (float)Math.sqrt(m[M10] * m[M10] + m[M11] * m[M11]); + return out; + } + + @Override + public Matrix setScaling(Vec2f by) + { + return setScaling( by.x, by.y ); + } + + @Override + public Matrix setScaling(float by) + { + return setScaling( by, by ); + } + + public Matrix setScaling(float x, float y) + { + getScaling( x, y, this.value ); + return this; + } + + public Matrix rotate(float radians, Matrix out) + { + return rotate( Numbers.cos( radians ), Numbers.sin( radians ), out ); + } + + public Matrix rotateDegrees(float degrees, Matrix out) + { + return rotate( degrees * Numbers.DEGREE_TO_RADIAN, out ); + } + + public Matrix rotaten(float radians) + { + return rotate( radians, create() ); + } + + public Matrix rotaten(float cos, float sin) + { + return rotate( cos, sin, create() ); + } + + public Matrix rotatenDegrees(float degrees) + { + return rotate( degrees * Numbers.DEGREE_TO_RADIAN, create() ); + } + + public Matrix rotatei(float radians) + { + return rotate( radians, this ); + } + + public Matrix rotatei(float cos, float sin) + { + return rotate( cos, sin, this ); + } + + public Matrix rotateiDegrees(float degrees) + { + return rotate( degrees * Numbers.DEGREE_TO_RADIAN, this ); + } + + public Matrix rotate(float cos, float sin, Matrix out) + { + return out.set( multiply( this.value, getRotation( cos, sin, temp0 ) ) ); + } + + public float getRotation() + { + float[] m = this.value; + + return (float)Math.atan2(m[M10], m[M00]); + } + + public Matrix setRotation(float cos, float sin) + { + getRotation( cos, sin, this.value ); + return this; + } + + public Matrix setRotation(float radians) + { + return setRotation( Numbers.cos( radians ), Numbers.sin( radians ) ); + } + + @Override + public Matrix setRotation(float cos, float sin, Vec3f axis) + { + getAxisRotation( cos, sin, axis, this.value ); + return this; + } + + @Override + public Matrix rotate(float cos, float sin, Vec3f axis, Matrix out) + { + return out.set( getAxisRotation( cos, sin, axis, temp0 ) ); + } + + public Matrix skew(Vec2f by, Matrix out) + { + return skew( by.x, by.y, out ); + } + + public Matrix skew(float x, float y, Matrix out) + { + return out.set( multiply( this.value, getSkewing( x, y, temp0 ) ) ); + } + + public Vec2f getSkewing(Vec2f out) + { + return out; + } + + public Matrix setSkewing(Vec2f by) + { + return setSkewing( by.x, by.y ); + } + + public Matrix skewn(Vec2f by) + { + return skew( by, create() ); + } + + public Matrix skewi(Vec2f by) + { + return skew( by, this ); + } + + public Vec2f getSkewing() + { + return getSkewing( createVector() ); + } + + public Matrix setSkewing(float x, float y) + { + getSkewing( x, y, this.value ); + return this; + } + + @Override + public Matrix transpose(Matrix out) + { + float[] val = this.value; + float v01 = val[M10]; + float v02 = val[M20]; + float v10 = val[M01]; + float v12 = val[M21]; + float v20 = val[M02]; + float v21 = val[M12]; + val[M01] = v01; + val[M02] = v02; + val[M10] = v10; + val[M12] = v12; + val[M20] = v20; + val[M21] = v21; + return this; + } + + @Override + public Matrix invert(Matrix out) + { + float d = determinant(); + + if (d == 0) return null; + + float invd = 1.0f / d; + float[] dst = temp0; + float[] m = this.value; + + dst[M00] = invd * (m[M11] * m[M22] - m[M21] * m[M12]); + dst[M10] = invd * (m[M20] * m[M12] - m[M10] * m[M22]); + dst[M20] = invd * (m[M10] * m[M21] - m[M20] * m[M11]); + dst[M01] = invd * (m[M21] * m[M02] - m[M01] * m[M22]); + dst[M11] = invd * (m[M00] * m[M22] - m[M20] * m[M02]); + dst[M21] = invd * (m[M20] * m[M01] - m[M00] * m[M21]); + dst[M02] = invd * (m[M01] * m[M12] - m[M11] * m[M02]); + dst[M12] = invd * (m[M10] * m[M02] - m[M00] * m[M12]); + dst[M22] = invd * (m[M00] * m[M11] - m[M10] * m[M01]); + + return out.set( dst ); + } + + @Override + public Matrix multiply(Matrix base, Matrix by, Matrix out) + { + return out.set( multiply( base.get(), by.get() ) ); + } + + @Override + public Matrix set(float[] values) + { + float[] m = this.value; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + return this; + } + + @Override + public Matrix create() + { + return new Matrix2(); + } + + @Override + public Matrix create(float[] values) + { + return new Matrix2( values ); + } + + @Override + public float[] get() + { + return value; + } + + @Override + public int hashCode() + { + float[] m = this.value; + + return (int)(m[M22] + m[M00] * 10 + m[M01] * 100 + m[M10] * 1000 + + m[M11] * 10000 + m[M02] * 100000 + m[M20] * 1000000 + + m[M12] * 10000000 + m[M21] * 100000000); + } + + private static void setIdentity(float[] out) + { + out[M00] = 1; + out[M10] = 0; + out[M20] = 0; + + out[M01] = 0; + out[M11] = 1; + out[M21] = 0; + + out[M02] = 0; + out[M12] = 0; + out[M22] = 1; + } + + private static float[] getTranslation(float x, float y, float[] out) + { + setIdentity( out ); + out[M02] = x; + out[M12] = y; + return out; + } + + private static float[] getScaling(float x, float y, float[] out) + { + setIdentity( out ); + out[M00] = x; + out[M11] = y; + return out; + } + + private static float[] getSkewing(float x, float y, float[] out) + { + setIdentity( out ); + out[M10] = x; + out[M01] = y; + return out; + } + + private static float[] getRotation(float cos, float sin, float[] out) + { + setIdentity( out ); + out[M00] = cos; + out[M10] = sin; + out[M01] = -sin; + out[M11] = cos; + return out; + } + + private static float[] getAxisRotation(float cos, float sin, Vec3f axis, float[] out) + { + float oc = 1.0f - cos; + out[M00] = oc * axis.x * axis.x + cos; + out[M10] = oc * axis.x * axis.y - axis.z * sin; + out[M20] = oc * axis.z * axis.x + axis.y * sin; + out[M01] = oc * axis.x * axis.y + axis.z * sin; + out[M11] = oc * axis.y * axis.y + cos; + out[M21] = oc * axis.y * axis.z - axis.x * sin; + out[M02] = oc * axis.z * axis.x - axis.y * sin; + out[M12] = oc * axis.y * axis.z + axis.x * sin; + out[M22] = oc * axis.z * axis.z + cos; + return out; + } + + private static float[] multiply(float[] a_, float[] b_) + { + float[] out = temp1; + + out[M00] = a_[M00] * b_[M00] + a_[M01] * b_[M10] + a_[M02] * b_[M20]; + out[M01] = a_[M00] * b_[M01] + a_[M01] * b_[M11] + a_[M02] * b_[M21]; + out[M02] = a_[M00] * b_[M02] + a_[M01] * b_[M12] + a_[M02] * b_[M22]; + + out[M10] = a_[M10] * b_[M00] + a_[M11] * b_[M10] + a_[M12] * b_[M20]; + out[M11] = a_[M10] * b_[M01] + a_[M11] * b_[M11] + a_[M12] * b_[M21]; + out[M12] = a_[M10] * b_[M02] + a_[M11] * b_[M12] + a_[M12] * b_[M22]; + + out[M20] = a_[M20] * b_[M00] + a_[M21] * b_[M10] + a_[M22] * b_[M20]; + out[M21] = a_[M20] * b_[M01] + a_[M21] * b_[M11] + a_[M22] * b_[M21]; + out[M22] = a_[M20] * b_[M02] + a_[M21] * b_[M12] + a_[M22] * b_[M22]; + + return out; + } + +} diff --git a/src/com/axe2d/node/NodeList2.java b/src/com/axe2d/node/NodeList2.java new file mode 100644 index 0000000..328a10a --- /dev/null +++ b/src/com/axe2d/node/NodeList2.java @@ -0,0 +1,41 @@ +package com.axe2d.node; + +import java.util.Comparator; + +import com.axe.collect.List; +import com.axe.collect.ListArray; +import com.axe.collect.ListLinked; +import com.axe.collect.ListSorted; +import com.axe.math.Vec2f; +import com.axe.node.Node; +import com.axe.node.NodeList; + +public class NodeList2> extends NodeList +{ + + public static > NodeList2 create2(List nodes) + { + return new NodeList2( nodes ); + } + + public static > NodeList2 linked2() + { + return new NodeList2( ListLinked.create() ); + } + + public static > NodeList2 array2(T ... nodes) + { + return new NodeList2( ListArray.create( nodes ) ); + } + + public static > NodeList2 sorted2(Comparator comparator, T ... nodes) + { + return new NodeList2( ListSorted.create( comparator, nodes ) ); + } + + protected NodeList2(List nodes) + { + super( nodes ); + } + +} diff --git a/src/com/axe2d/node/Nodes2.java b/src/com/axe2d/node/Nodes2.java new file mode 100644 index 0000000..28b78d0 --- /dev/null +++ b/src/com/axe2d/node/Nodes2.java @@ -0,0 +1,40 @@ +package com.axe2d.node; + +import java.util.Arrays; + +import com.axe.math.Vec2f; +import com.axe.node.Node; +import com.axe.node.Nodes; + +public class Nodes2> extends Nodes +{ + + public Nodes2(N ... initial) + { + this( DEFAULT_CAPACITY, DEFAULT_INCREASE, DEFAULT_EXPIRE, initial ); + } + + public Nodes2(int capacity, N ... initial) + { + this( capacity, DEFAULT_INCREASE, DEFAULT_EXPIRE, initial ); + } + + public Nodes2(int capacity, boolean expireOnEmpty, N ... initial) + { + this( capacity, DEFAULT_INCREASE, expireOnEmpty, initial ); + } + + public Nodes2(boolean expireOnEmpty, N ... initial) + { + this( DEFAULT_CAPACITY, DEFAULT_INCREASE, expireOnEmpty, initial ); + } + + public Nodes2(int capacity, int increase, boolean expireOnEmpty, N ... initial) + { + this.nodes = Arrays.copyOf( initial, Math.max( capacity, initial.length ) ); + this.size = initial.length; + this.increase = increase; + this.expireOnEmpty = expireOnEmpty; + } + +} diff --git a/src/com/axe2d/sprite/PhysicsSprite2.java b/src/com/axe2d/sprite/PhysicsSprite2.java new file mode 100644 index 0000000..cc57087 --- /dev/null +++ b/src/com/axe2d/sprite/PhysicsSprite2.java @@ -0,0 +1,64 @@ +package com.axe2d.sprite; + +import com.axe.Axe; +import com.axe.game.GameState; +import com.axe.math.Numbers; +import com.axe.math.Vec2f; +import com.axe.sprite.PhysicsSprite; +import com.axe2d.node.Nodes2; + +public class PhysicsSprite2 extends PhysicsSprite +{ + + public static final int VIEW = Axe.renderers.create(); + public static final int VIEW_MANY = Axe.renderers.create(); + + public static Nodes2 many() + { + Nodes2 sprites = new Nodes2<>(); + + sprites.setRenderer( VIEW_MANY ); + + return sprites; + } + + public PhysicsSprite2() + { + super( VIEW, Vec2f.FACTORY ); + } + + + @Override + public boolean getBounds(GameState state, Vec2f min, Vec2f max) + { + float hw = size.x * scale.x; + float hh = size.y * scale.y; + + min.x = -hw * anchor.x; + min.y = -hh * anchor.y; + max.x = hw * (1 - anchor.x); + max.x = hh * (1 - anchor.y); + + if ( rotation.v != 0 ) + { + float vx = Math.abs( Numbers.cos( rotation.v ) ); + float vy = Math.abs( Numbers.sin( rotation.v ) ); + float rw = Math.abs( vx * hw + vy * hh ) - hw; + float rh = Math.abs( vx * hh + vy * hw ) - hh; + + min.x -= rw; + min.y -= rh; + max.x += rw; + max.y += rh; + } + + min.add( position ); + max.add( position ); + + min.adds( velocity, -state.reverse ); + max.adds( velocity, -state.reverse ); + + return true; + } + +} diff --git a/src/com/axe2d/sprite/SimpleSprite2.java b/src/com/axe2d/sprite/SimpleSprite2.java new file mode 100644 index 0000000..40b33d0 --- /dev/null +++ b/src/com/axe2d/sprite/SimpleSprite2.java @@ -0,0 +1,46 @@ +package com.axe2d.sprite; + +import com.axe.Axe; +import com.axe.game.GameState; +import com.axe.math.Vec2f; +import com.axe.sprite.SimpleSprite; +import com.axe2d.node.Nodes2; + +public class SimpleSprite2 extends SimpleSprite +{ + + public static final int VIEW = Axe.renderers.create(); + public static final int VIEW_MANY = Axe.renderers.create(); + + public static Nodes2 many() + { + Nodes2 sprites = new Nodes2<>(); + + sprites.setRenderer( VIEW_MANY ); + + return sprites; + } + + public SimpleSprite2() + { + super( VIEW, Vec2f.FACTORY ); + } + + @Override + public boolean getBounds(GameState state, Vec2f min, Vec2f max) + { + min.set( position ); + max.set( position ); + max.add( size ); + + return true; + } + + public SimpleSprite2 setPosition(float x, float y) + { + this.position.set( x, y ); + + return this; + } + +} diff --git a/src/com/axe2d/sprite/Sprite2.java b/src/com/axe2d/sprite/Sprite2.java new file mode 100644 index 0000000..9498120 --- /dev/null +++ b/src/com/axe2d/sprite/Sprite2.java @@ -0,0 +1,60 @@ +package com.axe2d.sprite; + +import com.axe.Axe; +import com.axe.game.GameState; +import com.axe.math.Numbers; +import com.axe.math.Vec2f; +import com.axe.sprite.Sprite; +import com.axe2d.node.Nodes2; + +public class Sprite2 extends Sprite +{ + + public static final int VIEW = Axe.renderers.create(); + public static final int VIEW_MANY = Axe.renderers.create(); + + public static Nodes2 many() + { + Nodes2 sprites = new Nodes2<>(); + + sprites.setRenderer( VIEW_MANY ); + + return sprites; + } + + public Sprite2() + { + super( VIEW, Vec2f.FACTORY ); + } + + @Override + public boolean getBounds(GameState state, Vec2f min, Vec2f max) + { + float hw = size.x * scale.x; + float hh = size.y * scale.y; + + min.x = -hw * anchor.x; + min.y = -hh * anchor.y; + max.x = hw * (1 - anchor.x); + max.y = hh * (1 - anchor.y); + + if ( rotation.v != 0 ) + { + float vx = Math.abs( Numbers.cos( rotation.v ) ); + float vy = Math.abs( Numbers.sin( rotation.v ) ); + float rw = Math.abs( vx * hw + vy * hh ) - hw; + float rh = Math.abs( vx * hh + vy * hw ) - hh; + + min.x -= rw; + min.y -= rh; + max.x += rw; + max.y += rh; + } + + min.add( position ); + max.add( position ); + + return true; + } + +} diff --git a/src/com/axe3d/Camera3.java b/src/com/axe3d/Camera3.java new file mode 100644 index 0000000..074e3ef --- /dev/null +++ b/src/com/axe3d/Camera3.java @@ -0,0 +1,135 @@ +package com.axe3d; + +import com.axe.AbstractCamera; +import com.axe.core.Attribute; +import com.axe.game.GameState; +import com.axe.math.Bound3f; +import com.axe.math.Numbers; +import com.axe.math.Quaternion3; +import com.axe.math.Scalarf; +import com.axe.math.Vec3f; +import com.axe.math.calc.CalculatorCamera3; +import com.axe.node.Node; +import com.axe3d.math.Frustum3; + +public class Camera3 extends AbstractCamera implements Attribute +{ + + private static final Bound3f BOUNDS = new Bound3f(); + private static final Vec3f CENTER = new Vec3f(); + private static final Scalarf RADIUS = new Scalarf(); + private static final Vec3f MIN = new Vec3f(); + private static final Vec3f MAX = new Vec3f(); + + public static final int SIZE = 40; + + public final Scalarf yaw = new Scalarf(); + public final Scalarf pitch = new Scalarf(); + public final Scalarf roll = new Scalarf(); + public final Scalarf distance = new Scalarf(); + public final Vec3f focus = new Vec3f(); + public final Scalarf fov = new Scalarf(60f); + public final Scalarf aspect = new Scalarf(); // + public final Scalarf near = new Scalarf(0.01f); + public final Scalarf far = new Scalarf(100.0f); + public final Frustum3 frustum = new Frustum3(); + public final Vec3f direction = new Vec3f(); // + public final Vec3f right = new Vec3f(); // + public final Vec3f up = new Vec3f(); // + public final Vec3f position = new Vec3f(); // + public final Vec3f forward = new Vec3f(); // + public final Quaternion3 rollq = new Quaternion3(); // + + @Override + public void update() + { + // Wrap all angles between 0 and 2PI + yaw.mod(Numbers.PI2); + pitch.mod(Numbers.PI2); + roll.mod(Numbers.PI2); + + // Use yaw and pitch to calculate the direction and right vector. + // The right vector always lies on the x,z plane. All three vectors + // are unit vectors. + float cosy = yaw.cos(); + float siny = yaw.sin(); + float cosp = pitch.cos(); + float sinp = pitch.sin(); + + direction.normal(siny * cosp, sinp, -cosy * cosp); + right.set(cosy, 0f, siny); + forward.set(siny, 0f, -cosy); + up.cross(right, direction); + + // If there is roll, setup the quaternion using the direction and + // the requested roll angle, and rotate up and right. + if (roll.v != 0f) + { + rollq.set(direction, -roll.v); + rollq.rotate(up); + rollq.rotate(right); + } + + // Start at the focus and back out (or forward into) by the given + // distance in the current looking direction. + position.set(focus); + position.adds(direction, -distance.v); + + // Update Planes + frustum.update(position, direction, up, right, fov.v, near.v, far.v); + } + + @Override + public boolean intersects(GameState state, Node node) + { + if (node.getSphere(state, CENTER, RADIUS)) + { + float distance = frustum.distance( CENTER ); + + if ( distance > RADIUS.v ) + { + return false; + } + } + + if (node.getBounds(state, MIN, MAX)) + { + BOUNDS.set( MIN, MAX ); + + if ( frustum.sign( BOUNDS ) == Frustum3.Outside ) + { + return false; + } + } + + return true; + } + + public Vec3f getOrientation( Vec3f out ) + { + out.x = pitch.v; + out.y = yaw.v; + out.z = roll.v; + + return out; + } + + @Override + public Camera3 clone() + { + return copy( create() ); + } + + @Override + public CalculatorCamera3 getCalculator() + { + return CalculatorCamera3.INSTANCE; + } + + @Override + public Camera3 get() + { + return this; + } + +} diff --git a/src/com/axe3d/Scene3.java b/src/com/axe3d/Scene3.java new file mode 100644 index 0000000..f9590d4 --- /dev/null +++ b/src/com/axe3d/Scene3.java @@ -0,0 +1,9 @@ +package com.axe3d; + +import com.axe.AbstractScene; +import com.axe.math.Vec3f; + +public class Scene3 extends AbstractScene +{ + +} diff --git a/src/com/axe3d/View3.java b/src/com/axe3d/View3.java new file mode 100644 index 0000000..636c263 --- /dev/null +++ b/src/com/axe3d/View3.java @@ -0,0 +1,154 @@ +package com.axe3d; + +import com.axe.AbstractView; +import com.axe.Camera; +import com.axe.Scene; +import com.axe.gfx.ProjectionOutside; +import com.axe.math.Bound2i; +import com.axe.math.Numbers; +import com.axe.math.Scalarf; +import com.axe.math.Vec2i; +import com.axe.math.Vec3f; +import com.axe.window.Window; +import com.axe3d.math.Matrix3; + +public abstract class View3 extends AbstractView +{ + + protected static final Bound2i BOUNDS = new Bound2i(); + + public Scene3 scene; + public Camera3 camera; + public Matrix3 projection; + public Matrix3 view; + public Matrix3 combined; + public Matrix3 inverted; + + public View3(Window window, Scene3 scene) + { + super( window ); + + this.scene = scene; + this.camera = new Camera3(); + this.projection = new Matrix3(); + this.view = new Matrix3(); + this.combined = new Matrix3(); + } + + @Override + public Matrix3 getProjectionMatrix() + { + return projection; + } + + @Override + public Matrix3 getViewMatrix() + { + return view; + } + + @Override + public Matrix3 getCombinedMatrix() + { + return combined; + } + + @Override + public void update() + { + final float parentWidth = window.getWidth(); + final float parentHeight = window.getHeight(); + final float viewWidth = placement.getWidth( parentWidth ); + final float viewHeight = placement.getHeight( parentHeight ); + final Vec3f position = camera.position; + final Vec3f forward = camera.forward; + final Vec3f right = camera.right; + final Vec3f up = camera.up; + final Scalarf near = camera.near; + final Scalarf far = camera.far; + final Scalarf fov = camera.fov; + + projection.perspective( fov.v, viewWidth / viewHeight, near.v, far.v ); + + view.lookAt( position, forward, up, right ); + + combined.set( projection ); + combined.multiplyi( view ); + combined.invert( inverted ); + } + + @Override + public Vec3f project(Vec2i mouse, ProjectionOutside outside, Vec3f out) + { + final int windowWidth = window.getWidth(); + final int windowHeight = window.getHeight(); + final Bound2i view = placement.getBounds( windowWidth, windowHeight, BOUNDS ); + + out.x = (float)(mouse.x - view.l) / view.width(); + out.y = (float)((windowHeight - mouse.y) - view.b) / view.height(); + + if (!inProjection(out, outside)) + { + return null; + } + + // TODO + + return out; + } + + @Override + public Vec3f unproject(Vec3f point, ProjectionOutside outside, Vec3f out) + { + final int windowWidth = window.getWidth(); + final int windowHeight = window.getHeight(); + final Bound2i view = placement.getBounds( windowWidth, windowHeight, BOUNDS ); + + // TODO + + if (!inProjection(out, outside)) + { + return null; + } + + out.x = ((out.x * view.width()) + view.l); + out.y = windowHeight - (out.y * view.height() + view.b); + + return out; + } + + protected boolean inProjection(Vec3f out, ProjectionOutside outside) + { + if (out.x < 0 || out.y < 0 || out.x >= 1 || out.y >= 1 || out.z < 0 || out.z >= 1) + { + switch (outside) { + case Ignore: + return false; + case Clamp: + out.x = Numbers.clamp( out.x, 0, 1 ); + out.y = Numbers.clamp( out.y, 0, 1 ); + out.z = Numbers.clamp( out.z, 0, 1 ); + break; + case Relative: + // No action necessary + break; + } + } + + return true; + } + + @Override + public > S getScene() + { + return (S)scene; + } + + @Override + public > C getCamera() + { + return (C)camera; + } + + +} diff --git a/src/com/axe3d/math/Frustum3.java b/src/com/axe3d/math/Frustum3.java new file mode 100644 index 0000000..8792562 --- /dev/null +++ b/src/com/axe3d/math/Frustum3.java @@ -0,0 +1,114 @@ +package com.axe3d.math; + +import com.axe.math.Bound3f; +import com.axe.math.Plane3f; +import com.axe.math.Sphere3f; +import com.axe.math.Vec3f; +import com.axe.math.Vec3i; + +public class Frustum3 +{ + public static final int Outside = -1; + public static final int Inside = 1; + public static final int Intersects = 2; + + public static final int Left = 0; + public static final int Right = 1; + public static final int Bottom = 2; + public static final int Top = 3; + public static final int Near = 4; + public static final int Far = 5; + + private static final int PLANE_COUNT = 6; + + public final Plane3f[] planes = { + new Plane3f(), new Plane3f(), new Plane3f(), + new Plane3f(), new Plane3f(), new Plane3f() + }; + + public void update(Vec3f position, Vec3f direction, Vec3f up, Vec3f right, float fov, float near, float far) + { + // TODO + } + + public Plane3f plane(int i) + { + return planes[i]; + } + + public float distance(Vec3f v) + { + float distance = planes[0].distance(v); + + for (int i = 1; i < PLANE_COUNT; i++) + { + float d = planes[i].distance(v); + + if (d >= 0) + { + if (distance < 0) + { + distance = d; + } + else + { + distance = (d < distance ? d : distance); + } + } + else if (d > distance) + { + distance = d; + } + } + return distance; + } + + public int sign(Vec3f v) + { + return (int)Math.signum(distance(v)); + } + + public float distance(Vec3i v) + { + return distance(new Vec3f(v.x, v.y, v.z)); + } + + public int sign(Vec3i v) + { + return (int)Math.signum(distance(v)); + } + + public int sign(Bound3f b) + { + int inside = 0; + + for (int i = 0; i < PLANE_COUNT; i++) + { + int sign = planes[i].sign( b ); + + if ( sign == Plane3f.BACK ) + { + return Outside; + } + if ( sign == Plane3f.FRONT ) + { + inside++; + } + } + + return (inside == PLANE_COUNT ? Inside : Intersects); + } + + public float distance(Sphere3f s) + { + float d = distance(s.center()); + int g = (int)Math.signum(d); + return d - (g * s.r); + } + + public int sign(Sphere3f s) + { + return (int)Math.signum(distance(s)); + } + +} diff --git a/src/com/axe3d/math/Matrix3.java b/src/com/axe3d/math/Matrix3.java new file mode 100644 index 0000000..3c2f0d4 --- /dev/null +++ b/src/com/axe3d/math/Matrix3.java @@ -0,0 +1,571 @@ +package com.axe3d.math; + +import com.axe.math.Matrix; +import com.axe.math.Vec3f; + +public class Matrix3 implements Matrix +{ + public static final int VALUES = 16; + + public static final int M00 = 0; + public static final int M01 = 4; + public static final int M02 = 8; + public static final int M03 = 12; + public static final int M10 = 1; + public static final int M11 = 5; + public static final int M12 = 9; + public static final int M13 = 13; + public static final int M20 = 2; + public static final int M21 = 6; + public static final int M22 = 10; + public static final int M23 = 14; + public static final int M30 = 3; + public static final int M31 = 7; + public static final int M32 = 11; + public static final int M33 = 15; + + private static float[] temp0 = new float[VALUES]; + private static float[] temp1 = new float[VALUES]; + + private static Vec3f EYE = new Vec3f(); + private static Vec3f UP = new Vec3f(); + private static Vec3f FORWARD = new Vec3f(); + private static Vec3f SIDE = new Vec3f(); + + public float[] value = new float[VALUES]; + + public Matrix3() + { + this.identity(); + } + + public Matrix3(float[] value) + { + this.set(value); + } + + @Override + public Vec3f createVector() + { + return new Vec3f(); + } + + @Override + public Matrix identity() + { + setIdentity( this.value ); + return this; + } + + @Override + public float determinant() + { + float[] m = this.value; + + return m[M30] * m[M21] * m[M12] * m[M03] - + m[M20] * m[M31] * m[M12] * m[M03] - + m[M30] * m[M11] * m[M22] * m[M03] + + m[M10] * m[M31] * m[M22] * m[M03] + + m[M20] * m[M11] * m[M32] * m[M03] - + m[M10] * m[M21] * m[M32] * m[M03] - + m[M30] * m[M21] * m[M02] * m[M13] + + m[M20] * m[M31] * m[M02] * m[M13] + + m[M30] * m[M01] * m[M22] * m[M13] - + m[M00] * m[M31] * m[M22] * m[M13] - + m[M20] * m[M01] * m[M32] * m[M13] + + m[M00] * m[M21] * m[M32] * m[M13] + + m[M30] * m[M11] * m[M02] * m[M23] - + m[M10] * m[M31] * m[M02] * m[M23] - + m[M30] * m[M01] * m[M12] * m[M23] + + m[M00] * m[M31] * m[M12] * m[M23] + + m[M10] * m[M01] * m[M32] * m[M23] - + m[M00] * m[M11] * m[M32] * m[M23] - + m[M20] * m[M11] * m[M02] * m[M33] + + m[M10] * m[M21] * m[M02] * m[M33] + + m[M20] * m[M01] * m[M12] * m[M33] - + m[M00] * m[M21] * m[M12] * m[M33] - + m[M10] * m[M01] * m[M22] * m[M33] + + m[M00] * m[M11] * m[M22] * m[M33]; + } + + + @Override + public Matrix3 ortho( Vec3f min, Vec3f max ) + { + return ortho( min.x, max.y, max.x, min.y, min.z, max.z ); + } + + public Matrix3 ortho(float left, float top, float right, float bottom) + { + return ortho( left, top, right, bottom, -1, 1 ); + } + + public Matrix3 ortho(float left, float top, float right, float bottom, float near, float far) + { + float[] m = this.value; + float dx = (right - left); + float dy = (top - bottom); + float dz = (far - near); + float xo = 2 / dx; + float yo = 2 / dy; + float zo = -2 / dz; + float tx = -(right + left) / dx; + float ty = -(top + bottom) / dy; + float tz = -(far + near) / dz; + + m[M00] = xo; + m[M10] = 0; + m[M20] = 0; + m[M30] = 0; + + m[M01] = 0; + m[M11] = yo; + m[M21] = 0; + m[M31] = 0; + + m[M02] = 0; + m[M12] = 0; + m[M22] = zo; + m[M32] = 0; + + m[M03] = tx; + m[M13] = ty; + m[M23] = tz; + m[M33] = 1; + + return this; + } + + public Matrix3 perspective(float fov, float aspectRatio, float near, float far) + { + float[] m = this.value; + float dz = (near - far); + float cotangent = (float)(1.0 / Math.tan((fov * (Math.PI / 180)) / 2.0)); + float l_a1 = (far + near) / dz; + float l_a2 = (2 * far * near) / dz; + + m[M00] = cotangent / aspectRatio; + m[M10] = 0; + m[M20] = 0; + m[M30] = 0; + + m[M01] = 0; + m[M11] = cotangent; + m[M21] = 0; + m[M31] = 0; + + m[M02] = 0; + m[M12] = 0; + m[M22] = l_a1; + m[M32] = -1; + + m[M03] = 0; + m[M13] = 0; + m[M23] = l_a2; + m[M33] = 0; + + return this; + } + + public Matrix3 lookAt(float eyex, float eyey, float eyez, float cx, float cy, float cz, float upx, float upy, float upz) + { + Vec3f e = EYE; + Vec3f f = FORWARD; + Vec3f u = UP; + Vec3f s = SIDE; + + e.set( eyex, eyey, eyez ); + + f.set( cx, cy, cz ); + f.sub( e ); + + s.cross( f, u ); + s.normal(); + + u.cross( s, f ); + + return lookAt( e, f, u, s ); + } + + public Matrix3 lookAt(Vec3f eye, Vec3f forward, Vec3f up, Vec3f right) + { + float[] m = this.value; + + m[M00] = right.x; + m[M01] = right.y; + m[M02] = right.z; + m[M03] = -eye.x; + + m[M10] = up.x; + m[M11] = up.y; + m[M12] = up.z; + m[M13] = -eye.y; + + m[M20] = -forward.x; + m[M21] = -forward.y; + m[M22] = -forward.z; + m[M23] = -eye.z; + + m[M30] = 0; + m[M31] = 0; + m[M32] = 0; + m[M33] = 1; + + return this; + } + + @Override + public Vec3f transform(Vec3f point, Vec3f out) + { + return transform( point.x, point.y, point.z, out ); + } + + public Vec3f transform(float x, float y, float z, Vec3f out) + { + float[] m = this.value; + float w = m[M30] * x + m[M31] * y + m[M32] * z + m[M33]; + float inv_w = 1f / w; + + out.x = (m[M00] * x + m[M01] * y + m[M02] * z + m[M03]) * inv_w; + out.y = (m[M10] * x + m[M11] * y + m[M12] * z + m[M13]) * inv_w; + out.z = (m[M20] * x + m[M21] * y + m[M22] * z + m[M23]) * inv_w; + + return out; + } + + @Override + public Vec3f transform(Vec3f point) + { + return transform( point, new Vec3f() ); + } + + @Override + public Vec3f transformVector(Vec3f vector, Vec3f out) + { + return transformVector( vector.x, vector.y, vector.z, out ); + } + + public Vec3f transformVector(float x, float y, float z, Vec3f out) + { + float[] m = this.value; + + out.x = m[M00] * x + m[M01] * y + m[M02] * z; + out.y = m[M10] * x + m[M11] * y + m[M12] * z; + out.z = m[M20] * x + m[M21] * y + m[M22] * z; + + return out; + } + + @Override + public Vec3f transformVector(Vec3f vector) + { + return transformVector( vector, new Vec3f() ); + } + + @Override + public Matrix translate(Vec3f by, Matrix out) + { + return out.set( multiply( this.value, getTranslation( by.x, by.y, by.z, temp0 ) ) ); + } + + @Override + public Matrix pretranslate(Vec3f by) + { + float[] m = this.value; + m[M03] += by.x; + m[M13] += by.y; + m[M23] += by.z; + return this; + } + + @Override + public Vec3f getTranslation(Vec3f out) + { + float[] m = this.value; + out.x = m[M03]; + out.y = m[M13]; + out.z = m[M23]; + return out; + } + + @Override + public Matrix setTranslation(Vec3f by) + { + getTranslation( by.x, by.y, by.z, this.value ); + return this; + } + + @Override + public Matrix scale(Vec3f by, Matrix out) + { + return out.set( multiply( this.value, getScaling( by.x, by.y, by.z, temp0 ) ) ); + } + + @Override + public Matrix scale(float by, Matrix out) + { + return out.set( multiply( this.value, getScaling( by, by, by, temp0 ) ) ); + } + + @Override + public Matrix prescale(Vec3f by) + { + return prescale( by.x, by.y, by.z ); + } + + @Override + public Matrix prescale(float by) + { + return prescale( by, by, by ); + } + + public Matrix prescale(float x, float y, float z) + { + float[] m = this.value; + m[M00] *= x; + m[M11] *= y; + m[M22] *= z; + return this; + } + + @Override + public Vec3f getScaling(Vec3f out) + { + float[] m = this.value; + out.x = (float)Math.sqrt(m[M00] * m[M00] + m[M01] * m[M01] + m[M02] * m[M02]); + out.y = (float)Math.sqrt(m[M10] * m[M10] + m[M11] * m[M11] + m[M12] * m[M12]); + out.z = (float)Math.sqrt(m[M20] * m[M20] + m[M21] * m[M21] + m[M22] * m[M22]); + return out; + } + + @Override + public Matrix setScaling(Vec3f by) + { + getScaling( by.x, by.y, by.z, this.value ); + return this; + } + + @Override + public Matrix setScaling(float by) + { + getScaling( by, by, by, this.value ); + return this; + } + + @Override + public Matrix setRotation(float cos, float sin, Vec3f axis) + { + getAxisRotation( cos, sin, axis, this.value ); + return this; + } + + @Override + public Matrix rotate(float cos, float sin, Vec3f axis, Matrix out) + { + return out.set( getAxisRotation( cos, sin, axis, temp0 ) ); + } + + // TODO skewXY + // TODO skewXZ + // TODO skewYZ + // TODO rotationX + // TODO rotationY + // TODO rotationZ + // TODO quaternions + + @Override + public Matrix transpose(Matrix out) + { + float[] tmp = temp0; + float[] m = this.value; + + tmp[M00] = m[M00]; + tmp[M01] = m[M10]; + tmp[M02] = m[M20]; + tmp[M03] = m[M30]; + tmp[M10] = m[M01]; + tmp[M11] = m[M11]; + tmp[M12] = m[M21]; + tmp[M13] = m[M31]; + tmp[M20] = m[M02]; + tmp[M21] = m[M12]; + tmp[M22] = m[M22]; + tmp[M23] = m[M32]; + tmp[M30] = m[M03]; + tmp[M31] = m[M13]; + tmp[M32] = m[M23]; + tmp[M33] = m[M33]; + + return set(tmp); + } + + @Override + public Matrix invert(Matrix out) + { + float d = determinant(); + + if (d == 0) return null; + + float invd = 1.0f / d; + float[] dst = temp0; + float[] m = this.value; + + dst[M00] = invd * (m[M12] * m[M23] * m[M31] - m[M13] * m[M22] * m[M31] + m[M13] * m[M21] * m[M32] - m[M11] * m[M23] * m[M32] - m[M12] * m[M21] * m[M33] + m[M11] * m[M22] * m[M33]); + dst[M01] = invd * (m[M03] * m[M22] * m[M31] - m[M02] * m[M23] * m[M31] - m[M03] * m[M21] * m[M32] + m[M01] * m[M23] * m[M32] + m[M02] * m[M21] * m[M33] - m[M01] * m[M22] * m[M33]); + dst[M02] = invd * (m[M02] * m[M13] * m[M31] - m[M03] * m[M12] * m[M31] + m[M03] * m[M11] * m[M32] - m[M01] * m[M13] * m[M32] - m[M02] * m[M11] * m[M33] + m[M01] * m[M12] * m[M33]); + dst[M03] = invd * (m[M03] * m[M12] * m[M21] - m[M02] * m[M13] * m[M21] - m[M03] * m[M11] * m[M22] + m[M01] * m[M13] * m[M22] + m[M02] * m[M11] * m[M23] - m[M01] * m[M12] * m[M23]); + dst[M10] = invd * (m[M13] * m[M22] * m[M30] - m[M12] * m[M23] * m[M30] - m[M13] * m[M20] * m[M32] + m[M10] * m[M23] * m[M32] + m[M12] * m[M20] * m[M33] - m[M10] * m[M22] * m[M33]); + dst[M11] = invd * (m[M02] * m[M23] * m[M30] - m[M03] * m[M22] * m[M30] + m[M03] * m[M20] * m[M32] - m[M00] * m[M23] * m[M32] - m[M02] * m[M20] * m[M33] + m[M00] * m[M22] * m[M33]); + dst[M12] = invd * (m[M03] * m[M12] * m[M30] - m[M02] * m[M13] * m[M30] - m[M03] * m[M10] * m[M32] + m[M00] * m[M13] * m[M32] + m[M02] * m[M10] * m[M33] - m[M00] * m[M12] * m[M33]); + dst[M13] = invd * (m[M02] * m[M13] * m[M20] - m[M03] * m[M12] * m[M20] + m[M03] * m[M10] * m[M22] - m[M00] * m[M13] * m[M22] - m[M02] * m[M10] * m[M23] + m[M00] * m[M12] * m[M23]); + dst[M20] = invd * (m[M11] * m[M23] * m[M30] - m[M13] * m[M21] * m[M30] + m[M13] * m[M20] * m[M31] - m[M10] * m[M23] * m[M31] - m[M11] * m[M20] * m[M33] + m[M10] * m[M21] * m[M33]); + dst[M21] = invd * (m[M03] * m[M21] * m[M30] - m[M01] * m[M23] * m[M30] - m[M03] * m[M20] * m[M31] + m[M00] * m[M23] * m[M31] + m[M01] * m[M20] * m[M33] - m[M00] * m[M21] * m[M33]); + dst[M22] = invd * (m[M01] * m[M13] * m[M30] - m[M03] * m[M11] * m[M30] + m[M03] * m[M10] * m[M31] - m[M00] * m[M13] * m[M31] - m[M01] * m[M10] * m[M33] + m[M00] * m[M11] * m[M33]); + dst[M23] = invd * (m[M03] * m[M11] * m[M20] - m[M01] * m[M13] * m[M20] - m[M03] * m[M10] * m[M21] + m[M00] * m[M13] * m[M21] + m[M01] * m[M10] * m[M23] - m[M00] * m[M11] * m[M23]); + dst[M30] = invd * (m[M12] * m[M21] * m[M30] - m[M11] * m[M22] * m[M30] - m[M12] * m[M20] * m[M31] + m[M10] * m[M22] * m[M31] + m[M11] * m[M20] * m[M32] - m[M10] * m[M21] * m[M32]); + dst[M31] = invd * (m[M01] * m[M22] * m[M30] - m[M02] * m[M21] * m[M30] + m[M02] * m[M20] * m[M31] - m[M00] * m[M22] * m[M31] - m[M01] * m[M20] * m[M32] + m[M00] * m[M21] * m[M32]); + dst[M32] = invd * (m[M02] * m[M11] * m[M30] - m[M01] * m[M12] * m[M30] - m[M02] * m[M10] * m[M31] + m[M00] * m[M12] * m[M31] + m[M01] * m[M10] * m[M32] - m[M00] * m[M11] * m[M32]); + dst[M33] = invd * (m[M01] * m[M12] * m[M20] - m[M02] * m[M11] * m[M20] + m[M02] * m[M10] * m[M21] - m[M00] * m[M12] * m[M21] - m[M01] * m[M10] * m[M22] + m[M00] * m[M11] * m[M22]); + + return out.set( dst ); + } + + @Override + public Matrix multiply(Matrix base, Matrix by, Matrix out) + { + return out.set( multiply( base.get(), by.get() ) ); + } + + @Override + public Matrix set(float[] values) + { + float[] m = this.value; + m[0] = values[0]; + m[1] = values[1]; + m[2] = values[2]; + m[3] = values[3]; + m[4] = values[4]; + m[5] = values[5]; + m[6] = values[6]; + m[7] = values[7]; + m[8] = values[8]; + m[9] = values[9]; + m[10] = values[10]; + m[11] = values[11]; + m[12] = values[12]; + m[13] = values[13]; + m[14] = values[14]; + m[15] = values[15]; + return this; + } + + @Override + public Matrix create() + { + return new Matrix3(); + } + + @Override + public Matrix create(float[] values) + { + return new Matrix3( values ); + } + + @Override + public float[] get() + { + return value; + } + + // TODO + + private static void setIdentity(float[] out) + { + out[M00] = 1; + out[M01] = 0; + out[M02] = 0; + out[M03] = 0; + out[M10] = 0; + out[M11] = 1; + out[M12] = 0; + out[M13] = 0; + out[M20] = 0; + out[M21] = 0; + out[M22] = 1; + out[M23] = 0; + out[M30] = 0; + out[M31] = 0; + out[M32] = 0; + out[M33] = 1; + } + + private static float[] getTranslation(float x, float y, float z, float[] out) + { + setIdentity( out ); + out[M03] = x; + out[M13] = y; + out[M23] = z; + return out; + } + + private static float[] getScaling(float x, float y, float z, float[] out) + { + setIdentity( out ); + out[M00] = x; + out[M11] = y; + out[M22] = z; + return out; + } + + /* + + private static float[] getSkewing(float x, float y, float z, float[] out) + { + setIdentity( out ); + out[M10] = x; + out[M01] = y; + return out; + } + + private static float[] getRotation(float yaw, float pitch, float roll, float[] out) + { + return out; + } + + */ + + private static float[] getAxisRotation(float cos, float sin, Vec3f axis, float[] out) + { + float oc = 1.0f - cos; + out[M00] = oc * axis.x * axis.x + cos; + out[M10] = oc * axis.x * axis.y - axis.z * sin; + out[M20] = oc * axis.z * axis.x + axis.y * sin; + out[M01] = oc * axis.x * axis.y + axis.z * sin; + out[M11] = oc * axis.y * axis.y + cos; + out[M21] = oc * axis.y * axis.z - axis.x * sin; + out[M02] = oc * axis.z * axis.x - axis.y * sin; + out[M12] = oc * axis.y * axis.z + axis.x * sin; + out[M22] = oc * axis.z * axis.z + cos; + return out; + } + + private static float[] multiply(float[] a_, float[] b_) + { + float[] out = temp1; + + out[M00] = a_[M00] * b_[M00] + a_[M01] * b_[M10] + a_[M02] * b_[M20]; + out[M01] = a_[M00] * b_[M01] + a_[M01] * b_[M11] + a_[M02] * b_[M21]; + out[M02] = a_[M00] * b_[M02] + a_[M01] * b_[M12] + a_[M02] * b_[M22]; + + out[M10] = a_[M10] * b_[M00] + a_[M11] * b_[M10] + a_[M12] * b_[M20]; + out[M11] = a_[M10] * b_[M01] + a_[M11] * b_[M11] + a_[M12] * b_[M21]; + out[M12] = a_[M10] * b_[M02] + a_[M11] * b_[M12] + a_[M12] * b_[M22]; + + out[M20] = a_[M20] * b_[M00] + a_[M21] * b_[M10] + a_[M22] * b_[M20]; + out[M21] = a_[M20] * b_[M01] + a_[M21] * b_[M11] + a_[M22] * b_[M21]; + out[M22] = a_[M20] * b_[M02] + a_[M21] * b_[M12] + a_[M22] * b_[M22]; + + return out; + } + +} diff --git a/src/com/axe3d/node/NodeList3.java b/src/com/axe3d/node/NodeList3.java new file mode 100644 index 0000000..89d29ec --- /dev/null +++ b/src/com/axe3d/node/NodeList3.java @@ -0,0 +1,41 @@ +package com.axe3d.node; + +import java.util.Comparator; + +import com.axe.collect.List; +import com.axe.collect.ListArray; +import com.axe.collect.ListLinked; +import com.axe.collect.ListSorted; +import com.axe.math.Vec3f; +import com.axe.node.Node; +import com.axe.node.NodeList; + +public class NodeList3> extends NodeList +{ + + public static > NodeList3 create3(List nodes) + { + return new NodeList3( nodes ); + } + + public static > NodeList3 linked3() + { + return new NodeList3( ListLinked.create() ); + } + + public static > NodeList3 array3(T ... nodes) + { + return new NodeList3( ListArray.create( nodes ) ); + } + + public static > NodeList3 sorted3(Comparator comparator, T ... nodes) + { + return new NodeList3( ListSorted.create( comparator, nodes ) ); + } + + protected NodeList3(List nodes) + { + super( nodes ); + } + +} diff --git a/src/com/axe3d/node/Nodes3.java b/src/com/axe3d/node/Nodes3.java new file mode 100644 index 0000000..888fd9a --- /dev/null +++ b/src/com/axe3d/node/Nodes3.java @@ -0,0 +1,40 @@ +package com.axe3d.node; + +import java.util.Arrays; + +import com.axe.math.Vec3f; +import com.axe.node.Node; +import com.axe.node.Nodes; + +public class Nodes3> extends Nodes +{ + + public Nodes3(N ... initial) + { + this( DEFAULT_CAPACITY, DEFAULT_INCREASE, DEFAULT_EXPIRE, initial ); + } + + public Nodes3(int capacity, N ... initial) + { + this( capacity, DEFAULT_INCREASE, DEFAULT_EXPIRE, initial ); + } + + public Nodes3(int capacity, boolean expireOnEmpty, N ... initial) + { + this( capacity, DEFAULT_INCREASE, expireOnEmpty, initial ); + } + + public Nodes3(boolean expireOnEmpty, N ... initial) + { + this( DEFAULT_CAPACITY, DEFAULT_INCREASE, expireOnEmpty, initial ); + } + + public Nodes3(int capacity, int increase, boolean expireOnEmpty, N ... initial) + { + this.nodes = Arrays.copyOf( initial, Math.max( capacity, initial.length ) ); + this.size = initial.length; + this.increase = increase; + this.expireOnEmpty = expireOnEmpty; + } + +} diff --git a/src/com/axe3d/sprite/PhysicsSprite3.java b/src/com/axe3d/sprite/PhysicsSprite3.java new file mode 100644 index 0000000..86cb745 --- /dev/null +++ b/src/com/axe3d/sprite/PhysicsSprite3.java @@ -0,0 +1,17 @@ +package com.axe3d.sprite; + +import com.axe.Axe; +import com.axe.math.Vec3f; +import com.axe.sprite.PhysicsSprite; + +public class PhysicsSprite3 extends PhysicsSprite +{ + + public static final int VIEW = Axe.renderers.create(); + + public PhysicsSprite3() + { + super( VIEW, Vec3f.FACTORY ); + } + +} diff --git a/src/com/axe3d/sprite/SimpleSprite3.java b/src/com/axe3d/sprite/SimpleSprite3.java new file mode 100644 index 0000000..05ee199 --- /dev/null +++ b/src/com/axe3d/sprite/SimpleSprite3.java @@ -0,0 +1,36 @@ +package com.axe3d.sprite; + +import com.axe.Axe; +import com.axe.game.GameState; +import com.axe.math.Vec3f; +import com.axe.sprite.SimpleSprite; + +public class SimpleSprite3 extends SimpleSprite +{ + + public static final int VIEW = Axe.renderers.create(); + + public SimpleSprite3() + { + super( VIEW, Vec3f.FACTORY ); + } + + @Override + public boolean getBounds(GameState state, Vec3f min, Vec3f max) + { + min.set( position ); + max.set( position ); + + // size is dependent on camera + + return false; + } + + public SimpleSprite3 setPosition(float x, float y, float z) + { + this.position.set( x, y, z ); + + return this; + } + +} diff --git a/src/com/axe3d/sprite/Sprite3.java b/src/com/axe3d/sprite/Sprite3.java new file mode 100644 index 0000000..3d44aa3 --- /dev/null +++ b/src/com/axe3d/sprite/Sprite3.java @@ -0,0 +1,17 @@ +package com.axe3d.sprite; + +import com.axe.Axe; +import com.axe.math.Vec3f; +import com.axe.sprite.Sprite; + +public class Sprite3 extends Sprite +{ + + public static final int VIEW = Axe.renderers.create(); + + public Sprite3() + { + super( VIEW, Vec3f.FACTORY ); + } + +} diff --git a/test/com/axe/math/TestBound2f.java b/test/com/axe/math/TestBound2f.java new file mode 100644 index 0000000..0f6653b --- /dev/null +++ b/test/com/axe/math/TestBound2f.java @@ -0,0 +1,156 @@ +package com.axe.math; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class TestBound2f +{ + + @Test + public void include() + { + Bound2f b = new Bound2f(1, 2); + + assertEquals( 1, b.l, 0 ); + assertEquals( 1, b.r, 0 ); + assertEquals( 2, b.t, 0 ); + assertEquals( 2, b.b, 0 ); + + b.include( 0.5f, 3 ); + + assertEquals( 0.5, b.l, 0 ); + assertEquals( 1, b.r, 0 ); + assertEquals( 3, b.t, 0 ); + assertEquals( 2, b.b, 0 ); + + b.include( 2, -1 ); + + assertEquals( b.l, 0.5, 0 ); + assertEquals( b.r, 2, 0 ); + assertEquals( b.t, 3, 0 ); + assertEquals( b.b, -1, 0 ); + } + + @Test + public void setMinMax() + { + Bound2f b = new Bound2f(); + + Vec2f min = new Vec2f( 0.5f, -1 ); + Vec2f max = new Vec2f( 3, 2 ); + + b.set( min, max ); + + assertEquals( b.l, 0.5f, 0 ); + assertEquals( b.r, 3, 0 ); + assertEquals( b.t, 2, 0 ); + assertEquals( b.b, -1, 0 ); + } + + @Test + public void setWidthFromLeft() + { + Bound2f b = new Bound2f(1, 3, 2, 2); + + b.setWidthFromLeft(4); + + assertEquals( b.l, 1, 0 ); + assertEquals( b.r, 5, 0 ); + assertEquals( b.t, 3, 0 ); + assertEquals( b.b, 2, 0 ); + } + + @Test + public void setWidthFromRight() + { + Bound2f b = new Bound2f(1, 3, 2, 2); + + b.setWidthFromRight(4); + + assertEquals( b.l, -2, 0 ); + assertEquals( b.r, 2, 0 ); + assertEquals( b.t, 3, 0 ); + assertEquals( b.b, 2, 0 ); + } + + @Test + public void setHeightFromTop() + { + Bound2f b = new Bound2f(1, 3, 2, 2); + + b.setHeightFromTop(4); + + assertEquals( b.l, 1, 0 ); + assertEquals( b.r, 2, 0 ); + assertEquals( b.t, 3, 0 ); + assertEquals( b.b, -1, 0 ); + } + + @Test + public void setHeightFromBottom() + { + Bound2f b = new Bound2f(1, 3, 2, 2); + + b.setHeightFromBottom(4); + + assertEquals( b.l, 1, 0 ); + assertEquals( b.r, 2, 0 ); + assertEquals( b.t, 6, 0 ); + assertEquals( b.b, 2, 0 ); + } + + @Test + public void expand() + { + Bound2f b = new Bound2f(1, 3, 2, 2); + + b.expand(1, 2); + + assertEquals( b.l, 0, 0 ); + assertEquals( b.r, 3, 0 ); + assertEquals( b.t, 5, 0 ); + assertEquals( b.b, 0, 0 ); + } + + @Test + public void isNegative() + { + Bound2f b = new Bound2f(0, 0, 0, 0); + + assertFalse( b.isNegative() ); + + b.l = 1; + + assertTrue( b.isNegative() ); + + b.l = 0; + + assertFalse( b.isNegative() ); + + b.b = 1; + + assertTrue( b.isNegative() ); + } + + @Test + public void x() + { + Bound2f b = new Bound2f(1, 4, 4, 2); + + assertEquals( 1, b.x( 0 ), 0 ); + assertEquals( 2.5f, b.x( 0.5f ), 0 ); + assertEquals( 4, b.x( 1 ), 0 ); + } + + @Test + public void y() + { + Bound2f b = new Bound2f(1, 4, 4, 2); + + assertEquals( 4, b.y( 0 ), 0 ); + assertEquals( 3, b.y( 0.5f ), 0 ); + assertEquals( 2, b.y( 1 ), 0 ); + } + +}