All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pepsoft.worldpainter.WorldPainter Maven / Gradle / Ivy

There is a newer version: 2.23.2
Show newest version
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.pepsoft.worldpainter;

import org.pepsoft.util.MemoryUtils;
import org.pepsoft.util.ProgressReceiver;
import org.pepsoft.worldpainter.TileRenderer.LightOrigin;
import org.pepsoft.worldpainter.biomeschemes.CustomBiomeManager;
import org.pepsoft.worldpainter.brushes.BrushShape;
import org.pepsoft.worldpainter.layers.Biome;
import org.pepsoft.worldpainter.layers.Layer;
import org.pepsoft.worldpainter.tools.BiomesTileProvider;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import static org.pepsoft.worldpainter.Constants.*;

/**
 *
 * @author pepijn
 */
public class WorldPainter extends WorldPainterView implements MouseMotionListener, PropertyChangeListener {
    public WorldPainter(ColourScheme colourScheme, BiomeScheme biomeScheme, CustomBiomeManager customBiomeManager) {
        super(false, Math.max(Runtime.getRuntime().availableProcessors() - 1, 1), false);
        this.colourScheme = colourScheme;
        this.biomeScheme = biomeScheme;
        this.customBiomeManager = customBiomeManager;
        setOpaque(true);
        addMouseMotionListener(this);
        enableInputMethods(false);
    }

    public WorldPainter(Dimension dimension, ColourScheme colourScheme, BiomeScheme biomeScheme, CustomBiomeManager customBiomeManager) {
        this(colourScheme, biomeScheme, customBiomeManager);
        setDimension(dimension);
    }

    @Override
    public final Dimension getDimension() {
        return dimension;
    }

    @Override
    public final void setDimension(Dimension dimension) {
        Dimension oldDimension = this.dimension;
        if ((oldDimension != null) && ((oldDimension.getDim() == DIM_NORMAL) || (oldDimension.getDim() == DIM_NORMAL_CEILING))) {
            oldDimension.getWorld().removePropertyChangeListener("spawnPoint", this);
        }
        this.dimension = dimension;
        if (dimension != null) {
            drawContours = dimension.isContoursEnabled();
            contourSeparation = dimension.getContourSeparation();
            if ((dimension.getDim() == DIM_NORMAL) || (dimension.getDim() == DIM_NORMAL_CEILING)) {
                dimension.getWorld().addPropertyChangeListener("spawnPoint", this);
            }

            setGridSize(dimension.getGridSize());
            setPaintGrid(dimension.isGridEnabled());
            
            if (Configuration.getInstance().getOverlayType() != Configuration.OverlayType.SCALE_ON_LOAD) {
                setOverlayScale(dimension.getOverlayScale());
            } else {
                setOverlayScale(1.0f);
            }
            setOverlayTransparency(dimension.getOverlayTransparency());
            setOverlayOffsetX(dimension.getOverlayOffsetX());
            setOverlayOffsetY(dimension.getOverlayOffsetY());
            setOverlay(null);
            setDrawOverlay(dimension.isOverlayEnabled());
            setMarkerCoords(((dimension.getDim() == DIM_NORMAL) || (dimension.getDim() == DIM_NORMAL_CEILING)) ? dimension.getWorld().getSpawnPoint() : null);
        } else {
            setOverlay(null);
            setMarkerCoords(null);
        }
        firePropertyChange("dimension", oldDimension, dimension);
        refreshTiles();
    }

    public ColourScheme getColourScheme() {
        return colourScheme;
    }

    public void setColourScheme(ColourScheme colourScheme) {
        this.colourScheme = colourScheme;
        refreshTiles();
    }

    public BiomeScheme getBiomeScheme() {
        return biomeScheme;
    }

    public void setBiomeScheme(BiomeScheme biomeScheme) {
        this.biomeScheme = biomeScheme;
        if (! hiddenLayers.contains(Biome.INSTANCE)) {
            refreshTiles();
        }
    }

    public boolean isDrawBrush() {
        return drawBrush;
    }

    public void setDrawBrush(boolean drawBrush) {
        if (drawBrush != this.drawBrush) {
            this.drawBrush = drawBrush;
            firePropertyChange("drawBrush", !drawBrush, drawBrush);
            repaintWorld(getBrushBounds());
        }
    }

    public boolean isDrawViewDistance() {
        return drawViewDistance;
    }

    public void setDrawViewDistance(boolean drawViewDistance) {
        if (drawViewDistance != this.drawViewDistance) {
            this.drawViewDistance = drawViewDistance;
            firePropertyChange("drawViewDistance", !drawViewDistance, drawViewDistance);
            repaintWorld(mouseX - VIEW_DISTANCE_RADIUS, mouseY - VIEW_DISTANCE_RADIUS, VIEW_DISTANCE_DIAMETER + 1, VIEW_DISTANCE_DIAMETER + 1);
        }
    }

