Captain-Chaos / WorldPainter

WorldPainter is an interactive graphical map generator for the indie game Minecraft.
http://www.worldpainter.net/
GNU General Public License v3.0
341 stars 58 forks source link

[Bug] Discrete layer SelectionChunk not supported (Eyedropper) #314

Closed Stonley890 closed 11 months ago

Stonley890 commented 1 year ago

Exception when attempting to use the Eyedropper on a selection.

java.lang.UnsupportedOperationException: Discrete layer SelectionChunk not supported
    at org.pepsoft.worldpainter.tools.Eyedropper.lambda$tick$0(Eyedropper.java:86)
    at java.util.HashMap.forEach(HashMap.java:1421)
    at org.pepsoft.worldpainter.tools.Eyedropper.tick(Eyedropper.java:72)
    at org.pepsoft.worldpainter.operations.MouseOrTabletOperation.lambda$penButtonEvent$2(MouseOrTabletOperation.java:276)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:771)
    at java.awt.EventQueue$4.run(EventQueue.java:722)
    at java.awt.EventQueue$4.run(EventQueue.java:716)
    at java.security.AccessController.doPrivileged(AccessController.java:399)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)

WorldPainter version: 2.17.1 (20230513143848)

java.version: 17.0.7
java.vendor: Private Build
java.vm.version: 17.0.7+7-Ubuntu-0ubuntu122.04.2
java.vm.vendor: Private Build
java.vm.name: OpenJDK 64-Bit Server VM
os.name: Linux
os.arch: amd64
os.version: 5.19.0-41-generic
user.home: /home/stonley890
user.dir: /opt/worldpainter
user.country: US
user.language: en

Free memory: 2047132648 bytes
Total memory size: 3468689408 bytes
Max memory size: 8359247872 bytes

World name: Pyrrhia
Platform: Minecraft 1.19 or later (org.pepsoft.anvil.1.19)
Seed: -2162662994473143498
Bounds: 0, 0 => 76, 65
Height: 320
Number of tiles: 5082
Layers in use: Rocks, Chasms, Mushrooms, Giant Rocks, Deciduous, Swamp, Stone Shore, Palm Trees, Ice Spikes, Bamboo Jungle, Annotations, Frozen Peaks, Mangrove Swamp, Biome, Old Growth Taiga, SelectionChunk, Dark Forest, Caverns, Acacia Trees, Lava, Birch Forest, Frost, Ice Spikes Biome, Mountain, Plains Flowers, Caves, SelectionBlock, Pine, Jungle, Icebergs, Coral Reef, Sunflower Plains
Border: ENDLESS_VOID @ 62
Sub surface material: Stone Mix
Water height: 62

