mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-27 06:20:02 +00:00
Choose procedural seeds by typing or reroll
Seed box lists tried seeds; bar keeps its size.
This commit is contained in:
1 parent
f30f761ae6
commit
93f5615f00
3 files changed
+186
-32
No files matched your search
@@ -214,6 +214,7 @@ final class ObjectExplorer {
|
|||||||
int variantIndex = reloadingShownObject ? shownVariantIndex : 0;
|
int variantIndex = reloadingShownObject ? shownVariantIndex : 0;
|
||||||
shownKey = key;
|
shownKey = key;
|
||||||
if (!reloadingShownObject) {
|
if (!reloadingShownObject) {
|
||||||
|
variantPicker.forgetTriedSeeds();
|
||||||
showVariantList(List.of(), OptionalLong.empty(), OptionalLong.empty(), 0);
|
showVariantList(List.of(), OptionalLong.empty(), OptionalLong.empty(), 0);
|
||||||
}
|
}
|
||||||
long revision = startLoading(key);
|
long revision = startLoading(key);
|
||||||
@@ -364,8 +365,7 @@ final class ObjectExplorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String galleryHeading() {
|
private String galleryHeading() {
|
||||||
String seed = shownSeed.isPresent() ? " · seed " + shownSeed.getAsLong() : "";
|
return String.format(Locale.ROOT, "%s · %d variants%s", shownKey, shownVariants.size(), seedNote());
|
||||||
return String.format(Locale.ROOT, "%s · %d variants%s", shownKey, shownVariants.size(), seed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectMesh.Bounds largestSize(List<ObjectMesh> meshes) {
|
private static ObjectMesh.Bounds largestSize(List<ObjectMesh> meshes) {
|
||||||
@@ -426,9 +426,17 @@ final class ObjectExplorer {
|
|||||||
String cut = layerCut.isCutting()
|
String cut = layerCut.isCutting()
|
||||||
? String.format(Locale.ROOT, " · showing layers 1-%d of %d", layerCut.shownLayerCount(), layerCut.layerCount())
|
? String.format(Locale.ROOT, " · showing layers 1-%d of %d", layerCut.shownLayerCount(), layerCut.layerCount())
|
||||||
: "";
|
: "";
|
||||||
String seed = shownSeed.isPresent() ? " · seed " + shownSeed.getAsLong() : "";
|
|
||||||
statusBar.showSummary(String.format(Locale.ROOT, "%s%s · %,d blocks · %,d faces drawn%s",
|
statusBar.showSummary(String.format(Locale.ROOT, "%s%s · %,d blocks · %,d faces drawn%s",
|
||||||
summary.key(), seed, summary.blockCount(), mesh.faceCount(), cut));
|
summary.key(), seedNote(), summary.blockCount(), mesh.faceCount(), cut));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A seed being tried is not in the pack until its "seed" field says so.
|
||||||
|
private String seedNote() {
|
||||||
|
if (shownSeed.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String note = " · seed " + shownSeed.getAsLong();
|
||||||
|
return shownSeed.equals(packSeed) ? note : note + " (the pack has " + packSeed.getAsLong() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSeeThrough(Set<String> blockTypes) {
|
private void showSeeThrough(Set<String> blockTypes) {
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*
|
||||||
|
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.volmit.iris.core.gui;
|
||||||
|
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
import javax.swing.DefaultListCellRenderer;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.OptionalLong;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the seed of a procedural object's variants is chosen: typed in, picked again from the seeds tried on this
|
||||||
|
* object with the pack's own first, or rerolled at random. It keeps its size whatever it shows, so the bar it sits in
|
||||||
|
* never shifts under the mouse.
|
||||||
|
*/
|
||||||
|
final class ObjectSeedChooser extends JPanel {
|
||||||
|
private static final long serialVersionUID = 3127745038254406118L;
|
||||||
|
private static final int FIELD_WIDTH = 120;
|
||||||
|
private static final int TRIED_SEEDS_KEPT = 12;
|
||||||
|
// Small enough to read back and type, and far more seeds than anyone tries.
|
||||||
|
private static final int RANDOM_SEED_BOUND = 1_000_000;
|
||||||
|
|
||||||
|
private final JComboBox<Long> seeds = new JComboBox<>();
|
||||||
|
private final LongConsumer onSeedChosen;
|
||||||
|
private final List<Long> triedSeedsNewestFirst = new ArrayList<>();
|
||||||
|
private OptionalLong packSeed = OptionalLong.empty();
|
||||||
|
private long shownSeed;
|
||||||
|
private boolean showingSeeds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param onSeedChosen told the seed to bake the variants from whenever the user chooses another one
|
||||||
|
*/
|
||||||
|
ObjectSeedChooser(LongConsumer onSeedChosen) {
|
||||||
|
super(new BorderLayout(StudioTheme.scale(4), 0));
|
||||||
|
this.onSeedChosen = onSeedChosen;
|
||||||
|
setOpaque(false);
|
||||||
|
JLabel label = new JLabel("Seed");
|
||||||
|
label.setFont(StudioTheme.smallFont());
|
||||||
|
label.setForeground(StudioTheme.SECONDARY_TEXT);
|
||||||
|
seeds.setEditable(true);
|
||||||
|
seeds.setFont(StudioTheme.smallFont());
|
||||||
|
seeds.setToolTipText("Type a seed and press Enter, or pick one tried before");
|
||||||
|
seeds.setRenderer(new PackSeedMarker());
|
||||||
|
seeds.setPreferredSize(new Dimension(StudioTheme.scale(FIELD_WIDTH), seeds.getPreferredSize().height));
|
||||||
|
seeds.addActionListener(event -> {
|
||||||
|
if (!showingSeeds) {
|
||||||
|
EventQueue.invokeLater(this::chooseEnteredSeed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(label, BorderLayout.WEST);
|
||||||
|
add(seeds, BorderLayout.CENTER);
|
||||||
|
add(StudioToolbar.button("Reroll", "Bake the variants from a random seed", this::reroll), BorderLayout.EAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Starts a new object's list of tried seeds. */
|
||||||
|
void forgetTriedSeeds() {
|
||||||
|
triedSeedsNewestFirst.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSeed(long shown, OptionalLong pack) {
|
||||||
|
shownSeed = shown;
|
||||||
|
packSeed = pack;
|
||||||
|
if (!isPackSeed(shown)) {
|
||||||
|
rememberTried(shown);
|
||||||
|
}
|
||||||
|
DefaultComboBoxModel<Long> choices = new DefaultComboBoxModel<>();
|
||||||
|
pack.ifPresent(choices::addElement);
|
||||||
|
triedSeedsNewestFirst.forEach(choices::addElement);
|
||||||
|
showingSeeds = true;
|
||||||
|
try {
|
||||||
|
seeds.setModel(choices);
|
||||||
|
seeds.setSelectedItem(shown);
|
||||||
|
} finally {
|
||||||
|
showingSeeds = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rememberTried(long seed) {
|
||||||
|
triedSeedsNewestFirst.remove(seed);
|
||||||
|
triedSeedsNewestFirst.add(0, seed);
|
||||||
|
while (triedSeedsNewestFirst.size() > TRIED_SEEDS_KEPT) {
|
||||||
|
triedSeedsNewestFirst.remove(triedSeedsNewestFirst.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A pick from the list arrives as a seed, anything typed as text.
|
||||||
|
private void chooseEnteredSeed() {
|
||||||
|
OptionalLong entered = parse(seeds.getSelectedItem());
|
||||||
|
if (entered.isEmpty()) {
|
||||||
|
UIManager.getLookAndFeel().provideErrorFeedback(seeds);
|
||||||
|
showSeed(shownSeed, packSeed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (entered.getAsLong() != shownSeed) {
|
||||||
|
onSeedChosen.accept(entered.getAsLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OptionalLong parse(Object entered) {
|
||||||
|
if (entered instanceof Long seed) {
|
||||||
|
return OptionalLong.of(seed);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OptionalLong.of(Long.parseLong(String.valueOf(entered).trim()));
|
||||||
|
} catch (NumberFormatException notASeed) {
|
||||||
|
return OptionalLong.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reroll() {
|
||||||
|
long seed;
|
||||||
|
do {
|
||||||
|
seed = ThreadLocalRandom.current().nextInt(RANDOM_SEED_BOUND);
|
||||||
|
} while (seed == shownSeed);
|
||||||
|
onSeedChosen.accept(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPackSeed(Object seed) {
|
||||||
|
return packSeed.isPresent() && seed instanceof Long value && value == packSeed.getAsLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class PackSeedMarker extends DefaultListCellRenderer {
|
||||||
|
private static final long serialVersionUID = -6061513307766393251L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean selected, boolean focused) {
|
||||||
|
super.getListCellRendererComponent(list, value, index, selected, focused);
|
||||||
|
if (index >= 0 && isPackSeed(value)) {
|
||||||
|
setText(value + " (pack)");
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,48 +43,38 @@ final class ObjectVariantPicker extends JPanel {
|
|||||||
private static final long serialVersionUID = -2716301839488217455L;
|
private static final long serialVersionUID = -2716301839488217455L;
|
||||||
private static final int BOTTOM_MARGIN = 14;
|
private static final int BOTTOM_MARGIN = 14;
|
||||||
private static final int VARIANT_LABEL_WIDTH = 110;
|
private static final int VARIANT_LABEL_WIDTH = 110;
|
||||||
private static final int SEED_LABEL_WIDTH = 100;
|
|
||||||
private static final int SECTION_GAP = 10;
|
private static final int SECTION_GAP = 10;
|
||||||
private static final String VARIANT_SHORTCUT_HINT = " · [ and ]";
|
private static final String VARIANT_SHORTCUT_HINT = " · [ and ]";
|
||||||
private static final String COMPARE_SHORTCUT_HINT = " · G";
|
private static final String COMPARE_SHORTCUT_HINT = " · G";
|
||||||
|
|
||||||
private final JLabel shownVariant = label(VARIANT_LABEL_WIDTH);
|
private final JLabel shownVariant = label(VARIANT_LABEL_WIDTH);
|
||||||
private final JLabel shownSeed = label(SEED_LABEL_WIDTH);
|
|
||||||
private final JComponent variantStepper;
|
private final JComponent variantStepper;
|
||||||
private final JComponent seedStepper;
|
private final ObjectSeedChooser seedChooser;
|
||||||
private final JButton packSeedButton;
|
|
||||||
private final JButton compareButton;
|
private final JButton compareButton;
|
||||||
private final IntConsumer onVariantChosen;
|
private final IntConsumer onVariantChosen;
|
||||||
private final LongConsumer onSeedChosen;
|
|
||||||
private int variantCount;
|
private int variantCount;
|
||||||
private int shownIndex;
|
private int shownIndex;
|
||||||
private OptionalLong seed = OptionalLong.empty();
|
private boolean seeded;
|
||||||
private OptionalLong packSeed = OptionalLong.empty();
|
|
||||||
private int coveredLeftWidth;
|
private int coveredLeftWidth;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param onVariantChosen told the index of the variant to show whenever the user steps to another one
|
* @param onVariantChosen told the index of the variant to show whenever the user steps to another one
|
||||||
* @param onSeedChosen told the seed to bake the variants from whenever the user steps to another one
|
* @param onSeedChosen told the seed to bake the variants from whenever the user chooses another one
|
||||||
* @param onCompareToggled told when the user switches between one variant and all of them
|
* @param onCompareToggled told when the user switches between one variant and all of them
|
||||||
*/
|
*/
|
||||||
ObjectVariantPicker(IntConsumer onVariantChosen, LongConsumer onSeedChosen, Runnable onCompareToggled) {
|
ObjectVariantPicker(IntConsumer onVariantChosen, LongConsumer onSeedChosen, Runnable onCompareToggled) {
|
||||||
this.onVariantChosen = onVariantChosen;
|
this.onVariantChosen = onVariantChosen;
|
||||||
this.onSeedChosen = onSeedChosen;
|
|
||||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
setBorder(StudioTheme.emptyBorder(3, 4, 3, 4));
|
setBorder(StudioTheme.emptyBorder(3, 4, 3, 4));
|
||||||
variantStepper = stepper(shownVariant,
|
variantStepper = stepper(shownVariant,
|
||||||
StudioToolbar.button("‹", "Previous variant" + VARIANT_SHORTCUT_HINT, () -> stepBy(-1)),
|
StudioToolbar.button("‹", "Previous variant" + VARIANT_SHORTCUT_HINT, () -> stepBy(-1)),
|
||||||
StudioToolbar.button("›", "Next variant" + VARIANT_SHORTCUT_HINT, () -> stepBy(1)));
|
StudioToolbar.button("›", "Next variant" + VARIANT_SHORTCUT_HINT, () -> stepBy(1)));
|
||||||
seedStepper = stepper(shownSeed,
|
seedChooser = new ObjectSeedChooser(onSeedChosen);
|
||||||
StudioToolbar.button("‹", "Bake the variants from the previous seed", () -> stepSeedBy(-1)),
|
|
||||||
StudioToolbar.button("›", "Bake the variants from the next seed", () -> stepSeedBy(1)));
|
|
||||||
packSeedButton = StudioToolbar.button("Pack seed", "", () -> packSeed.ifPresent(onSeedChosen::accept));
|
|
||||||
compareButton = StudioToolbar.button("", "", onCompareToggled);
|
compareButton = StudioToolbar.button("", "", onCompareToggled);
|
||||||
add(variantStepper);
|
add(variantStepper);
|
||||||
add(Box.createHorizontalStrut(StudioTheme.scale(SECTION_GAP)));
|
add(Box.createHorizontalStrut(StudioTheme.scale(SECTION_GAP)));
|
||||||
add(seedStepper);
|
add(seedChooser);
|
||||||
add(packSeedButton);
|
|
||||||
add(Box.createHorizontalStrut(StudioTheme.scale(SECTION_GAP)));
|
add(Box.createHorizontalStrut(StudioTheme.scale(SECTION_GAP)));
|
||||||
add(compareButton);
|
add(compareButton);
|
||||||
showComparing(false);
|
showComparing(false);
|
||||||
@@ -153,19 +143,19 @@ final class ObjectVariantPicker extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the seed the variants are baked from, with a way back to the pack's own seed once another is chosen.
|
* Shows the seed the variants are baked from; empty for objects not generated from a seed.
|
||||||
*/
|
*/
|
||||||
void showSeed(OptionalLong shown, OptionalLong pack) {
|
void showSeed(OptionalLong shown, OptionalLong pack) {
|
||||||
seed = shown;
|
seeded = shown.isPresent();
|
||||||
packSeed = pack;
|
seedChooser.setVisible(seeded);
|
||||||
shownSeed.setText(shown.isPresent() ? "Seed " + shown.getAsLong() : " ");
|
shown.ifPresent(seed -> seedChooser.showSeed(seed, pack));
|
||||||
seedStepper.setVisible(shown.isPresent());
|
|
||||||
boolean explored = shown.isPresent() && !shown.equals(pack);
|
|
||||||
packSeedButton.setVisible(explored);
|
|
||||||
pack.ifPresent(value -> packSeedButton.setToolTipText("Back to the seed the pack sets, " + value));
|
|
||||||
showWhenUseful();
|
showWhenUseful();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void forgetTriedSeeds() {
|
||||||
|
seedChooser.forgetTriedSeeds();
|
||||||
|
}
|
||||||
|
|
||||||
void showComparing(boolean comparing) {
|
void showComparing(boolean comparing) {
|
||||||
compareButton.setText(comparing ? "3D view" : "All variants");
|
compareButton.setText(comparing ? "3D view" : "All variants");
|
||||||
compareButton.setToolTipText((comparing ? "Show the chosen variant in 3D" : "Show every variant side by side") + COMPARE_SHORTCUT_HINT);
|
compareButton.setToolTipText((comparing ? "Show the chosen variant in 3D" : "Show every variant side by side") + COMPARE_SHORTCUT_HINT);
|
||||||
@@ -181,12 +171,8 @@ final class ObjectVariantPicker extends JPanel {
|
|||||||
onVariantChosen.accept(shownIndex);
|
onVariantChosen.accept(shownIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stepSeedBy(int step) {
|
|
||||||
seed.ifPresent(current -> onSeedChosen.accept(current + step));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showWhenUseful() {
|
private void showWhenUseful() {
|
||||||
setVisible(variantCount > 1 || seed.isPresent());
|
setVisible(variantCount > 1 || seeded);
|
||||||
relayout();
|
relayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user