    public boolean isDrawWalkingDistance() {
        return drawWalkingDistance;
    }

    public void setDrawWalkingDistance(boolean drawWalkingDistance) {
        if (drawWalkingDistance != this.drawWalkingDistance) {
            this.drawWalkingDistance = drawWalkingDistance;
            firePropertyChange("drawWalkingDistance", !drawWalkingDistance, drawWalkingDistance);
            repaintWorld(mouseX - DAY_NIGHT_WALK_DISTANCE_RADIUS, mouseY - DAY_NIGHT_WALK_DISTANCE_RADIUS, DAY_NIGHT_WALK_DISTANCE_DIAMETER + 1, DAY_NIGHT_WALK_DISTANCE_DIAMETER + 1);
        }
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        int oldRadius = this.radius;
        int oldEffectiveRadius = this.effectiveRadius;
        this.radius = radius;
        if ((brushShape == BrushShape.CIRCLE) || ((brushRotation % 90) == 0)) {
            effectiveRadius = radius;
        } else {
            double a = brushRotation / 180.0 * Math.PI;
            effectiveRadius = (int) Math.ceil(Math.abs(Math.sin(a)) * radius + Math.abs(Math.cos(a)) * radius);
        }
        firePropertyChange("radius", oldRadius, radius);
        if (drawBrush && (brushShape != BrushShape.CUSTOM)) {
            int largestRadius = Math.max(oldEffectiveRadius, effectiveRadius);
            int diameter = largestRadius * 2 + 1;
            repaintWorld(mouseX - largestRadius, mouseY - largestRadius, diameter, diameter);
        }
    }

    public BrushShape getBrushShape() {
        return brushShape;
    }

    public void setBrushShape(BrushShape brushShape) {
        if (brushShape != this.brushShape) {
            BrushShape oldBrushShape = this.brushShape;
            Rectangle oldBounds = getBrushBounds();
            this.brushShape = brushShape;
            if ((brushShape == BrushShape.CIRCLE) || (brushShape == BrushShape.CUSTOM) || ((brushRotation % 90) == 0)) {
                effectiveRadius = radius;
            } else {
                double a = brushRotation / 180.0 * Math.PI;
                effectiveRadius = (int) Math.ceil(Math.abs(Math.sin(a)) * radius + Math.abs(Math.cos(a)) * radius);
            }
            firePropertyChange("brushShape", oldBrushShape, brushShape);
            if (drawBrush) {
                repaintWorld(getBrushBounds().union(oldBounds));
            }
        }
    }

    public BufferedImage getOverlay() {
        return overlay;
    }

    public void setOverlay(BufferedImage overlay) {
        BufferedImage oldOverlay = this.overlay;
        this.overlay = overlay;
        if (overlayTransparency < 1.0f) {
            repaint();
        }
        firePropertyChange("overlay", oldOverlay, overlay);
    }

    public int getOverlayOffsetX() {
        return overlayOffsetX;
    }

    public void setOverlayOffsetX(int overlayOffsetX) {
        if (overlayOffsetX != this.overlayOffsetX) {
            int oldOverlayOffsetX = this.overlayOffsetX;
            this.overlayOffsetX = overlayOffsetX;
            if ((overlay != null) && (overlayTransparency < 1.0f)) {
                repaint();
            }
            firePropertyChange("overlayOffsetX", oldOverlayOffsetX, overlayOffsetX);
        }
    }

    public int getOverlayOffsetY() {
        return overlayOffsetY;
    }

    public void setOverlayOffsetY(int overlayOffsetY) {
        if (overlayOffsetY != this.overlayOffsetY) {
            int oldOverlayOffsetY = this.overlayOffsetY;
            this.overlayOffsetY = overlayOffsetY;
            if ((overlay != null) && (overlayTransparency < 1.0f)) {
                repaint();
            }
            firePropertyChange("overlayOffsetY", oldOverlayOffsetY, overlayOffsetY);
        }
    }

    public float getOverlayScale() {
        return overlayScale;
    }

    public void setOverlayScale(float overlayScale) {
        if (overlayScale != this.overlayScale) {
            float oldOverlayScale = this.overlayScale;
            this.overlayScale = overlayScale;
            if ((overlay != null) && (overlayTransparency < 1.0f)) {
                repaint();
            }
            firePropertyChange("overlayScale", oldOverlayScale, overlayScale);
        }
    }

    public float getOverlayTransparency() {
        return overlayTransparency;
    }

    public void setOverlayTransparency(float overlayTransparency) {
        if (overlayTransparency != this.overlayTransparency) {
            float oldOverlayTransparency = this.overlayTransparency;
            this.overlayTransparency = overlayTransparency;
            if (overlay != null) {
                repaint();
            }
            firePropertyChange("overlayTransparency", oldOverlayTransparency, overlayTransparency);
        }
    }