Operation: Smooth
Radius: 28
Brush: Spike Circle (radius=17, brushShape=CIRCLE, level=1.0)/Sine Circle (radius=28, brushShape=CIRCLE, level=0.51)
Level: 1.0/0.51
Zoom: 1
Hidden layers: [Chasms, Biome, Caves, Frost, Caverns
Stonley890 commented 1 year ago

I wrote up a fix to WorldPainter/WPGUI/src/main/java/org/pepsoft/worldpainter/tools/Eyedropper.java based on what I could tell. In all honesty, I have no idea if it would work as expected but I'm learning Java and figured it would be a good exercise.

package org.pepsoft.worldpainter.tools;

import org.pepsoft.minecraft.Material;
import org.pepsoft.util.DesktopUtils;
import org.pepsoft.util.IconUtils;
import org.pepsoft.worldpainter.ColourScheme;
import org.pepsoft.worldpainter.Dimension;
import org.pepsoft.worldpainter.Terrain;
import org.pepsoft.worldpainter.WorldPainterView;
import org.pepsoft.worldpainter.biomeschemes.BiomeHelper;
import org.pepsoft.worldpainter.biomeschemes.CustomBiomeManager;
import org.pepsoft.worldpainter.layers.Annotations;
import org.pepsoft.worldpainter.layers.Biome;
import org.pepsoft.worldpainter.layers.Layer;
import org.pepsoft.worldpainter.operations.MouseOrTabletOperation;
import org.pepsoft.worldpainter.selection.SelectionBlock;
import org.pepsoft.worldpainter.selection.SelectionChunk;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Map;

import static org.pepsoft.minecraft.Constants.COLOUR_NAMES;
import static org.pepsoft.minecraft.Material.WOOLS;
import static org.pepsoft.util.IconUtils.createScaledColourIcon;
import static org.pepsoft.util.IconUtils.scaleIcon;

/**
 * A one-click operation that shows a list of paints where the user has clicked and allows the user to select one. Once
 * the selection is made, one of the methods on the configured {@link SelectionListener callback} is invoked to inform
 * the client of the selected paint.
 *
 * <p>A {@link #setCallback(SelectionListener) callback} <em>must</em> be configured or this operation will throw an
 * exception.
 */
public final class Eyedropper extends MouseOrTabletOperation {
    public Eyedropper(WorldPainterView view, ColourScheme colourScheme, CustomBiomeManager customBiomeManager) {
        super("Eyedropper", "Select a paint from the map", view, "operation.eyedropper");
        this.colourScheme = colourScheme;
        this.customBiomeManager = customBiomeManager;
    }

    public SelectionListener getCallback() {
        return callback;
    }

    public void setCallback(SelectionListener callback) {
        this.callback = callback;
    }

    @Override
    protected void tick(int x, int y, boolean inverse, boolean first, float dynamicLevel) {
        if (! first) {
            throw new InternalError("Should never happen");
        }
        final Dimension dimension = getDimension();
        final Terrain terrain = dimension.getTerrainAt(x, y);
        final Map<Layer, Integer> layers = dimension.getLayersAt(x, y);
        if ((terrain == null) && (layers == null)) {
            DesktopUtils.beep();
        } else {
            // Conscious choice to always show the popup menu, even if there is only one choice, to always give feedback
            // to the user as to exactly what value was selected
            popupMenu = new JPopupMenu();
            if (terrain != null) {
                popupMenu.add(new AbstractAction(terrain.getName(), new ImageIcon(terrain.getScaledIcon(16, colourScheme))) {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        callback.terrainSelected(terrain);
                    }
                });
            }
            if (layers != null) {
                final BiomeHelper biomeHelper = new BiomeHelper(colourScheme, customBiomeManager, dimension.getWorld().getPlatform());
                layers.forEach((layer, value) -> {
                    final String name;
                    final Icon icon;
                    if (layer instanceof Biome) {
                        name = biomeHelper.getBiomeName(value);
                        icon = biomeHelper.getBiomeIcon(value);
                    } else if (layer instanceof Annotations) {
                        final int colourIndex = value - ((value < 8) ? 1 : 0);
                        name = COLOUR_NAMES[colourIndex] + " Annotations";
                        icon = createScaledColourIcon(colourScheme.getColour(WOOLS[colourIndex]));
                    } else if (! layer.discrete) {
                        name = layer.getName();
                        icon = new ImageIcon(scaleIcon(layer.getIcon(), 16));
                    } else if ((layer instanceof SelectionBlock) || (layer instanceof SelectionChunk)) {
                        name = "Selection";
                        icon = IconUtils.loadScaledIcon("org/pepsoft/worldpainter/icons/edit_selection.png");
                    } else {
                        throw new UnsupportedOperationException("Discrete layer " + layer + " not supported");
                    }
                    popupMenu.add(new AbstractAction(name, icon) {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            callback.layerSelected(layer, value);
                        }
                    });
                });
            }
            final WorldPainterView view = getView();
            final Point menuCoords = view.worldToView(x, y);
            popupMenu.show(view, menuCoords.x, menuCoords.y);
        }
    }

    @Override
    protected void deactivate() {
        if ((popupMenu != null) && popupMenu.isShowing()) {
            popupMenu.setVisible(false);
            popupMenu = null;
        }
        super.deactivate();
    }

    private final ColourScheme colourScheme;
    private final CustomBiomeManager customBiomeManager;
    private SelectionListener callback;
    private JPopupMenu popupMenu;

    /**
     * A selection callback that will be notified of the selected value, or if the operation was cancelled.
     */
    public interface SelectionListener {
        /**
         * The user has selected the specified terrain type.
         */
        void terrainSelected(Terrain terrain);

        /**
         * The user has selected the specified layer, which had the specified value at the selected location.
         */
        void layerSelected(Layer layer, int value);

        /**
         * The selection operation has been cancelled.
         *
         * @param byUser Whether the selection was cancelled by an affirmative action by the user (such as pressing Esc)
         *               or as a secondary effect (such as selecting a different tool, loading a different dimension,
         *               etc.)
         */
        void selectionCancelled(boolean byUser);
    }
}
Captain-Chaos commented 11 months ago

This was fixed in version 2.18.0.