/* * Copyright 2014 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.polyworld.viewer.layers; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Collection; import java.util.List; import java.util.function.Function; import org.terasology.math.geom.Rect2i; import org.terasology.math.Region3i; import org.terasology.math.geom.BaseVector2f; import org.terasology.math.geom.ImmutableVector2f; import org.terasology.math.geom.Vector2f; import org.terasology.polyworld.graph.Corner; import org.terasology.polyworld.graph.Edge; import org.terasology.polyworld.graph.Graph; import org.terasology.polyworld.graph.GraphFacet; import org.terasology.polyworld.graph.Region; import org.terasology.polyworld.graph.Triangle; import org.terasology.rendering.nui.properties.Checkbox; import org.terasology.world.viewer.layers.AbstractFacetLayer; import org.terasology.world.viewer.layers.FacetLayerConfig; import org.terasology.world.viewer.layers.Renders; import org.terasology.world.viewer.layers.ZOrder; import org.terasology.world.viewer.picker.CirclePickerClosest; /** * Draws the generated graph on a AWT graphics instance */ @Renders(value = GraphFacet.class, order = ZOrder.BIOME + 1) public class GraphFacetLayer extends AbstractFacetLayer { private Config config = new Config(); public GraphFacetLayer() { setVisible(false); // use default settings } /** * This can be called only through reflection since Config is private * @param config the layer configuration info */ public GraphFacetLayer(Config config) { this.config = config; } @Override public void render(BufferedImage img, org.terasology.world.generation.Region region) { GraphFacet graphFacet = region.getFacet(GraphFacet.class); Graphics2D g = img.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int dx = region.getRegion().minX(); int dy = region.getRegion().minZ(); g.translate(-dx, -dy); for (Graph graph : graphFacet.getAllGraphs()) { if (config.showEdges) { drawEdges(g, graph); } if (config.showTris) { drawTriangles(g, graph); } if (config.showCorners) { drawCorners(g, graph); } if (config.showSites) { drawSites(g, graph); } if (config.showBounds) { drawBounds(g, graph); } } if (config.showLookUp) { drawTriangleLookup(g, graphFacet); } g.dispose(); } private void drawTriangleLookup(Graphics2D g, GraphFacet graphFacet) { Rect2i worldReg = graphFacet.getWorldRegion(); for (int z = worldReg.minY(); z < worldReg.maxY(); z++) { for (int x = worldReg.minX(); x < worldReg.maxX(); x++) { Triangle tri = graphFacet.getWorldTriangle(x, z); if (tri == null) { g.setStroke(new BasicStroke(3f)); g.setColor(Color.MAGENTA); g.drawLine(x, z, x, z); } } } } @Override public String getWorldText(org.terasology.world.generation.Region region, int wx, int wy) { GraphFacet graphFacet = region.getFacet(GraphFacet.class); CirclePickerClosest<Corner> cornerPicker = new CirclePickerClosest<>(new Vector2f(wx, wy), c -> 3); CirclePickerClosest<Region> sitePicker = new CirclePickerClosest<>(new Vector2f(wx, wy), r -> 3); for (Graph graph : graphFacet.getAllGraphs()) { if (graph.getBounds().contains(wx, wy)) { Triangle tri = graphFacet.getWorldTriangle(wx, wy); cornerPicker.offer(tri.getCorner1().getLocation(), tri.getCorner1()); cornerPicker.offer(tri.getCorner2().getLocation(), tri.getCorner2()); sitePicker.offer(tri.getRegion().getCenter(), tri.getRegion()); if (cornerPicker.getClosest() != null) { return cornerPicker.getClosest().toString(); } if (sitePicker.getClosest() != null) { return sitePicker.getClosest().toString(); } } } return null; } public boolean isShowEdges() { return config.showEdges; } public void setShowEdges(boolean showEdges) { if (config.showEdges != showEdges) { config.showEdges = showEdges; notifyObservers(); } } public boolean isShowBounds() { return config.showBounds; } public void setShowBounds(boolean showBounds) { if (config.showBounds != showBounds) { config.showBounds = showBounds; notifyObservers(); } } public boolean isShowCorners() { return config.showCorners; } public void setShowCorners(boolean showCorners) { if (config.showCorners != showCorners) { config.showCorners = showCorners; notifyObservers(); } } public boolean isShowSites() { return config.showSites; } public void setShowSites(boolean showSites) { if (config.showSites != showSites) { config.showSites = showSites; notifyObservers(); } } public boolean isShowTris() { return config.showTris; } public void setShowTris(boolean showTris) { if (config.showTris != showTris) { config.showTris = showTris; notifyObservers(); } } public boolean isShowLookup() { return config.showLookUp; } public void setShowLookup(boolean showLookUp) { if (config.showLookUp != showLookUp) { config.showLookUp = showLookUp; notifyObservers(); } } public static void drawEdges(Graphics2D g, Graph graph) { g.setStroke(new BasicStroke(1)); g.setColor(new Color(192, 192, 192, 160)); for (Edge e : graph.getEdges()) { BaseVector2f r0c = e.getCorner0().getLocation(); BaseVector2f r1c = e.getCorner1().getLocation(); g.drawLine((int) r0c.getX(), (int) r0c.getY(), (int) r1c.getX(), (int) r1c.getY()); } } public static void drawPolys(Graphics2D g, Graph graph, Function<Region, Color> colorFunc) { List<Region> regions = graph.getRegions(); for (final Region reg : regions) { Color col = colorFunc.apply(reg); Collection<Corner> pts = reg.getCorners(); int[] xPoints = new int[pts.size()]; int[] yPoints = new int[pts.size()]; int i = 0; for (Corner corner : pts) { xPoints[i] = (int) corner.getLocation().getX(); yPoints[i] = (int) corner.getLocation().getY(); i++; } g.setColor(col); g.fillPolygon(xPoints, yPoints, pts.size()); } } public static void drawTriangles(Graphics2D g, Graph graph) { List<Region> regions = graph.getRegions(); g.setColor(new Color(64, 64, 255, 224)); for (final Region reg : regions) { BaseVector2f p0 = reg.getCenter(); for (Corner c : reg.getCorners()) { BaseVector2f p1 = c.getLocation(); g.draw(new Line2D.Double(p0.getX(), p0.getY(), p1.getX(), p1.getY())); } } } public static void drawSites(Graphics2D g, Graph graph) { List<Region> centers = graph.getRegions(); g.setColor(Color.ORANGE); for (Region regs : centers) { BaseVector2f c = regs.getCenter(); g.fill(new Rectangle2D.Double(c.getX() - 1, c.getY() - 1, 2, 2)); } } public static void drawCorners(Graphics2D g, Graph graph) { g.setColor(Color.WHITE); for (Corner c : graph.getCorners()) { ImmutableVector2f loc = c.getLocation(); g.fill(new Rectangle2D.Double(loc.getX() - 1, loc.getY() - 1, 2, 2)); } } public static void drawBounds(Graphics2D g, Graph graph) { Rect2i bounds = graph.getBounds(); g.setColor(Color.PINK); g.drawRect(bounds.minX(), bounds.minY(), bounds.width(), bounds.height()); } public static void fillBounds(Graphics2D g, Graph graph) { Rect2i bounds = graph.getBounds(); g.setColor(Color.MAGENTA); g.fillRect(bounds.minX() + 1, bounds.minY() + 1, bounds.width() - 1, bounds.height() - 1); } @Override public FacetLayerConfig getConfig() { return config; } /** * Persistent data */ private static class Config implements FacetLayerConfig { @Checkbox private boolean showEdges = true; @Checkbox private boolean showBounds = true; @Checkbox private boolean showCorners = true; @Checkbox private boolean showSites = true; @Checkbox private boolean showLookUp; @Checkbox private boolean showTris; } }