    public boolean isDrawOverlay() {
        return drawOverlay;
    }

    public void setDrawOverlay(boolean drawOverlay) {
        if (drawOverlay != this.drawOverlay) {
            this.drawOverlay = drawOverlay;
            if (overlay != null) {
                repaint();
            } else if (drawOverlay && (dimension.getOverlay() != null)) {
                loadOverlay();
            }
            if (drawOverlay == this.drawOverlay) {
                firePropertyChange("drawOverlay", ! drawOverlay, drawOverlay);
            }
        }
    }

    public Shape getCustomBrushShape() {
        return customBrushShape;
    }

    public void setCustomBrushShape(Shape customBrushShape) {
        Shape oldCustomBrushShape = this.customBrushShape;
        this.customBrushShape = customBrushShape;
        if ((drawBrush) && (brushShape == BrushShape.CUSTOM)) {
            repaintWorld(customBrushShape.getBounds());
        }
        firePropertyChange("customBrushShape", oldCustomBrushShape, customBrushShape);
    }

    public int getContourSeparation() {
        return contourSeparation;
    }

    public void setContourSeparation(int contourSeparation) {
        if (contourSeparation != this.contourSeparation) {
            int oldContourSeparation = this.contourSeparation;
            this.contourSeparation = contourSeparation;
            refreshTiles();
            firePropertyChange("contourSeparation", oldContourSeparation, contourSeparation);
        }
    }

    public boolean isDrawContours() {
        return drawContours;
    }

    public void setDrawContours(boolean drawContours) {
        if (drawContours != this.drawContours) {
            this.drawContours = drawContours;
            refreshTiles();
            firePropertyChange("drawContours", ! drawContours, drawContours);
        }
    }

    public void refreshBrush() {
        Point mousePos = getMousePosition();
        if (mousePos != null) {
            mouseMoved(new MouseEvent(this, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, mousePos.x, mousePos.y, 0, false));
        }
    }

    public void addHiddenLayer(Layer hiddenLayer) {
        if (! hiddenLayers.contains(hiddenLayer)) {
            Set oldHiddenLayers = new HashSet<>(hiddenLayers);
            hiddenLayers.add(hiddenLayer);
            if (dimension != null) {
                tileProvider.addHiddenLayer(hiddenLayer);
                if ((hiddenLayer == TileRenderer.TERRAIN_AS_LAYER) || (hiddenLayer == TileRenderer.FLUIDS_AS_LAYER)) {
                    refreshTiles();
                } else {
                    refreshTilesForLayer(hiddenLayer, true);
                }
            }
            firePropertyChange("hiddenLayers", oldHiddenLayers, hiddenLayers);
        }
    }

    public void removeHiddenLayer(Layer hiddenLayer) {
        if (hiddenLayers.contains(hiddenLayer)) {
            Set oldHiddenLayers = new HashSet<>(hiddenLayers);
            hiddenLayers.remove(hiddenLayer);
            if (dimension != null) {
                tileProvider.removeHiddenLayer(hiddenLayer);
                if ((hiddenLayer == TileRenderer.TERRAIN_AS_LAYER) || (hiddenLayer == TileRenderer.FLUIDS_AS_LAYER)) {
                    refreshTiles();
                } else {
                    refreshTilesForLayer(hiddenLayer, true);
                }
            }
            firePropertyChange("hiddenLayers", oldHiddenLayers, hiddenLayers);
        }
    }

    public Set getHiddenLayers() {
        return (Set) hiddenLayers.clone();
    }

    public void refreshTiles() {
        if (dimension != null) {
            int biomeAlgorithm = -1;
            if (drawBiomes
                    && (dimension.getDim() == DIM_NORMAL)
                    && ((dimension.getBorder() == null) || (! dimension.getBorder().isEndless()))) {
                World2 world = dimension.getWorld();
                if ((world != null) && (world.getPlatform() != null)) {
                    if (world.getPlatform().equals(DefaultPlugin.JAVA_MCREGION)) {
                        biomeAlgorithm = BIOME_ALGORITHM_1_1;
                    } else if (world.getPlatform().equals(DefaultPlugin.JAVA_ANVIL)) {
                        if (world.getGenerator() == Generator.DEFAULT) {
                            biomeAlgorithm = BIOME_ALGORITHM_1_7_DEFAULT;
                        } else if (world.getGenerator() == Generator.LARGE_BIOMES) {
                            biomeAlgorithm = BIOME_ALGORITHM_1_7_LARGE;
                        }
                    }
                }
            }
            tileProvider = new WPTileProvider(dimension, colourScheme, biomeScheme, customBiomeManager, hiddenLayers, drawContours, contourSeparation, lightOrigin, drawBorders, (biomeAlgorithm != -1) ? new BiomesTileProvider(biomeAlgorithm, dimension.getMinecraftSeed(), colourScheme, 0, true) : null, true);
            if (getTileProviderCount() == 0) {
                addTileProvider(tileProvider);
            } else {
                replaceTileProvider(0, tileProvider);
            }
        } else {
            if (getTileProviderCount() > 0) {
                removeTileProvider(0);
            }
            tileProvider = null;
        }
    }

