vazkii.recubed.client.renders.PieChartRender.java Source code

Java tutorial

Introduction

Here is the source code for vazkii.recubed.client.renders.PieChartRender.java

Source

/**
 * This class was created by <Vazkii>. It's distributed as
 * part of the ReCubed Mod.
 *
 * ReCubed is Open Source and distributed under a
 * Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License
 * (http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_GB)
 *
 * File Created @ [Dec 13, 2013, 6:02:37 PM (GMT)]
 */
package vazkii.recubed.client.renders;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;

import net.minecraft.client.Minecraft;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.StatCollector;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector2f;

import vazkii.recubed.api.internal.Category;
import vazkii.recubed.api.internal.PlayerCategoryData;
import vazkii.recubed.client.core.handler.ClientCacheHandler;
import vazkii.recubed.client.core.helper.RenderHelper;
import vazkii.recubed.common.core.helper.MiscHelper;

public strictfp final class PieChartRender {

    public boolean clickable;
    public int totalVal;

    public Collection<Entry> entries = new TreeSet();

    public static class Entry implements Comparable<Entry> {
        int val;
        float angle;
        int color;
        public String name;

        public Entry(int val, String name) {
            this(val, MiscHelper.generateColorFromString(name), name);
        }

        public Entry(int val, int color, String name) {
            this.val = val;
            this.color = color;
            this.name = name;
        }

        @Override
        public int compareTo(Entry e) {
            return name.compareTo(e.name);
        }

    }

    public static PieChartRender fromCategory(Category category) {
        PieChartRender render = new PieChartRender(true);
        for (String s : category.playerData.keySet())
            render.entries.add(new Entry(category.getTotalValueFromPlayerData(s), s));

        int totalVal = render.buildAngles();
        if (totalVal == 0)
            return null;
        render.truncateSmallValues(totalVal);

        return render;
    }

    public static PieChartRender fromPlayerData(PlayerCategoryData data) {
        PieChartRender render = new PieChartRender(false);
        for (String s : data.stats.keySet())
            render.entries.add(new Entry(data.stats.get(s), s));

        int totalVal = render.buildAngles();
        if (totalVal == 0)
            return null;
        render.truncateSmallValues(totalVal);

        return render;
    }

    private PieChartRender(boolean clickable) {
        this.clickable = clickable;
    }

    private int buildAngles() {
        int totalValue = 0;
        for (Entry entry : entries)
            totalValue += entry.val;

        float mul = 360F / totalValue;
        for (Entry entry : entries)
            entry.angle = entry.val * mul;

        return totalValue;
    }

    private void truncateSmallValues(int totalValue) {
        List<Entry> sortedEntries = new ArrayList(entries);

        Collections.sort(sortedEntries, new Comparator<Entry>() {

            @Override
            public int compare(Entry a, Entry b) {
                return b.val - a.val;
            }

        });

        List<Entry> newEntries = new ArrayList();
        float totalAngle = 0;
        int totalVal = 0;
        int size = 9;
        int i = 0;
        for (Entry entry : sortedEntries) {
            if (i >= size) {
                Entry othersEntry = new Entry(totalValue - totalVal, "recubed.misc.others");
                othersEntry.angle = 360F - totalAngle;
                newEntries.add(othersEntry);

                break;
            }
            totalAngle += entry.angle;
            totalVal += entry.val;
            newEntries.add(entry);

            i++;
        }

        this.totalVal = totalVal;
        entries = newEntries;
    }

    public Entry renderChart(int radius, int x, int y, int mx, int my) {
        GL11.glPushMatrix();
        GL11.glDisable(GL11.GL_TEXTURE_2D);

        Entry tooltip = null;
        float tooltipDeg = 0;

        boolean mouseIn = (x - mx) * (x - mx) + (y - my) * (y - my) <= radius * radius;
        float angle = mouseAngle(x, y, mx, my);
        int highlight = 5;

        GL11.glShadeModel(GL11.GL_SMOOTH);
        float totalDeg = 0;
        for (Entry entry : entries) {
            boolean mouseInSector = mouseIn && angle > totalDeg && angle < totalDeg + entry.angle;
            Color color = new Color(entry.color).brighter();
            Color color1 = ClientCacheHandler.useGradients ? new Color(entry.color).darker().darker() : color;

            if (mouseInSector) {
                tooltip = entry;
                tooltipDeg = totalDeg;

                radius += highlight;
            }

            GL11.glBegin(GL11.GL_TRIANGLE_FAN);

            GL11.glColor4ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue(), (byte) 255);
            GL11.glVertex2i(x, y);

            GL11.glColor4ub((byte) color1.getRed(), (byte) color1.getGreen(), (byte) color1.getBlue(), (byte) 255);
            for (float i = entry.angle; i >= 0; i -= 0.01) {
                float rad = (float) ((i + totalDeg) / 180F * Math.PI);
                GL11.glVertex2d(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
            }
            totalDeg += entry.angle;

            GL11.glColor4ub((byte) 0, (byte) 0, (byte) 0, (byte) 255);
            GL11.glVertex2i(x, y);
            GL11.glEnd();

            if (mouseInSector)
                radius -= highlight;
        }
        GL11.glShadeModel(GL11.GL_FLAT);

        totalDeg = 0;
        GL11.glLineWidth(2F);
        GL11.glColor4f(0F, 0F, 0F, 1F);
        for (Entry entry : entries) {
            if (Math.round(entry.angle) == 360)
                break;

            boolean mouseInSector = mouseIn && angle > totalDeg && angle < totalDeg + entry.angle;

            if (mouseInSector)
                radius += highlight;

            float rad = (float) (totalDeg / 180F * Math.PI);

            GL11.glBegin(GL11.GL_LINES);
            GL11.glVertex2i(x, y);
            GL11.glVertex2d(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
            GL11.glEnd();

            if (mouseInSector)
                radius -= highlight;

            totalDeg += entry.angle;
        }

        GL11.glLineWidth(3F);
        GL11.glColor4f(0F, 0F, 0F, 1F);

        GL11.glBegin(GL11.GL_LINE_LOOP);
        for (float i = 0; i < 360; i++) {
            boolean sectorHighlighted = tooltip != null && i >= tooltipDeg && i < tooltip.angle + tooltipDeg;
            boolean first = tooltip != null && tooltip.angle != 360 && i - tooltipDeg == 0;
            boolean last = tooltip != null && tooltip.angle != 360 && i - (tooltipDeg + tooltip.angle) == -1;

            if (first)
                addVertexForAngle(x, y, i, radius);

            if (sectorHighlighted)
                radius += highlight;

            addVertexForAngle(x, y, i, radius);

            if (last)
                addVertexForAngle(x, y, i + 1, radius);

            if (sectorHighlighted)
                radius -= highlight;
        }
        GL11.glEnd();

        if (tooltip != null) {
            List<String> tooltipList = new ArrayList(
                    Arrays.asList(StatCollector.translateToLocal(tooltip.name), EnumChatFormatting.GRAY + ""
                            + tooltip.val + " (" + Math.round(tooltip.angle / 3.6F * 100D) / 100D + "%)"));
            if (Minecraft.getMinecraft().gameSettings.advancedItemTooltips)
                tooltipList.add(EnumChatFormatting.GRAY + "" + EnumChatFormatting.ITALIC + tooltip.name);
            RenderHelper.renderTooltip(mx, my, tooltipList);
        }

        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glPopMatrix();

        return tooltip;
    }

    private static void addVertexForAngle(int x, int y, float i, int radius) {
        double rad = i / 180F * Math.PI;
        GL11.glVertex2d(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
    }

    private static float mouseAngle(int x, int y, int mx, int my) {
        Vector2f baseVec = new Vector2f(1F, 0F);
        Vector2f mouseVec = new Vector2f(mx - x, my - y);

        float ang = (float) (Math.acos(Vector2f.dot(baseVec, mouseVec) / (baseVec.length() * mouseVec.length()))
                * (180F / Math.PI));
        return my < y ? 360F - ang : ang;
    }
}