1
0
mirror of https://github.com/demodude4u/Factorio-FBSR.git synced 2024-11-27 08:20:57 +02:00

Rendering optimization, and added filter inserter for logistics

This commit is contained in:
Weston Rye (Demod) 2017-06-02 17:21:16 -04:00
parent 8837c524b4
commit 4b0fa3e676
6 changed files with 182 additions and 183 deletions

View File

@ -7,8 +7,6 @@ import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
@ -57,8 +55,6 @@ public class FBSR {
private static final double FOOTER_HEIGHT = 0.5;
private static Map<String, BufferedImage> modImageCache = new HashMap<>();
private static volatile String version = null;
private static final Map<String, Color> itemColorCache = new HashMap<>();
@ -115,9 +111,7 @@ public class FBSR {
private static BufferedImage applyRendering(BlueprintReporting reporting, int tileSize, List<Renderer> renderers)
throws JSONException, FileNotFoundException, IOException {
Area area = new Area();
renderers.forEach(r -> area.add(new Area(r.getBounds())));
Rectangle2D bounds = area.getBounds2D();
Rectangle2D.Double bounds = computeBounds(renderers);
bounds.setFrameFromDiagonal(Math.floor(bounds.getMinX()) - 1, Math.floor(bounds.getMinY()) - 1,
Math.ceil(bounds.getMaxX()) + 1, Math.ceil(bounds.getMaxY()) + 1 + FOOTER_HEIGHT);
BufferedImage image = new BufferedImage((int) (bounds.getWidth() * tileSize),
@ -219,26 +213,38 @@ public class FBSR {
return image;
}
private static void debugTracePath(Consumer<Renderer> register, DataTable table, WorldMap map, Point2D.Double pos,
String itemName) {
paintLogisticsAt(map, pos, itemName);
paintReverseLogisticsAt(map, pos, itemName);
drawDot(register, table, pos, itemName);
}
private static void drawDot(Consumer<Renderer> register, DataTable table, Point2D.Double pos, String itemName) {
register.accept(new Renderer(Layer.WIRE, pos) {
@Override
public void render(Graphics2D g) {
g.setColor(getItemLogisticColor(table, itemName));
g.fill(new Ellipse2D.Double(pos.x - 0.25, pos.y - 0.25, 0.5, 0.5));
private static Rectangle2D.Double computeBounds(List<Renderer> renderers) {
if (renderers.isEmpty()) {
return new Rectangle2D.Double();
}
boolean first = true;
double minX = 0, minY = 0, maxX = 0, maxY = 0;
for (Renderer renderer : renderers) {
Rectangle2D.Double bounds = renderer.bounds;
if (first) {
first = false;
minX = bounds.getMinX();
minY = bounds.getMinY();
maxX = bounds.getMaxX();
maxY = bounds.getMaxY();
} else {
minX = Math.min(minX, bounds.getMinX());
minY = Math.min(minY, bounds.getMinY());
maxX = Math.max(maxX, bounds.getMaxX());
maxY = Math.max(maxY, bounds.getMaxY());
}
});
}
return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
}
private static Color getItemLogisticColor(DataTable table, String itemName) {
return itemColorCache.computeIfAbsent(itemName, k -> {
DataPrototype prototype = table.getItem(k).get();
Optional<DataPrototype> optProto = table.getItem(k);
if (!optProto.isPresent()) {
System.err.println("ITEM MISSING FOR LOGISTICS: " + k);
return Color.MAGENTA;
}
DataPrototype prototype = optProto.get();
BufferedImage image = FactorioData.getModImage(prototype.lua().get("icon"));
Color color = Utils.getAverageColor(image);
// return new Color(color.getRGB() | 0xA0A0A0);
@ -259,59 +265,14 @@ public class FBSR {
return version;
}
private static void paintLogisticsAt(WorldMap map, Point2D.Double startPos, String itemName) {
ArrayDeque<Point2D.Double> work = new ArrayDeque<>();
work.add(startPos);
while (!work.isEmpty()) {
Point2D.Double pos = work.poll();
map.getLogisticGridCell(pos).ifPresent(c -> {
if (!c.getTransits().isPresent() || !c.getTransits().get().contains(itemName)) {
c.addTransit(itemName);
c.getMove().ifPresent(d -> {
work.add(d.offset(pos, 0.5));
});
c.getWarp().ifPresent(p -> {
work.add(p);
});
}
});
}
}
private static void paintReverseLogisticsAt(WorldMap map, Point2D.Double startPos, String itemName) {
ArrayDeque<Point2D.Double> work = new ArrayDeque<>();
map.getLogisticGridCell(startPos).ifPresent(c -> {
c.getMovedFrom().ifPresent(l -> {
l.forEach(d -> work.add(d.offset(startPos, 0.5)));
});
c.getWarpedFrom().ifPresent(l -> {
l.forEach(p -> work.add(p));
});
});
while (!work.isEmpty()) {
Point2D.Double pos = work.poll();
map.getLogisticGridCell(pos).ifPresent(c -> {
if (!c.getTransits().isPresent() || !c.getTransits().get().contains(itemName)) {
c.addTransit(itemName);
c.getMovedFrom().ifPresent(l -> {
l.forEach(d -> work.add(d.offset(pos, 0.5)));
});
c.getWarpedFrom().ifPresent(l -> {
l.forEach(p -> work.add(p));
});
}
});
}
}
private static void populateReverseLogistics(WorldMap map) {
Table<Integer, Integer, LogisticGridCell> logisticGrid = map.getLogisticGrid();
logisticGrid.cellSet().forEach(c -> {
Point2D.Double pos = new Point2D.Double(c.getRowKey() / 2.0 + 0.25, c.getColumnKey() / 2.0 + 0.25);
LogisticGridCell cell = c.getValue();
cell.getMove().ifPresent(d -> {
map.getLogisticGridCell(d.offset(pos, 0.5)).ifPresent(mc -> mc.addMovedFrom(d.back()));
map.getLogisticGridCell(d.offset(pos, 0.5)).filter(mc -> mc.acceptMoveFrom(d))
.ifPresent(mc -> mc.addMovedFrom(d.back()));
});
cell.getWarp().ifPresent(p -> {
map.getLogisticGridCell(p).ifPresent(mc -> mc.addWarpedFrom(pos));
@ -347,34 +308,32 @@ public class FBSR {
}
});
// logisticGrid.cellSet().stream().filter(c ->
// c.getValue().isTransitEnd()).forEach(c -> {
// Set<String> inputs = c.getValue().getInputs().get();
// for (String item : inputs) {
// work.add(new Pair<>(map.getLogisticCellPosition(c), c.getValue()));
// while (!work.isEmpty()) {
// Pair<Point2D.Double, LogisticGridCell> pair = work.pop();
// Point2D.Double cellPos = pair.getKey();
// LogisticGridCell cell = pair.getValue();
// if (cell.addTransit(item)) {
// cell.getMovedFrom().ifPresent(l -> {
// for (Direction d : l) {
// Point2D.Double nextCellPos = d.offset(cellPos, 0.5);
// map.getLogisticGridCell(nextCellPos).filter(nc ->
// !nc.isBlockTransit())
// .ifPresent(next -> work.add(new Pair<>(nextCellPos, next)));
// }
// });
// cell.getWarpedFrom().ifPresent(l -> {
// for (Point2D.Double p : l) {
// map.getLogisticGridCell(p).filter(nc -> !nc.isBlockTransit())
// .ifPresent(next -> work.add(new Pair<>(p, next)));
// }
// });
// }
// }
// }
// });
logisticGrid.cellSet().stream().filter(c -> c.getValue().isTransitEnd()).forEach(c -> {
Set<String> inputs = c.getValue().getInputs().get();
for (String item : inputs) {
work.add(new Pair<>(map.getLogisticCellPosition(c), c.getValue()));
while (!work.isEmpty()) {
Pair<Point2D.Double, LogisticGridCell> pair = work.pop();
Point2D.Double cellPos = pair.getKey();
LogisticGridCell cell = pair.getValue();
if (cell.addTransit(item)) {
cell.getMovedFrom().ifPresent(l -> {
for (Direction d : l) {
Point2D.Double nextCellPos = d.offset(cellPos, 0.5);
map.getLogisticGridCell(nextCellPos).filter(nc -> !nc.isBlockTransit())
.ifPresent(next -> work.add(new Pair<>(nextCellPos, next)));
}
});
cell.getWarpedFrom().ifPresent(l -> {
for (Point2D.Double p : l) {
map.getLogisticGridCell(p).filter(nc -> !nc.isBlockTransit())
.ifPresent(next -> work.add(new Pair<>(p, next)));
}
});
}
}
}
});
}
@ -438,13 +397,6 @@ public class FBSR {
}
});
// debugTracePath(renderers::add, table, map, new Point2D.Double(0.25,
// 0.25), "debug");
// debugTracePath(map, renderers, new Point2D.Double(-7.75, -7.75),
// Color.blue);
// debugTracePath(map, renderers, new Point2D.Double(7.75, -7.75),
// Color.green);
showLogisticGrid(renderers::add, table, map);
return applyRendering(reporting, (int) Math.round(TypeRendererFactory.tileSize), renderers);
@ -471,36 +423,50 @@ public class FBSR {
Stroke ps = g.getStroke();
g.setStroke(
new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.setColor(Utils.withAlpha(getItemLogisticColor(table, itemName), 128));
g.setColor(Utils.withAlpha(getItemLogisticColor(table, itemName),
255 - 127 / s.size()));
g.draw(new Line2D.Double(d.right().offset(pos, shift),
d.right().offset(d.offset(pos, 0.5), shift)));
g.setStroke(ps);
}
});
});
// cell.getWarp()
// .filter(p ->
// map.getLogisticGridCell(p).map(LogisticGridCell::isAccepting).orElse(false))
// .ifPresent(p -> {
// register.accept(new Renderer(Layer.LOGISTICS_WARP, pos) {
//
// @Override
// public void render(Graphics2D g) {
// Stroke ps = g.getStroke();
// g.setStroke(
// new BasicStroke(0.15f, BasicStroke.CAP_ROUND,
// BasicStroke.JOIN_ROUND));
// g.setColor(Utils.withAlpha(getItemLogisticColor(table,
// itemName), 128));
// g.draw(new Line2D.Double(pos, p));
// g.setStroke(ps);
// }
// });
// });
i++;
}
});
// cell.getMovedFrom().ifPresent(l -> {
// for (Direction d : l) {
// Point2D.Double p = d.offset(pos, 0.5);
// register.accept(new Renderer(Layer.LOGISTICS_WARP, p) {
// @Override
// public void render(Graphics2D g) {
// Stroke ps = g.getStroke();
// g.setStroke(new BasicStroke(2 / 32f, BasicStroke.CAP_ROUND,
// BasicStroke.JOIN_ROUND));
// g.setColor(Color.cyan);
// g.draw(new Line2D.Double(pos, p));
// g.setStroke(ps);
// }
// });
// }
// });
// cell.getWarpedFrom().ifPresent(l -> {
// for (Point2D.Double p : l) {
// register.accept(new Renderer(Layer.LOGISTICS_WARP, p) {
// @Override
// public void render(Graphics2D g) {
// Stroke ps = g.getStroke();
// g.setStroke(new BasicStroke(2 / 32f, BasicStroke.CAP_ROUND,
// BasicStroke.JOIN_ROUND));
// g.setColor(Color.magenta);
// g.draw(new Line2D.Double(pos, p));
// g.setStroke(ps);
// }
// });
// }
// });
});
}
}

