Skip to content

Commit f296a23

Browse files
committed
Add knobs for editing angles
This adds a new UI component to edit angles. The new radial knob control allows smoothly transitioning from +180 to -180 degrees. see #288 (github)
1 parent b19affc commit f296a23

File tree

12 files changed

+425
-306
lines changed

12 files changed

+425
-306
lines changed

ChangeLog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
LATEST
22

3+
* Added radial knob control to improve editing angles like sun azimuth.
34
* Fixed incorrect dark oak leaf color blending.
45
* Added new solid color sky mode.
56
* Fixed wrong texture for pink and gray glazed terracotta.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* Copyright (c) 2018 Jesper Öqvist <jesper@llbit.se>
2+
*
3+
* This file is part of Chunky.
4+
*
5+
* Chunky is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* Chunky is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
* You should have received a copy of the GNU General Public License
15+
* along with Chunky. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
package se.llbit.chunky.ui;
18+
19+
import javafx.beans.property.SimpleDoubleProperty;
20+
import javafx.geometry.Point2D;
21+
import javafx.scene.control.Button;
22+
import javafx.scene.control.ContentDisplay;
23+
import javafx.scene.control.Label;
24+
import javafx.scene.control.Tooltip;
25+
import javafx.scene.image.Image;
26+
import javafx.scene.image.ImageView;
27+
import javafx.scene.input.MouseEvent;
28+
import javafx.scene.layout.StackPane;
29+
import se.llbit.math.QuickMath;
30+
31+
/**
32+
* A control for editing angles with a knob and text field.
33+
*/
34+
public class AngleAdjuster extends Adjuster<Double> {
35+
private final Label knob;
36+
private final ImageView dimple;
37+
private final SimpleDoubleProperty angle = new SimpleDoubleProperty();
38+
39+
public AngleAdjuster() {
40+
super(new SimpleDoubleProperty());
41+
ImageView wheel = new ImageView(new Image(getClass().getResourceAsStream("jog_wheel.png")));
42+
knob = new Label();
43+
knob.setGraphic(wheel);
44+
knob.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
45+
knob.setOnMouseDragged(this::handleDrag);
46+
knob.setOnMousePressed(this::handleDrag);
47+
dimple = new ImageView(new Image(getClass().getResourceAsStream("jog_wheel_dimple.png")));
48+
dimple.setTranslateX(16);
49+
dimple.setDisable(true);
50+
StackPane stackPane = new StackPane();
51+
stackPane.getChildren().addAll(knob, dimple);
52+
getChildren().setAll(nameLbl, valueField, stackPane);
53+
angle.bindBidirectional(value);
54+
angle.addListener((observable, oldValue, newValue) -> {
55+
double angle = newValue.doubleValue();
56+
angle = QuickMath.degToRad(angle);
57+
dimple.setTranslateX(Math.cos(angle) * 16);
58+
dimple.setTranslateY(- Math.sin(angle) * 16);
59+
});
60+
}
61+
62+
@Override public void setTooltip(String tooltip) {
63+
super.setTooltip(tooltip);
64+
knob.setTooltip(new Tooltip(tooltip));
65+
}
66+
67+
private void handleDrag(MouseEvent event) {
68+
Point2D pos = knob.localToParent(event.getX(), event.getY());
69+
Point2D center = knob.localToParent(0, 0);
70+
center = center.add(30, 30);
71+
Point2D diff = new Point2D(pos.getX() - center.getX(), - (pos.getY() - center.getY()));
72+
double angle = Math.atan2(diff.getY(), diff.getX());
73+
setAndUpdate(QuickMath.radToDeg(angle));
74+
}
75+
76+
@Override protected Double clamp(Number value) {
77+
return value.doubleValue();
78+
}
79+
}

chunky/src/java/se/llbit/chunky/ui/render/EntitiesTab.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@
3838
import se.llbit.chunky.entity.Poseable;
3939
import se.llbit.chunky.renderer.scene.PlayerModel;
4040
import se.llbit.chunky.renderer.scene.Scene;
41+
import se.llbit.chunky.ui.AngleAdjuster;
4142
import se.llbit.chunky.ui.DoubleAdjuster;
4243
import se.llbit.chunky.ui.RenderControlsFxController;
4344
import se.llbit.chunky.entity.Entity;
4445
import se.llbit.chunky.entity.PlayerEntity;
4546
import se.llbit.json.Json;
4647
import se.llbit.json.JsonArray;
4748
import se.llbit.json.JsonObject;
49+
import se.llbit.math.QuickMath;
4850
import se.llbit.math.Vector3;
4951

