Added an auto-splitter for manual entries. Currently just used for the Marx generator entry.

This commit is contained in:
malte0811 2017-10-20 17:36:17 +02:00
parent a4de0c800d
commit 59527ba600
3 changed files with 239 additions and 33 deletions

View file

@ -40,6 +40,7 @@ import malte0811.industrialWires.client.gui.GuiPanelComponent;
import malte0811.industrialWires.client.gui.GuiPanelCreator;
import malte0811.industrialWires.client.gui.GuiRSPanelConn;
import malte0811.industrialWires.client.gui.GuiRenameKey;
import malte0811.industrialWires.client.manual.TextSplitter;
import malte0811.industrialWires.client.panelmodel.PanelModelLoader;
import malte0811.industrialWires.client.render.Shaders;
import malte0811.industrialWires.client.render.TileRenderJacobsLadder;
@ -71,7 +72,10 @@ import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.obj.OBJLoader;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.WeakHashMap;
public class ClientProxy extends CommonProxy {
@Override
@ -246,33 +250,30 @@ public class ClientProxy extends CommonProxy {
new ManualPages.Crafting(m, "industrialwires.panel_meter", new ItemStack(IndustrialWires.panelComponent, 1, 8))
);
List<MarxOreHandler.OreInfo> ores = MarxOreHandler.getRecipes();
List<ManualPages> marxEntry = new ArrayList<>();
marxEntry.add(new ManualPages.Text(m, IndustrialWires.MODID + ".marx0"));
marxEntry.add(new ManualPageMultiblock(m, IndustrialWires.MODID + ".marx1", MultiblockMarx.INSTANCE));
marxEntry.add(new ManualPages.Text(m, IndustrialWires.MODID + ".marx2"));
marxEntry.add(new ManualPages.Text(m, IndustrialWires.MODID + ".marx3"));
marxEntry.add(new ManualPages.Text(m, IndustrialWires.MODID + ".marx4"));
marxEntry.add(new ManualPages.Text(m, IndustrialWires.MODID + ".marx5"));
marxEntry.add(new ManualPages.Text(m, IndustrialWires.MODID + ".marx6"));
String text = I18n.format("ie.manual.entry.industrialwires.marx7")+"\n";
for (int i = 0; i < ores.size(); ) {
for (int j = 0; j < 12 && i < ores.size(); j+=4, i++) {
MarxOreHandler.OreInfo curr = ores.get(i);
if (!curr.exampleInput.isEmpty())
{
text += I18n.format(IndustrialWires.MODID + ".desc.input") + ": §l" + curr.exampleInput.get(0).getDisplayName() + "§r\n";
text += I18n.format(IndustrialWires.MODID + ".desc.output") + ": " + Utils.formatDouble(curr.maxYield, "0.#") + "x" + curr.output.get().getDisplayName() + "\n";
if (curr.outputSmall != null && !curr.outputSmall.get().isEmpty())
{
text += I18n.format(IndustrialWires.MODID + ".desc.alt") + ": " + curr.smallMax + "x" + curr.outputSmall.get().getDisplayName() + "\n";
j++;
}
text += I18n.format(IndustrialWires.MODID + ".desc.ideal_e") + ": " + Utils.formatDouble(curr.avgEnergy * MarxOreHandler.defaultEnergy / 1000, "0.#") + " kJ\n\n";
String text = I18n.format("ie.manual.entry.industrialwires.marx");
for (int i = 0; i < ores.size(); i++) {
MarxOreHandler.OreInfo curr = ores.get(i);
if (!curr.exampleInput.isEmpty()) {
text += I18n.format(IndustrialWires.MODID + ".desc.input") + ": §l" + curr.exampleInput.get(0).getDisplayName() + "§r<br>";
text += I18n.format(IndustrialWires.MODID + ".desc.output") + ": " + Utils.formatDouble(curr.maxYield, "0.#") + "x" + curr.output.get().getDisplayName() + "<br>";
if (curr.outputSmall != null && !curr.outputSmall.get().isEmpty()) {
text += I18n.format(IndustrialWires.MODID + ".desc.alt") + ": " + curr.smallMax + "x" + curr.outputSmall.get().getDisplayName() + "<br>";
}
text += I18n.format(IndustrialWires.MODID + ".desc.ideal_e") + ": " + Utils.formatDouble(curr.avgEnergy * MarxOreHandler.defaultEnergy / 1000, "0.#") + " kJ<br><br>";
}
marxEntry.add(new ManualPages.Text(m, text));
text = "";
}
boolean uni = m.fontRenderer.getUnicodeFlag();
m.fontRenderer.setUnicodeFlag(true);
m.entryRenderPre();
TextSplitter splitter = new TextSplitter(m.fontRenderer::getStringWidth, 16, 120,
(s)->new ManualPages.Text(m, s));
splitter.addSpecialPage(0, 0, 6,
(s)->new ManualPageMultiblock(m, s,
MultiblockMarx.INSTANCE));
splitter.split(text);
m.entryRenderPost();
m.fontRenderer.setUnicodeFlag(uni);
List<ManualPages> marxEntry = splitter.toManualEntry();
m.addEntry("industrialwires.marx", IndustrialWires.MODID, marxEntry.toArray(new ManualPages[marxEntry.size()]));
}

View file

@ -0,0 +1,212 @@
/*
* This file is part of Industrial Wires.
* Copyright (C) 2016-2017 malte0811
* Industrial Wires is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* Industrial Wires is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Industrial Wires. If not, see <http://www.gnu.org/licenses/>.
*/
package malte0811.industrialWires.client.manual;
import blusunrize.lib.manual.ManualPages;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class TextSplitter {
private final Function<String, Integer> width;
private final int lineWidth;
private Map<Integer, Map<Integer, Page>> linesOnSpecialPages = new HashMap<>();
private Map<Integer, Page> pageToSpecial = new HashMap<>();
private List<List<String>> entry = new ArrayList<>();
private Page defPage;
public TextSplitter(Function<String, Integer> w, int lP, int lW, Function<String, ManualPages> defaultPage) {
width = w;
lineWidth = lW;
defPage = new Page(lP, defaultPage);
}
public void clearSpecial() {
linesOnSpecialPages.clear();
}
public void addSpecialPage(int ref, int offset, int linesOnPage, Function<String, ManualPages> factory) {
if (offset<0||(ref!=-1&&ref<0)) {
throw new IllegalArgumentException();
}
if (!linesOnSpecialPages.containsKey(ref)) {
linesOnSpecialPages.put(ref, new HashMap<>());
}
linesOnSpecialPages.get(ref).put(offset, new Page(linesOnPage, factory));
}
// I added labels to all break statements to make it more readable
@SuppressWarnings({"UnnecessaryLabelOnBreakStatement", "UnusedLabel"})
public void split(String in) {
String[] wordsAndSpaces = splitWhitespace(in);
int pos = 0;
List<String> overflow = new ArrayList<>();
updateSpecials(-1, 0);
entry:while (pos<wordsAndSpaces.length) {
List<String> page = new ArrayList<>();
page.addAll(overflow);
overflow.clear();
page:while (page.size()<getLinesOnPage(entry.size())&&pos<wordsAndSpaces.length) {
String line = "";
int currWidth = 0;
line:while (pos<wordsAndSpaces.length&&currWidth<lineWidth) {
String text = wordsAndSpaces[pos];
if (pos<wordsAndSpaces.length) {
int textWidth = getWidth(text);
if (currWidth + textWidth < lineWidth||line.length()==0) {
pos++;
if (text.equals("<np>")) {
page.add(line);
break page;
} else if (text.equals("<br>")) {
break line;
} else if (text.startsWith("<&")&&text.endsWith(">")) {
int id = Integer.parseInt(text.substring(2, text.length()-1));
int pageForId = entry.size();
Map<Integer, Page> specialForId = linesOnSpecialPages.get(id);
if (specialForId!=null&&specialForId.containsKey(0)) {
if (page.size()>getLinesOnPage(pageForId)) {
pageForId++;
}
}
updateSpecials(id, pageForId);
} else if (!Character.isWhitespace(text.charAt(0))||line.length()!=0) {//Don't add whitespace at the start of a line
line += text;
currWidth += textWidth;
}
} else {
break line;
}
}
}
page.add(line);
}
if (!page.stream().allMatch(String::isEmpty)) {
int linesMax = getLinesOnPage(entry.size());
if (page.size()>linesMax) {
overflow.addAll(page.subList(linesMax, page.size()));
page = page.subList(0, linesMax-1);
}
entry.add(page);
}
}
}
public List<ManualPages> toManualEntry() {
List<ManualPages> ret = new ArrayList<>(entry.size());
for (int i = 0; i < entry.size(); i++) {
String s = entry.get(i).stream().collect(Collectors.joining("\n"));
ret.add(pageToSpecial.getOrDefault(i, defPage).factory.apply(s));
}
return ret;
}
private int getWidth(String text) {
switch (text) {
case "<br>":
case "<np>":
return 0;
default:
if (text.startsWith("<link;")) {
text = text.substring(text.indexOf(';') + 1);
text = text.substring(text.indexOf(';') + 1, text.lastIndexOf(';'));
}
return width.apply(text);
}
}
private int getLinesOnPage(int id) {
if (pageToSpecial.containsKey(id)) {
return pageToSpecial.get(id).lines;
}
return defPage.lines;
}
private void updateSpecials(int ref, int page) {
if (linesOnSpecialPages.containsKey(ref)) {
for (Map.Entry<Integer, Page> entry :linesOnSpecialPages.get(ref).entrySet()) {
int specialPage = page+entry.getKey();
if (pageToSpecial.containsKey(specialPage)) {
throw new IllegalStateException("Page "+specialPage+" was registered already");
}
pageToSpecial.put(specialPage, entry.getValue());
}
} else if (ref!=-1) {//Default reference for page 0
System.out.println("WARNING: Reference "+ref+" was found, but no special pages were registered for it");
}
}
private String[] splitWhitespace(String in) {
List<String> parts = new ArrayList<>();
for (int i = 0;i<in.length();) {
StringBuilder here = new StringBuilder();
char first = in.charAt(i);
here.append(first);
i++;
for (;i<in.length();) {
char hereC = in.charAt(i);
byte action = shouldSplit(first, hereC);
if ((action&1)!=0) {
here.append(in.charAt(i));
i++;
}
if ((action&2)!=0||(action&1)==0) {
break;
}
}
parts.add(here.toString());
}
return parts.toArray(new String[parts.size()]);
}
/**
* @return
* &1: add
* &2: end here
*/
private byte shouldSplit(char start, char here) {
byte ret = 0b01;
if (Character.isWhitespace(start)^Character.isWhitespace(here)) {
ret = 0b10;
}
if (here=='<') {
ret = 0b10;
}
if (start=='<') {
ret = 0b01;
if (here=='>') {
ret |= 0b10;
}
}
return ret;
}
public List<List<String>> getEntry() {
return entry;
}
private class Page {
final int lines;
final Function<String, ManualPages> factory;
public Page(int l, Function<String, ManualPages> f) {
factory = f;
lines = l;
}
}
}

View file

@ -131,14 +131,7 @@ ie.manual.entry.industrialwires.jacobs1=These are the required power values in E
ie.manual.entry.industrialwires.marx.name=Marx Generator
ie.manual.entry.industrialwires.marx.subtext=I'm Erwin-Otto, not Karl!
ie.manual.entry.industrialwires.marx0=A Marx Generator is a device use to produce high-voltage high-energy pulses. These pulses are visible as lightning between the output terminals and can be used to process ores. Each type of ore has an ideal amount of processing energy (see <link;industrialwires.marx;§oAppendix B§r;7>). The precise values are unknown, estimate values with 10%% accuracy can be found at the end of this entry. The factor between the actual value and the estimate is the same for all types of ore.
ie.manual.entry.industrialwires.marx1=§lConstruction§r<br>The above plan shows a 5-stage generator capable of producing 3 block lightning. An arbitrary amount of stages can be added by increasing the number of
ie.manual.entry.industrialwires.marx2="middle" layers. Power (either IF or EU) is connected to the HV connector, the redstone wire for the control signals is connected to the redstone connector<br>§lEnergy§r<br>Each stage of the Marx generator consists of a 1.6μF capacitor that is charged to up to 250kV (see <link;industrialwires.marx;§oAppendix A§r;6>). When the generator is fully charged the voltage of each capacitor is roughly equal to the charging voltage. The total energy is the sum of the energy stored in the individual
ie.manual.entry.industrialwires.marx3=capacitors and is split equally between the ores to be processed.<br>§lControl signals§r<br>Voltages are represented by 2 signals: The first signal is simply proportional to the voltage to represent. The second signal is proportional to the voltage in the "gap" between 2 values of the first signal, thus allowing more precise control/measurements. Panel components capable of interacting with analog signals usually support this dual-channel setup.
ie.manual.entry.industrialwires.marx4=The charging voltage is controlled by the white and yellow signals. The voltages of the top and bottom capacitor are output to the magenta and pink resp. the orange and lime signals. The light blue signal is a firing control. If it is high the generator will attempt to fire. If the voltage of the bottom capacitor is lower than 125 kV or the total voltage is lower than 30%% of the maximum output voltage the generator will misfire, discharging the capacitors without actually producing lightning.
ie.manual.entry.industrialwires.marx5=§lSafety§r<br>Due to the high voltages and energies involved in firing a Marx generator a safe distance should be maintained to avoid injury or death. Even outside of this area hearing protection (As provided by Immersive Engineering) is obligatory. Formulas to calculate the safe distances can be found in <link;industrialwires.marx;§oAppendix A§r;6>.
ie.manual.entry.industrialwires.marx6=§lAppendix A: Formulas§r<br>Energy stored in a capacitor:<br>E=0.5*C*U^2<br>E: Energy, C: Capacitance, U: Voltage<br>Voltage from redstone signals:<br>U=250/255*(16*a+b)<br>U: Voltage, a: First signal, b: Second signal<br>Safe distance (Physical damage):<br>r=sqrt(e/50,000)<br>r: Safe distance, e: Energy stored<br>Safe distance (Ear damage):<br>r=sqrt(e)/50<br>r: Safe distance, e: Energy stored
ie.manual.entry.industrialwires.marx7=§lAppendix B: Ore Energy Values§r
ie.manual.entry.industrialwires.marx=A Marx Generator is a device use to produce high-voltage high-energy pulses. These pulses are visible as lightning between the output terminals and can be used to process ores. Each type of ore has an ideal amount of processing energy (see <link;industrialwires.marx;§oAppendix B§r;7>). The precise values are unknown, estimate values with 10%% accuracy can be found at the end of this entry. The factor between the actual value and the estimate is the same for all types of ore.<br>§lConstruction§r<&0><br>The above plan shows a 5-stage generator capable of producing 3 block lightning. An arbitrary amount of stages can be added by increasing the number of "middle" layers. Power (either IF or EU) is connected to the HV connector, the redstone wire for the control signals is connected to the redstone connector<br>§lEnergy§r<br>Each stage of the Marx generator consists of a 1.6μF capacitor that is charged to up to 250kV (see <link;industrialwires.marx;§oAppendix A§r;6>). When the generator is fully charged the voltage of each capacitor is roughly equal to the charging voltage. The total energy is the sum of the energy stored in the individual capacitors and is split equally between the ores to be processed.<br>§lControl signals§r<br>Voltages are represented by 2 signals: The first signal is simply proportional to the voltage to represent. The second signal is proportional to the voltage in the "gap" between 2 values of the first signal, thus allowing more precise control/measurements. Panel components capable of interacting with analog signals usually support this dual-channel setup. The charging voltage is controlled by the white and yellow signals. The voltages of the top and bottom capacitor are output to the magenta and pink resp. the orange and lime signals. The light blue signal is a firing control. If it is high the generator will attempt to fire. If the voltage of the bottom capacitor is lower than 125 kV or the total voltage is lower than 30%% of the maximum output voltage the generator will misfire, discharging the capacitors without actually producing lightning.<br>§lSafety§r<br>Due to the high voltages and energies involved in firing a Marx generator a safe distance should be maintained to avoid injury or death. Even outside of this area hearing protection (As provided by Immersive Engineering) is obligatory. Formulas to calculate the safe distances can be found in <link;industrialwires.marx;§oAppendix A§r;6>.<np>§lAppendix A: Formulas§r<br>Energy stored in a capacitor:<br>E=0.5*C*U^2<br>E: Energy, C: Capacitance, U: Voltage<br><br>Voltage from redstone signals:<br>U=250/255*(16*a+b)<br>U: Voltage, a: First signal, b: Second signal<br><br>Safe distance (Physical damage):<br>r=sqrt(e/50,000)<br>r: Safe distance, e: Energy stored<br><br>Safe distance (Ear damage):<br>r=sqrt(e)/50<br>r: Safe distance, e: Energy stored<np>§lAppendix B: Ore Energy Values§r<br>
ie.manual.entry.industrialwires.intro.name=Introduction
ie.manual.entry.industrialwires.intro.subtext=