From bd5260e0f3f6a17f7d0aacb7baf631a6d98d59ba Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sat, 22 Sep 2018 14:10:36 +0200 Subject: [PATCH] Better parenthesis button behavior --- .../it/cavallium/warppi/device/Keyboard.java | 8 +- .../warppi/gui/expression/blocks/Block.java | 17 +- .../gui/expression/blocks/BlockContainer.java | 41 +- .../gui/expression/blocks/BlockDivision.java | 4 +- .../gui/expression/blocks/BlockLogarithm.java | 10 +- .../expression/blocks/BlockParenthesis.java | 1 - .../blocks/BlockParenthesisAbstract.java | 8 +- .../gui/expression/blocks/BlockPower.java | 2 +- .../gui/expression/blocks/BlockPower2.java | 2 +- .../expression/blocks/BlockSquareRoot.java | 2 +- .../gui/expression/blocks/IParenthesis.java | 5 + .../gui/expression/blocks/TreeBlock.java | 6 + .../gui/expression/blocks/TreeContainer.java | 6 + .../containers/InlineInputContainer.java | 7 + .../expression/containers/InputContainer.java | 35 +- .../containers/NormalInputContainer.java | 39 +- .../containers/OutputContainer.java | 14 +- .../java/it/cavallium/warppi/util/Utils.java | 1 + core/src/main/resources/font_norm.rft | Bin 1956 -> 1975 bytes core/src/main/resources/font_smal.rft | Bin 954 -> 963 bytes core/src/main/resources/font_smallest.rft | Bin 676 -> 683 bytes core/src/main/resources/font_square.rft | Bin 837 -> 846 bytes core/src/main/resources/old_font_big_2x.rft | Bin 2583 -> 2610 bytes core/src/main/resources/old_font_small_2x.rft | Bin 1804 -> 1823 bytes desktop/math-rules-cache.zip | Bin 102487 -> 102518 bytes .../impl/swing/SwingAdvancedButton.java | 16 +- .../graphicengine/impl/swing/SwingWindow.java | 55 +- .../src/main/resources/desktop-buttons.png | Bin 600 -> 767 bytes .../src/main/resources/desktop-buttons.xcf | Bin 5339 -> 6462 bytes {engine-gpu => engine-jogl}/.classpath | 54 +- {engine-gpu => engine-jogl}/.gitignore | 0 {engine-gpu => engine-jogl}/.project | 46 +- .../org.eclipse.core.resources.prefs | 6 +- .../.settings/org.eclipse.jdt.core.prefs | 12 +- .../.settings/org.eclipse.m2e.core.prefs | 8 +- {engine-gpu => engine-jogl}/pom.xml | 122 +- .../graphicengine/impl/jogl/JOGLEngine.java | 404 +++--- .../gui/graphicengine/impl/jogl/JOGLFont.java | 470 +++---- .../graphicengine/impl/jogl/JOGLRenderer.java | 892 ++++++------- .../gui/graphicengine/impl/jogl/JOGLSkin.java | 150 +-- .../graphicengine/impl/jogl/NEWTWindow.java | 1104 ++++++++--------- .../main/java/rules/functions/NumberRule.java | 15 +- 42 files changed, 1863 insertions(+), 1699 deletions(-) create mode 100644 core/src/main/java/it/cavallium/warppi/gui/expression/blocks/IParenthesis.java create mode 100644 core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeBlock.java create mode 100644 core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeContainer.java rename {engine-gpu => engine-jogl}/.classpath (97%) rename {engine-gpu => engine-jogl}/.gitignore (100%) rename {engine-gpu => engine-jogl}/.project (95%) rename {engine-gpu => engine-jogl}/.settings/org.eclipse.core.resources.prefs (96%) rename {engine-gpu => engine-jogl}/.settings/org.eclipse.jdt.core.prefs (97%) rename {engine-gpu => engine-jogl}/.settings/org.eclipse.m2e.core.prefs (95%) rename {engine-gpu => engine-jogl}/pom.xml (96%) rename {engine-gpu => engine-jogl}/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java (96%) rename {engine-gpu => engine-jogl}/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java (96%) rename {engine-gpu => engine-jogl}/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java (97%) rename {engine-gpu => engine-jogl}/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java (95%) rename {engine-gpu => engine-jogl}/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java (97%) diff --git a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java index d9edafea..fd1e9a4a 100644 --- a/core/src/main/java/it/cavallium/warppi/device/Keyboard.java +++ b/core/src/main/java/it/cavallium/warppi/device/Keyboard.java @@ -639,7 +639,7 @@ public class Keyboard { { "", null, null } /* 1,7 */ }, { /* ROW 2 */ { "", null, null }, /* 2,0 */ - { "√", null, null }, /* 2,1 */ + { "√▯", null, null }, /* 2,1 */ { "", null, null }, /* 2,2 */ { "⇩", null, null }, /* 2,3 */ { "↶", null, null }, /* 2,4 */ @@ -649,8 +649,8 @@ public class Keyboard { }, { /* ROW 3 */ { "", null, null }, /* 3,0 */ { "", null, null }, /* 3,1 */ - { "^x", null, null }, /* 3,2 */ - { "^2", null, null }, /* 3,3 */ + { "▯^▯", null, null }, /* 3,2 */ + { "▯^2", null, null }, /* 3,3 */ { "", null, null }, /* 3,4 */ { "", null, null }, /* 3,5 */ { "", null, null }, /* 3,6 */ @@ -658,7 +658,7 @@ public class Keyboard { }, { /* ROW 4 */ { "", null, null }, /* 4,0 */ { "", null, null }, /* 4,1 */ - { "(", null, null }, /* 4,2 */ + { "(▯)", null, null }, /* 4,2 */ { ")", null, null }, /* 4,3 */ { "", null, null }, /* 4,4 */ { "S⇔D", null, null }, /* 4,5 */ diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java index 3a68f6cf..8636249e 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/Block.java @@ -9,12 +9,13 @@ import it.cavallium.warppi.math.MathContext; import it.cavallium.warppi.math.parser.features.interfaces.Feature; import it.cavallium.warppi.util.Error; -public abstract class Block implements GraphicalElement { +public abstract class Block implements TreeBlock, GraphicalElement { protected boolean small; protected int width; protected int height; protected int line; + protected TreeContainer parent; /** * @@ -69,4 +70,18 @@ public abstract class Block implements GraphicalElement { } public abstract Feature toFeature(MathContext context) throws Error; + + @Override + public TreeContainer getParentContainer() { + return parent; + } + + @Override + public boolean hasParent() { + return parent != null; + } + + public void setParent(TreeContainer parent) { + this.parent = parent; + } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java index f52887de..ee43d260 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockContainer.java @@ -15,7 +15,7 @@ import it.cavallium.warppi.util.Error; import it.cavallium.warppi.util.Errors; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -public class BlockContainer implements GraphicalElement { +public class BlockContainer implements TreeContainer, GraphicalElement { private static boolean initialized = false; @@ -28,33 +28,35 @@ public class BlockContainer implements GraphicalElement { private int line; public final boolean withBorder; private boolean autoMinimums; + private TreeBlock parent; - public BlockContainer() { - this(false, BlockContainer.getDefaultCharWidth(false), BlockContainer.getDefaultCharHeight(false), true); + public BlockContainer(TreeBlock parent) { + this(parent, false, BlockContainer.getDefaultCharWidth(false), BlockContainer.getDefaultCharHeight(false), true); autoMinimums = true; } - public BlockContainer(final boolean small) { - this(small, BlockContainer.getDefaultCharWidth(small), BlockContainer.getDefaultCharHeight(small), true); + public BlockContainer(TreeBlock parent, final boolean small) { + this(parent, small, BlockContainer.getDefaultCharWidth(small), BlockContainer.getDefaultCharHeight(small), true); autoMinimums = true; } - public BlockContainer(final boolean small, final ObjectArrayList content) { - this(small, BlockContainer.getDefaultCharWidth(small), BlockContainer.getDefaultCharHeight(small), content, true); + public BlockContainer(TreeBlock parent, final boolean small, final ObjectArrayList content) { + this(parent, small, BlockContainer.getDefaultCharWidth(small), BlockContainer.getDefaultCharHeight(small), content, true); autoMinimums = true; } - public BlockContainer(final boolean small, final boolean withBorder) { - this(small, BlockContainer.getDefaultCharWidth(small), BlockContainer.getDefaultCharHeight(small), withBorder); + public BlockContainer(TreeBlock parent, final boolean small, final boolean withBorder) { + this(parent, small, BlockContainer.getDefaultCharWidth(small), BlockContainer.getDefaultCharHeight(small), withBorder); autoMinimums = true; } - public BlockContainer(final boolean small, final int minWidth, final int minHeight, final boolean withBorder) { - this(small, minWidth, minHeight, new ObjectArrayList<>(), withBorder); + public BlockContainer(TreeBlock parent, final boolean small, final int minWidth, final int minHeight, final boolean withBorder) { + this(parent, small, minWidth, minHeight, new ObjectArrayList<>(), withBorder); autoMinimums = false; } - public BlockContainer(final boolean small, final int minWidth, final int minHeight, final ObjectArrayList content, final boolean withBorder) { + public BlockContainer(TreeBlock parent, final boolean small, final int minWidth, final int minHeight, final ObjectArrayList content, final boolean withBorder) { + this.parent = parent; this.small = small; this.minWidth = minWidth; this.minHeight = minHeight; @@ -65,6 +67,16 @@ public class BlockContainer implements GraphicalElement { this.content = content; recomputeDimensions(); } + + @Override + public TreeBlock getParentBlock() { + return parent; + } + + @Override + public boolean hasParent() { + return parent != null; + } public void addBlock(final int position, final Block b) { addBlockUnsafe(position, b); @@ -72,6 +84,7 @@ public class BlockContainer implements GraphicalElement { } public void addBlockUnsafe(final int position, final Block b) { + b.setParent(this); if (b.isSmall() != small) b.setSmall(small); if (position >= content.size()) @@ -86,6 +99,7 @@ public class BlockContainer implements GraphicalElement { } public void appendBlockUnsafe(final Block b) { + b.setParent(this); if (b.isSmall() != small) b.setSmall(small); content.add(b); @@ -97,11 +111,12 @@ public class BlockContainer implements GraphicalElement { } public void removeBlockUnsafe(final Block b) { + b.setParent(null); content.remove(b); } public void removeAt(final int i) { - content.remove(i); + content.remove(i).setParent(null); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java index 093ebf6c..48718901 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockDivision.java @@ -19,8 +19,8 @@ public class BlockDivision extends Block { private int h1; public BlockDivision() { - containerUp = new BlockContainer(false); - containerDown = new BlockContainer(false); + containerUp = new BlockContainer(this, false); + containerDown = new BlockContainer(this, false); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java index 6f762dc0..a4beae1e 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockLogarithm.java @@ -10,7 +10,7 @@ import it.cavallium.warppi.math.parser.features.interfaces.Feature; import it.cavallium.warppi.util.Error; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -public class BlockLogarithm extends Block { +public class BlockLogarithm extends Block implements IParenthesis { private final BlockContainer containerBase; private final BlockContainer containerNumber; @@ -28,14 +28,14 @@ public class BlockLogarithm extends Block { private int toph; public BlockLogarithm() { - containerBase = new BlockContainer(true); - containerNumber = new BlockContainer(false); + containerBase = new BlockContainer(this, true); + containerNumber = new BlockContainer(this, false); recomputeDimensions(); } public BlockLogarithm(final ObjectArrayList blocks) { - containerBase = new BlockContainer(true); - containerNumber = new BlockContainer(false, blocks); + containerBase = new BlockContainer(this, true); + containerNumber = new BlockContainer(this, false, blocks); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java index 15c3bf8e..175d9a44 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesis.java @@ -9,7 +9,6 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; public class BlockParenthesis extends BlockParenthesisAbstract { public BlockParenthesis() { - super(); } public BlockParenthesis(final ObjectArrayList blocks) { diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java index db9537d9..b5a0dd10 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockParenthesisAbstract.java @@ -8,7 +8,7 @@ import it.cavallium.warppi.math.parser.features.interfaces.Feature; import it.cavallium.warppi.util.Error; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -public abstract class BlockParenthesisAbstract extends Block { +public abstract class BlockParenthesisAbstract extends Block implements IParenthesis { private final BlockContainer containerNumber; @@ -18,20 +18,20 @@ public abstract class BlockParenthesisAbstract extends Block { private int chh; protected BlockParenthesisAbstract(final String prefix) { - containerNumber = new BlockContainer(false); + containerNumber = new BlockContainer(this, false); this.prefix = prefix; recomputeDimensions(); } public BlockParenthesisAbstract() { - containerNumber = new BlockContainer(false); + containerNumber = new BlockContainer(this, false); prefix = null; recomputeDimensions(); } public BlockParenthesisAbstract(final ObjectArrayList blocks) { - containerNumber = new BlockContainer(false, blocks); + containerNumber = new BlockContainer(this, false, blocks); prefix = null; recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java index 1fe926bd..7cd61129 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower.java @@ -14,7 +14,7 @@ public class BlockPower extends Block { private final BlockContainer containerExponent; public BlockPower() { - containerExponent = new BlockContainer(true); + containerExponent = new BlockContainer(this, true); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java index d6c3cbee..c9c89591 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockPower2.java @@ -14,7 +14,7 @@ public class BlockPower2 extends Block { private final BlockContainer containerExponent; public BlockPower2() { - containerExponent = new BlockContainer(true); + containerExponent = new BlockContainer(this, true); containerExponent.addBlock(0, new BlockNumericChar('2')); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java index e3a68176..90eca02a 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/BlockSquareRoot.java @@ -16,7 +16,7 @@ public class BlockSquareRoot extends Block { private int h1; public BlockSquareRoot() { - containerNumber = new BlockContainer(false); + containerNumber = new BlockContainer(this, false); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/IParenthesis.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/IParenthesis.java new file mode 100644 index 00000000..12122558 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/IParenthesis.java @@ -0,0 +1,5 @@ +package it.cavallium.warppi.gui.expression.blocks; + +public interface IParenthesis { + +} diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeBlock.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeBlock.java new file mode 100644 index 00000000..54fe4aba --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeBlock.java @@ -0,0 +1,6 @@ +package it.cavallium.warppi.gui.expression.blocks; + +public interface TreeBlock { + public TreeContainer getParentContainer(); + public boolean hasParent(); +} diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeContainer.java new file mode 100644 index 00000000..95b2553a --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/blocks/TreeContainer.java @@ -0,0 +1,6 @@ +package it.cavallium.warppi.gui.expression.blocks; + +public interface TreeContainer { + public TreeBlock getParentBlock(); + public boolean hasParent(); +} diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InlineInputContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InlineInputContainer.java index 20c85d42..e9d57d8a 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InlineInputContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InlineInputContainer.java @@ -1,8 +1,15 @@ package it.cavallium.warppi.gui.expression.containers; +import it.cavallium.warppi.gui.expression.Caret; import it.cavallium.warppi.gui.expression.InputContext; import it.cavallium.warppi.gui.expression.blocks.Block; import it.cavallium.warppi.gui.expression.blocks.BlockChar; +import it.cavallium.warppi.gui.expression.blocks.BlockReference; +import it.cavallium.warppi.gui.graphicengine.GraphicEngine; +import it.cavallium.warppi.gui.graphicengine.Renderer; +import it.cavallium.warppi.math.MathContext; +import it.cavallium.warppi.math.parser.features.interfaces.Feature; +import it.cavallium.warppi.util.Error; public class InlineInputContainer extends InputContainer { diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java index b8e0cf5d..b98acc47 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/InputContainer.java @@ -11,6 +11,8 @@ import it.cavallium.warppi.gui.expression.InputContext; import it.cavallium.warppi.gui.expression.blocks.Block; import it.cavallium.warppi.gui.expression.blocks.BlockContainer; import it.cavallium.warppi.gui.expression.blocks.BlockReference; +import it.cavallium.warppi.gui.expression.blocks.TreeContainer; +import it.cavallium.warppi.gui.expression.blocks.TreeBlock; import it.cavallium.warppi.gui.expression.layouts.InputLayout; import it.cavallium.warppi.gui.graphicengine.GraphicEngine; import it.cavallium.warppi.gui.graphicengine.Renderer; @@ -21,8 +23,8 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; public abstract class InputContainer implements GraphicalElement, InputLayout, Serializable { private static final long serialVersionUID = 923589369317765667L; - private final BlockContainer root; - private Caret caret; + protected final BlockContainer root; + protected Caret caret; private static final float CARET_DURATION = 0.5f; private float caretTime; private int maxPosition = 0; @@ -53,7 +55,7 @@ public abstract class InputContainer implements GraphicalElement, InputLayout, S public InputContainer(final InputContext ic, final boolean small, final int minWidth, final int minHeight) { inputContext = ic; caret = new Caret(CaretState.VISIBLE_ON, 0); - root = new BlockContainer(small, false); + root = new BlockContainer(null, small, false); } public void typeChar(final char c) { @@ -69,10 +71,10 @@ public abstract class InputContainer implements GraphicalElement, InputLayout, S maxPosition = root.computeCaretMaxBound(); root.recomputeDimensions(); } + closeExtra(); } caretTime = 0; caret.turnOn(); - closeExtra(); } public void typeChar(final String c) { @@ -98,6 +100,11 @@ public abstract class InputContainer implements GraphicalElement, InputLayout, S return selectedBlock; } + public BlockReference getBlockAtCaretPosition(int i) { + final BlockReference selectedBlock = root.getBlock(new Caret(CaretState.HIDDEN, i)); + return selectedBlock; + } + public void moveLeft() { final int curPos = caret.getPosition(); if (curPos > 0) @@ -109,10 +116,10 @@ public abstract class InputContainer implements GraphicalElement, InputLayout, S closeExtra(); } - public void moveRight() { + public void moveRight(int delta) { final int curPos = caret.getPosition(); - if (curPos + 1 < maxPosition) - caret.setPosition(curPos + 1); + if (curPos + delta < maxPosition) + caret.setPosition(curPos + delta); else caret.setPosition(0); caret.turnOn(); @@ -120,6 +127,20 @@ public abstract class InputContainer implements GraphicalElement, InputLayout, S closeExtra(); } + public void moveTo(int position) { + if (position < maxPosition) + caret.setPosition(position); + else + caret.setPosition(0); + caret.turnOn(); + caretTime = 0; + closeExtra(); + } + + public void moveRight() { + moveRight(1); + } + @Override public void recomputeDimensions() { root.recomputeDimensions(); diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java index 6a05f41b..821f78ff 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/NormalInputContainer.java @@ -1,5 +1,7 @@ package it.cavallium.warppi.gui.expression.containers; +import it.cavallium.warppi.gui.expression.Caret; +import it.cavallium.warppi.gui.expression.CaretState; import it.cavallium.warppi.gui.expression.InputContext; import it.cavallium.warppi.gui.expression.blocks.Block; import it.cavallium.warppi.gui.expression.blocks.BlockChar; @@ -8,12 +10,16 @@ import it.cavallium.warppi.gui.expression.blocks.BlockDivision; import it.cavallium.warppi.gui.expression.blocks.BlockLogarithm; import it.cavallium.warppi.gui.expression.blocks.BlockNumericChar; import it.cavallium.warppi.gui.expression.blocks.BlockParenthesis; +import it.cavallium.warppi.gui.expression.blocks.BlockParenthesisAbstract; import it.cavallium.warppi.gui.expression.blocks.BlockPower; import it.cavallium.warppi.gui.expression.blocks.BlockPower2; import it.cavallium.warppi.gui.expression.blocks.BlockReference; import it.cavallium.warppi.gui.expression.blocks.BlockSine; import it.cavallium.warppi.gui.expression.blocks.BlockSquareRoot; import it.cavallium.warppi.gui.expression.blocks.BlockVariable; +import it.cavallium.warppi.gui.expression.blocks.IParenthesis; +import it.cavallium.warppi.gui.expression.blocks.TreeBlock; +import it.cavallium.warppi.gui.expression.blocks.TreeContainer; import it.cavallium.warppi.math.MathematicalSymbols; public class NormalInputContainer extends InputContainer { @@ -92,9 +98,33 @@ public class NormalInputContainer extends InputContainer { public void typeChar(final char c) { super.typeChar(c); switch (c) { - case MathematicalSymbols.PARENTHESIS_CLOSE: - moveRight(); - case MathematicalSymbols.DIVISION: + case MathematicalSymbols.PARENTHESIS_CLOSE: { + BlockReference ref = getSelectedBlock(); + if (ref == null) { + break; + } else { + Caret newCaret = new Caret(CaretState.HIDDEN, caret.getPosition()); + BlockContainer currentContainer; + BlockReference newRef = ref; + int safeExit = 0; + do { + currentContainer = (BlockContainer) newRef.get().getParentContainer(); + int initialRelativeIndex = currentContainer.getContent().indexOf(newRef.get()); + int newIndex = newCaret.getPosition() + (currentContainer.getContent().size() - initialRelativeIndex); + newRef = getBlockAtCaretPosition(newIndex); + newCaret.setPosition(newIndex); + safeExit++; + } while (newRef != null && newRef.get() instanceof IParenthesis == false && currentContainer != null && safeExit < 100000); + if (safeExit >= 100000) { + System.err.println("Error 0x001030: Infinite loop"); + } + if (newRef != null) { + moveTo(newCaret.getPosition()); + } + } + break; + } + case MathematicalSymbols.DIVISION: { @SuppressWarnings("unchecked") final BlockReference ref = (BlockReference) getSelectedBlock(); @SuppressWarnings("unused") @@ -128,7 +158,8 @@ public class NormalInputContainer extends InputContainer { moveRight(); moveRight();// Move to the divisor } - + break; + } } } } diff --git a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/OutputContainer.java b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/OutputContainer.java index 16fef8d0..2395663b 100644 --- a/core/src/main/java/it/cavallium/warppi/gui/expression/containers/OutputContainer.java +++ b/core/src/main/java/it/cavallium/warppi/gui/expression/containers/OutputContainer.java @@ -19,22 +19,22 @@ public abstract class OutputContainer implements GraphicalElement, OutputLayout, public OutputContainer() { roots = new ObjectArrayList<>(); - roots.add(new BlockContainer()); + roots.add(new BlockContainer(null)); } public OutputContainer(final boolean small) { roots = new ObjectArrayList<>(); - roots.add(new BlockContainer(small)); + roots.add(new BlockContainer(null, small)); } public OutputContainer(final boolean small, final int minWidth, final int minHeight) { roots = new ObjectArrayList<>(); - roots.add(new BlockContainer(small)); + roots.add(new BlockContainer(null, small)); } public void setContentAsSingleGroup(final ObjectArrayList blocks) { roots.clear(); - final BlockContainer bcnt = new BlockContainer(); + final BlockContainer bcnt = new BlockContainer(null); for (final Block block : blocks) bcnt.appendBlockUnsafe(block); roots.add(bcnt); @@ -44,7 +44,7 @@ public abstract class OutputContainer implements GraphicalElement, OutputLayout, public void setContentAsMultipleGroups(final ObjectArrayList> roots) { this.roots.clear(); for (final ObjectArrayList blocks : roots) { - final BlockContainer bcnt = new BlockContainer(); + final BlockContainer bcnt = new BlockContainer(null); for (final Block block : blocks) bcnt.appendBlockUnsafe(block); this.roots.add(bcnt); @@ -55,7 +55,7 @@ public abstract class OutputContainer implements GraphicalElement, OutputLayout, public void setContentAsMultipleElements(final ObjectArrayList elems) { roots.clear(); for (final Block block : elems) { - final BlockContainer bcnt = new BlockContainer(); + final BlockContainer bcnt = new BlockContainer(null); bcnt.appendBlockUnsafe(block); roots.add(bcnt); } @@ -125,7 +125,7 @@ public abstract class OutputContainer implements GraphicalElement, OutputLayout, public void clear() { roots.clear(); - roots.add(new BlockContainer()); + roots.add(new BlockContainer(null)); recomputeDimensions(); } diff --git a/core/src/main/java/it/cavallium/warppi/util/Utils.java b/core/src/main/java/it/cavallium/warppi/util/Utils.java index 097274f4..6d7f4741 100644 --- a/core/src/main/java/it/cavallium/warppi/util/Utils.java +++ b/core/src/main/java/it/cavallium/warppi/util/Utils.java @@ -47,6 +47,7 @@ public class Utils { public static final int scaleMode = BigDecimal.ROUND_HALF_UP; public static final RoundingMode scaleMode2 = RoundingMode.HALF_UP; + public static final int maxAutoFractionDigits = 5; public static boolean newtMode = true; diff --git a/core/src/main/resources/font_norm.rft b/core/src/main/resources/font_norm.rft index d4e5e1e7e1d58028d00ea0f0784ac4b637e19b0c..e3b842db0faf10ac8868098995ffd131bafc97fe 100644 GIT binary patch delta 27 jcmZ3&zny=>5_VzL_5Uw$F*<0lGAs~bY6#$BtbYIih6xDs delta 7 Ocmdnazl49o5_SL#F#^>9 diff --git a/core/src/main/resources/font_smal.rft b/core/src/main/resources/font_smal.rft index 0f034b497f24d9640f6e260e56f6f03d9228ad3a..fa999c2f36cddeffc264308e6a1aa026336dcc41 100644 GIT binary patch delta 17 ZcmdnRewcm3E@n>E^}jS0b!ja60RTZ>2j&0( delta 7 OcmX@izKeasE@l7>%L4fT diff --git a/core/src/main/resources/font_smallest.rft b/core/src/main/resources/font_smallest.rft index 508ac55141895192886db8b8172ed81c1103dcd0..5ea9458b1cd493c92d60d8398b8fa4cc96db8343 100644 GIT binary patch delta 15 XcmZ3&x|(&v5+-)l^}jS0bw2D>J9+`d=E0x-=I3002EC2ZaCt delta 7 OcmX@dc9d;{D>DELTLPE> diff --git a/core/src/main/resources/old_font_big_2x.rft b/core/src/main/resources/old_font_big_2x.rft index 0b9e1ff36db50b82f47fb24707489141affdca56..b6def7d8bd18d4eff5df5bc50a8888e336819ab7 100644 GIT binary patch delta 35 icmbO(vPoovIG42Q`u`WW7#%cN85W2zH3T4-@jn2&%nAem delta 7 OcmdlaGF@bYI2Qm4=mJ9k diff --git a/core/src/main/resources/old_font_small_2x.rft b/core/src/main/resources/old_font_small_2x.rft index 55c45cf42daf546f6a38a9b1e67febb714e723df..31b32fa7e29f426a4d36494ce374c9631e455a45 100644 GIT binary patch delta 27 jcmeC-o6om_hh128{r?MGj1C&C3=2e<8UnZ&>mL9BbLR*t delta 7 OcmbQw*Tc7ghaCV3*8&Rw diff --git a/desktop/math-rules-cache.zip b/desktop/math-rules-cache.zip index e6c2c06a05e804db9569c5930f980169bcfc1a8e..47361c374ed9b302a4d6239063938b587da9f135 100644 GIT binary patch delta 44640 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 delta 44540 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 B6@&l) diff --git a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingAdvancedButton.java b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingAdvancedButton.java index 7a076600..94f4cc81 100644 --- a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingAdvancedButton.java +++ b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingAdvancedButton.java @@ -5,6 +5,7 @@ package it.cavallium.warppi.gui.graphicengine.impl.swing; import java.awt.AlphaComposite; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; @@ -26,6 +27,8 @@ public class SwingAdvancedButton extends JButton { public boolean drawColor = false; public boolean drawDefaultComponent = false; public int state; + protected boolean hover; + public Color basicForeground; public SwingAdvancedButton() { setOpaque(false); @@ -69,10 +72,14 @@ public class SwingAdvancedButton extends JButton { final AlphaComposite acomp = AlphaComposite.getInstance(3, 1.0f); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g2d.setComposite(acomp); - g2d.drawImage(backgroundImage, 0, (int) backgroundSize.getHeight() * -state, (int) backgroundSize.getWidth(), (int) (backgroundSize.getHeight() * 3), null); + g2d.drawImage(backgroundImage, 0, (int) backgroundSize.getHeight() * -(hover ? state+1 : state), (int) backgroundSize.getWidth(), (int) (backgroundSize.getHeight() * 4), null); g2d.setFont(g.getFont()); g2d.setColor(super.getForeground()); - g2d.drawString(getText(), super.getWidth() / 2 - g.getFontMetrics().stringWidth(getText()) / 2, super.getHeight() / 2 + g.getFontMetrics().getHeight() / 4); + int y = super.getHeight() / 2 + g.getFontMetrics().getHeight() / 4; + if (state == 2) { + y += 8; + } + g2d.drawString(getText(), super.getWidth() / 2 - g.getFontMetrics().stringWidth(getText()) / 2, y); g2d.dispose(); } if (drawDefaultComponent) @@ -102,4 +109,9 @@ public class SwingAdvancedButton extends JButton { public boolean getCanClick() { return canclick; } + + public void setBasicForeground(Color color) { + basicForeground = color; + super.setForeground(basicForeground); + } } diff --git a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java index 0e66dc6e..945616ae 100644 --- a/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java +++ b/desktop/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/swing/SwingWindow.java @@ -203,12 +203,16 @@ public class SwingWindow extends JFrame { buttonsPanel = new JPanel(); buttonsPanelContainer.add(buttonsPanel, BorderLayout.CENTER); buttonsPanel.setLayout(new GridLayout(9, 7)); - buttonsPanel.setBackground(Color.GRAY); + buttonsPanel.setBackground(Color.BLACK); buttonsPanel.setDoubleBuffered(false); buttonsPanel.setVisible(true); for (int row = 0; row < 5; row++) for (int col = 0; col < 7; col++) - createBtn(row, col); + if (row == 0 && col == 2 || row == 0 && col == 4 || row == 2 && col == 2) { + createBlankBox(); + } else { + createBtn(row, col); + } for (int row = 5; row < 8; row++) { createBlankBox(); for (int col = 0; col < 5; col++) @@ -234,7 +238,7 @@ public class SwingWindow extends JFrame { final SwingAdvancedButton b = new SwingAdvancedButton(img, new Dimension((int) (BTN_SIZE * 1.5), BTN_SIZE)); b.drawDefaultComponent = false; b.setText(Keyboard.getKeyName(row, col)); - b.setForeground(Color.BLACK); + b.setBasicForeground(Color.BLACK); Font f = b.getFont(); f = f.deriveFont(Font.BOLD, BTN_SIZE / 3); b.setFont(f); @@ -245,6 +249,43 @@ public class SwingWindow extends JFrame { Keyboard.keyReleasedRaw(row, col); c.grabFocus(); }); + b.addMouseListener(new MouseListener() { + @Override + public void mouseReleased(MouseEvent e) { + // TODO Auto-generated method stub + + } + @Override + public void mousePressed(MouseEvent e) { + // TODO Auto-generated method stub + + } + @Override + public void mouseExited(MouseEvent e) { + if (b.state == 2) { + b.setForeground(b.basicForeground.darker()); + } else { + b.setForeground(b.basicForeground); + } + b.hover = false; + b.repaint(); + } + @Override + public void mouseEntered(MouseEvent e) { + if (b.state == 2) { + b.setForeground(b.basicForeground); + } else { + b.setForeground(b.basicForeground.brighter()); + } + b.hover = true; + b.repaint(); + } + @Override + public void mouseClicked(MouseEvent e) { + // TODO Auto-generated method stub + + } + }); buttons[row][col] = b; buttonsPanel.add(b); } @@ -261,9 +302,9 @@ public class SwingWindow extends JFrame { else btn.state = 0; if (val && Keyboard.hasKeyName(row, col)) - btn.setForeground(Color.RED); + btn.setBasicForeground(Color.RED); else - btn.setForeground(Color.BLACK); + btn.setBasicForeground(Color.BLACK); } } } @@ -280,9 +321,9 @@ public class SwingWindow extends JFrame { else btn.state = 0; if (val && Keyboard.hasKeyName(row, col)) - btn.setForeground(new Color(255, 120, 0)); + btn.setBasicForeground(new Color(255, 120, 0)); else - btn.setForeground(Color.BLACK); + btn.setBasicForeground(Color.BLACK); } } } diff --git a/desktop/src/main/resources/desktop-buttons.png b/desktop/src/main/resources/desktop-buttons.png index a7dc55c810abe124dbd13dbac8f475e5bde5ca93..29ef7a8f936f7f8c34015652a11f4be7cc673960 100644 GIT binary patch literal 767 zcmeAS@N?(olHy`uVBq!ia0vp^20+}v!3HFKPrUjBq}Y-<@l{dZqJ z#-ka~BJ-U2UCDtEn>lCMzy03Z`SU=A$*)qo4fo%d2Z{)~UJN~M{k-7Z)((b>EkgfA z7wzqh>a@?lDsRNdBH+M)MCh?vJU`=79_`#X13fF4h!2Jn!lVCHLO5xyuKCe z#sCr5-LZ*^`_^T5J^FFbP0l+XkKYEVO6 literal 600 zcmeAS@N?(olHy`uVBq!ia0vp^20)y^!3HFwdp;HbDYhhUcNd2LAh=-f^2tCE&H|6f zVg?31We{epSZZGe6l5>)^mS!_#K|qlW^_*Blmr6<<5N!;$B>F!Z*QOPT9qJi{Ns8f z!53S2)H?JQJ}u$ZYkgt0XNsOm#!J%@X(fvXLHER6B+>$_&RkAPXe+LI@LVtU%io6j zSG5KVhfI9q&p$6_$=R?`^Z>gWxvuz6J7@W zzm3Z>BXq<{V!wasu=~enV7s6@qTxMr*Wrg7HjC9|>%9wG-Yd!VK>3N4{Dqt$)&X|9;K;|zYSHVCL+tNMX<=+2o;Akk<{@5>YIQNRzpDm&MMvN>14h%>{ z9oL6@v%#(i4dwm+MfXVc`<1J>By`t@haSz^YSj4R@q32L%)2^Rf7mU!k+=QXGv@7S zzdtiFTrx4O=3;%!UpeppWPRUBPr?}$KXA>cX@1Q5v?zE#>tzn*1oVMurT1AJ*X@{e_ zTPvQ+q((9=77UJ@w&r@aaEG1Ty6)Zi2h#T6kJp*|d->%|c9HmB@2=OLd$fxemjuu z-AfPN%qE6|Ni}*jJ^4S>D+kiD>o@y$%L8jf4^l`cna=lS-rJqG)Ax0{ne39Gt)v#T zSF!17g2bR33?Ofy0jQ$jV;I`^04`R@2(${2q5j%EtkdG;Zo&{VOsx|r6A`NizK-aoaFN#z&w1#QYP(L&~VQj6V+InoA9 zyCEG*pSM}+#kJo9Ub<2=lBsMiqurkIF4#Y{+Ro|vqRY8#Nz3L^FVaO`pI=xgrc12^ z;adIGB|L|h@^g?x`78#$Z^HVY2|MyZn&d$)+Z>c1u4ZuhHfynA+Xma z?0wl(_#{!kN(1i^@O2^T4`F?ugmpf>IT)PdTp@jYFGnnT8YA88AbxqYLAnuaH2pJz>01v6@{WLRVh-LAsc%NnIpWDao*DqJQHM_!$-V` zhd3TUY@%v+usz6hANwD#?qmM}QYsV#-q0KpOo(wv73DF2y$8PRRmKUrpJTLoMs{n&=ruPP>B?~TRqi3 zZ(c>&imI-2cSjV2a&N=p^*G$u`JW_L8O&Fbz%uN$qh` zHBJ<$23XVUN)0VxOVJY6rn~z(;j;aGy@)#R2h2ok-b^Q=xVGcyb&h8g~ng`|k;6 zQ0{d2;CQc!I*nyCb5(h=ml6p4%G_WY7H?Q^9kSBgd01= zFw8bC%M4L8tdbJe%Sza$8Q7k%RoJ33s=48mrcM9P|_Cc-%z* delta 276 zcmdmIbX#+RC}YAzF&##`jn0vbf_oXjfcpf576{zDm63~)@xbH*OwtBVppttaG?+91 zk_iw#GYf>~_zs~NHqT`{#khGM+er{%$i{eK@&z7g%`XsH0avIh5QSt4uLxA$bh8L= z2=nBVA}c5J2--|uC(6$x7zcER6qjHVM2X1x$+d#woQohl!A+Cr3Mx-NA~ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine-gpu/.gitignore b/engine-jogl/.gitignore similarity index 100% rename from engine-gpu/.gitignore rename to engine-jogl/.gitignore diff --git a/engine-gpu/.project b/engine-jogl/.project similarity index 95% rename from engine-gpu/.project rename to engine-jogl/.project index 16748742..fcaf3a80 100644 --- a/engine-gpu/.project +++ b/engine-jogl/.project @@ -1,23 +1,23 @@ - - - warppi-engine-jogl - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - + + + warppi-engine-jogl + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/engine-gpu/.settings/org.eclipse.core.resources.prefs b/engine-jogl/.settings/org.eclipse.core.resources.prefs similarity index 96% rename from engine-gpu/.settings/org.eclipse.core.resources.prefs rename to engine-jogl/.settings/org.eclipse.core.resources.prefs index 654c1750..e9441bb1 100644 --- a/engine-gpu/.settings/org.eclipse.core.resources.prefs +++ b/engine-jogl/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,3 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding/=UTF-8 +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding/=UTF-8 diff --git a/engine-gpu/.settings/org.eclipse.jdt.core.prefs b/engine-jogl/.settings/org.eclipse.jdt.core.prefs similarity index 97% rename from engine-gpu/.settings/org.eclipse.jdt.core.prefs rename to engine-jogl/.settings/org.eclipse.jdt.core.prefs index f494d1de..b8947ec6 100644 --- a/engine-gpu/.settings/org.eclipse.jdt.core.prefs +++ b/engine-jogl/.settings/org.eclipse.jdt.core.prefs @@ -1,6 +1,6 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.8 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/engine-gpu/.settings/org.eclipse.m2e.core.prefs b/engine-jogl/.settings/org.eclipse.m2e.core.prefs similarity index 95% rename from engine-gpu/.settings/org.eclipse.m2e.core.prefs rename to engine-jogl/.settings/org.eclipse.m2e.core.prefs index 14b697b7..f897a7f1 100644 --- a/engine-gpu/.settings/org.eclipse.m2e.core.prefs +++ b/engine-jogl/.settings/org.eclipse.m2e.core.prefs @@ -1,4 +1,4 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/engine-gpu/pom.xml b/engine-jogl/pom.xml similarity index 96% rename from engine-gpu/pom.xml rename to engine-jogl/pom.xml index 2ba92b27..75e1731d 100644 --- a/engine-gpu/pom.xml +++ b/engine-jogl/pom.xml @@ -1,61 +1,61 @@ - - 4.0.0 - - - it.cavallium - warppi - ${project.version} - - warppi-engine-jogl - - bundle - - WarpPI Calculator JOGL Engine - WarpPI Calculator engine-jogl project - - - it.cavallium - warppi-core - ${project.version} - - - org.jogamp.jogl - jogl-all-main - 2.3.2 - - - org.jogamp.gluegen - gluegen-rt-main - 2.3.2 - - - ar.com.hjg - pngj - - - - - - org.apache.felix - maven-bundle-plugin - true - - - it.cavallium.warppi.* - warppi-engine-jogl - - - - - org.apache.maven.plugins - maven-source-plugin - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - + + 4.0.0 + + + it.cavallium + warppi + ${project.version} + + warppi-engine-jogl + + bundle + + WarpPI Calculator JOGL Engine + WarpPI Calculator engine-jogl project + + + it.cavallium + warppi-core + ${project.version} + + + org.jogamp.jogl + jogl-all-main + 2.3.2 + + + org.jogamp.gluegen + gluegen-rt-main + 2.3.2 + + + ar.com.hjg + pngj + + + + + + org.apache.felix + maven-bundle-plugin + true + + + it.cavallium.warppi.* + warppi-engine-jogl + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + diff --git a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java similarity index 96% rename from engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java rename to engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java index e95fc6ec..66820757 100644 --- a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLEngine.java @@ -1,203 +1,203 @@ -package it.cavallium.warppi.gui.graphicengine.impl.jogl; - -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Semaphore; - -import com.jogamp.opengl.GLProfile; -import com.jogamp.opengl.util.texture.Texture; - -import it.cavallium.warppi.Engine; -import it.cavallium.warppi.StaticVars; -import it.cavallium.warppi.flow.Observable; -import it.cavallium.warppi.gui.graphicengine.BinaryFont; -import it.cavallium.warppi.gui.graphicengine.GraphicEngine; -import it.cavallium.warppi.gui.graphicengine.RenderingLoop; -import it.cavallium.warppi.gui.graphicengine.Skin; - -public class JOGLEngine implements GraphicEngine { - - private volatile boolean initialized; - private volatile boolean created; - private NEWTWindow wnd; - private RenderingLoop d; - private JOGLRenderer r; - private final Map fontCache = new HashMap<>(); - int[] size; - private final CopyOnWriteArrayList registeredFonts = new CopyOnWriteArrayList<>(); - private final Semaphore exitSemaphore = new Semaphore(0); - protected LinkedList registeredTextures; - protected LinkedList unregisteredTextures; - - @Override - public int[] getSize() { - return size; - } - - @Override - public boolean isInitialized() { - return initialized; - } - - @Override - public void setTitle(final String title) { - wnd.window.setTitle(title); - } - - @Override - public void setResizable(final boolean r) { - if (!r) - wnd.window.setPosition(0, 0); - wnd.window.setResizable(r); - wnd.window.setUndecorated(!r); - wnd.window.setPointerVisible(r); - } - - @Override - public void setDisplayMode(final int ww, final int wh) { - wnd.setSize(ww, wh); - } - - @Override - public void create() { - create(null); - } - - @Override - public void create(final Runnable onInitialized) { - initialized = false; - created = false; - size = new int[] { StaticVars.screenSize[0], StaticVars.screenSize[1] }; - created = true; - registeredTextures = new LinkedList<>(); - unregisteredTextures = new LinkedList<>(); - r = new JOGLRenderer(); - wnd = new NEWTWindow(this); - wnd.create(); - setDisplayMode(StaticVars.screenSize[0], StaticVars.screenSize[1]); - setResizable(Engine.getPlatform().getSettings().isDebugEnabled()); - initialized = true; - wnd.onInitialized = onInitialized; - } - - @Override - public Observable onResize() { - return wnd.onResizeEvent; - } - - @Override - public int getWidth() { - return size[0]; - } - - @Override - public int getHeight() { - return size[1]; - } - - @Override - public void destroy() { - if (initialized && created) { - initialized = false; - created = false; - exitSemaphore.release(); - wnd.window.destroy(); - } - } - - @Override - public void start(final RenderingLoop d) { - this.d = d; - wnd.window.setVisible(true); - } - - @Override - public void repaint() { - if (d != null & r != null && JOGLRenderer.gl != null) - d.refresh(); - } - - @Override - public JOGLRenderer getRenderer() { - return r; - } - - @Override - public BinaryFont loadFont(final String name) throws IOException { - for (final Entry entry : fontCache.entrySet()) - if (entry.getKey().equals(name)) - return entry.getValue(); - final JOGLFont font = new JOGLFont(this, name); - fontCache.put(name, font); - return font; - } - - @Override - public BinaryFont loadFont(final String path, final String name) throws IOException { - for (final Entry entry : fontCache.entrySet()) - if (entry.getKey().equals(name)) - return entry.getValue(); - final JOGLFont font = new JOGLFont(this, path, name); - fontCache.put(name, font); - return font; - } - - @Override - public Skin loadSkin(final String file) throws IOException { - return new JOGLSkin(this, file); - } - - @Override - public void waitForExit() { - try { - exitSemaphore.acquire(); - } catch (final InterruptedException e) {} - } - - @Override - public boolean isSupported() { - if (StaticVars.startupArguments.isEngineForced() && StaticVars.startupArguments.isGPUEngineForced() == false) - return false; - if (StaticVars.startupArguments.isHeadlessEngineForced()) - return false; - boolean available = false; - boolean errored = false; - try { - available = GLProfile.isAvailable(GLProfile.GL2ES2); - } catch (final Exception ex) { - errored = true; - System.err.println("OpenGL Error: " + ex.getMessage()); - } - if (!available && !errored) - System.err.println(GLProfile.glAvailabilityToString()); - return available; - } - - @Override - public boolean doesRefreshPauses() { - return false; - } - - public void registerFont(final JOGLFont gpuFont) { - registeredFonts.add(gpuFont); - } - - @Override - public boolean supportsFontRegistering() { - return true; - } - - @Override - public CopyOnWriteArrayList getRegisteredFonts() { - return registeredFonts; - } - - public void registerTexture(final Texture t) { - unregisteredTextures.addLast(t); - } - +package it.cavallium.warppi.gui.graphicengine.impl.jogl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Semaphore; + +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.util.texture.Texture; + +import it.cavallium.warppi.Engine; +import it.cavallium.warppi.StaticVars; +import it.cavallium.warppi.flow.Observable; +import it.cavallium.warppi.gui.graphicengine.BinaryFont; +import it.cavallium.warppi.gui.graphicengine.GraphicEngine; +import it.cavallium.warppi.gui.graphicengine.RenderingLoop; +import it.cavallium.warppi.gui.graphicengine.Skin; + +public class JOGLEngine implements GraphicEngine { + + private volatile boolean initialized; + private volatile boolean created; + private NEWTWindow wnd; + private RenderingLoop d; + private JOGLRenderer r; + private final Map fontCache = new HashMap<>(); + int[] size; + private final CopyOnWriteArrayList registeredFonts = new CopyOnWriteArrayList<>(); + private final Semaphore exitSemaphore = new Semaphore(0); + protected LinkedList registeredTextures; + protected LinkedList unregisteredTextures; + + @Override + public int[] getSize() { + return size; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public void setTitle(final String title) { + wnd.window.setTitle(title); + } + + @Override + public void setResizable(final boolean r) { + if (!r) + wnd.window.setPosition(0, 0); + wnd.window.setResizable(r); + wnd.window.setUndecorated(!r); + wnd.window.setPointerVisible(r); + } + + @Override + public void setDisplayMode(final int ww, final int wh) { + wnd.setSize(ww, wh); + } + + @Override + public void create() { + create(null); + } + + @Override + public void create(final Runnable onInitialized) { + initialized = false; + created = false; + size = new int[] { StaticVars.screenSize[0], StaticVars.screenSize[1] }; + created = true; + registeredTextures = new LinkedList<>(); + unregisteredTextures = new LinkedList<>(); + r = new JOGLRenderer(); + wnd = new NEWTWindow(this); + wnd.create(); + setDisplayMode(StaticVars.screenSize[0], StaticVars.screenSize[1]); + setResizable(Engine.getPlatform().getSettings().isDebugEnabled()); + initialized = true; + wnd.onInitialized = onInitialized; + } + + @Override + public Observable onResize() { + return wnd.onResizeEvent; + } + + @Override + public int getWidth() { + return size[0]; + } + + @Override + public int getHeight() { + return size[1]; + } + + @Override + public void destroy() { + if (initialized && created) { + initialized = false; + created = false; + exitSemaphore.release(); + wnd.window.destroy(); + } + } + + @Override + public void start(final RenderingLoop d) { + this.d = d; + wnd.window.setVisible(true); + } + + @Override + public void repaint() { + if (d != null & r != null && JOGLRenderer.gl != null) + d.refresh(); + } + + @Override + public JOGLRenderer getRenderer() { + return r; + } + + @Override + public BinaryFont loadFont(final String name) throws IOException { + for (final Entry entry : fontCache.entrySet()) + if (entry.getKey().equals(name)) + return entry.getValue(); + final JOGLFont font = new JOGLFont(this, name); + fontCache.put(name, font); + return font; + } + + @Override + public BinaryFont loadFont(final String path, final String name) throws IOException { + for (final Entry entry : fontCache.entrySet()) + if (entry.getKey().equals(name)) + return entry.getValue(); + final JOGLFont font = new JOGLFont(this, path, name); + fontCache.put(name, font); + return font; + } + + @Override + public Skin loadSkin(final String file) throws IOException { + return new JOGLSkin(this, file); + } + + @Override + public void waitForExit() { + try { + exitSemaphore.acquire(); + } catch (final InterruptedException e) {} + } + + @Override + public boolean isSupported() { + if (StaticVars.startupArguments.isEngineForced() && StaticVars.startupArguments.isGPUEngineForced() == false) + return false; + if (StaticVars.startupArguments.isHeadlessEngineForced()) + return false; + boolean available = false; + boolean errored = false; + try { + available = GLProfile.isAvailable(GLProfile.GL2ES2); + } catch (final Exception ex) { + errored = true; + System.err.println("OpenGL Error: " + ex.getMessage()); + } + if (!available && !errored) + System.err.println(GLProfile.glAvailabilityToString()); + return available; + } + + @Override + public boolean doesRefreshPauses() { + return false; + } + + public void registerFont(final JOGLFont gpuFont) { + registeredFonts.add(gpuFont); + } + + @Override + public boolean supportsFontRegistering() { + return true; + } + + @Override + public CopyOnWriteArrayList getRegisteredFonts() { + return registeredFonts; + } + + public void registerTexture(final Texture t) { + unregisteredTextures.addLast(t); + } + } \ No newline at end of file diff --git a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java similarity index 96% rename from engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java rename to engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java index 3db2befa..3e02de0d 100644 --- a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLFont.java @@ -1,236 +1,236 @@ -package it.cavallium.warppi.gui.graphicengine.impl.jogl; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.file.Files; - -import com.jogamp.opengl.GLException; -import com.jogamp.opengl.util.texture.Texture; - -import ar.com.hjg.pngj.ImageInfo; -import ar.com.hjg.pngj.ImageLineHelper; -import ar.com.hjg.pngj.ImageLineInt; -import ar.com.hjg.pngj.PngWriter; -import it.cavallium.warppi.Engine; -import it.cavallium.warppi.gui.graphicengine.BinaryFont; -import it.cavallium.warppi.gui.graphicengine.GraphicEngine; -import it.cavallium.warppi.gui.graphicengine.impl.common.RFTFont; - -public class JOGLFont implements BinaryFont { - - public Texture texture; - public int textureW; - public int textureH; - public int charW; - public int charH; - public int minCharIndex; - public int maxCharIndex; - public int[] intervals; - public int intervalsTotalSize = 0; - public int memoryWidth; - public int memoryHeight; - public int memoryWidthOfEachColumn; - - private boolean initialized = false; - private File tmpFont; - - JOGLFont(final GraphicEngine g, final String name) throws IOException { - this(g, null, name); - } - - public JOGLFont(final GraphicEngine g, final String path, final String name) throws IOException { - load(path, name); - ((JOGLEngine) g).registerFont(this); - } - - @Override - public void load(final String name) throws IOException { - load(null, name); - } - - public void load(final String path, final String name) throws IOException { - RFTFont font; - if (path == null) - font = RFTFont.loadTemporaryFont(name); - else - font = RFTFont.loadTemporaryFont(path, name); - charW = font.charW; - charH = font.charH; - minCharIndex = font.minBound; - maxCharIndex = font.maxBound; - intervals = font.intervals; - intervalsTotalSize = font.intervalsTotalSize; - boolean[][] rawchars = font.rawchars; - font = null; - Engine.getPlatform().gc(); - pregenTexture(rawchars); - rawchars = null; - Engine.getPlatform().gc(); - } - - public int[] getCharIndexes(final String txt) { - final int[] indexes = new int[txt.length()]; - int i = 0; - for (final char c : txt.toCharArray()) { - indexes[i] = compressIndex((c & 0xFFFF) - minCharIndex); - i++; - } - return indexes; - } - - public int getCharIndex(final char c) { - final int originalIndex = c & 0xFFFF; - return compressIndex(originalIndex); - } - - private int compressIndex(final int originalIndex) { - int compressedIndex = 0; - for (int i = 0; i < intervals.length; i += 3) - if (intervals[i] > originalIndex) - break; - else if (originalIndex <= intervals[i + 1]) { - compressedIndex += originalIndex - intervals[i]; - break; - } else - compressedIndex += intervals[i + 2]; - return compressedIndex; - } - - @SuppressWarnings("unused") - private int decompressIndex(final int compressedIndex) { - final int originalIndex = 0; - int i = 0; - for (final int intvl = 0; i < intervals.length; i += 3) { - i += intervals[intvl + 2]; - if (i >= compressedIndex) - return intervals[intvl + 1] - (i - compressedIndex); - } - return originalIndex; - } - - private void pregenTexture(boolean[][] chars) throws IOException { - final int totalChars = intervalsTotalSize; - int w = powerOf2((int) Math.ceil(Math.sqrt(totalChars) * charW)); - int h = powerOf2((int) Math.ceil(Math.sqrt(totalChars) * charH)); - int maxIndexW = (int) Math.floor((double) w / (double) charW) - 1; - int maxIndexH = (int) Math.floor((double) h / (double) charH) - 1; - if (w > h) { - System.out.println("w > h"); - h = powerOf2((int) Math.ceil((double) totalChars / (double) maxIndexW * charH)); - maxIndexH = (int) Math.floor((double) h / (double) charH) - 1; - } else { - System.out.println("w <= h"); - w = powerOf2((int) Math.ceil((double) totalChars / (double) maxIndexH * charW)); - maxIndexW = (int) Math.floor((double) w / (double) charW) - 1; - } -// final int h = powerOf2((int) (Math.ceil(Math.sqrt(totalChars) * charH))); - - System.out.println((int) Math.ceil(Math.sqrt(totalChars) * charW) + " * " + (int) Math.ceil(Math.sqrt(totalChars) * charH) + " --> " + w + " * " + h); - - final File f = Files.createTempFile("texture-font-", ".png").toFile(); - f.deleteOnExit(); - final FileOutputStream outputStream = new FileOutputStream(f); - final ImageInfo imi = new ImageInfo(w, h, 8, true); // 8 bits per channel, alpha - // open image for writing to a output stream - final PngWriter png = new PngWriter(outputStream, imi); - for (int y = 0; y < png.imgInfo.rows; y++) { - final ImageLineInt iline = new ImageLineInt(imi); - final int[] xValues = new int[imi.cols]; - for (int indexX = 0; indexX <= maxIndexW; indexX++) {// this line will be written to all rows - final int charY = y % charH; - final int indexY = (y - charY) / charH; - final int i = indexY * (maxIndexW + 1) + indexX - minCharIndex; - boolean[] currentChar; - if (i < totalChars && (currentChar = chars[i]) != null) - for (int charX = 0; charX < charW; charX++) - if (i >= 0 & i < totalChars && currentChar != null && currentChar[charX + charY * charW]) - xValues[indexX * charW + charX] = 0xFFFFFFFF; - } - ImageLineHelper.setPixelsRGBA8(iline, xValues); - if (y % 10 == 0) - System.out.println(y + "/" + png.imgInfo.rows); - png.writeRow(iline); - } - chars = null; - png.end(); - Engine.getPlatform().gc(); - - try { - memoryWidth = w; - memoryHeight = h; - memoryWidthOfEachColumn = maxIndexW + 1; - textureW = w; - textureH = h; - outputStream.flush(); - outputStream.close(); - Engine.getPlatform().gc(); - tmpFont = f; - } catch (GLException | IOException e) { - e.printStackTrace(); - } - } - - private void genTexture() { - try { - texture = JOGLRenderer.importTexture(tmpFont, true); - tmpFont = null; - } catch (GLException | IOException e) { - e.printStackTrace(); - } - } - - private int powerOf2(final int i) { - return i > 1 ? Integer.highestOneBit(i - 1) << 1 : 1; - } - - @Override - public void initialize(final GraphicEngine d) { - genTexture(); - tmpFont = null; - initialized = true; - } - - @Override - public void use(final GraphicEngine d) { - if (!initialized) - initialize(d); - final JOGLRenderer r = (JOGLRenderer) d.getRenderer(); - r.currentFont = this; - r.useTexture(texture, textureW, textureH); - } - - @Override - public int getStringWidth(final String text) { - final int w = charW * text.length(); - if (text.length() > 0) - return w; - else - return 0; - } - - @Override - public int getCharacterWidth() { - return charW; - } - - @Override - public int getCharacterHeight() { - return charH; - } - - @Override - public boolean isInitialized() { - return initialized; - } - - @Override - public int getSkinWidth() { - return memoryWidth; - } - - @Override - public int getSkinHeight() { - return memoryHeight; - } +package it.cavallium.warppi.gui.graphicengine.impl.jogl; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; + +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.util.texture.Texture; + +import ar.com.hjg.pngj.ImageInfo; +import ar.com.hjg.pngj.ImageLineHelper; +import ar.com.hjg.pngj.ImageLineInt; +import ar.com.hjg.pngj.PngWriter; +import it.cavallium.warppi.Engine; +import it.cavallium.warppi.gui.graphicengine.BinaryFont; +import it.cavallium.warppi.gui.graphicengine.GraphicEngine; +import it.cavallium.warppi.gui.graphicengine.impl.common.RFTFont; + +public class JOGLFont implements BinaryFont { + + public Texture texture; + public int textureW; + public int textureH; + public int charW; + public int charH; + public int minCharIndex; + public int maxCharIndex; + public int[] intervals; + public int intervalsTotalSize = 0; + public int memoryWidth; + public int memoryHeight; + public int memoryWidthOfEachColumn; + + private boolean initialized = false; + private File tmpFont; + + JOGLFont(final GraphicEngine g, final String name) throws IOException { + this(g, null, name); + } + + public JOGLFont(final GraphicEngine g, final String path, final String name) throws IOException { + load(path, name); + ((JOGLEngine) g).registerFont(this); + } + + @Override + public void load(final String name) throws IOException { + load(null, name); + } + + public void load(final String path, final String name) throws IOException { + RFTFont font; + if (path == null) + font = RFTFont.loadTemporaryFont(name); + else + font = RFTFont.loadTemporaryFont(path, name); + charW = font.charW; + charH = font.charH; + minCharIndex = font.minBound; + maxCharIndex = font.maxBound; + intervals = font.intervals; + intervalsTotalSize = font.intervalsTotalSize; + boolean[][] rawchars = font.rawchars; + font = null; + Engine.getPlatform().gc(); + pregenTexture(rawchars); + rawchars = null; + Engine.getPlatform().gc(); + } + + public int[] getCharIndexes(final String txt) { + final int[] indexes = new int[txt.length()]; + int i = 0; + for (final char c : txt.toCharArray()) { + indexes[i] = compressIndex((c & 0xFFFF) - minCharIndex); + i++; + } + return indexes; + } + + public int getCharIndex(final char c) { + final int originalIndex = c & 0xFFFF; + return compressIndex(originalIndex); + } + + private int compressIndex(final int originalIndex) { + int compressedIndex = 0; + for (int i = 0; i < intervals.length; i += 3) + if (intervals[i] > originalIndex) + break; + else if (originalIndex <= intervals[i + 1]) { + compressedIndex += originalIndex - intervals[i]; + break; + } else + compressedIndex += intervals[i + 2]; + return compressedIndex; + } + + @SuppressWarnings("unused") + private int decompressIndex(final int compressedIndex) { + final int originalIndex = 0; + int i = 0; + for (final int intvl = 0; i < intervals.length; i += 3) { + i += intervals[intvl + 2]; + if (i >= compressedIndex) + return intervals[intvl + 1] - (i - compressedIndex); + } + return originalIndex; + } + + private void pregenTexture(boolean[][] chars) throws IOException { + final int totalChars = intervalsTotalSize; + int w = powerOf2((int) Math.ceil(Math.sqrt(totalChars) * charW)); + int h = powerOf2((int) Math.ceil(Math.sqrt(totalChars) * charH)); + int maxIndexW = (int) Math.floor((double) w / (double) charW) - 1; + int maxIndexH = (int) Math.floor((double) h / (double) charH) - 1; + if (w > h) { + System.out.println("w > h"); + h = powerOf2((int) Math.ceil((double) totalChars / (double) maxIndexW * charH)); + maxIndexH = (int) Math.floor((double) h / (double) charH) - 1; + } else { + System.out.println("w <= h"); + w = powerOf2((int) Math.ceil((double) totalChars / (double) maxIndexH * charW)); + maxIndexW = (int) Math.floor((double) w / (double) charW) - 1; + } +// final int h = powerOf2((int) (Math.ceil(Math.sqrt(totalChars) * charH))); + + System.out.println((int) Math.ceil(Math.sqrt(totalChars) * charW) + " * " + (int) Math.ceil(Math.sqrt(totalChars) * charH) + " --> " + w + " * " + h); + + final File f = Files.createTempFile("texture-font-", ".png").toFile(); + f.deleteOnExit(); + final FileOutputStream outputStream = new FileOutputStream(f); + final ImageInfo imi = new ImageInfo(w, h, 8, true); // 8 bits per channel, alpha + // open image for writing to a output stream + final PngWriter png = new PngWriter(outputStream, imi); + for (int y = 0; y < png.imgInfo.rows; y++) { + final ImageLineInt iline = new ImageLineInt(imi); + final int[] xValues = new int[imi.cols]; + for (int indexX = 0; indexX <= maxIndexW; indexX++) {// this line will be written to all rows + final int charY = y % charH; + final int indexY = (y - charY) / charH; + final int i = indexY * (maxIndexW + 1) + indexX - minCharIndex; + boolean[] currentChar; + if (i < totalChars && (currentChar = chars[i]) != null) + for (int charX = 0; charX < charW; charX++) + if (i >= 0 & i < totalChars && currentChar != null && currentChar[charX + charY * charW]) + xValues[indexX * charW + charX] = 0xFFFFFFFF; + } + ImageLineHelper.setPixelsRGBA8(iline, xValues); + if (y % 10 == 0) + System.out.println(y + "/" + png.imgInfo.rows); + png.writeRow(iline); + } + chars = null; + png.end(); + Engine.getPlatform().gc(); + + try { + memoryWidth = w; + memoryHeight = h; + memoryWidthOfEachColumn = maxIndexW + 1; + textureW = w; + textureH = h; + outputStream.flush(); + outputStream.close(); + Engine.getPlatform().gc(); + tmpFont = f; + } catch (GLException | IOException e) { + e.printStackTrace(); + } + } + + private void genTexture() { + try { + texture = JOGLRenderer.importTexture(tmpFont, true); + tmpFont = null; + } catch (GLException | IOException e) { + e.printStackTrace(); + } + } + + private int powerOf2(final int i) { + return i > 1 ? Integer.highestOneBit(i - 1) << 1 : 1; + } + + @Override + public void initialize(final GraphicEngine d) { + genTexture(); + tmpFont = null; + initialized = true; + } + + @Override + public void use(final GraphicEngine d) { + if (!initialized) + initialize(d); + final JOGLRenderer r = (JOGLRenderer) d.getRenderer(); + r.currentFont = this; + r.useTexture(texture, textureW, textureH); + } + + @Override + public int getStringWidth(final String text) { + final int w = charW * text.length(); + if (text.length() > 0) + return w; + else + return 0; + } + + @Override + public int getCharacterWidth() { + return charW; + } + + @Override + public int getCharacterHeight() { + return charH; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public int getSkinWidth() { + return memoryWidth; + } + + @Override + public int getSkinHeight() { + return memoryHeight; + } } \ No newline at end of file diff --git a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java similarity index 97% rename from engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java rename to engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java index 9c2817e3..6f6ed6e9 100644 --- a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLRenderer.java @@ -1,447 +1,447 @@ -package it.cavallium.warppi.gui.graphicengine.impl.jogl; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.FloatBuffer; -import java.nio.file.Files; - -import javax.imageio.ImageIO; - -import com.jogamp.common.nio.Buffers; -import com.jogamp.opengl.GL; -import com.jogamp.opengl.GL2ES1; -import com.jogamp.opengl.GLException; -import com.jogamp.opengl.util.texture.Texture; -import com.jogamp.opengl.util.texture.TextureData; -import com.jogamp.opengl.util.texture.TextureIO; - -import it.cavallium.warppi.Engine; -import it.cavallium.warppi.gui.graphicengine.BinaryFont; -import it.cavallium.warppi.gui.graphicengine.Renderer; - -public class JOGLRenderer implements Renderer { - - public static GL2ES1 gl; - - private static final int ELEMENTS_MAX_COUNT_PER_BUFFER = 128; - private static final int ELEMENT_VERTICES_COUNT = 6; - - private static final int vertSize = 3; - - private static final int texSize = 2; - - private static final int colSize = 4; - - private static final int vertBuffer = 0; - - private static final int texBuffer = 1; - - private static final int colBuffer = 2; - - private static final int vertMax = JOGLRenderer.vertSize * JOGLRenderer.ELEMENT_VERTICES_COUNT * JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; - - private static final int texMax = JOGLRenderer.texSize * JOGLRenderer.ELEMENT_VERTICES_COUNT * JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; - - private static final int colMax = JOGLRenderer.colSize * JOGLRenderer.ELEMENT_VERTICES_COUNT * JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; - - private int[] handlers; - FloatBuffer fbVertices; - FloatBuffer fbTextures; - FloatBuffer fbColors; - int fbElements; - - float[] currentColor = new float[24]; - float[] currentClearColorARGBf = new float[] { 1f, 197f / 255f, 194f / 255f, 175f / 255f }; - boolean currentTexEnabled; - Texture currentTex; - float currentTexWidth; - float currentTexHeight; - - JOGLFont currentFont; - - @Override - public void glColor3i(final int r, final int gg, final int b) { - final float red = r / 255f; - final float gre = gg / 255f; - final float blu = b / 255f; - //currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, }; - currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, }; //OK - } - - @Override - public void glColor3f(final float red, final float gre, final float blu) { - // currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, }; - currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, };//OK - } - - @Override - public void glColor4f(final float red, final float gre, final float blu, final float alp) { - // currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, }; - currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, };//ok - - } - - @Override - public void glColor(final int rgb) { - final int alpha = rgb >> 24 & 0xFF; - final int red = rgb >> 16 & 0xFF; - final int green = rgb >> 8 & 0xFF; - final int blue = rgb & 0xFF; - glColor4i(red, green, blue, alpha); - } - - @Override - public int glGetClearColor() { - return (int) (currentClearColorARGBf[0] * 255) << 24 | (int) (currentClearColorARGBf[1] * 255) << 16 | (int) (currentClearColorARGBf[2] * 255) << 8 | (int) (currentClearColorARGBf[3] * 255); - } - - @Override - public void glClearColor(final int rgb) { - final float alpha = (rgb >> 24 & 0xFF) / 255f; - final float red = (rgb >> 16 & 0xFF) / 255f; - final float green = (rgb >> 8 & 0xFF) / 255f; - final float blue = (rgb & 0xFF) / 255f; - glClearColor4f(red, green, blue, alpha); - } - - @Override - public void glColor4i(final int r, final int g, final int b, final int a) { - final float red = r / 255f; - final float gre = g / 255f; - final float blu = b / 255f; - final float alp = a / 255f; - //currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, }; - currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, };//OK - } - - @Override - public void glClearColor4i(final int red, final int green, final int blue, final int alpha) { - final float ros = red / 255f; - final float gre = green / 255f; - final float blu = blue / 255f; - final float alp = alpha / 255f; - currentClearColorARGBf = new float[] { alp, ros, gre, blu }; - } - - @Override - public void glClearColor4f(final float red, final float green, final float blue, final float alpha) { - currentClearColorARGBf = new float[] { alpha, red, green, blue }; - } - - @Override - public void glClear(final int screenWidth, final int screenHeight) { - glColor(glGetClearColor()); - glFillColor(0, 0, screenWidth, screenHeight); - } - - @Override - public void glDrawLine(final float x0, final float y0, final float x1, final float y1) { - glFillColor(x0, y0, x1 - x0 + 1, y1 - y0 + 1); - } - - @Override - public void glFillRect(final float x, final float y, final float width, final float height, float uvX, float uvY, - float uvWidth, float uvHeight) { - enableTexture(); - if (uvWidth < 0) - uvX -= uvWidth; - if (uvHeight < 0) - uvY -= uvHeight; - uvWidth /= currentTexWidth; - uvX /= currentTexWidth; - uvHeight /= currentTexHeight; - uvY = 1 - uvY / currentTexHeight - uvHeight; -// final float[] vertices = { x, y, 0.0f, x, y + height, 0.0f, x + width, y, 0.0f, x + width, y + height, 0.0f, }; -// final float[] tex_vertices = { uvX, uvY + uvHeight, uvX, uvY, uvX + uvWidth, uvY + uvHeight, uvX + uvWidth, uvY, }; - //V0 x, y, 0.0f - //V1 x, y + height, 0.0f - //V2 x + width, y, 0.0f - //V3 x + width, y + height, 0.0f - - //NV0 = V1 - //NV1 = V3 - //NV2 = V0 - - //NV3 = V0 - //NV4 = V3 - //NV5 = V2 - - final float[] vertices = { x, y + height, 0.0f, x + width, y + height, 0.0f, x, y, 0.0f, x, y, 0.0f, x + width, y + height, 0.0f, x + width, y, 0.0f }; - final float[] tex_vertices = { uvX, uvY, uvX + uvWidth, uvY, uvX, uvY + uvHeight, uvX, uvY + uvHeight, uvX + uvWidth, uvY, uvX + uvWidth, uvY + uvHeight }; - fbElements++; - fbVertices.put(vertices); - fbTextures.put(tex_vertices); - fbColors.put(currentColor); - } - - @Override - public void glFillColor(final float x0, final float y0, final float w1, final float h1) { - disableTexture(); -// final float[] vertices = { x0, y0, 0.0f, x0, y0 + h1, 0.0f, x0 + w1, y0, 0.0f, x0 + w1, y0 + h1, 0.0f, }; -// final float[] tex_vertices = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, }; - //V0 x0, y0, 0.0f - //V1 x0, y0 + h1, 0.0f - //V2 x0 + w1, y0, 0.0f - //V3 x0 + w1, y0 + h1, 0.0f - - //NV0 = V1 - //NV1 = V3 - //NV2 = V0 - - //NV3 = V0 - //NV4 = V3 - //NV5 = V2 - - final float[] vertices = { x0, y0 + h1, 0.0f, x0 + w1, y0 + h1, 0.0f, x0, y0, 0.0f, x0, y0, 0.0f, x0 + w1, y0 + h1, 0.0f, x0 + w1, y0, 0.0f, }; - final float[] tex_vertices = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, }; - fbElements++; - fbVertices.put(vertices); - fbTextures.put(tex_vertices); - fbColors.put(currentColor); - } - - @Override - public void glDrawStringLeft(final float x, final float y, final String text) { - final int txtLen = text.length(); - final int[] txtArray = currentFont.getCharIndexes(text); - int tableIndexX; - int tableIndexY; - for (int currentCharIndex = 0; currentCharIndex < txtLen; currentCharIndex++) { - tableIndexX = txtArray[currentCharIndex] % currentFont.memoryWidthOfEachColumn; - tableIndexY = (txtArray[currentCharIndex] - tableIndexX) / currentFont.memoryWidthOfEachColumn; - glFillRect(x + (float) currentCharIndex * (float) currentFont.charW, y, currentFont.charW, currentFont.charH, tableIndexX * currentFont.charW, tableIndexY * currentFont.charH, currentFont.charW, currentFont.charH); - } - } - - @Override - public void glDrawStringCenter(final float x, final float y, final String text) { - glDrawStringLeft(x - currentFont.getStringWidth(text) / 2, y, text); - } - - @Override - public void glDrawStringRight(final float x, final float y, final String text) { - glDrawStringLeft(x - currentFont.getStringWidth(text), y, text); - } - - @Override - public void glDrawCharLeft(final int x, final int y, final char ch) { - final int index = currentFont.getCharIndex(ch); - final int tableIndexX = index % currentFont.memoryWidthOfEachColumn; - final int tableIndexY = (index - tableIndexX) / currentFont.memoryWidthOfEachColumn; - glFillRect(x, y, currentFont.charW, currentFont.charH, tableIndexX * currentFont.charW, tableIndexY * currentFont.charH, currentFont.charW, currentFont.charH); - } - - @Override - public void glDrawCharCenter(final int x, final int y, final char ch) { - glDrawCharLeft(x - currentFont.charW / 2, y, ch); - } - - @Override - public void glDrawCharRight(final int x, final int y, final char ch) { - glDrawCharLeft(x - currentFont.charW, y, ch); - } - - @Override - public BinaryFont getCurrentFont() { - return currentFont; - } - - @Deprecated - static Texture importTexture(final GL gl, final String string) throws IOException { - final FileInputStream f = new FileInputStream("test.png"); - final TextureData tx_dat = TextureIO.newTextureData(gl.getGLProfile(), f, false, TextureIO.PNG); - final Texture tex = new Texture(gl, tx_dat); - tex.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - tex.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); -// tex.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); -// tex.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); - return tex; - } - - static OpenedTextureData openTexture(final String file, final boolean isResource) throws GLException, IOException { - BufferedImage img = ImageIO.read(isResource ? JOGLRenderer.class.getResource("/" + file) : new File(file).toURI().toURL()); - File f; - if (isResource) { - f = Files.createTempFile("texture-", ".png").toFile(); - f.deleteOnExit(); - ImageIO.write(img, "png", f); - } else - f = new File(file); - final int imgW = img.getWidth(); - final int imgH = img.getHeight(); - img = null; - Engine.getPlatform().gc(); - return new OpenedTextureData(imgW, imgH, f, isResource); - } - - public static class OpenedTextureData { - public final int w; - public final int h; - public final File f; - public final boolean deleteOnExit; - - /** - * @param w - * @param h - * @param f - * @param deleteOnExit - */ - public OpenedTextureData(final int w, final int h, final File f, final boolean deleteOnExit) { - this.w = w; - this.h = h; - this.f = f; - this.deleteOnExit = deleteOnExit; - } - - } - - static Texture importTexture(File f, final boolean deleteOnExit) throws GLException, IOException { - final Texture tex = TextureIO.newTexture(f, false); - if (deleteOnExit && f.exists()) - try { - if (Engine.getPlatform().getSettings().isDebugEnabled()) - throw new IOException("Delete on exit!"); - f.delete(); - } catch (final Exception ex) { - f.deleteOnExit(); - } - tex.setTexParameteri(JOGLRenderer.gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - tex.setTexParameteri(JOGLRenderer.gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - f = null; - return tex; - } - - public void initDrawCycle() { - final boolean textureChange = precTexEnabled != currentTexEnabled || precTex != currentTex; - if (fbVertices == null) { - fbVertices = Buffers.newDirectFloatBuffer(JOGLRenderer.vertMax); - fbTextures = Buffers.newDirectFloatBuffer(JOGLRenderer.texMax); - fbColors = Buffers.newDirectFloatBuffer(JOGLRenderer.colMax); - handlers = new int[3]; - JOGLRenderer.gl.glGenBuffers(3, handlers, 0); - } - startDrawSegment(false); - if (textureChange) - changeTexture(); - } - - public void endDrawCycle() { - final boolean textureChange = precTexEnabled != currentTexEnabled || precTex != currentTex; - if (textureChange) { - if (fbElements > 0) - endDrawSegment(); - changeTexture(); - } else if (fbElements > 0) - endDrawSegment(); - } - - private void changeTexture() { - precTexEnabled = currentTexEnabled; - precTex = currentTex; - if (currentTexEnabled) { - JOGLRenderer.gl.glEnable(GL.GL_TEXTURE_2D); - currentTex.bind(JOGLRenderer.gl); - } else - JOGLRenderer.gl.glDisable(GL.GL_TEXTURE_2D); - firstBufferTexDataCall = true; - } - - public void startDrawSegment(final boolean continuation) { - if (!continuation || cycleEnded) - fbElements = 0; - cycleEnded = false; - } - - private boolean precTexEnabled; - private Texture precTex; - private boolean cycleEnded = true; - - public void doDrawSegment() { - final boolean textureChange = precTexEnabled != currentTexEnabled || precTex != currentTex; - final boolean changeRequired = fbElements >= JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; - if (textureChange) { - if (fbElements > 0) { - endDrawSegment(); - startDrawSegment(false); - } - changeTexture(); - } else if (fbElements > 0 && changeRequired) { - endDrawSegment(); - startDrawSegment(true); - } - } - - boolean firstBufferDataCall = true; - boolean firstBufferTexDataCall = true; - - public void endDrawSegment() { - fbVertices.flip(); - fbTextures.flip(); - fbColors.flip(); - -// gl.glVertexPointer(vertSize, GL.GL_FLOAT, 0, fbVertices); - JOGLRenderer.gl.glBindBuffer(GL.GL_ARRAY_BUFFER, handlers[JOGLRenderer.vertBuffer]); - if (firstBufferTexDataCall) - JOGLRenderer.gl.glBufferData(GL.GL_ARRAY_BUFFER, fbVertices.limit() * Buffers.SIZEOF_FLOAT, fbVertices, GL.GL_STATIC_DRAW); - else - JOGLRenderer.gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, fbVertices.limit() * Buffers.SIZEOF_FLOAT, fbVertices); - JOGLRenderer.gl.glVertexPointer(JOGLRenderer.vertSize, GL.GL_FLOAT, 0, 0l); -// gl.glTexCoordPointer(texSize, GL.GL_FLOAT, 0, fbTextures); - JOGLRenderer.gl.glBindBuffer(GL.GL_ARRAY_BUFFER, handlers[JOGLRenderer.texBuffer]); - if (firstBufferTexDataCall) - JOGLRenderer.gl.glBufferData(GL.GL_ARRAY_BUFFER, fbTextures.limit() * Buffers.SIZEOF_FLOAT, fbTextures, GL.GL_STATIC_DRAW); - else - JOGLRenderer.gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, fbTextures.limit() * Buffers.SIZEOF_FLOAT, fbTextures); - JOGLRenderer.gl.glTexCoordPointer(JOGLRenderer.texSize, GL.GL_FLOAT, 0, 0l); -// gl.glColorPointer(colSize, GL.GL_FLOAT, 0, fbColors); - JOGLRenderer.gl.glBindBuffer(GL.GL_ARRAY_BUFFER, handlers[JOGLRenderer.colBuffer]); - if (firstBufferTexDataCall) - JOGLRenderer.gl.glBufferData(GL.GL_ARRAY_BUFFER, fbColors.limit() * Buffers.SIZEOF_FLOAT, fbColors, GL.GL_STATIC_DRAW); - else - JOGLRenderer.gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, fbColors.limit() * Buffers.SIZEOF_FLOAT, fbColors); - JOGLRenderer.gl.glColorPointer(JOGLRenderer.colSize, GL.GL_FLOAT, 0, 0l); - - fbVertices.limit(JOGLRenderer.vertMax); - fbTextures.limit(JOGLRenderer.texMax); - fbColors.limit(JOGLRenderer.colMax); - JOGLRenderer.gl.glDrawArrays(GL.GL_TRIANGLES, 0, fbElements * JOGLRenderer.ELEMENT_VERTICES_COUNT); - //gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, fbElements * ELEMENT_VERTICES_COUNT); - firstBufferDataCall = false; - firstBufferTexDataCall = false; - cycleEnded = true; - -// deleteBuffer(fbVertices); -// deleteBuffer(txVertices); -// deleteBuffer(colVertices); -// fbVertices = null; -// txVertices = null; -// colVertices = null; - } - - @Override - public void glClearSkin() { - if (currentTex != null) { - currentTex = null; - doDrawSegment(); - } - } - - void disableTexture() { - currentTexEnabled = false; - doDrawSegment(); - } - - void enableTexture() { - currentTexEnabled = true; - doDrawSegment(); - } - - void useTexture(final Texture t, final float w, final float h) { - currentTex = t; - currentTexWidth = w; - currentTexHeight = h; - enableTexture(); - } +package it.cavallium.warppi.gui.graphicengine.impl.jogl; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.FloatBuffer; +import java.nio.file.Files; + +import javax.imageio.ImageIO; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2ES1; +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureData; +import com.jogamp.opengl.util.texture.TextureIO; + +import it.cavallium.warppi.Engine; +import it.cavallium.warppi.gui.graphicengine.BinaryFont; +import it.cavallium.warppi.gui.graphicengine.Renderer; + +public class JOGLRenderer implements Renderer { + + public static GL2ES1 gl; + + private static final int ELEMENTS_MAX_COUNT_PER_BUFFER = 128; + private static final int ELEMENT_VERTICES_COUNT = 6; + + private static final int vertSize = 3; + + private static final int texSize = 2; + + private static final int colSize = 4; + + private static final int vertBuffer = 0; + + private static final int texBuffer = 1; + + private static final int colBuffer = 2; + + private static final int vertMax = JOGLRenderer.vertSize * JOGLRenderer.ELEMENT_VERTICES_COUNT * JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; + + private static final int texMax = JOGLRenderer.texSize * JOGLRenderer.ELEMENT_VERTICES_COUNT * JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; + + private static final int colMax = JOGLRenderer.colSize * JOGLRenderer.ELEMENT_VERTICES_COUNT * JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; + + private int[] handlers; + FloatBuffer fbVertices; + FloatBuffer fbTextures; + FloatBuffer fbColors; + int fbElements; + + float[] currentColor = new float[24]; + float[] currentClearColorARGBf = new float[] { 1f, 197f / 255f, 194f / 255f, 175f / 255f }; + boolean currentTexEnabled; + Texture currentTex; + float currentTexWidth; + float currentTexHeight; + + JOGLFont currentFont; + + @Override + public void glColor3i(final int r, final int gg, final int b) { + final float red = r / 255f; + final float gre = gg / 255f; + final float blu = b / 255f; + //currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, }; + currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, }; //OK + } + + @Override + public void glColor3f(final float red, final float gre, final float blu) { + // currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, }; + currentColor = new float[] { red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, red, gre, blu, 1.0f, };//OK + } + + @Override + public void glColor4f(final float red, final float gre, final float blu, final float alp) { + // currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, }; + currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, };//ok + + } + + @Override + public void glColor(final int rgb) { + final int alpha = rgb >> 24 & 0xFF; + final int red = rgb >> 16 & 0xFF; + final int green = rgb >> 8 & 0xFF; + final int blue = rgb & 0xFF; + glColor4i(red, green, blue, alpha); + } + + @Override + public int glGetClearColor() { + return (int) (currentClearColorARGBf[0] * 255) << 24 | (int) (currentClearColorARGBf[1] * 255) << 16 | (int) (currentClearColorARGBf[2] * 255) << 8 | (int) (currentClearColorARGBf[3] * 255); + } + + @Override + public void glClearColor(final int rgb) { + final float alpha = (rgb >> 24 & 0xFF) / 255f; + final float red = (rgb >> 16 & 0xFF) / 255f; + final float green = (rgb >> 8 & 0xFF) / 255f; + final float blue = (rgb & 0xFF) / 255f; + glClearColor4f(red, green, blue, alpha); + } + + @Override + public void glColor4i(final int r, final int g, final int b, final int a) { + final float red = r / 255f; + final float gre = g / 255f; + final float blu = b / 255f; + final float alp = a / 255f; + //currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, }; + currentColor = new float[] { red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, red, gre, blu, alp, };//OK + } + + @Override + public void glClearColor4i(final int red, final int green, final int blue, final int alpha) { + final float ros = red / 255f; + final float gre = green / 255f; + final float blu = blue / 255f; + final float alp = alpha / 255f; + currentClearColorARGBf = new float[] { alp, ros, gre, blu }; + } + + @Override + public void glClearColor4f(final float red, final float green, final float blue, final float alpha) { + currentClearColorARGBf = new float[] { alpha, red, green, blue }; + } + + @Override + public void glClear(final int screenWidth, final int screenHeight) { + glColor(glGetClearColor()); + glFillColor(0, 0, screenWidth, screenHeight); + } + + @Override + public void glDrawLine(final float x0, final float y0, final float x1, final float y1) { + glFillColor(x0, y0, x1 - x0 + 1, y1 - y0 + 1); + } + + @Override + public void glFillRect(final float x, final float y, final float width, final float height, float uvX, float uvY, + float uvWidth, float uvHeight) { + enableTexture(); + if (uvWidth < 0) + uvX -= uvWidth; + if (uvHeight < 0) + uvY -= uvHeight; + uvWidth /= currentTexWidth; + uvX /= currentTexWidth; + uvHeight /= currentTexHeight; + uvY = 1 - uvY / currentTexHeight - uvHeight; +// final float[] vertices = { x, y, 0.0f, x, y + height, 0.0f, x + width, y, 0.0f, x + width, y + height, 0.0f, }; +// final float[] tex_vertices = { uvX, uvY + uvHeight, uvX, uvY, uvX + uvWidth, uvY + uvHeight, uvX + uvWidth, uvY, }; + //V0 x, y, 0.0f + //V1 x, y + height, 0.0f + //V2 x + width, y, 0.0f + //V3 x + width, y + height, 0.0f + + //NV0 = V1 + //NV1 = V3 + //NV2 = V0 + + //NV3 = V0 + //NV4 = V3 + //NV5 = V2 + + final float[] vertices = { x, y + height, 0.0f, x + width, y + height, 0.0f, x, y, 0.0f, x, y, 0.0f, x + width, y + height, 0.0f, x + width, y, 0.0f }; + final float[] tex_vertices = { uvX, uvY, uvX + uvWidth, uvY, uvX, uvY + uvHeight, uvX, uvY + uvHeight, uvX + uvWidth, uvY, uvX + uvWidth, uvY + uvHeight }; + fbElements++; + fbVertices.put(vertices); + fbTextures.put(tex_vertices); + fbColors.put(currentColor); + } + + @Override + public void glFillColor(final float x0, final float y0, final float w1, final float h1) { + disableTexture(); +// final float[] vertices = { x0, y0, 0.0f, x0, y0 + h1, 0.0f, x0 + w1, y0, 0.0f, x0 + w1, y0 + h1, 0.0f, }; +// final float[] tex_vertices = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, }; + //V0 x0, y0, 0.0f + //V1 x0, y0 + h1, 0.0f + //V2 x0 + w1, y0, 0.0f + //V3 x0 + w1, y0 + h1, 0.0f + + //NV0 = V1 + //NV1 = V3 + //NV2 = V0 + + //NV3 = V0 + //NV4 = V3 + //NV5 = V2 + + final float[] vertices = { x0, y0 + h1, 0.0f, x0 + w1, y0 + h1, 0.0f, x0, y0, 0.0f, x0, y0, 0.0f, x0 + w1, y0 + h1, 0.0f, x0 + w1, y0, 0.0f, }; + final float[] tex_vertices = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, }; + fbElements++; + fbVertices.put(vertices); + fbTextures.put(tex_vertices); + fbColors.put(currentColor); + } + + @Override + public void glDrawStringLeft(final float x, final float y, final String text) { + final int txtLen = text.length(); + final int[] txtArray = currentFont.getCharIndexes(text); + int tableIndexX; + int tableIndexY; + for (int currentCharIndex = 0; currentCharIndex < txtLen; currentCharIndex++) { + tableIndexX = txtArray[currentCharIndex] % currentFont.memoryWidthOfEachColumn; + tableIndexY = (txtArray[currentCharIndex] - tableIndexX) / currentFont.memoryWidthOfEachColumn; + glFillRect(x + (float) currentCharIndex * (float) currentFont.charW, y, currentFont.charW, currentFont.charH, tableIndexX * currentFont.charW, tableIndexY * currentFont.charH, currentFont.charW, currentFont.charH); + } + } + + @Override + public void glDrawStringCenter(final float x, final float y, final String text) { + glDrawStringLeft(x - currentFont.getStringWidth(text) / 2, y, text); + } + + @Override + public void glDrawStringRight(final float x, final float y, final String text) { + glDrawStringLeft(x - currentFont.getStringWidth(text), y, text); + } + + @Override + public void glDrawCharLeft(final int x, final int y, final char ch) { + final int index = currentFont.getCharIndex(ch); + final int tableIndexX = index % currentFont.memoryWidthOfEachColumn; + final int tableIndexY = (index - tableIndexX) / currentFont.memoryWidthOfEachColumn; + glFillRect(x, y, currentFont.charW, currentFont.charH, tableIndexX * currentFont.charW, tableIndexY * currentFont.charH, currentFont.charW, currentFont.charH); + } + + @Override + public void glDrawCharCenter(final int x, final int y, final char ch) { + glDrawCharLeft(x - currentFont.charW / 2, y, ch); + } + + @Override + public void glDrawCharRight(final int x, final int y, final char ch) { + glDrawCharLeft(x - currentFont.charW, y, ch); + } + + @Override + public BinaryFont getCurrentFont() { + return currentFont; + } + + @Deprecated + static Texture importTexture(final GL gl, final String string) throws IOException { + final FileInputStream f = new FileInputStream("test.png"); + final TextureData tx_dat = TextureIO.newTextureData(gl.getGLProfile(), f, false, TextureIO.PNG); + final Texture tex = new Texture(gl, tx_dat); + tex.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + tex.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); +// tex.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); +// tex.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + return tex; + } + + static OpenedTextureData openTexture(final String file, final boolean isResource) throws GLException, IOException { + BufferedImage img = ImageIO.read(isResource ? JOGLRenderer.class.getResource("/" + file) : new File(file).toURI().toURL()); + File f; + if (isResource) { + f = Files.createTempFile("texture-", ".png").toFile(); + f.deleteOnExit(); + ImageIO.write(img, "png", f); + } else + f = new File(file); + final int imgW = img.getWidth(); + final int imgH = img.getHeight(); + img = null; + Engine.getPlatform().gc(); + return new OpenedTextureData(imgW, imgH, f, isResource); + } + + public static class OpenedTextureData { + public final int w; + public final int h; + public final File f; + public final boolean deleteOnExit; + + /** + * @param w + * @param h + * @param f + * @param deleteOnExit + */ + public OpenedTextureData(final int w, final int h, final File f, final boolean deleteOnExit) { + this.w = w; + this.h = h; + this.f = f; + this.deleteOnExit = deleteOnExit; + } + + } + + static Texture importTexture(File f, final boolean deleteOnExit) throws GLException, IOException { + final Texture tex = TextureIO.newTexture(f, false); + if (deleteOnExit && f.exists()) + try { + if (Engine.getPlatform().getSettings().isDebugEnabled()) + throw new IOException("Delete on exit!"); + f.delete(); + } catch (final Exception ex) { + f.deleteOnExit(); + } + tex.setTexParameteri(JOGLRenderer.gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + tex.setTexParameteri(JOGLRenderer.gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + f = null; + return tex; + } + + public void initDrawCycle() { + final boolean textureChange = precTexEnabled != currentTexEnabled || precTex != currentTex; + if (fbVertices == null) { + fbVertices = Buffers.newDirectFloatBuffer(JOGLRenderer.vertMax); + fbTextures = Buffers.newDirectFloatBuffer(JOGLRenderer.texMax); + fbColors = Buffers.newDirectFloatBuffer(JOGLRenderer.colMax); + handlers = new int[3]; + JOGLRenderer.gl.glGenBuffers(3, handlers, 0); + } + startDrawSegment(false); + if (textureChange) + changeTexture(); + } + + public void endDrawCycle() { + final boolean textureChange = precTexEnabled != currentTexEnabled || precTex != currentTex; + if (textureChange) { + if (fbElements > 0) + endDrawSegment(); + changeTexture(); + } else if (fbElements > 0) + endDrawSegment(); + } + + private void changeTexture() { + precTexEnabled = currentTexEnabled; + precTex = currentTex; + if (currentTexEnabled) { + JOGLRenderer.gl.glEnable(GL.GL_TEXTURE_2D); + currentTex.bind(JOGLRenderer.gl); + } else + JOGLRenderer.gl.glDisable(GL.GL_TEXTURE_2D); + firstBufferTexDataCall = true; + } + + public void startDrawSegment(final boolean continuation) { + if (!continuation || cycleEnded) + fbElements = 0; + cycleEnded = false; + } + + private boolean precTexEnabled; + private Texture precTex; + private boolean cycleEnded = true; + + public void doDrawSegment() { + final boolean textureChange = precTexEnabled != currentTexEnabled || precTex != currentTex; + final boolean changeRequired = fbElements >= JOGLRenderer.ELEMENTS_MAX_COUNT_PER_BUFFER; + if (textureChange) { + if (fbElements > 0) { + endDrawSegment(); + startDrawSegment(false); + } + changeTexture(); + } else if (fbElements > 0 && changeRequired) { + endDrawSegment(); + startDrawSegment(true); + } + } + + boolean firstBufferDataCall = true; + boolean firstBufferTexDataCall = true; + + public void endDrawSegment() { + fbVertices.flip(); + fbTextures.flip(); + fbColors.flip(); + +// gl.glVertexPointer(vertSize, GL.GL_FLOAT, 0, fbVertices); + JOGLRenderer.gl.glBindBuffer(GL.GL_ARRAY_BUFFER, handlers[JOGLRenderer.vertBuffer]); + if (firstBufferTexDataCall) + JOGLRenderer.gl.glBufferData(GL.GL_ARRAY_BUFFER, fbVertices.limit() * Buffers.SIZEOF_FLOAT, fbVertices, GL.GL_STATIC_DRAW); + else + JOGLRenderer.gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, fbVertices.limit() * Buffers.SIZEOF_FLOAT, fbVertices); + JOGLRenderer.gl.glVertexPointer(JOGLRenderer.vertSize, GL.GL_FLOAT, 0, 0l); +// gl.glTexCoordPointer(texSize, GL.GL_FLOAT, 0, fbTextures); + JOGLRenderer.gl.glBindBuffer(GL.GL_ARRAY_BUFFER, handlers[JOGLRenderer.texBuffer]); + if (firstBufferTexDataCall) + JOGLRenderer.gl.glBufferData(GL.GL_ARRAY_BUFFER, fbTextures.limit() * Buffers.SIZEOF_FLOAT, fbTextures, GL.GL_STATIC_DRAW); + else + JOGLRenderer.gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, fbTextures.limit() * Buffers.SIZEOF_FLOAT, fbTextures); + JOGLRenderer.gl.glTexCoordPointer(JOGLRenderer.texSize, GL.GL_FLOAT, 0, 0l); +// gl.glColorPointer(colSize, GL.GL_FLOAT, 0, fbColors); + JOGLRenderer.gl.glBindBuffer(GL.GL_ARRAY_BUFFER, handlers[JOGLRenderer.colBuffer]); + if (firstBufferTexDataCall) + JOGLRenderer.gl.glBufferData(GL.GL_ARRAY_BUFFER, fbColors.limit() * Buffers.SIZEOF_FLOAT, fbColors, GL.GL_STATIC_DRAW); + else + JOGLRenderer.gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, fbColors.limit() * Buffers.SIZEOF_FLOAT, fbColors); + JOGLRenderer.gl.glColorPointer(JOGLRenderer.colSize, GL.GL_FLOAT, 0, 0l); + + fbVertices.limit(JOGLRenderer.vertMax); + fbTextures.limit(JOGLRenderer.texMax); + fbColors.limit(JOGLRenderer.colMax); + JOGLRenderer.gl.glDrawArrays(GL.GL_TRIANGLES, 0, fbElements * JOGLRenderer.ELEMENT_VERTICES_COUNT); + //gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, fbElements * ELEMENT_VERTICES_COUNT); + firstBufferDataCall = false; + firstBufferTexDataCall = false; + cycleEnded = true; + +// deleteBuffer(fbVertices); +// deleteBuffer(txVertices); +// deleteBuffer(colVertices); +// fbVertices = null; +// txVertices = null; +// colVertices = null; + } + + @Override + public void glClearSkin() { + if (currentTex != null) { + currentTex = null; + doDrawSegment(); + } + } + + void disableTexture() { + currentTexEnabled = false; + doDrawSegment(); + } + + void enableTexture() { + currentTexEnabled = true; + doDrawSegment(); + } + + void useTexture(final Texture t, final float w, final float h) { + currentTex = t; + currentTexWidth = w; + currentTexHeight = h; + enableTexture(); + } } \ No newline at end of file diff --git a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java similarity index 95% rename from engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java rename to engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java index 1361202c..fa69134a 100644 --- a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/JOGLSkin.java @@ -1,75 +1,75 @@ -package it.cavallium.warppi.gui.graphicengine.impl.jogl; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -import com.jogamp.opengl.GLException; -import com.jogamp.opengl.util.texture.Texture; - -import it.cavallium.warppi.gui.graphicengine.GraphicEngine; -import it.cavallium.warppi.gui.graphicengine.Skin; -import it.cavallium.warppi.gui.graphicengine.impl.jogl.JOGLRenderer.OpenedTextureData; - -public class JOGLSkin implements Skin { - - public Texture t; - public int w; - public int h; - - private String texturePath; - private boolean initialized = false; - private boolean isResource; - - JOGLSkin(final GraphicEngine d, final String file) throws IOException { - load(file); - } - - @Override - public void load(final String file) throws IOException { - final boolean isResource = !Files.exists(Paths.get(file)); - if (isResource && this.getClass().getClassLoader().getResource(file) == null) - throw new IOException("File '" + file + "' not found!"); - texturePath = file; - this.isResource = isResource; - } - - @Override - public void initialize(final GraphicEngine d) { - try { - final OpenedTextureData i = JOGLRenderer.openTexture(texturePath, isResource); - t = JOGLRenderer.importTexture(i.f, i.deleteOnExit); - w = i.w; - h = i.h; - ((JOGLEngine) d).registerTexture(t); - initialized = true; - } catch (GLException | IOException e) { - e.printStackTrace(); - System.exit(1); - } - } - - @Override - public void use(final GraphicEngine d) { - if (!initialized) - initialize(d); - final JOGLRenderer r = (JOGLRenderer) d.getRenderer(); - r.useTexture(t, w, h); - } - - @Override - public boolean isInitialized() { - return initialized; - } - - @Override - public int getSkinWidth() { - return w; - } - - @Override - public int getSkinHeight() { - return h; - } - -} +package it.cavallium.warppi.gui.graphicengine.impl.jogl; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.util.texture.Texture; + +import it.cavallium.warppi.gui.graphicengine.GraphicEngine; +import it.cavallium.warppi.gui.graphicengine.Skin; +import it.cavallium.warppi.gui.graphicengine.impl.jogl.JOGLRenderer.OpenedTextureData; + +public class JOGLSkin implements Skin { + + public Texture t; + public int w; + public int h; + + private String texturePath; + private boolean initialized = false; + private boolean isResource; + + JOGLSkin(final GraphicEngine d, final String file) throws IOException { + load(file); + } + + @Override + public void load(final String file) throws IOException { + final boolean isResource = !Files.exists(Paths.get(file)); + if (isResource && this.getClass().getClassLoader().getResource(file) == null) + throw new IOException("File '" + file + "' not found!"); + texturePath = file; + this.isResource = isResource; + } + + @Override + public void initialize(final GraphicEngine d) { + try { + final OpenedTextureData i = JOGLRenderer.openTexture(texturePath, isResource); + t = JOGLRenderer.importTexture(i.f, i.deleteOnExit); + w = i.w; + h = i.h; + ((JOGLEngine) d).registerTexture(t); + initialized = true; + } catch (GLException | IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + @Override + public void use(final GraphicEngine d) { + if (!initialized) + initialize(d); + final JOGLRenderer r = (JOGLRenderer) d.getRenderer(); + r.useTexture(t, w, h); + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public int getSkinWidth() { + return w; + } + + @Override + public int getSkinHeight() { + return h; + } + +} diff --git a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java similarity index 97% rename from engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java rename to engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java index 93a52ea2..50b3c7d8 100644 --- a/engine-gpu/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java +++ b/engine-jogl/src/main/java/it/cavallium/warppi/gui/graphicengine/impl/jogl/NEWTWindow.java @@ -1,553 +1,553 @@ -/** - * Copyright 2012-2013 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package it.cavallium.warppi.gui.graphicengine.impl.jogl; - -import java.util.List; - -import com.jogamp.newt.event.KeyEvent; -import com.jogamp.newt.event.KeyListener; -import com.jogamp.newt.event.MouseEvent; -import com.jogamp.newt.event.MouseListener; -import com.jogamp.newt.event.WindowEvent; -import com.jogamp.newt.event.WindowListener; -import com.jogamp.newt.event.WindowUpdateEvent; -import com.jogamp.newt.opengl.GLWindow; -import com.jogamp.opengl.GL; -import com.jogamp.opengl.GL2ES1; -import com.jogamp.opengl.GLAutoDrawable; -import com.jogamp.opengl.GLCapabilities; -import com.jogamp.opengl.GLEventListener; -import com.jogamp.opengl.GLProfile; -import com.jogamp.opengl.fixedfunc.GLLightingFunc; -import com.jogamp.opengl.fixedfunc.GLMatrixFunc; -import com.jogamp.opengl.fixedfunc.GLPointerFunc; -import com.jogamp.opengl.util.Animator; -import com.jogamp.opengl.util.texture.Texture; - -import it.cavallium.warppi.Engine; -import it.cavallium.warppi.StaticVars; -import it.cavallium.warppi.device.Keyboard; -import it.cavallium.warppi.event.Key; -import it.cavallium.warppi.event.TouchEndEvent; -import it.cavallium.warppi.event.TouchMoveEvent; -import it.cavallium.warppi.event.TouchPoint; -import it.cavallium.warppi.event.TouchStartEvent; -import it.cavallium.warppi.flow.BehaviorSubject; -import it.cavallium.warppi.flow.SimpleSubject; -import it.cavallium.warppi.flow.Subject; -import it.cavallium.warppi.gui.graphicengine.GraphicEngine; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -/** - * - * @author Xerxes Rånby (xranby) - * @author Andrea Cavalli (@Cavallium) - */ - -class NEWTWindow implements GLEventListener { - - private final JOGLEngine disp; - private final JOGLRenderer renderer; - public GLWindow window; - public volatile float windowZoom = 1; - public int[] realWindowSize; - public Runnable onInitialized; - public volatile boolean refreshViewport; - public List touches = new ObjectArrayList<>(); - - final BehaviorSubject onRealResize; - final BehaviorSubject onResizeEvent = BehaviorSubject.create(); - private final BehaviorSubject onZoom = BehaviorSubject.create(); - private final Subject onGLContext = SimpleSubject.create(); - - public NEWTWindow(final JOGLEngine disp) { - this.disp = disp; - renderer = disp.getRenderer(); - disp.size[0] = StaticVars.screenSize[0]; - disp.size[1] = StaticVars.screenSize[1]; - realWindowSize = new int[] { StaticVars.screenSize[0], StaticVars.screenSize[1] }; - windowZoom = StaticVars.windowZoomFunction.apply(StaticVars.windowZoom.getLastValue()); - onRealResize = BehaviorSubject.create(new Integer[] { (int) (StaticVars.screenSize[0] * windowZoom), (int) (StaticVars.screenSize[1] * windowZoom) }); - - onRealResize.subscribe((realSize) -> { - System.err.println("[[[SET REALWINDOWZOOM"); - realWindowSize[0] = realSize[0]; - realWindowSize[1] = realSize[1]; - disp.size[0] = realSize[0] / (int) windowZoom; - disp.size[1] = realSize[1] / (int) windowZoom; - System.err.println("[[[" + realWindowSize[0]); - System.err.println("[[[" + windowZoom); - System.err.println("[[[" + disp.size[0]); - onResizeEvent.onNext(new Integer[] { disp.size[0], disp.size[1] }); - refreshViewport = true; - }); - StaticVars.windowZoom$.subscribe((zoom) -> { - onZoom.onNext(zoom); - }); - onZoom.subscribe((z) -> { - if (windowZoom != 0) { - windowZoom = z; - disp.size[0] = (int) (realWindowSize[0] / windowZoom); - disp.size[1] = (int) (realWindowSize[1] / windowZoom); - StaticVars.screenSize[0] = disp.size[0]; - StaticVars.screenSize[1] = disp.size[1]; - System.err.println("[[[SET WINDOWZOOM"); - System.err.println("[[[" + realWindowSize[0]); - System.err.println("[[[" + windowZoom); - System.err.println("[[[A:" + disp.size[0]); - refreshViewport = true; - } - }); - } - - public void create() { - System.out.println("Loading OpenGL..."); - System.out.println(GLProfile.glAvailabilityToString()); - if (!GLProfile.isAvailable(GLProfile.GL2ES1)) { - System.err.println("Le OpenGL non sono presenti su questo computer!"); - return; - } - if (Engine.getPlatform().getSettings().isDebugEnabled()) - System.setProperty("jnlp.newt.window.icons", "res/icons/calculator-016.png res/icons/calculator-018.png res/icons/calculator-256.png"); - final GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL2ES1)); - System.out.println("Loaded OpenGL"); - // We may at this point tweak the caps and request a translucent drawable - caps.setHardwareAccelerated(true); - caps.setBackgroundOpaque(true); //transparency window -// caps.setSampleBuffers(true); -// caps.setNumSamples(4); - - final GLWindow glWindow = GLWindow.create(caps); - window = glWindow; - - glWindow.setTitle("WarpPI Calculator by Andrea Cavalli (@Cavallium)"); - - glWindow.addWindowListener(new WindowListener() { - - @Override - public void windowDestroyNotify(final WindowEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void windowDestroyed(final WindowEvent e) { - final GraphicEngine engine = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine; - if (engine.isInitialized()) - engine.destroy(); - } - - @Override - public void windowGainedFocus(final WindowEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void windowLostFocus(final WindowEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void windowMoved(final WindowEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void windowRepaint(final WindowUpdateEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void windowResized(final WindowEvent e) { - - } - - }); - glWindow.addKeyListener(new KeyListener() { - @Override - public void keyPressed(final KeyEvent arg0) { - Keyboard.debugKeyCode = arg0.getKeyCode(); - } - - @Override - public void keyReleased(final KeyEvent arg0) { - switch (arg0.getKeyCode()) { - case KeyEvent.VK_ESCAPE: - Keyboard.keyReleased(Key.POWEROFF); - break; - case KeyEvent.VK_D: - Keyboard.keyReleased(Key.debug_DEG); - break; - case KeyEvent.VK_R: - Keyboard.keyReleased(Key.debug_RAD); - break; - case KeyEvent.VK_G: - Keyboard.keyReleased(Key.debug_GRA); - break; - case KeyEvent.VK_X: - if (Keyboard.alpha) - Keyboard.keyReleased(Key.LETTER_X); - else - Keyboard.keyReleased(Key.NONE); - break; - case KeyEvent.VK_P: - if (Keyboard.alpha) - Keyboard.keyReleased(Key.PI); - else - Keyboard.keyReleased(Key.NONE); - break; - case KeyEvent.VK_B: - if (Keyboard.shift) - Keyboard.keyReleased(Key.BRIGHTNESS_CYCLE_REVERSE); - else if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.BRIGHTNESS_CYCLE); - else - Keyboard.keyReleased(Key.ZOOM_MODE); - break; - case KeyEvent.VK_ENTER: - if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.SIMPLIFY); - else - Keyboard.keyReleased(Key.NONE); - int row = 2; - int col = 1; - Keyboard.debugKeysDown[row - 1][col - 1] = false; - break; - case KeyEvent.VK_1: - if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.debug1); - else - Keyboard.keyReleased(Key.NONE); - break; - case KeyEvent.VK_2: - if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.debug2); - else - Keyboard.keyReleased(Key.NONE); - break; - case KeyEvent.VK_3: - if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.debug3); - else - Keyboard.keyReleased(Key.NONE); - break; - case KeyEvent.VK_4: - if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.debug4); - else - Keyboard.keyReleased(Key.NONE); - break; - case KeyEvent.VK_5: - if (!Keyboard.shift && !Keyboard.alpha) - Keyboard.keyReleased(Key.debug5); - else - Keyboard.keyReleased(Key.NONE); - break; - case 0x15: - case KeyEvent.VK_SHIFT: - Keyboard.keyReleased(Key.SHIFT); - if (Keyboard.shift) { - Keyboard.keyPressed(Key.SHIFT); - Keyboard.keyReleased(Key.SHIFT); - } - break; - case KeyEvent.VK_CONTROL: - Keyboard.keyReleased(Key.ALPHA); - if (Keyboard.alpha) { - Keyboard.keyPressed(Key.ALPHA); - Keyboard.keyReleased(Key.ALPHA); - } - break; - case KeyEvent.VK_M: - Keyboard.keyPressed(Key.SURD_MODE); - break; - case KeyEvent.VK_LEFT: - //LEFT - row = 2; - col = 3; - Keyboard.debugKeysDown[row - 1][col - 1] = false; - break; - case KeyEvent.VK_RIGHT: - //RIGHT - row = 2; - col = 5; - Keyboard.debugKeysDown[row - 1][col - 1] = false; - break; - case KeyEvent.VK_UP: - //UP - row = 1; - col = 4; - Keyboard.debugKeysDown[row - 1][col - 1] = false; - break; - case KeyEvent.VK_DOWN: - //Down - row = 3; - col = 4; - Keyboard.debugKeysDown[row - 1][col - 1] = false; - break; - case (short) 12: - //Down - row = 2; - col = 4; - Keyboard.debugKeysDown[row - 1][col - 1] = false; - break; - } - } - }); - glWindow.addMouseListener(new MouseListener() { - - @Override - public void mouseClicked(final MouseEvent e) { -// List newPoints = new ObjectArrayList<>(); -// List changedPoints = new ObjectArrayList<>(); -// List oldPoints = touches; -// int[] xs = e.getAllX(); -// int[] ys = e.getAllY(); -// float[] ps = e.getAllPressures(); -// short[] is = e.getAllPointerIDs(); -// for (int i = 0; i < e.getPointerCount(); i++) { -// newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); -// } -// -// changedPoints.add(newPoints.get(0)); -// newPoints.remove(0); -// touches = newPoints; -// Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchStart(new TouchStartEvent(changedPoints, touches)); -// Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchEnd(new TouchEndEvent(changedPoints, touches)); - } - - @Override - public void mouseEntered(final MouseEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void mouseExited(final MouseEvent e) { - // TODO Auto-generated method stub - - } - - @Override - public void mousePressed(final MouseEvent e) { - final List newPoints = new ObjectArrayList<>(); - final List changedPoints = new ObjectArrayList<>(); - @SuppressWarnings("unused") - final List oldPoints = touches; - final int[] xs = e.getAllX(); - final int[] ys = e.getAllY(); - final float[] ps = e.getAllPressures(); - final short[] is = e.getAllPointerIDs(); - for (int i = 0; i < e.getPointerCount(); i++) - newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); - changedPoints.add(newPoints.get(0)); - touches = newPoints; - Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchStart(new TouchStartEvent(changedPoints, touches)); - } - - @Override - public void mouseReleased(final MouseEvent e) { - final List newPoints = new ObjectArrayList<>(); - final List changedPoints = new ObjectArrayList<>(); - @SuppressWarnings("unused") - final List oldPoints = touches; - final int[] xs = e.getAllX(); - final int[] ys = e.getAllY(); - final float[] ps = e.getAllPressures(); - final short[] is = e.getAllPointerIDs(); - for (int i = 0; i < e.getPointerCount(); i++) - newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); - changedPoints.add(newPoints.get(0)); - newPoints.remove(0); - touches = newPoints; - Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchEnd(new TouchEndEvent(changedPoints, touches)); - } - - @Override - public void mouseMoved(final MouseEvent e) {} - - private long lastDraggedTime = 0; - - @Override - public void mouseDragged(final MouseEvent e) { - final long curTime = System.currentTimeMillis(); - if (curTime - lastDraggedTime > 50) { - lastDraggedTime = curTime; - final List newPoints = new ObjectArrayList<>(); - final List changedPoints = new ObjectArrayList<>(); - final List oldPoints = touches; - final int[] xs = e.getAllX(); - final int[] ys = e.getAllY(); - final float[] ps = e.getAllPressures(); - final short[] is = e.getAllPointerIDs(); - for (int i = 0; i < e.getPointerCount(); i++) - newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); - newPoints.forEach((newp) -> { - oldPoints.forEach((oldp) -> { - if (newp.getID() == oldp.getID()) - if (newp.equals(oldp) == false) - changedPoints.add(newp); - }); - }); - touches = newPoints; - Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchMove(new TouchMoveEvent(changedPoints, touches)); - } - } - - @Override - public void mouseWheelMoved(final MouseEvent e) { - - } - - }); - - glWindow.addGLEventListener(this /* GLEventListener */); - final Animator animator = new Animator(); - animator.add(glWindow); - animator.start(); - } - - @Override - public void init(final GLAutoDrawable drawable) { - final GL2ES1 gl = drawable.getGL().getGL2ES1(); - onGLContext.onNext(gl); - - if (Engine.getPlatform().getSettings().isDebugEnabled()) - //Vsync - gl.setSwapInterval(1); - else - //Vsync - gl.setSwapInterval(2); - - //Textures - gl.glEnable(GL.GL_TEXTURE_2D); - - //Transparency - gl.glEnable(GL.GL_BLEND); - gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); - gl.glShadeModel(GLLightingFunc.GL_FLAT); - - //Multisampling - //gl.glEnable(GL.GL_MULTISAMPLE); - - try { - renderer.currentTex = ((JOGLSkin) disp.loadSkin("test.png")).t; - } catch (final Exception e) { - e.printStackTrace(); - } - - if (onInitialized != null) { - onInitialized.run(); - onInitialized = null; - } - - System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); - System.err.println("INIT GL IS: " + gl.getClass().getName()); - System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR)); - System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER)); - System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION)); - } - - @Override - public void reshape(final GLAutoDrawable glad, final int x, final int y, final int width, final int height) { - onRealResize.onNext(new Integer[] { width, height }); - } - - @Override - public void display(final GLAutoDrawable glad) { - final GL2ES1 gl = glad.getGL().getGL2ES1(); - JOGLRenderer.gl = gl; - onGLContext.onNext(gl); - - final boolean linear = windowZoom % (int) windowZoom != 0f; - if (refreshViewport) { - System.err.println("[[[REFVP"); - refreshViewport = false; - gl.glViewport(0, 0, realWindowSize[0], realWindowSize[1]); - - gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); - gl.glLoadIdentity(); - - gl.glOrtho(0.0, disp.size[0], disp.size[1], 0.0, -1, 1); - - gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - gl.glLoadIdentity(); - - for (final Texture t : disp.registeredTextures) { - t.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, linear ? GL.GL_LINEAR : GL.GL_NEAREST); - t.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); - } - } - while (disp.unregisteredTextures.isEmpty() == false) { - final Texture t = disp.unregisteredTextures.pop(); - t.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, linear ? GL.GL_LINEAR : GL.GL_NEAREST); - t.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); - disp.registeredTextures.addLast(t); - } - - gl.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY); - gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY); - gl.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); - - renderer.initDrawCycle(); - - disp.repaint(); - - renderer.endDrawCycle(); - - gl.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY); - gl.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); - gl.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY); - - JOGLRenderer.gl = null; - - } - - void setSize(final int width, final int height) { - int zoom = (int) windowZoom; - if (zoom == 0) - zoom = onZoom.getLastValue().intValue(); - if (zoom == 0) - zoom = 1; - window.setSize(width * zoom, height * zoom); - onRealResize.onNext(new Integer[] { width * zoom, height * zoom }); - } - - @Override - public void dispose(final GLAutoDrawable drawable) { - System.out.println("cleanup"); -// final GL2ES1 gl = drawable.getGL().getGL2ES1(); - System.exit(0); - } - +/** + * Copyright 2012-2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package it.cavallium.warppi.gui.graphicengine.impl.jogl; + +import java.util.List; + +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2ES1; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLEventListener; +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.fixedfunc.GLLightingFunc; +import com.jogamp.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.opengl.fixedfunc.GLPointerFunc; +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.texture.Texture; + +import it.cavallium.warppi.Engine; +import it.cavallium.warppi.StaticVars; +import it.cavallium.warppi.device.Keyboard; +import it.cavallium.warppi.event.Key; +import it.cavallium.warppi.event.TouchEndEvent; +import it.cavallium.warppi.event.TouchMoveEvent; +import it.cavallium.warppi.event.TouchPoint; +import it.cavallium.warppi.event.TouchStartEvent; +import it.cavallium.warppi.flow.BehaviorSubject; +import it.cavallium.warppi.flow.SimpleSubject; +import it.cavallium.warppi.flow.Subject; +import it.cavallium.warppi.gui.graphicengine.GraphicEngine; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +/** + * + * @author Xerxes Rånby (xranby) + * @author Andrea Cavalli (@Cavallium) + */ + +class NEWTWindow implements GLEventListener { + + private final JOGLEngine disp; + private final JOGLRenderer renderer; + public GLWindow window; + public volatile float windowZoom = 1; + public int[] realWindowSize; + public Runnable onInitialized; + public volatile boolean refreshViewport; + public List touches = new ObjectArrayList<>(); + + final BehaviorSubject onRealResize; + final BehaviorSubject onResizeEvent = BehaviorSubject.create(); + private final BehaviorSubject onZoom = BehaviorSubject.create(); + private final Subject onGLContext = SimpleSubject.create(); + + public NEWTWindow(final JOGLEngine disp) { + this.disp = disp; + renderer = disp.getRenderer(); + disp.size[0] = StaticVars.screenSize[0]; + disp.size[1] = StaticVars.screenSize[1]; + realWindowSize = new int[] { StaticVars.screenSize[0], StaticVars.screenSize[1] }; + windowZoom = StaticVars.windowZoomFunction.apply(StaticVars.windowZoom.getLastValue()); + onRealResize = BehaviorSubject.create(new Integer[] { (int) (StaticVars.screenSize[0] * windowZoom), (int) (StaticVars.screenSize[1] * windowZoom) }); + + onRealResize.subscribe((realSize) -> { + System.err.println("[[[SET REALWINDOWZOOM"); + realWindowSize[0] = realSize[0]; + realWindowSize[1] = realSize[1]; + disp.size[0] = realSize[0] / (int) windowZoom; + disp.size[1] = realSize[1] / (int) windowZoom; + System.err.println("[[[" + realWindowSize[0]); + System.err.println("[[[" + windowZoom); + System.err.println("[[[" + disp.size[0]); + onResizeEvent.onNext(new Integer[] { disp.size[0], disp.size[1] }); + refreshViewport = true; + }); + StaticVars.windowZoom$.subscribe((zoom) -> { + onZoom.onNext(zoom); + }); + onZoom.subscribe((z) -> { + if (windowZoom != 0) { + windowZoom = z; + disp.size[0] = (int) (realWindowSize[0] / windowZoom); + disp.size[1] = (int) (realWindowSize[1] / windowZoom); + StaticVars.screenSize[0] = disp.size[0]; + StaticVars.screenSize[1] = disp.size[1]; + System.err.println("[[[SET WINDOWZOOM"); + System.err.println("[[[" + realWindowSize[0]); + System.err.println("[[[" + windowZoom); + System.err.println("[[[A:" + disp.size[0]); + refreshViewport = true; + } + }); + } + + public void create() { + System.out.println("Loading OpenGL..."); + System.out.println(GLProfile.glAvailabilityToString()); + if (!GLProfile.isAvailable(GLProfile.GL2ES1)) { + System.err.println("Le OpenGL non sono presenti su questo computer!"); + return; + } + if (Engine.getPlatform().getSettings().isDebugEnabled()) + System.setProperty("jnlp.newt.window.icons", "res/icons/calculator-016.png res/icons/calculator-018.png res/icons/calculator-256.png"); + final GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL2ES1)); + System.out.println("Loaded OpenGL"); + // We may at this point tweak the caps and request a translucent drawable + caps.setHardwareAccelerated(true); + caps.setBackgroundOpaque(true); //transparency window +// caps.setSampleBuffers(true); +// caps.setNumSamples(4); + + final GLWindow glWindow = GLWindow.create(caps); + window = glWindow; + + glWindow.setTitle("WarpPI Calculator by Andrea Cavalli (@Cavallium)"); + + glWindow.addWindowListener(new WindowListener() { + + @Override + public void windowDestroyNotify(final WindowEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void windowDestroyed(final WindowEvent e) { + final GraphicEngine engine = Engine.INSTANCE.getHardwareDevice().getDisplayManager().engine; + if (engine.isInitialized()) + engine.destroy(); + } + + @Override + public void windowGainedFocus(final WindowEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void windowLostFocus(final WindowEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void windowMoved(final WindowEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void windowRepaint(final WindowUpdateEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void windowResized(final WindowEvent e) { + + } + + }); + glWindow.addKeyListener(new KeyListener() { + @Override + public void keyPressed(final KeyEvent arg0) { + Keyboard.debugKeyCode = arg0.getKeyCode(); + } + + @Override + public void keyReleased(final KeyEvent arg0) { + switch (arg0.getKeyCode()) { + case KeyEvent.VK_ESCAPE: + Keyboard.keyReleased(Key.POWEROFF); + break; + case KeyEvent.VK_D: + Keyboard.keyReleased(Key.debug_DEG); + break; + case KeyEvent.VK_R: + Keyboard.keyReleased(Key.debug_RAD); + break; + case KeyEvent.VK_G: + Keyboard.keyReleased(Key.debug_GRA); + break; + case KeyEvent.VK_X: + if (Keyboard.alpha) + Keyboard.keyReleased(Key.LETTER_X); + else + Keyboard.keyReleased(Key.NONE); + break; + case KeyEvent.VK_P: + if (Keyboard.alpha) + Keyboard.keyReleased(Key.PI); + else + Keyboard.keyReleased(Key.NONE); + break; + case KeyEvent.VK_B: + if (Keyboard.shift) + Keyboard.keyReleased(Key.BRIGHTNESS_CYCLE_REVERSE); + else if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.BRIGHTNESS_CYCLE); + else + Keyboard.keyReleased(Key.ZOOM_MODE); + break; + case KeyEvent.VK_ENTER: + if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.SIMPLIFY); + else + Keyboard.keyReleased(Key.NONE); + int row = 2; + int col = 1; + Keyboard.debugKeysDown[row - 1][col - 1] = false; + break; + case KeyEvent.VK_1: + if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.debug1); + else + Keyboard.keyReleased(Key.NONE); + break; + case KeyEvent.VK_2: + if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.debug2); + else + Keyboard.keyReleased(Key.NONE); + break; + case KeyEvent.VK_3: + if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.debug3); + else + Keyboard.keyReleased(Key.NONE); + break; + case KeyEvent.VK_4: + if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.debug4); + else + Keyboard.keyReleased(Key.NONE); + break; + case KeyEvent.VK_5: + if (!Keyboard.shift && !Keyboard.alpha) + Keyboard.keyReleased(Key.debug5); + else + Keyboard.keyReleased(Key.NONE); + break; + case 0x15: + case KeyEvent.VK_SHIFT: + Keyboard.keyReleased(Key.SHIFT); + if (Keyboard.shift) { + Keyboard.keyPressed(Key.SHIFT); + Keyboard.keyReleased(Key.SHIFT); + } + break; + case KeyEvent.VK_CONTROL: + Keyboard.keyReleased(Key.ALPHA); + if (Keyboard.alpha) { + Keyboard.keyPressed(Key.ALPHA); + Keyboard.keyReleased(Key.ALPHA); + } + break; + case KeyEvent.VK_M: + Keyboard.keyPressed(Key.SURD_MODE); + break; + case KeyEvent.VK_LEFT: + //LEFT + row = 2; + col = 3; + Keyboard.debugKeysDown[row - 1][col - 1] = false; + break; + case KeyEvent.VK_RIGHT: + //RIGHT + row = 2; + col = 5; + Keyboard.debugKeysDown[row - 1][col - 1] = false; + break; + case KeyEvent.VK_UP: + //UP + row = 1; + col = 4; + Keyboard.debugKeysDown[row - 1][col - 1] = false; + break; + case KeyEvent.VK_DOWN: + //Down + row = 3; + col = 4; + Keyboard.debugKeysDown[row - 1][col - 1] = false; + break; + case (short) 12: + //Down + row = 2; + col = 4; + Keyboard.debugKeysDown[row - 1][col - 1] = false; + break; + } + } + }); + glWindow.addMouseListener(new MouseListener() { + + @Override + public void mouseClicked(final MouseEvent e) { +// List newPoints = new ObjectArrayList<>(); +// List changedPoints = new ObjectArrayList<>(); +// List oldPoints = touches; +// int[] xs = e.getAllX(); +// int[] ys = e.getAllY(); +// float[] ps = e.getAllPressures(); +// short[] is = e.getAllPointerIDs(); +// for (int i = 0; i < e.getPointerCount(); i++) { +// newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); +// } +// +// changedPoints.add(newPoints.get(0)); +// newPoints.remove(0); +// touches = newPoints; +// Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchStart(new TouchStartEvent(changedPoints, touches)); +// Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchEnd(new TouchEndEvent(changedPoints, touches)); + } + + @Override + public void mouseEntered(final MouseEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void mouseExited(final MouseEvent e) { + // TODO Auto-generated method stub + + } + + @Override + public void mousePressed(final MouseEvent e) { + final List newPoints = new ObjectArrayList<>(); + final List changedPoints = new ObjectArrayList<>(); + @SuppressWarnings("unused") + final List oldPoints = touches; + final int[] xs = e.getAllX(); + final int[] ys = e.getAllY(); + final float[] ps = e.getAllPressures(); + final short[] is = e.getAllPointerIDs(); + for (int i = 0; i < e.getPointerCount(); i++) + newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); + changedPoints.add(newPoints.get(0)); + touches = newPoints; + Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchStart(new TouchStartEvent(changedPoints, touches)); + } + + @Override + public void mouseReleased(final MouseEvent e) { + final List newPoints = new ObjectArrayList<>(); + final List changedPoints = new ObjectArrayList<>(); + @SuppressWarnings("unused") + final List oldPoints = touches; + final int[] xs = e.getAllX(); + final int[] ys = e.getAllY(); + final float[] ps = e.getAllPressures(); + final short[] is = e.getAllPointerIDs(); + for (int i = 0; i < e.getPointerCount(); i++) + newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); + changedPoints.add(newPoints.get(0)); + newPoints.remove(0); + touches = newPoints; + Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchEnd(new TouchEndEvent(changedPoints, touches)); + } + + @Override + public void mouseMoved(final MouseEvent e) {} + + private long lastDraggedTime = 0; + + @Override + public void mouseDragged(final MouseEvent e) { + final long curTime = System.currentTimeMillis(); + if (curTime - lastDraggedTime > 50) { + lastDraggedTime = curTime; + final List newPoints = new ObjectArrayList<>(); + final List changedPoints = new ObjectArrayList<>(); + final List oldPoints = touches; + final int[] xs = e.getAllX(); + final int[] ys = e.getAllY(); + final float[] ps = e.getAllPressures(); + final short[] is = e.getAllPointerIDs(); + for (int i = 0; i < e.getPointerCount(); i++) + newPoints.add(Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().makePoint(is[i], xs[i], ys[i], disp.getWidth(), disp.getHeight(), 5, 5, ps[i], 0)); + newPoints.forEach((newp) -> { + oldPoints.forEach((oldp) -> { + if (newp.getID() == oldp.getID()) + if (newp.equals(oldp) == false) + changedPoints.add(newp); + }); + }); + touches = newPoints; + Engine.INSTANCE.getHardwareDevice().getInputManager().getTouchDevice().onTouchMove(new TouchMoveEvent(changedPoints, touches)); + } + } + + @Override + public void mouseWheelMoved(final MouseEvent e) { + + } + + }); + + glWindow.addGLEventListener(this /* GLEventListener */); + final Animator animator = new Animator(); + animator.add(glWindow); + animator.start(); + } + + @Override + public void init(final GLAutoDrawable drawable) { + final GL2ES1 gl = drawable.getGL().getGL2ES1(); + onGLContext.onNext(gl); + + if (Engine.getPlatform().getSettings().isDebugEnabled()) + //Vsync + gl.setSwapInterval(1); + else + //Vsync + gl.setSwapInterval(2); + + //Textures + gl.glEnable(GL.GL_TEXTURE_2D); + + //Transparency + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + gl.glShadeModel(GLLightingFunc.GL_FLAT); + + //Multisampling + //gl.glEnable(GL.GL_MULTISAMPLE); + + try { + renderer.currentTex = ((JOGLSkin) disp.loadSkin("test.png")).t; + } catch (final Exception e) { + e.printStackTrace(); + } + + if (onInitialized != null) { + onInitialized.run(); + onInitialized = null; + } + + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + System.err.println("INIT GL IS: " + gl.getClass().getName()); + System.err.println("GL_VENDOR: " + gl.glGetString(GL.GL_VENDOR)); + System.err.println("GL_RENDERER: " + gl.glGetString(GL.GL_RENDERER)); + System.err.println("GL_VERSION: " + gl.glGetString(GL.GL_VERSION)); + } + + @Override + public void reshape(final GLAutoDrawable glad, final int x, final int y, final int width, final int height) { + onRealResize.onNext(new Integer[] { width, height }); + } + + @Override + public void display(final GLAutoDrawable glad) { + final GL2ES1 gl = glad.getGL().getGL2ES1(); + JOGLRenderer.gl = gl; + onGLContext.onNext(gl); + + final boolean linear = windowZoom % (int) windowZoom != 0f; + if (refreshViewport) { + System.err.println("[[[REFVP"); + refreshViewport = false; + gl.glViewport(0, 0, realWindowSize[0], realWindowSize[1]); + + gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + gl.glLoadIdentity(); + + gl.glOrtho(0.0, disp.size[0], disp.size[1], 0.0, -1, 1); + + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + gl.glLoadIdentity(); + + for (final Texture t : disp.registeredTextures) { + t.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, linear ? GL.GL_LINEAR : GL.GL_NEAREST); + t.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + } + } + while (disp.unregisteredTextures.isEmpty() == false) { + final Texture t = disp.unregisteredTextures.pop(); + t.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, linear ? GL.GL_LINEAR : GL.GL_NEAREST); + t.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + disp.registeredTextures.addLast(t); + } + + gl.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY); + gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY); + gl.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); + + renderer.initDrawCycle(); + + disp.repaint(); + + renderer.endDrawCycle(); + + gl.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY); + gl.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); + gl.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY); + + JOGLRenderer.gl = null; + + } + + void setSize(final int width, final int height) { + int zoom = (int) windowZoom; + if (zoom == 0) + zoom = onZoom.getLastValue().intValue(); + if (zoom == 0) + zoom = 1; + window.setSize(width * zoom, height * zoom); + onRealResize.onNext(new Integer[] { width * zoom, height * zoom }); + } + + @Override + public void dispose(final GLAutoDrawable drawable) { + System.out.println("cleanup"); +// final GL2ES1 gl = drawable.getGL().getGL2ES1(); + System.exit(0); + } + } \ No newline at end of file diff --git a/rules/src/main/java/rules/functions/NumberRule.java b/rules/src/main/java/rules/functions/NumberRule.java index 12af8253..bd106b45 100644 --- a/rules/src/main/java/rules/functions/NumberRule.java +++ b/rules/src/main/java/rules/functions/NumberRule.java @@ -12,6 +12,7 @@ import it.cavallium.warppi.math.functions.Division; import it.cavallium.warppi.math.functions.Number; import it.cavallium.warppi.math.rules.Rule; import it.cavallium.warppi.math.rules.RuleType; +import it.cavallium.warppi.util.Utils; import it.unimi.dsi.fastutil.objects.ObjectArrayList; /** @@ -46,11 +47,15 @@ public class NumberRule implements Rule { final MathContext mathContext = f.getMathContext(); if (mathContext.exactMode) if (((Number) f).isInteger() == false) { - final Number divisor = new Number(mathContext, BigInteger.TEN.pow(((Number) f).getNumberOfDecimalPlaces())); - final Function number = new Number(mathContext, ((Number) f).getTerm().multiply(divisor.getTerm())); - final Function div = new Division(mathContext, number, divisor); - result.add(div); - return result; + final int decimalPlaces = ((Number) f).getNumberOfDecimalPlaces(); + final int decimalDigits = decimalPlaces + 1; + if (decimalDigits < Utils.maxAutoFractionDigits) { + final Number divisor = new Number(mathContext, BigInteger.TEN.pow(decimalPlaces)); + final Function number = new Number(mathContext, ((Number) f).getTerm().multiply(divisor.getTerm())); + final Function div = new Division(mathContext, number, divisor); + result.add(div); + return result; + } } } return null;