    public void refreshTilesForLayer(Layer layer, boolean evenIfHidden) {
        if ((hiddenLayers.contains(layer) && (! evenIfHidden))
                || (dimension == null)) {
            return;
        }
        int count = 0;
        Set coords = new HashSet<>();
        for (Tile tile: dimension.getTiles()) {
            if (tile.hasLayer(layer)) {
                coords.add(new Point(tile.getX(), tile.getY()));
                count++;
            }
        }
        if (count > 0) {
            refresh(tileProvider, coords);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Refreshing " + count + " tiles for layer " + layer.getName());
        }
    }
    
    @Override
    public void updateStatusBar(int x, int y) {
        App.getInstance().updateStatusBar(x, y);
    }

    public BufferedImage getImage() throws ProgressReceiver.OperationCancelled {
        if (dimension == null) {
            return null;
        }
        TileRenderer tileRenderer = new TileRenderer(dimension, colourScheme, biomeScheme, customBiomeManager, 0);
        tileRenderer.setContourLines(drawContours);
        tileRenderer.setContourSeparation(contourSeparation);
        tileRenderer.setHiddenLayers(hiddenLayers);
        tileRenderer.setLightOrigin(lightOrigin);
        int xOffset = dimension.getLowestX(), yOffset = dimension.getLowestY();
        BufferedImage image = new BufferedImage(dimension.getWidth() << TILE_SIZE_BITS, dimension.getHeight() << TILE_SIZE_BITS, BufferedImage.TYPE_INT_ARGB);
        for (Tile tile: dimension.getTiles()) {
            tileRenderer.renderTile(tile, image, (tile.getX() - xOffset) << TILE_SIZE_BITS, (tile.getY() - yOffset) << TILE_SIZE_BITS);
        }
        return image;
    }

    public void rotateLightLeft() {
        lightOrigin = lightOrigin.left();
        refreshTiles();
    }

    public void rotateLightRight() {
        lightOrigin = lightOrigin.right();
        refreshTiles();
    }
    
    public LightOrigin getLightOrigin() {
        return lightOrigin;
    }
    
    public void setLightOrigin(LightOrigin lightOrigin) {
        if (lightOrigin == null) {
            throw new NullPointerException();
        }
        if (lightOrigin != this.lightOrigin) {
            this.lightOrigin = lightOrigin;
            refreshTiles();
        }
    }
    
    public void moveToSpawn() {
        if ((dimension != null) && (dimension.getDim() == DIM_NORMAL)) {
            moveToMarker();
        }
    }

    public Point getViewCentreInWorldCoords() {
        return new Point(getViewX(), getViewY());
    }

    public int getBrushRotation() {
        return brushRotation;
    }
    
    public void setBrushRotation(int brushRotation) {
        int oldBrushRotation = this.brushRotation;
        int oldEffectiveRadius = effectiveRadius;
        this.brushRotation = brushRotation;
        if ((brushShape == BrushShape.CIRCLE) || ((brushRotation % 90) == 0)) {
            effectiveRadius = radius;
        } else {
            double a = brushRotation / 180.0 * Math.PI;
            effectiveRadius = (int) Math.ceil(Math.abs(Math.sin(a)) * radius + Math.abs(Math.cos(a)) * radius);
        }
        firePropertyChange("brushRotation", oldBrushRotation, brushRotation);
        if (drawBrush && (brushShape != BrushShape.CIRCLE)) {
            int largestRadius = Math.max(oldEffectiveRadius, effectiveRadius);
            int diameter = largestRadius * 2 + 1;
            repaintWorld(mouseX - largestRadius, mouseY - largestRadius, diameter, diameter);
        }
    }
    
    public void minecraftSeedChanged(Dimension dimension, long newSeed) {
        if ((! isInhibitUpdates()) && (! hiddenLayers.contains(Biome.INSTANCE))) {
            refreshTiles();
        }
    }

    public boolean isDrawMinecraftBorder() {
        return drawMinecraftBorder;
    }

    public void setDrawMinecraftBorder(boolean drawMinecraftBorder) {
        if (drawMinecraftBorder != this.drawMinecraftBorder) {
            this.drawMinecraftBorder = drawMinecraftBorder;
            firePropertyChange("drawMinecraftBorder", ! drawMinecraftBorder, drawMinecraftBorder);
            repaint();
        }
    }