View File

@ -2,6 +2,7 @@ package com.demod.fbsr;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@ -27,16 +28,27 @@ public class LogisticGridCell {
return true;
}
public void addMovedFrom(Direction dir) {
if (acceptFilter.filter(a -> a.ordinal() != dir.ordinal()).isPresent()) {
return;
public void addInput(String itemName) {
if (!inputs.isPresent()) {
inputs = Optional.of(new LinkedHashSet<>());
}
inputs.get().add(itemName);
}
public void addMovedFrom(Direction dir) {
if (!movedFrom.isPresent()) {
movedFrom = Optional.of(new ArrayList<>());
}
movedFrom.get().add(dir);
}
public void addOutput(String itemName) {
if (!outputs.isPresent()) {
outputs = Optional.of(new LinkedHashSet<>());
}
outputs.get().add(itemName);
}
public boolean addTransit(String itemName) {
if (!transits.isPresent()) {
transits = Optional.of(new TreeSet<>());

View File

@ -48,6 +48,7 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
private static final String USERID_DEMOD = "100075603016814592";
private static final int ATTACHMENT_MAX_SIZE = 1024 * 1024;
private static final int BP_MAX_SIZE = 4000000;
private static final Pattern blueprintPattern = Pattern.compile("([0-9][A-Za-z0-9+\\/=\\r\\n]{90,})");
@ -118,7 +119,7 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
try (BufferedReader br = new BufferedReader(
new InputStreamReader(WebUtils.limitMaxBytes(hc.getInputStream(), 1000000)))) {
new InputStreamReader(WebUtils.limitMaxBytes(hc.getInputStream(), BP_MAX_SIZE)))) {
String line;
while ((line = br.readLine()) != null) {
contentBuilder.append(line).append('\n');

View File

@ -77,7 +77,7 @@ public class AssemblingMachineRendering extends TypeRendererFactory {
String recipeName = entity.json().optString("recipe", null);
if (recipeName != null) {
RecipePrototype protoRecipe = dataTable.getRecipe(recipeName).get();
setLogisticMachine(map, entity, prototype, protoRecipe);
setLogisticMachine(map, dataTable, entity, prototype, protoRecipe);
}
}

View File

@ -1,18 +1,26 @@
package com.demod.fbsr.render;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import org.json.JSONObject;
import com.demod.factorio.DataTable;
import com.demod.factorio.FactorioData;
import com.demod.factorio.Utils;
import com.demod.factorio.prototype.EntityPrototype;
import com.demod.fbsr.BlueprintEntity;
import com.demod.fbsr.Direction;
import com.demod.fbsr.LogisticGridCell;
import com.demod.fbsr.Renderer;
import com.demod.fbsr.Renderer.Layer;
import com.demod.fbsr.WorldMap;
@ -38,8 +46,9 @@ public class InserterRendering extends TypeRendererFactory {
Sprite sprite = getSpriteFromAnimation(prototype.lua().get("platform_picture").get("sheet"));
sprite.source.x += sprite.source.width * (dir.back().cardinal());
Sprite spriteArm1 = getSpriteFromAnimation(prototype.lua().get("hand_base_picture"));
Sprite spriteArm2 = getSpriteFromAnimation(prototype.lua().get("hand_open_picture"));
// Sprite spriteArmBase =
// getSpriteFromAnimation(prototype.lua().get("hand_base_picture"));
Sprite spriteArmHand = getSpriteFromAnimation(prototype.lua().get("hand_open_picture"));
double armStretch = -prototype.lua().get("pickup_position").get(2).todouble();
register.accept(spriteRenderer(sprite, entity, prototype));
@ -49,9 +58,9 @@ public class InserterRendering extends TypeRendererFactory {
AffineTransform pat = g.getTransform();
{
Rectangle2D.Double bounds = spriteArm2.bounds;
Rectangle source = spriteArm2.source;
BufferedImage image = spriteArm2.image;
Rectangle2D.Double bounds = spriteArmHand.bounds;
Rectangle source = spriteArmHand.source;
BufferedImage image = spriteArmHand.image;
g.translate(pos.x, pos.y);
g.rotate(dir.back().ordinal() * Math.PI / 4.0);
@ -64,28 +73,31 @@ public class InserterRendering extends TypeRendererFactory {
g.setTransform(pat);
}
});
// register.accept(new Renderer(Layer.LOGISTICS_MOVE, sprite.bounds) {
// @Override
// public void render(Graphics2D g) {
// double armStretch =
// -prototype.lua().get("pickup_position").get(2).todouble();
// Point2D.Double inPos = dir.offset(pos, armStretch);
// Point2D.Double outPos = dir.offset(pos, -armStretch);
//
// map.getBelt(outPos).ifPresent(b -> {
// BeltBend bend = map.getBeltBend(outPos, b);
// Direction cellDir = dir.back().rotate(
// placeItemDir[b.getFacing().rotate(-dir.back().ordinal()).cardinal()][bend.ordinal()]);
//
// g.setColor(Color.cyan);
// g.draw(new Line2D.Double(pos, cellDir.offset(outPos, 0.25)));
// g.setColor(Color.magenta);
// g.drawString(dir.back().cardinal() + "|" + b.getFacing().cardinal() +
// "|" + bend.ordinal(),
// (float) outPos.x, (float) outPos.y);
// });
// }
// });
if (entity.json().has("filters")) {
List<String> items = new ArrayList<>();
Utils.forEach(entity.json().getJSONArray("filters"), (JSONObject j) -> {
items.add(j.getString("name"));
});
if (!items.isEmpty()) {
String itemName = items.get(0);
Sprite spriteIcon = new Sprite();
spriteIcon.image = FactorioData.getModImage(dataTable.getItem(itemName).get().lua().get("icon"));
spriteIcon.source = new Rectangle(0, 0, spriteIcon.image.getWidth(), spriteIcon.image.getHeight());
spriteIcon.bounds = new Rectangle2D.Double(-0.3, -0.3, 0.6, 0.6);
Renderer delegate = spriteRenderer(spriteIcon, entity, prototype);
register.accept(new Renderer(Layer.OVERLAY2, delegate.getBounds()) {
@Override
public void render(Graphics2D g) {
g.setColor(new Color(0, 0, 0, 128));
g.fill(spriteIcon.bounds);
delegate.render(g);
}
});
}
}
}
@Override
@ -98,22 +110,27 @@ public class InserterRendering extends TypeRendererFactory {
Point2D.Double inPos = dir.offset(pos, armStretch);
Point2D.Double outPos = dir.offset(pos, -armStretch);
Optional<BeltCell> belt = map.getBelt(outPos);
belt.ifPresent(b -> {
BeltBend bend = map.getBeltBend(outPos, b);
Direction cellDir = dir.back()
.rotate(placeItemDir[b.getFacing().rotate(-dir.back().ordinal()).cardinal()][bend.ordinal()]);
Direction cellDir;
setLogisticWarp(map, inPos, dir.frontLeft(), outPos, cellDir);
setLogisticWarp(map, inPos, dir.frontRight(), outPos, cellDir);
setLogisticWarp(map, inPos, dir.backLeft(), outPos, cellDir);
setLogisticWarp(map, inPos, dir.backRight(), outPos, cellDir);
});
if (!belt.isPresent()) {
setLogisticWarp(map, inPos, dir.frontLeft(), outPos, dir.frontRight());
setLogisticWarp(map, inPos, dir.frontRight(), outPos, dir.frontRight());
setLogisticWarp(map, inPos, dir.backLeft(), outPos, dir.frontRight());
setLogisticWarp(map, inPos, dir.backRight(), outPos, dir.frontRight());
Optional<BeltCell> belt = map.getBelt(outPos);
if (belt.isPresent()) {
BeltBend bend = map.getBeltBend(outPos, belt.get());
cellDir = dir.back().rotate(
placeItemDir[belt.get().getFacing().rotate(-dir.back().ordinal()).cardinal()][bend.ordinal()]);
} else {
cellDir = dir.frontRight();
}
setLogisticWarp(map, inPos, dir.frontLeft(), outPos, cellDir);
setLogisticWarp(map, inPos, dir.frontRight(), outPos, cellDir);
setLogisticWarp(map, inPos, dir.backLeft(), outPos, cellDir);
setLogisticWarp(map, inPos, dir.backRight(), outPos, cellDir);
if (entity.json().has("filters")) {
LogisticGridCell cell = map.getOrCreateLogisticGridCell(cellDir.offset(outPos, 0.25));
Utils.forEach(entity.json().getJSONArray("filters"), (JSONObject j) -> {
cell.addOutput(j.getString("name"));
});
}
}

View File

@ -22,6 +22,7 @@ import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.json.JSONArray;
import org.json.JSONObject;
@ -424,8 +425,14 @@ public class TypeRendererFactory {
// default do nothing
}
protected void setLogisticMachine(WorldMap map, BlueprintEntity entity, EntityPrototype prototype,
RecipePrototype recipe) {
protected void setLogisticAcceptFilter(WorldMap map, Point2D.Double gridPos, Direction cellDir,
Direction acceptFilter) {
LogisticGridCell cell = map.getOrCreateLogisticGridCell(cellDir.offset(gridPos, 0.25));
cell.setAcceptFilter(Optional.of(acceptFilter));
}
protected void setLogisticMachine(WorldMap map, DataTable dataTable, BlueprintEntity entity,
EntityPrototype prototype, RecipePrototype recipe) {
Point2D.Double entityPos = entity.getPosition();
Rectangle2D.Double box = prototype.getSelectionBox();
double xStart = entityPos.x + box.x;
@ -433,8 +440,10 @@ public class TypeRendererFactory {
double xEnd = xStart + box.width;
double yEnd = yStart + box.height;
Set<String> inputs = recipe.getInputs().keySet();
Set<String> outputs = recipe.getOutputs().keySet();
Set<String> inputs = recipe.getInputs().keySet().stream().filter(k -> dataTable.getItem(k).isPresent())
.collect(Collectors.toSet());
Set<String> outputs = recipe.getOutputs().keySet().stream().filter(k -> dataTable.getItem(k).isPresent())
.collect(Collectors.toSet());
Point2D.Double cellPos = new Point2D.Double();
for (cellPos.x = xStart + 0.25; cellPos.x < xEnd; cellPos.x += 0.5) {
@ -451,19 +460,13 @@ public class TypeRendererFactory {
map.getOrCreateLogisticGridCell(cellDir.offset(gridPos, 0.25)).setMove(Optional.of(moveDir));
}
protected void setLogisticMoveAndAcceptFilter(WorldMap map, Point2D.Double gridPos, Direction cellDir, Direction moveDir,
Direction acceptFilter) {
protected void setLogisticMoveAndAcceptFilter(WorldMap map, Point2D.Double gridPos, Direction cellDir,
Direction moveDir, Direction acceptFilter) {
LogisticGridCell cell = map.getOrCreateLogisticGridCell(cellDir.offset(gridPos, 0.25));
cell.setMove(Optional.of(moveDir));
cell.setAcceptFilter(Optional.of(acceptFilter));
}
protected void setLogisticAcceptFilter(WorldMap map, Point2D.Double gridPos, Direction cellDir,
Direction acceptFilter) {
LogisticGridCell cell = map.getOrCreateLogisticGridCell(cellDir.offset(gridPos, 0.25));
cell.setAcceptFilter(Optional.of(acceptFilter));
}
protected void setLogisticWarp(WorldMap map, Double gridPos1, Direction cellDir1, Double gridPos2,
Direction cellDir2) {
map.getOrCreateLogisticGridCell(cellDir1.offset(gridPos1, 0.25))