5052
import java.io.File;
@@ -217,15 +219,15 @@ private void updateEntity(Entity entity) {
217219
poseBox.setAlignment(Pos.CENTER_LEFT);
218220
poseBox.getChildren().addAll(new Label("Pose part"), partList);
219221

220-
DoubleAdjuster yaw = new DoubleAdjuster();
222+
AngleAdjuster yaw = new AngleAdjuster();
221223
yaw.setTooltip("Modifies yaw of currently selected entity part.");
222224
yaw.setName("yaw");
223225

224-
DoubleAdjuster pitch = new DoubleAdjuster();
226+
AngleAdjuster pitch = new AngleAdjuster();
225227
pitch.setTooltip("Modifies pitch of currently selected entity part.");
226228
pitch.setName("pitch");
227229

228-
DoubleAdjuster roll = new DoubleAdjuster();
230+
AngleAdjuster roll = new AngleAdjuster();
229231
roll.setTooltip("Modifies roll of currently selected entity part.");
230232
roll.setName("roll");
231233

@@ -240,24 +242,21 @@ private void updateEntity(Entity entity) {
240242

241243
partList.getSelectionModel().selectFirst(); // Updates the pose parameters.
242244

243-
pitch.setRange(-Math.PI, Math.PI);
244245
pitch.onValueChange(value -> {
245246
withPose(entity, partList.getValue(), partPose -> {
246-
partPose.set(0, Json.of(value));
247+
partPose.set(0, Json.of(Math.toRadians(value)));
247248
});
248249
scene.rebuildActorBvh();
249250
});
250-
yaw.setRange(-Math.PI, Math.PI);
251251
yaw.onValueChange(value -> {
252252
withPose(entity, partList.getValue(), partPose -> {
253-
partPose.set(1, Json.of(value));
253+
partPose.set(1, Json.of(Math.toRadians(value)));
254254
});
255255
scene.rebuildActorBvh();
256256
});
257-
roll.setRange(-Math.PI, Math.PI);
258257
roll.onValueChange(value -> {
259258
withPose(entity, partList.getValue(), partPose -> {
260-
partPose.set(2, Json.of(value));
259+
partPose.set(2, Json.of(Math.toRadians(value)));
261260
});
262261
scene.rebuildActorBvh();
263262
});

chunky/src/java/se/llbit/chunky/ui/render/LightingTab.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import se.llbit.chunky.renderer.scene.Scene;
2828
import se.llbit.chunky.renderer.scene.Sky;
2929
import se.llbit.chunky.renderer.scene.Sun;
30+
import se.llbit.chunky.ui.AngleAdjuster;
3031
import se.llbit.chunky.ui.DoubleAdjuster;
3132
import se.llbit.chunky.ui.RenderControlsFxController;
3233
import se.llbit.chunky.ui.SimpleColorPicker;
@@ -44,8 +45,8 @@ public class LightingTab extends ScrollPane implements RenderControlsTab, Initia
4445
@FXML private DoubleAdjuster skyIntensity;
4546
@FXML private DoubleAdjuster emitterIntensity;
4647
@FXML private DoubleAdjuster sunIntensity;
47-
@FXML private DoubleAdjuster sunAzimuth;
48-
@FXML private DoubleAdjuster sunAltitude;
48+
@FXML private AngleAdjuster sunAzimuth;
49+
@FXML private AngleAdjuster sunAltitude;
4950
@FXML private CheckBox enableEmitters;
5051
@FXML private CheckBox enableSunlight;
5152
@FXML private SimpleColorPicker sunColor;
@@ -84,16 +85,12 @@ public LightingTab() throws IOException {
8485
sunIntensity.onValueChange(value -> scene.sun().setIntensity(value));
8586

8687
sunAzimuth.setName("Sun azimuth");
87-
sunAzimuth.setTooltip("The angle to the sun from north");
88-
sunAzimuth.setRange(0, 360);
89-
sunAzimuth
90-
.onValueChange(value -> scene.sun().setAzimuth(QuickMath.degToRad(value)));
88+
sunAzimuth.setTooltip("Change the angle to the sun from north.");
89+
sunAzimuth.onValueChange(value -> scene.sun().setAzimuth(-QuickMath.degToRad(value)));
9190

9291
sunAltitude.setName("Sun altitude");
93-
sunAltitude.setTooltip("The angle to the sun above the horizon");
94-
sunAltitude.setRange(0, 90);
95-
sunAltitude
96-
.onValueChange(value -> scene.sun().setAltitude(QuickMath.degToRad(value)));
92+
sunAltitude.setTooltip("Change the angle to the sun above the horizon.");
93+
sunAltitude.onValueChange(value -> scene.sun().setAltitude(QuickMath.degToRad(value)));
9794

9895
enableEmitters.selectedProperty().addListener(
9996
(observable, oldValue, newValue) -> scene.setEmittersEnabled(newValue));
@@ -111,7 +108,7 @@ public LightingTab() throws IOException {
111108
skyIntensity.set(scene.sky().getSkyLight());
112109
emitterIntensity.set(scene.getEmitterIntensity());
113110
sunIntensity.set(scene.sun().getIntensity());
114-
sunAzimuth.set(QuickMath.radToDeg(scene.sun().getAzimuth()));
111+
sunAzimuth.set(-QuickMath.radToDeg(scene.sun().getAzimuth()));
115112
sunAltitude.set(QuickMath.radToDeg(scene.sun().getAltitude()));
116113
enableEmitters.setSelected(scene.getEmittersEnabled());
117114
enableSunlight.setSelected(scene.getDirectLight());

0 commit comments

Comments
 (0)