    public boolean isDrawBorders() {
        return drawBorders;
    }

    public void setDrawBorders(boolean drawBorders) {
        if (drawBorders != this.drawBorders) {
            this.drawBorders = drawBorders;
            firePropertyChange("drawBorders", ! drawBorders, drawBorders);
            refreshTiles();
        }
    }

    public boolean isDrawBiomes() {
        return drawBiomes;
    }

    public void setDrawBiomes(boolean drawBiomes) {
        if (drawBiomes != this.drawBiomes) {
            this.drawBiomes = drawBiomes;
            if ((dimension != null) && (dimension.getDim() == DIM_NORMAL)) {
                refreshTiles();
            }
            firePropertyChange("drawBiomes", ! drawBiomes, drawBiomes);
        }
    }

    // MouseMotionListener

    @Override
    public void mouseDragged(MouseEvent e) {
        int oldMouseX = mouseX;
        int oldMouseY = mouseY;
        Point mouseInWorld = viewToWorld(e.getPoint());
        mouseX = mouseInWorld.x;
        mouseY = mouseInWorld.y;
        if ((mouseX == oldMouseX) && (mouseY == oldMouseY)) {
            return;
        }
        Rectangle repaintArea = null; // The repaint area in world coordinates relative to the mouse position
        if (drawBrush) {
            if (brushShape != BrushShape.CUSTOM) {
                repaintArea = new Rectangle(-effectiveRadius, -effectiveRadius, effectiveRadius * 2 + 1, effectiveRadius * 2 + 1);
            } else {
                repaintArea = customBrushShape.getBounds();
            }
        }
        if (drawViewDistance) {
            Rectangle viewDistanceArea = new Rectangle(-VIEW_DISTANCE_RADIUS, -VIEW_DISTANCE_RADIUS, VIEW_DISTANCE_DIAMETER, VIEW_DISTANCE_DIAMETER);
            if (repaintArea != null) {
                repaintArea = repaintArea.union(viewDistanceArea);
            } else {
                repaintArea = viewDistanceArea;
            }
        }
        if (drawWalkingDistance) {
            Rectangle walkingDistanceArea = new Rectangle(-DAY_NIGHT_WALK_DISTANCE_RADIUS, -DAY_NIGHT_WALK_DISTANCE_RADIUS, DAY_NIGHT_WALK_DISTANCE_DIAMETER, DAY_NIGHT_WALK_DISTANCE_DIAMETER);
            if (repaintArea != null) {
                repaintArea = repaintArea.union(walkingDistanceArea);
            } else {
                repaintArea = walkingDistanceArea;
            }
        }
        if (repaintArea != null) {
            Rectangle oldRectangle = new Rectangle(oldMouseX + repaintArea.x, oldMouseY + repaintArea.y, repaintArea.width, repaintArea.height);
            Rectangle newRectangle = new Rectangle(mouseX + repaintArea.x, mouseY + repaintArea.y, repaintArea.width, repaintArea.height);
            if (oldRectangle.intersects(newRectangle)) {
                repaintWorld(oldRectangle.union(newRectangle));
            } else {
                // Two separate repaints to avoid having to repaint a huge area
                // just because the cursor jumps a large distance for some
                // reason
                repaintWorld(oldRectangle);
                SwingUtilities.invokeLater(() -> repaintWorld(newRectangle));
            }
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        mouseDragged(e);
    }

    // PropertyChangeListener

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if ((evt.getSource() == dimension.getWorld()) && evt.getPropertyName().equals("spawnPoint")) {
            setMarkerCoords((Point) evt.getNewValue());
        }
    }

