Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/main.min.js

Large diffs are not rendered by default.

74 changes: 64 additions & 10 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,11 +595,12 @@ class Animator {
folder.add(this, "pause");
folder.add(this, "reset");

// Note, for some reason when you call `.max()` on a slider controller it does
// correctly change how the slider behaves but does not change the range of values
// that can be entered into the text box attached to the slider. Oh well. We work
// around this by creating the slider with an unreasonably huge range and then calling
// `.min()` and `.max()` on it later.
// Note, for some reason when you call `.max()` on a slider controller
// it does correctly change how the slider behaves but does not change
// the range of values that can be entered into the text box attached
// to the slider. Oh well. We work around this by creating the slider
// with an unreasonably huge range and then calling `.min()` and
// `.max()` on it later.
this.time_scrubber = folder.add(this, "time", 0, 1e9, 0.001);
this.time_scrubber.onChange((value) => this.seek(value));

Expand Down Expand Up @@ -729,6 +730,7 @@ class Viewer {

this.scene = create_default_scene();
this.gui_controllers = {};
this.keydown_callbacks = {};
this.create_scene_tree();

this.add_default_scene_elements();
Expand All @@ -740,13 +742,22 @@ class Viewer {
// TODO: probably shouldn't be directly accessing window?
window.onload = (evt) => this.set_3d_pane_size();
window.addEventListener('resize', (evt) => this.set_3d_pane_size(), false);
window.addEventListener('keydown', (evt) => {this.on_keydown(evt);});

requestAnimationFrame(() => this.set_3d_pane_size());
if (animate || animate === undefined) {
this.animate();
}
}

on_keydown(e) {
if (e.code in this.keydown_callbacks) {
for (const o of this.keydown_callbacks[e.code]) {
o["callback"](e);
}
}
}

hide_background() {
this.scene.background = null;
this.set_dirty();
Expand Down Expand Up @@ -994,21 +1005,55 @@ class Viewer {
this.animator.load(animations, options);
}

set_control(name, callback, value, min, max, step) {
// keycode1 and keycode2 are the KeyboardEvent.code values, e.g. "KeyB",
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values
// Buttons have at most one keycode assigned to them (which causes the
// button to callback to fire). Sliders have two keycodes assigned to
// them; one to decrease the value by step, the other to increase it.
set_control(name, callback, value, min, max, step,
keycode1, keycode2) {
let my_callback = eval(callback);
let handler = {};
if (name in this.gui_controllers) {
this.gui.remove(this.gui_controllers[name]);
}
if (value !== undefined) {
handler[name] = value;
handler[name] = value;
this.gui_controllers[name] = this.gui.add(
handler, name, min, max, step);
this.gui_controllers[name].onChange(eval(callback));
this.gui_controllers[name].onChange(my_callback);
function add_callback(viewer, keycode, increment) {
if (keycode != undefined) {
let keydown_callback = {name: name, callback: () => {
// Decrease value by step (within limits), and trigger
// callback.
value = viewer.gui_controllers[name].getValue();
let new_value =
Math.min(Math.max(value + increment, min), max);
viewer.gui_controllers[name].setValue(new_value);
}};
if (keycode in viewer.keydown_callbacks) {
viewer.keydown_callbacks[keycode].push(keydown_callback);
} else {
viewer.keydown_callbacks[keycode] = [keydown_callback];
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two blocks here are identical except for the sign of the step. Make this a function instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

add_callback(this, keycode1, -step);
add_callback(this, keycode2, +step);
} else {
handler[name] = eval(callback);
handler[name] = my_callback;
this.gui_controllers[name] = this.gui.add(handler, name);
// The default layout for dat.GUI buttons is broken, with the button name artificially truncated at the same width that slider names are truncated. We fix that here.
this.gui_controllers[name].domElement.parentElement.querySelector('.property-name').style.width="100%";
if (keycode1 != undefined) {
let keydown_callback = {name: name, callback: my_callback};
if (keycode1 in this.keydown_callbacks) {
this.keydown_callbacks[keycode1].push(keydown_callback);
} else {
this.keydown_callbacks[keycode1] = [keydown_callback];
}
}
}
}

Expand All @@ -1029,6 +1074,15 @@ class Viewer {
this.gui.remove(this.gui_controllers[name]);
delete this.gui_controllers[name];
}
// Remove any callbacks associated with this name.
for (let code in this.keydown_callbacks) {
let i=this.keydown_callbacks[code].length;
while (i--) {
if (this.keydown_callbacks[code][i]["name"] == name) {
this.keydown_callbacks[code].splice(i, 1);
}
}
}
}

handle_command(cmd) {
Expand All @@ -1052,7 +1106,7 @@ class Viewer {
} else if (cmd.type == "set_target") {
this.set_camera_target(cmd.value);
} else if (cmd.type == "set_control") {
this.set_control(cmd.name, cmd.callback, cmd.value, cmd.min, cmd.max, cmd.step);
this.set_control(cmd.name, cmd.callback, cmd.value, cmd.min, cmd.max, cmd.step, cmd.keycode1, cmd.keycode2);
} else if (cmd.type == "set_control_value") {
this.set_control_value(cmd.name, cmd.value, cmd.invoke_callback);
} else if (cmd.type == "delete_control") {
Expand Down
31 changes: 25 additions & 6 deletions test/controls.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,35 @@
viewer.set_control("TestButton", () => {});
// Prove that we can reset a control with the same name.
viewer.set_control("TestButton", () => {
console.log("TestButton pressed");
});
// A second control.
viewer.set_control("TestButton2", () => {
console.log("TestButton pressed"); },
value=undefined, min=undefined, max=undefined, step=undefined,
keycode1="KeyB");
// A second control. This can also be "pressed" by pressing the
// "b" key.
viewer.set_control("TestButton2",
() => {
console.log("TestButton2 pressed");
});
},
value=undefined, min=undefined, max=undefined, step=undefined,
keycode1="KeyB");
// Delete the first. Only the second should remain in the gui.
viewer.delete_control("TestButton");

// A third control. Both buttons 2 and 3 should fire when the "b"
// key is pressed.
viewer.set_control("TestButton3",
() => {
console.log("TestButton3 pressed");
},
value=undefined, min=undefined, max=undefined, step=undefined,
keycode1="KeyB");

// The slider can value can also be descreased and increased using
// the left and right arrow keys, repsectively.
viewer.set_control("TestSlider", (value) => {
console.log("TestSlider = " + value);
}, .5, 0, 1, 0.01);
}, value=.5, min=0, max=1, step=0.01,
keycode1="ArrowLeft", keycode2="ArrowRight");
viewer.set_control_value("TestSlider", .2);

// Make sure nothing goes wrong here.
Expand All @@ -39,6 +56,8 @@
viewer.set_control("Test button with a really long name", () => {
console.log("Test button with a really long name pressed");
});

// viewer.keydown_callbacks[38] = ((e) => { console.log("Key pressed" + e.keyCode); });
</script>


Expand Down