mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-22 08:07:42 +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;
|
||||
shownKey = key;
|
||||
if (!reloadingShownObject) {
|
||||
variantPicker.forgetTriedSeeds();
|
||||
showVariantList(List.of(), OptionalLong.empty(), OptionalLong.empty(), 0);
|
||||
}
|
||||
long revision = startLoading(key);
|
||||
@@ -364,8 +365,7 @@ final class ObjectExplorer {
|
||||
}
|
||||
|
||||
private String galleryHeading() {
|
||||
String seed = shownSeed.isPresent() ? " · seed " + shownSeed.getAsLong() : "";
|
||||
return String.format(Locale.ROOT, "%s · %d variants%s", shownKey, shownVariants.size(), seed);
|
||||
return String.format(Locale.ROOT, "%s · %d variants%s", shownKey, shownVariants.size(), seedNote());
|
||||
}
|
||||
|
||||
private static ObjectMesh.Bounds largestSize(List<ObjectMesh> meshes) {
|
||||
@@ -426,9 +426,17 @@ final class ObjectExplorer {
|
||||
String cut = layerCut.isCutting()
|
||||
? 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",
|
||||
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) {
|
||||
|
||||
@@ -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 int BOTTOM_MARGIN = 14;
|
||||
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 String VARIANT_SHORTCUT_HINT = " · [ and ]";
|
||||
private static final String COMPARE_SHORTCUT_HINT = " · G";
|
||||
|
||||
private final JLabel shownVariant = label(VARIANT_LABEL_WIDTH);
|
||||
private final JLabel shownSeed = label(SEED_LABEL_WIDTH);
|
||||
private final JComponent variantStepper;
|
||||
private final JComponent seedStepper;
|
||||
private final JButton packSeedButton;
|
||||
private final ObjectSeedChooser seedChooser;
|
||||
private final JButton compareButton;
|
||||
private final IntConsumer onVariantChosen;
|
||||
private final LongConsumer onSeedChosen;
|
||||
private int variantCount;
|
||||
private int shownIndex;
|
||||
private OptionalLong seed = OptionalLong.empty();
|
||||
private OptionalLong packSeed = OptionalLong.empty();
|
||||
private boolean seeded;
|
||||
private int coveredLeftWidth;
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
ObjectVariantPicker(IntConsumer onVariantChosen, LongConsumer onSeedChosen, Runnable onCompareToggled) {
|
||||
this.onVariantChosen = onVariantChosen;
|
||||
this.onSeedChosen = onSeedChosen;
|
||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||
setOpaque(false);
|
||||
setBorder(StudioTheme.emptyBorder(3, 4, 3, 4));
|
||||
variantStepper = stepper(shownVariant,
|
||||
StudioToolbar.button("‹", "Previous variant" + VARIANT_SHORTCUT_HINT, () -> stepBy(-1)),
|
||||
StudioToolbar.button("›", "Next variant" + VARIANT_SHORTCUT_HINT, () -> stepBy(1)));
|
||||
seedStepper = stepper(shownSeed,
|
||||
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));
|
||||
seedChooser = new ObjectSeedChooser(onSeedChosen);
|
||||
compareButton = StudioToolbar.button("", "", onCompareToggled);
|
||||
add(variantStepper);
|
||||
add(Box.createHorizontalStrut(StudioTheme.scale(SECTION_GAP)));
|
||||
add(seedStepper);
|
||||
add(packSeedButton);
|
||||
add(seedChooser);
|
||||
add(Box.createHorizontalStrut(StudioTheme.scale(SECTION_GAP)));
|
||||
add(compareButton);
|
||||
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) {
|
||||
seed = shown;
|
||||
packSeed = pack;
|
||||
shownSeed.setText(shown.isPresent() ? "Seed " + shown.getAsLong() : " ");
|
||||
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));
|
||||
seeded = shown.isPresent();
|
||||
seedChooser.setVisible(seeded);
|
||||
shown.ifPresent(seed -> seedChooser.showSeed(seed, pack));
|
||||
showWhenUseful();
|
||||
}
|
||||
|
||||
void forgetTriedSeeds() {
|
||||
seedChooser.forgetTriedSeeds();
|
||||
}
|
||||
|
||||
void showComparing(boolean comparing) {
|
||||
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);
|
||||
@@ -181,12 +171,8 @@ final class ObjectVariantPicker extends JPanel {
|
||||
onVariantChosen.accept(shownIndex);
|
||||
}
|
||||
|
||||
private void stepSeedBy(int step) {
|
||||
seed.ifPresent(current -> onSeedChosen.accept(current + step));
|
||||
}
|
||||
|
||||
private void showWhenUseful() {
|
||||
setVisible(variantCount > 1 || seed.isPresent());
|
||||
setVisible(variantCount > 1 || seeded);
|
||||
relayout();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user