    int getOverlayImageSize() {
        return (overlay != null) ? MemoryUtils.getSize(overlay, Collections.>emptySet()) : 0;
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        // Paint the tiles, grid and markers:
        super.paintComponent(g);

        if (dimension != null) {
            // Paint anything else:
            final Graphics2D g2 = (Graphics2D) g;
            final Color savedColour = g2.getColor();
            final Object savedAAValue = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
//            final Object savedInterpolationValue = g2.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
            final Stroke savedStroke = g2.getStroke();
            final AffineTransform savedTransform = g2.getTransform();
            final Font savedFont = g2.getFont();
            try {
                if (drawMinecraftBorder && (dimension.getWorld() != null)) {
                    drawMinecraftBorderIfNecessary(g2, dimension.getWorld().getBorderSettings());
                }

                // Switch to world coordinate system
                final float scale = transformGraphics(g2);
                final float onePixel = 1 / scale;
                if (drawOverlay && (overlay != null)) {
                    drawOverlay(g2);
                }
                if (drawBrush || drawViewDistance || drawWalkingDistance) {
                    g2.setColor(Color.BLACK);
                    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                }
                if (drawBrush) {
                    g2.setStroke(new BasicStroke(onePixel, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, new float[] {3 * onePixel, 3 * onePixel}, 0));
                    final int diameter = radius * 2 + 1;
                    switch (brushShape) {
                        case CIRCLE:
                            g2.drawOval(mouseX - radius, mouseY - radius, diameter, diameter);
                            break;
                        case SQUARE:
                            if (brushRotation % 90 == 0) {
                                g2.drawRect(mouseX - radius, mouseY - radius, diameter, diameter);
                            } else {
                                AffineTransform existingTransform = g2.getTransform();
                                try {
                                    if (scale > 1.0f) {
                                        g2.rotate(brushRotation / 180.0 * Math.PI, mouseX + 0.5, mouseY + 0.5);
                                    } else {
                                        g2.rotate(brushRotation / 180.0 * Math.PI, mouseX, mouseY);
                                    }
                                    g2.drawRect(mouseX - radius, mouseY - radius, diameter, diameter);
                                } finally {
                                    g2.setTransform(existingTransform);
                                }
                            }
                            break;
                        case BITMAP:
                            final int arrowSize = radius / 2;
                            if (brushRotation == 0) {
                                g2.drawRect(mouseX - radius, mouseY - radius, diameter, diameter);
                                if (arrowSize > 0) {
                                    g2.drawLine(mouseX, mouseY - radius, mouseX - arrowSize, mouseY - radius + arrowSize);
                                    g2.drawLine(mouseX - arrowSize, mouseY - radius + arrowSize, mouseX + arrowSize + 1, mouseY - radius + arrowSize);
                                    g2.drawLine(mouseX + arrowSize + 1, mouseY - radius + arrowSize, mouseX + 1, mouseY - radius);
                                }
                            } else {
                                AffineTransform existingTransform = g2.getTransform();
                                try {
                                    if (scale > 1.0f) {
                                        g2.rotate(brushRotation / 180.0 * Math.PI, mouseX + 0.5, mouseY + 0.5);
                                    } else {
                                        g2.rotate(brushRotation / 180.0 * Math.PI, mouseX, mouseY);
                                    }
                                    g2.drawRect(mouseX - radius, mouseY - radius, diameter, diameter);
                                    if (arrowSize > 0) {
                                        g2.drawLine(mouseX, mouseY - radius, mouseX - arrowSize, mouseY - radius + arrowSize);
                                        g2.drawLine(mouseX - arrowSize, mouseY - radius + arrowSize, mouseX + arrowSize + 1, mouseY - radius + arrowSize);
                                        g2.drawLine(mouseX + arrowSize + 1, mouseY - radius + arrowSize, mouseX + 1, mouseY - radius);
                                    }
                                } finally {
                                    g2.setTransform(existingTransform);
                                }
                            }
                            break;
                        case CUSTOM:
                            AffineTransform existingTransform = g2.getTransform();
                            try {
                                g2.translate(mouseX, mouseY);
                                g2.draw(customBrushShape);
                            } finally {
                                g2.setTransform(existingTransform);
                            }
                    }
                }
                if (drawViewDistance) {
                    g2.setStroke(new BasicStroke(onePixel, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, new float[] {10 * onePixel, 10 * onePixel}, 0));
                    g2.drawOval(mouseX - VIEW_DISTANCE_RADIUS, mouseY - VIEW_DISTANCE_RADIUS, VIEW_DISTANCE_DIAMETER, VIEW_DISTANCE_DIAMETER);
                }
                if (drawWalkingDistance) {
                    g2.setStroke(new BasicStroke(onePixel, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, new float[] {20 * onePixel, 20 * onePixel}, 0));
                    g2.drawOval(mouseX - DAY_NIGHT_WALK_DISTANCE_RADIUS, mouseY - DAY_NIGHT_WALK_DISTANCE_RADIUS, DAY_NIGHT_WALK_DISTANCE_DIAMETER, DAY_NIGHT_WALK_DISTANCE_DIAMETER);
                    setFont(NORMAL_FONT.deriveFont(10 * onePixel));
                    g2.drawString("day + night", mouseX - DAY_NIGHT_WALK_DISTANCE_RADIUS + onePixel * 3, mouseY);
                    g2.drawOval(mouseX - DAY_WALK_DISTANCE_RADIUS, mouseY - DAY_WALK_DISTANCE_RADIUS, DAY_WALK_DISTANCE_DIAMETER, DAY_WALK_DISTANCE_DIAMETER);
                    g2.drawString("1 day", mouseX - DAY_WALK_DISTANCE_RADIUS + onePixel * 3, mouseY);
                    g2.drawOval(mouseX - FIVE_MINUTE_WALK_DISTANCE_RADIUS, mouseY - FIVE_MINUTE_WALK_DISTANCE_RADIUS, FIVE_MINUTE_WALK_DISTANCE_DIAMETER, FIVE_MINUTE_WALK_DISTANCE_DIAMETER);
                    g2.drawString("5 min.", mouseX - FIVE_MINUTE_WALK_DISTANCE_RADIUS + onePixel * 3, mouseY);
                }
            } finally {
                g2.setColor(savedColour);
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, savedAAValue);
//                if (savedInterpolationValue != null) {
//                    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, savedInterpolationValue);
//                }
                g2.setStroke(savedStroke);
                g2.setTransform(savedTransform);
                g2.setFont(savedFont);
            }
        }
    }

    /**
     * Get the rectangular bounds of the current brush shape.
     *
     * @return The bounds of the brush.
     */
    private Rectangle getBrushBounds() {
        if (brushShape == BrushShape.CUSTOM) {
            Rectangle bounds = customBrushShape.getBounds();
            bounds.translate(mouseX, mouseY);
            return bounds;
        } else {
            return new Rectangle(mouseX - effectiveRadius, mouseY - effectiveRadius, effectiveRadius * 2 + 1, effectiveRadius * 2 + 1);
        }
    }

    private void loadOverlay() {
        File file = dimension.getOverlay();
        if (file.isFile()) {
            if (file.canRead()) {
                logger.info("Loading image");
                BufferedImage myOverlay;
                try {
                    myOverlay = ImageIO.read(file);
                } catch (IOException e) {
                    logger.error("I/O error while loading image " + file ,e);
                    JOptionPane.showMessageDialog(this, "An error occurred while loading the overlay image.\nIt may not be a valid or supported image file, or the file may be corrupted.", "Error Loading Image", JOptionPane.ERROR_MESSAGE);
                    this.drawOverlay = false;
                    return;
                } catch (RuntimeException | Error e) {
                    logger.error(e.getClass().getSimpleName() + " while loading image " + file ,e);
                    JOptionPane.showMessageDialog(this, "An error occurred while loading the overlay image.\nThere may not be enough available memory, or the image may be too large.", "Error Loading Image", JOptionPane.ERROR_MESSAGE);
                    this.drawOverlay = false;
                    return;
                }
                switch (Configuration.getInstance().getOverlayType()) {
                    case OPTIMISE_ON_LOAD:
                        myOverlay = ConfigureViewDialog.scaleImage(myOverlay, getGraphicsConfiguration(), 100);
                        break;
                    case SCALE_ON_LOAD:
                        myOverlay = ConfigureViewDialog.scaleImage(myOverlay, getGraphicsConfiguration(), (int) (dimension.getOverlayScale() * 100));
                        break;
                }
                if (myOverlay != null) {
                    setOverlay(myOverlay);
                } else {
                    // The scaling or optimisation failed
                    this.drawOverlay = false;
                }
            } else {
                JOptionPane.showMessageDialog(this, "Access denied to overlay image\n" + file, "Error Enabling Overlay", JOptionPane.ERROR_MESSAGE);
                this.drawOverlay = false;
            }
        } else {
            JOptionPane.showMessageDialog(this, "Overlay image file not found\n" + file, "Error Enabling Overlay", JOptionPane.ERROR_MESSAGE);
            this.drawOverlay = false;
        }
    }

    private void drawMinecraftBorderIfNecessary(Graphics2D g2, World2.BorderSettings borderSettings) {
        final int size = borderSettings.getSize(), radius = size / 2;
        final Rectangle border = worldToView(borderSettings.getCentreX() - radius, borderSettings.getCentreY() - radius, size, size);
        Rectangle clip = g2.getClipBounds();
        if ((border.x >= clip.x) || (border.y >= clip.y) || ((border.x + border.width) < (clip.x + clip.width)) || ((border.y + border.height) < (clip.y + clip.height))) {
            g2.setColor(Color.RED);
            g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, new float[] {3, 3}, 0));
            if ((border.width < 5000) && (border.height < 5000)) {
                // If it's small enough performance of drawing it at once is fine
                g2.drawRect(border.x, border.y, border.width, border.height);
            } else if (clip.intersects(border)) {
                // For very large rectangles performance of drawing it as a rect
                // tanks, so draw each line individually, constraining the
                // lengths to the clip bounds
                g2.drawLine(border.x, Math.max(border.y, clip.y), border.x, Math.min(border.y + border.height, clip.y + clip.height));
                g2.drawLine(Math.max(border.x, clip.x), border.y + border.height, Math.min(border.x + border.width, clip.x + clip.width), border.y + border.height);
                g2.drawLine(border.x + border.width, Math.min(border.y + border.height, clip.y + clip.height), border.x + border.width, Math.max(border.y, clip.y));
                g2.drawLine(Math.min(border.x + border.width, clip.x + clip.width), border.y, Math.max(border.x, clip.x), border.y);
            }
        }
    }

    /**
     * Repaint an area in world coordinates, plus a few pixels extra to
     * compensate for sloppiness in painting the brush.
     * 
     * @param x The x coordinate of the area to repaint, in world coordinates.
     * @param y The y coordinate of the area to repaint, in world coordinates.
     * @param width The width of the area to repaint, in world coordinates.
     * @param height The height of the area to repaint, in world coordinates.
     */
    private void repaintWorld(int x, int y, int width, int height) {
        Rectangle area = worldToView(x, y, width, height);
        repaint(area.x - 2, area.y - 2, area.width + 4, area.height + 4);
    }

    /**
     * Repaint an area in world coordinates, plus a few pixels extra to
     * compensate for sloppiness in painting the brush.
     *
     * @param area The the area to repaint, in world coordinates.
     */
    private void repaintWorld(Rectangle area) {
        area = worldToView(area);
        repaint(area.x - 2, area.y - 2, area.width + 4, area.height + 4);
    }

    private void drawOverlay(Graphics2D g2) {
        if (overlayTransparency == 1.0f) {
            // Fully transparent
            return;
        } else {
            // Translucent or fully opaque
            Composite savedComposite = (overlayTransparency > 0.0f) ? g2.getComposite() : null;
            try {
                if (overlayTransparency > 0.0f) {
                    // Translucent
                    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f - overlayTransparency));
                }
                if (overlayScale == 1.0f) {
                    // 1:1 scale
                    g2.drawImage(overlay, overlayOffsetX, overlayOffsetY, null);
                } else {
                    int width = Math.round(overlay.getWidth() * overlayScale);
                    int height = Math.round(overlay.getHeight() * overlayScale);
                    Object savedInterpolation = g2.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
                    try {
                        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                        g2.drawImage(overlay, overlayOffsetX, overlayOffsetY, width, height, null);
                    } finally {
                        if (savedInterpolation != null) {
                            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, savedInterpolation);
                        }
                    }
                }
            } finally {
                if (overlayTransparency > 0.0f) {
                    // Translucent
                    g2.setComposite(savedComposite);
                }
            }
        }
    }

    @Override
    public Point getMousePosition() throws HeadlessException {
        Point translation = new Point(0, 0);
        Component component = this;
        while (component != null) {
            Point mousePosition = (component == this) ? super.getMousePosition() : component.getMousePosition();
            if (mousePosition != null) {
                mousePosition.translate(-translation.x, -translation.y);
                return mousePosition;
            } else {
                translation.translate(component.getX(), component.getY());
                component = component.getParent();
            }
        }
        return null;
    }
    
    private final HashSet hiddenLayers = new HashSet<>();
    private final CustomBiomeManager customBiomeManager;
    private Dimension dimension;
    private int mouseX, mouseY, radius, effectiveRadius, overlayOffsetX, overlayOffsetY, contourSeparation, brushRotation;
    private boolean drawBrush, drawOverlay, drawContours, drawViewDistance, drawWalkingDistance, drawMinecraftBorder = true,
        drawBorders = true, drawBiomes = true;
    private BrushShape brushShape;
    private float overlayScale = 1.0f;
    private float overlayTransparency = 0.5f;
    private ColourScheme colourScheme;
    private BiomeScheme biomeScheme;
    private BufferedImage overlay;
    private LightOrigin lightOrigin = LightOrigin.NORTHWEST;
    private WPTileProvider tileProvider;
    private Shape customBrushShape;

    private static final int VIEW_DISTANCE_RADIUS = 256;
    private static final int VIEW_DISTANCE_DIAMETER = 2 * VIEW_DISTANCE_RADIUS;
    private static final int FIVE_MINUTE_WALK_DISTANCE_RADIUS = 1280;
    private static final int FIVE_MINUTE_WALK_DISTANCE_DIAMETER = 2 * FIVE_MINUTE_WALK_DISTANCE_RADIUS;
    private static final int DAY_WALK_DISTANCE_RADIUS = 3328;
    private static final int DAY_WALK_DISTANCE_DIAMETER = 2 * DAY_WALK_DISTANCE_RADIUS;
    private static final int DAY_NIGHT_WALK_DISTANCE_RADIUS = 5120;
    private static final int DAY_NIGHT_WALK_DISTANCE_DIAMETER = 2 * DAY_NIGHT_WALK_DISTANCE_RADIUS;
    private static final Font NORMAL_FONT = new Font("SansSerif", Font.PLAIN, 10);
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(WorldPainter.class);
    private static final long serialVersionUID = 1L;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy