438 lines
10 KiB
Java

package com.hbm.module;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import javax.annotation.Nonnegative;
import org.lwjgl.opengl.GL11;
import com.google.common.annotations.Beta;
import com.hbm.inventory.gui.GuiInfoContainer;
import com.hbm.util.BobMathUtil;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.MathHelper;
/**
* Seven segment style displays for GUIs, tried to be as adaptable as possible. Still has some bugs that need to be ironed out but it works for the most part.
* @author UFFR
*
*/
public class NumberDisplay {
/*
* Moved the entire class into its own file to keep GuiInfoContainer tidy.
* This new class needs a reference to the GUI because of this so it can render things using the GUI's utils.
*/
protected GuiInfoContainer gui;
/** The display's X coordinate **/
private int displayX;
/** The display's Y coordinate **/
private int displayY;
/** The display's color, in hexadecimal **/
private int color;
/** The amount of padding between digits, default 3 **/
@Nonnegative
private byte padding = 3;
/** Does it blink or not, default false, not yet used **/
private boolean blink = false;
/** Max number the display can handle **/
private float maxNum;
/** Min number the display can handle **/
private float minNum;
private boolean customBounds = false;
// Should it be a decimal number?
private boolean isFloat = false;
// How many trailing zeros?
private byte floatPad = 1;
/** Does it pad out with zeros **/
private boolean pads = false;
/** Max number of digits the display has, default 3 **/
@Nonnegative
private byte digitLength = 3;
private Number numIn = 0;
private char[] toDisp = {'0', '0', '0'};
@Nonnegative
private short dispOffset = 0;
/** Length and thickness of segments. **/
private int verticalLength = 5;
private int horizontalLength = 4;
private int thickness = 1;
/**
* Construct a new number display
* @param dX X coordinate of the display
* @param dY Y coordinate of the display
* @param c Enum Color to use, invalid enums will default to yellow
*/
public NumberDisplay(GuiInfoContainer gui, int x, int y, EnumChatFormatting c)
{
this(gui, x, y);
setColor(enumToColor(c));
}
/**
* Construct a new number display
* @param x X coordinate of the display
* @param y Y coordinate of the display
* @param color Color to use, valid hexadecimal value required
*/
public NumberDisplay(GuiInfoContainer gui, int x, int y, int color)
{
this(gui, x, y);
setColor(color);
}
/**
* Construct a new number display, color is yellow
* @param x X coordinate of the display
* @param y Y coordinate of the display
*/
public NumberDisplay(GuiInfoContainer gui, int x, int y)
{
displayX = x;
displayY = y;
setColor(0xFFFF55);
this.gui = gui;
}
/**
* Returns a hexadecimal from EnumChatFormatting
* @param c Color to use
* @return
*/
private int enumToColor(EnumChatFormatting c) {
if(c.isColor()) {
switch(c) {
case AQUA:
return 0x55FFFF;
case BLACK:
return 0x000000;
case BLUE:
return 0x5555FF;
case DARK_AQUA:
return 0x00AAAA;
case DARK_BLUE:
return 0x0000AA;
case DARK_GRAY:
return 0x555555;
case DARK_GREEN:
return 0x00AA00;
case DARK_PURPLE:
return 0xAA00AA;
case DARK_RED:
return 0xAA0000;
case GOLD:
return 0xFFAA00;
case GRAY:
return 0xAAAAAA;
case GREEN:
return 0x55FF55;
case LIGHT_PURPLE:
return 0xFF55FF;
case RED:
return 0xFF5555;
case WHITE:
return 0xFFFFFF;
case YELLOW:
return 0xFFFF55;
default:
}
}
return 0xFFFF55;
}
/**
* Sets color
* @param color - The color in hexadecimal
**/
public void setColor(int color) {
this.color = color;
}
/**
* Draw custom number
* @param num - The char array that has the number
*/
public void drawNumber(char[] num)
{
if (blink && !BobMathUtil.getBlink())
return;
short gap = (short) (digitLength - num.length);
for (int i = 0; i < num.length; i++)
{
if (num[i] == '.')
gap--;
dispOffset = (short) ((padding + horizontalLength + 2 * thickness) * (i + gap));
drawChar(num[i]);
}
if (pads)
padOut(gap);
}
/** Draw the previously provided number **/
public void drawNumber()
{
if (isFloat)
formatForFloat();
drawNumber(toDisp);
}
public void drawNumber(Number num)
{
setNumber(num);
drawNumber();
}
private void padOut(short gap)
{
if (gap == 0)
return;
for (int i = 0; i < gap; i++)
{
dispOffset = (short) ((padding + horizontalLength + 2 * thickness) * i);
drawChar('0');
}
}
/** Draw a single character (requires dispOffset to be set) **/
public void drawChar(char num)
{
switch (num)
{
case '1':
drawVertical(1, 0);
drawVertical(1, 1);
break;
case '2':
drawHorizontal(0);
drawVertical(1, 0);
drawHorizontal(1);
drawVertical(0, 1);
drawHorizontal(2);
break;
case '3':
drawHorizontal(0);
drawHorizontal(1);
drawHorizontal(2);
drawVertical(1, 0);
drawVertical(1, 1);
break;
case '4':
drawVertical(0, 0);
drawVertical(1, 0);
drawVertical(1, 1);
drawHorizontal(1);
break;
case '5':
drawHorizontal(0);
drawHorizontal(1);
drawHorizontal(2);
drawVertical(0, 0);
drawVertical(1, 1);
break;
case '6':
drawHorizontal(0);
drawHorizontal(1);
drawHorizontal(2);
drawVertical(0, 0);
drawVertical(0, 1);
drawVertical(1, 1);
break;
case '7':
drawHorizontal(0);
drawVertical(1, 0);
drawVertical(1, 1);
break;
case '8':
drawHorizontal(0);
drawHorizontal(1);
drawHorizontal(2);
drawVertical(0, 0);
drawVertical(1, 0);
drawVertical(0, 1);
drawVertical(1, 1);
break;
case '9':
drawHorizontal(0);
drawHorizontal(1);
drawHorizontal(2);
drawVertical(0, 0);
drawVertical(1, 0);
drawVertical(1, 1);
break;
case '0':
drawHorizontal(0);
drawHorizontal(2);
drawVertical(0, 0);
drawVertical(0, 1);
drawVertical(1, 0);
drawVertical(1, 1);
break;
case '-':
drawHorizontal(1);
break;
case '.':
drawPeriod();
break;
case 'E':
default:
drawHorizontal(0);
drawHorizontal(1);
drawHorizontal(2);
drawVertical(0, 0);
drawVertical(0, 1);
break;
}
}
private void drawHorizontal(int pos)
{
byte offset = (byte) (pos * (verticalLength + thickness));
renderSegment(gui.getGuiLeft() + displayX + dispOffset + thickness, gui.getGuiTop() + displayY + offset, horizontalLength, thickness);
}
private void drawPeriod()
{
renderSegment(gui.getGuiLeft() + displayX + dispOffset + padding - (int) Math.ceil(padding / 2) + (horizontalLength + thickness),
gui.getGuiLeft() + displayY + 2 * (verticalLength + thickness), thickness, thickness);
}
private void drawVertical(int posX, int posY)
{
byte offsetX = (byte) (posX * (horizontalLength + thickness));
byte offsetY = (byte) (posY * (verticalLength + thickness));
renderSegment(gui.getGuiLeft() + displayX + offsetX + dispOffset, gui.getGuiTop() + displayY + offsetY + thickness, thickness, verticalLength);
}
/**
* drawTexturedModalRect() for cool kids
* @param renX X coordinate to render the part
* @param renY Y coordinate to render the part
* @param width Relevant for horizontals
* @param height Relevant for verticals
*/
private void renderSegment(int renX, int renY, int width, int height)
{
final Tessellator tess = Tessellator.instance;
final float z = gui.getZLevel();
GL11.glDisable(GL11.GL_TEXTURE_2D);
tess.startDrawingQuads();
tess.setColorOpaque_I(color);
tess.addVertex(renX, renY + height, z);
tess.addVertex(renX + width, renY + height, z);
tess.addVertex(renX + width, renY + 0, z);
tess.addVertex(renX, renY, z);
tess.draw();
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
public void setNumber(Number num)
{
numIn = num;
if (customBounds)
numIn = MathHelper.clamp_double(num.doubleValue(), minNum, maxNum);
if (isFloat)
formatForFloat();
else
{
toDisp = new Long(Math.round(numIn.doubleValue())).toString().toCharArray();
toDisp = truncOrExpand();
}
}
/** Get the set number **/
public Number getNumber()
{
return numIn;
}
/** Get the char array for display **/
public char[] getDispNumber()
{
return toDisp.clone();
}
/** Make the display blink **/
public NumberDisplay setBlinks(boolean doesBlink)
{
blink = doesBlink;
return this;
}
/** Padding between digits, default 3 **/
public NumberDisplay setPadding(@Nonnegative int p)
{
padding = (byte) p;
return this;
}
/** Max number of digits **/
public NumberDisplay setDigitLength(@Nonnegative int l)
{
digitLength = (byte) l;
toDisp = truncOrExpand();
return this;
}
/** Set sizes and thickness of horizontal and vertical segments. **/
public NumberDisplay setSegmentSize(int vertical, int horizontal, int thickness)
{
this.verticalLength = vertical;
this.horizontalLength = horizontal;
this.thickness = thickness;
return this;
}
/** Set custom number bounds **/
public NumberDisplay setMaxMin(float max, float min)
{
if (min > max)
throw new IllegalArgumentException("Minimum value is larger than maximum value!");
maxNum = max;
minNum = min;
customBounds = true;
return this;
}
/** Pad out the left side of the number with zeros **/
public NumberDisplay setPadNumber()
{
pads = true;
return this;
}
/** Set the number to be a decimal, default zero trailing is 1 **/
public NumberDisplay setFloat()
{
return setFloat(1);
}
/** Set the number to be a decimal with specified zero trailing **/
public NumberDisplay setFloat(@Nonnegative int pad)
{
floatPad = (byte) pad;
isFloat = true;
formatForFloat();
return this;
}
private void formatForFloat()
{
BigDecimal bd = new BigDecimal(numIn.toString());
bd = bd.setScale(floatPad, RoundingMode.HALF_UP);
// char[] proc = new Double(bd.doubleValue()).toString().toCharArray();
char[] proc = bd.toString().toCharArray();
proc = Double.valueOf(BobMathUtil.roundDecimal(numIn.doubleValue(), floatPad)).toString().toCharArray();
if (proc.length == digitLength)
toDisp = proc;
else
toDisp = truncOrExpand();
}
@Beta
private char[] truncOrExpand()
{
if (isFloat)
{
char[] out = Arrays.copyOf(toDisp, digitLength);
for (int i = 0; i < digitLength; i++)
if (out[i] == '\u0000')
out[i] = '0';
return out.clone();
}
return toDisp;
}
}