2. A slider. In HTML made with
3. A number display. In this case probably a separate
4. A container for all 3 of those pieces.
So, is ImGUI actually simpler than HTML or is it just the fact that
it has higher level components?
In other words, to do that with raw HTML requires creating 4
elements, childing the first 3 into one of them, responding to input
events, updating the number display when an input event arrives.
Updating both the number display and the element's value if
the value changes externally to the UI widgets.
But, if I had existing higher level UI components that already
handled is that enough to make things easier? Meaning how much of
Dear ImGUI's ease of use comes from its paradigm and how much from a
large library of higher level widgets?
This is kind of like comparing programming languages. For given
language, how much of the perceived benefit comes from the language
itself and how much from the standard libraries or common environment
it runs in.
Notes in implementation
getter setters vs direct assignment
ImGUI uses C++ ability to pass by reference. JavaScript has no
ability to pass by reference. In other words in C++ I can do this
void multBy2(int& v) {
v *= 2;
}
int foo = 123;
multBy2(foo);
cout << foo; // prints 246
There is no way to do this in JavaScript.
Following the Dear ImGUI API I first tried to work around this by
requiring you pass in an getter-setter like this
var foo = 123;
var fooGetterSetter = {
get() { return foo; }
set(v) { foo = v; }
};
which you could then use like this
// slider that goes from 0 to 200
ImHUI.sliderFloat("Some Value", fooGetterSetter, 0, 200);
Of course if the point of using one of these libraries is ease of use
then it sucks to have to make getter-setters.
I thought maybe I could make getter setter generators like the one gs
shown above. It means for the easiest usage you're required to use
objects so instead of bare foo you'd do something like
const data = {
foo: 123,
};
...
// slider that goes from 0 to 200
ImHUI.sliderFloat("Some Value", gs(data, 'foo'), 0, 200);
That has 2 problems though. One is that it can't be type checked
because you have to pass in a string to gs(object: Object,
propertyName: string).
The other is it's effectively generating a new getter-setter on every
invocation. To put it another way, while the easy to type code looks
like the line just above, the performant code would require creating
a getter-setter at init time like this
const data = {
foo: 123,
};
const fooGetterSetter = gs(data, 'foo');
...
// slider that goes from 0 to 200
ImHUI.sliderFloat("Some Value", fooGetterSetter, 0, 200);
I could probably make some function that generates getters/setters
for all properties but that also sounds yuck as it removes you from
your data.
const data = {
foo: 123,
};
const dataGetterSetters = generateGetterSetters(data)
...
// slider that goes from 0 to 200
ImHUI.sliderFloat("Some Value", dataGetterSetter.foo, 0, 200);
Another solution would be to require using an object and then make
all the ImHUI functions take an object and a property name as in
// slider that goes from 0 to 200
ImHUI.sliderFloat("Some Value", data, 'foo', 0, 200);
That has the same issue though that because you're passing in a
property name by string it's error prone and types can't be checked.
So, at least at the moment, I've ended up changing it so you pass in
the value and it passes back a new one
// slider that goes from 0 to 200
foo = ImHUI.sliderFloat("Some Value", foo, 0, 200);
// or
// slider that goes from 0 to 200
data.foo = ImHUI.sliderFloat("Some Value", data.foo, 0, 200);
It's far more performant than using getter-setters, on top of being
more performant than generating getter-setters. Further it's type
safe. Eslint or TypeScript can both warn you about non-existing
properties and possibly type mis-matches.
Figuring out the smallest building blocks
The 3rd widget I created was the sliderFloat which as I pointed out
above consists of 4 elements, a div for the label, a div for the
displayed value, an input[type=range] for the slider, and a container
to arrange them. When I first implemented it I made a class that
manages all 4 elements. But later I realized each of those 4 elements
is useful on its own so the current implementation is just nested
ImHUI calls. A sliderFloat is
function slideFloat(label: string, value: number, min: number = 0, max: number = 1) {
beginWrapper('slider-float');
value = sliderFloatNode(value, min, max);
text(value.toFixed(2));
text(prompt);
endWrapper();
return value;
}
The question for me is, what are the smallest building blocks?
For example a draggable window is currently hand coded as a
combination of parts. There's the outer div, it's scalable. There's
the title bar for the window, it has the text for the title and it's
draggable to move the window around. Can I separate those so a window
is built from these lower-level parts? That's something to explore.
Diagrams, Images, Graphs
You can see in the current live example I put in a version of
ImGUI::plotLines which takes a list of values and plots them as a 2D
line. The current implementation creates a 2D canvas using a
canvasNode which returns a Canvas2DRenderingContext. In other words,
if you want to draw something live you can build your own widget like
this
function circleGraph(zeroToOne: number) {
const ctx = canvasNode();
const {width, height} = ctx.canvas;
const radius = Math.min(width, height);
ctx.beginPath();
ctx.arc(width /2, height / 2, radius, 0, Math.PI * 2 * zeroToOne);
ctx.fill();
}
The canvas will be auto-sized to fit its container so you just draw
stuff on it.
The thing is, the canvas 2D api is not that fast. At what point
should I try to use WebGL or let you use WebGL. If I use WebGL
there's the context limit issues. Just something to think about.
Given the way ImGUIs work if you have 1000 lines to draw then every
time the UI updates you have to draw all 1000 lines. In C++ ImGUI
that's just inserting some data into the vertex buffers being
generated, but in JavaScript, with Canvas 2D, it's doing a lot more
work to call into the Canvas2D API.
It's something to explore.
So far it's just an Experiment
I have no idea where this is going. I don't have any projects that
need a GUI like this at the moment but maybe if I can get it into
something I think is kind of stable I'd consider using it over
something like dat.gui which is probably far and way the most common
UI library for WebGL visualizations.
Tags: development
Comments
The Day Unity Broke The Internet
---------------------------------------------------------------------
Please enable JavaScript to view the comments powered by Disqus.