add wgpu example with option to make plot the same size as outer window

This commit is contained in:
Benedikt Mandelkow 2020-10-13 00:36:31 +02:00 committed by 4bb4
parent 564392f0bf
commit 7d5df2f2d6
5 changed files with 478 additions and 0 deletions

View file

@ -25,4 +25,5 @@ members = [
exclude = [
"implot-sys-bindgen",
"implot-examples",
"implot-wgpu-examples",
]

View file

@ -55,6 +55,7 @@ fn main() -> io::Result<()> {
// Taken from the imgui-sys build as well
build.flag_if_supported("-Wno-return-type-c-linkage");
build.flag_if_supported("-Wno-unused-parameter");
build.flag_if_supported("-std=c++11");
for path in CPP_FILES {
assert_file_exists(path)?;

View file

@ -0,0 +1,16 @@
[package]
name = "wgpu_plotting"
version = "0.1.0"
authors = ["Benedikt Mandelkow <benedikt.mandelkow@rwth-aachen.de>", "imgui-wgpu contributors"]
edition = "2018"
[dependencies]
implot = { path = "../" }
wgpu = "^0.6.0"
wgpu-subscriber = "^0.1.0" # tracing, also chrome profiling format support
winit = "^0.22.2" # opening windows and handling input
futures = "^0.3.5" # executing async functions using blocking executor
imgui = "^0.5.0"
imgui-winit-support = "^0.5.0" # connection of input (keys) to imgui
imgui-wgpu = "^0.10.0" # imgui backend for drawing using wgpu

View file

@ -0,0 +1,234 @@
use imgui::{im_str, CollapsingHeader, Condition, FontSource};
use imgui_wgpu::Renderer;
use std::time::Instant;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::Window,
};
// the actual implot samples are in there
mod ui;
async fn run(event_loop: EventLoop<()>, window: Window, swapchain_format: wgpu::TextureFormat) {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
// Request an adapter which can render to our surface
compatible_surface: Some(&surface),
})
.await
.expect("Failed to find an appropiate adapter");
// Create the logical device and command queue
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
shader_validation: true,
},
None,
)
.await
.expect("Failed to create device");
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let mut sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: swapchain_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
};
let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);
// Set up dear imgui
let mut imgui = imgui::Context::create();
let implot = implot::Context::create();
let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);
platform.attach_window(
imgui.io_mut(),
&window,
imgui_winit_support::HiDpiMode::Default,
);
imgui.set_ini_filename(None);
let hidpi_factor = window.scale_factor();
let font_size = (13.0 * hidpi_factor) as f32;
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
imgui.fonts().add_font(&[FontSource::DefaultFontData {
config: Some(imgui::FontConfig {
oversample_h: 1,
pixel_snap_h: true,
size_pixels: font_size,
..Default::default()
}),
}]);
let style = imgui.style_mut();
style.use_classic_colors();
let mut renderer = Renderer::new(&mut imgui, &device, &queue, sc_desc.format);
let mut last_frame = Instant::now();
let mut last_cursor = None;
let mut showing_demo = false;
let mut make_fullscreen = false;
event_loop.run(move |event, _, control_flow| {
// Have the closure take ownership of the resources.
// `event_loop.run` never returns, therefore we must do this to ensure
// the resources are properly cleaned up.
let _ = (&instance, &adapter, &pipeline_layout);
let plot_ui = implot.get_plot_ui();
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
// Recreate the swap chain with the new size
sc_desc.width = size.width;
sc_desc.height = size.height;
swap_chain = device.create_swap_chain(&surface, &sc_desc);
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::MainEventsCleared => window.request_redraw(),
Event::RedrawEventsCleared => {
let now = Instant::now();
imgui.io_mut().update_delta_time(now - last_frame);
last_frame = now;
let frame = match swap_chain.get_current_frame() {
Ok(frame) => frame,
Err(e) => {
eprintln!("dropped frame: {:?}", e);
return;
}
};
platform
.prepare_frame(imgui.io_mut(), &window)
.expect("Failed to prepare frame");
let ui = imgui.frame();
{
let window = imgui::Window::new(im_str!("Hello implot"));
let window = if make_fullscreen {
let border = 10.0;
window.position([0.0, 0.0], Condition::Always).size(
[
sc_desc.width as f32 / hidpi_factor - border,
sc_desc.height as f32 / hidpi_factor - border,
],
Condition::Always,
)
} else {
window.size([400.0, 300.0], Condition::FirstUseEver)
};
window.build(&ui, || {
ui.text(im_str!("Hello from implot-rs!"));
ui.text_wrapped(im_str!(
"The headers here demo the line plotting features of the library. \
Have a look at the example source code to see how they are implemented.\n\
Check out the demo from ImPlot itself first \
(by enabling the 'Show demo' checkbox) for instructions \
on how to interact with ImPlot plots."
));
ui.checkbox(im_str!("Show demo"), &mut showing_demo);
ui.checkbox(
im_str!("make the implot window fill the whole outer window"),
&mut make_fullscreen,
);
// Show individual examples in collapsed headers
if CollapsingHeader::new(im_str!("Basic lineplot")).build(&ui) {
ui::show_basic_plot(&ui, &plot_ui);
}
if CollapsingHeader::new(im_str!("Configurable lineplot")).build(&ui) {
ui::show_configurable_plot(&ui, &plot_ui);
}
if CollapsingHeader::new(im_str!("Querying a plot")).build(&ui) {
ui::show_query_features_plot(&ui, &plot_ui);
}
if CollapsingHeader::new(im_str!("Styling a plot")).build(&ui) {
ui::show_style_plot(&ui, &plot_ui);
}
if CollapsingHeader::new(im_str!("Colormap selection")).build(&ui) {
ui::show_colormaps_plot(&ui, &plot_ui);
}
});
}
if showing_demo {
implot::show_demo_window(&mut showing_demo);
}
let mut encoder: wgpu::CommandEncoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
if last_cursor != Some(ui.mouse_cursor()) {
last_cursor = Some(ui.mouse_cursor());
platform.prepare_render(&ui, &window);
}
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.output.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
},
}],
depth_stencil_attachment: None,
});
renderer
.render(ui.render(), &queue, &device, &mut rpass)
.expect("Rendering failed");
drop(rpass); // renders to screen on drop, will probaly be changed in wgpu 0.7 or later
queue.submit(Some(encoder.finish()));
}
_ => {}
}
platform.handle_event(imgui.io_mut(), &window, &event);
});
}
fn main() {
let event_loop = EventLoop::new();
let window = winit::window::Window::new(&event_loop).unwrap();
wgpu_subscriber::initialize_default_subscriber(None);
// Temporarily avoid srgb formats for the swapchain on the web
futures::executor::block_on(run(event_loop, window, wgpu::TextureFormat::Bgra8UnormSrgb));
}

View file

@ -0,0 +1,226 @@
use imgui::{im_str, Condition, Ui};
use implot::{
get_plot_limits, get_plot_mouse_position, get_plot_query, is_plot_hovered, is_plot_queried,
push_style_color, push_style_var_f32, push_style_var_i32, set_colormap_from_preset,
set_colormap_from_vec, AxisFlags, Colormap, ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec4,
Marker, Plot, PlotColorElement, PlotFlags, PlotLine, PlotUi, StyleVar,
};
pub fn show_basic_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!(
"This header just plots a line with as little code as possible."
));
let content_width = ui.window_content_region_width();
Plot::new("Simple line plot")
// The size call could also be omitted, though the defaults don't consider window
// width, which is why we're not doing so here.
.size(content_width, 300.0)
.build(plot_ui, || {
// If this is called outside a plot build callback, the program will panic.
let x_positions = vec![0.1, 0.9];
let y_positions = vec![0.1, 0.9];
PlotLine::new("legend label").plot(&x_positions, &y_positions);
});
}
pub fn show_configurable_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!(
"This header demos what we can configure about plots."
));
// Settings for the plot
// - X and Y size in pixels
let x_size = 300.0;
let y_size = 200.0;
// - Strings for the axis labels
let x_label = "X label!";
let y_label = "Y label!";
// - Plot limits
let x_min = 2.0;
let x_max = 3.0;
let y_min = 1.0;
let y_max = 2.0;
// - Plot flags, see the PlotFlags docs for more info
let plot_flags = PlotFlags::NONE;
// - Axis flags, see the AxisFlags docs for more info. All flags are bitflags-created,
// so they support a bunch of convenient operations, see https://docs.rs/bitflags
let x_axis_flags = AxisFlags::NONE;
let y_axis_flags = AxisFlags::NONE;
// - Unlabelled X axis ticks
let x_ticks = vec![2.2, 2.5, 2.8];
// - Labelled Y axis ticks
let y_ticks = vec![(1.1, "A".to_owned()), (1.4, "B".to_owned())];
// Axis labels
Plot::new("Configured line plot")
.size(x_size, y_size)
.x_label(&x_label)
.y_label(&y_label)
.x_limits(
&ImPlotRange {
Min: x_min,
Max: x_max,
},
// Always means that the limits stay what we force them to here, even if the user
// scrolls or drags in the plot with the mouse. FirstUseEver sets the limits the
// first time the plot is drawn, but the user can then modify them and the change
// will stick.
Condition::Always,
)
.y_limits(
&ImPlotRange {
Min: y_min,
Max: y_max,
},
Condition::Always,
)
.x_ticks(&x_ticks, false)
.y_ticks_with_labels(&y_ticks, false)
// If any of these flag setting calls are omitted, the defaults are used.
.with_plot_flags(&plot_flags)
.with_x_axis_flags(&x_axis_flags)
.with_y_axis_flags(&y_axis_flags)
.build(plot_ui, || {
PlotLine::new("A line").plot(&vec![2.1, 2.9], &vec![1.1, 1.9]);
});
}
pub fn show_query_features_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!(
"This header demos how to use the querying features."
));
let content_width = ui.window_content_region_width();
// Create some containers for exfiltrating data from the closure below
let mut hover_pos: Option<ImPlotPoint> = None;
let mut plot_limits: Option<ImPlotLimits> = None;
let mut query_limits: Option<ImPlotLimits> = None;
// Draw a plot
Plot::new("Plot querying")
.size(content_width, 300.0)
.x_limits(&ImPlotRange { Min: 0.0, Max: 5.0 }, Condition::FirstUseEver)
.y_limits(&ImPlotRange { Min: 0.0, Max: 5.0 }, Condition::FirstUseEver)
.with_plot_flags(&(PlotFlags::NONE | PlotFlags::QUERY))
.build(plot_ui, || {
if is_plot_hovered() {
hover_pos = Some(get_plot_mouse_position());
}
if is_plot_queried() {
query_limits = Some(get_plot_query());
}
plot_limits = Some(get_plot_limits());
});
// Print some previously-exfiltrated info. This is because calling
// things like is_plot_hovered or get_plot_mouse_position() outside
// of an actual Plot is not allowed.
if let Some(pos) = hover_pos {
ui.text(im_str!("hovered at {}, {}", pos.x, pos.y));
}
if let Some(limits) = plot_limits {
ui.text(im_str!("Plot limits are {:#?}", limits));
}
if let Some(query) = query_limits {
ui.text(im_str!("Query limits are {:#?}", query));
}
}
pub fn show_style_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!(
"This header demos how to use the styling features."
));
let content_width = ui.window_content_region_width();
// The style stack works the same as for other imgui things - we can push
// things to have them apply, then pop again to undo the change. In implot-rs,
// pushing returns a value on which we have to call .pop() later. Pushing
// variables can be done outside of plot calls as well.
let style = push_style_color(&PlotColorElement::PlotBg, 1.0, 1.0, 1.0, 0.2);
Plot::new("Style demo plot")
.size(content_width, 300.0)
.x_limits(&ImPlotRange { Min: 0.0, Max: 6.0 }, Condition::Always)
.y_limits(
&ImPlotRange {
Min: -1.0,
Max: 3.0,
},
Condition::Always,
)
.with_plot_flags(&(PlotFlags::NONE))
.with_y_axis_flags(&(AxisFlags::NONE))
.build(plot_ui, || {
// Markers can be selected as shown here. The markers are internally represented
// as an u32, hence this calling style.
let markerchoice = push_style_var_i32(&StyleVar::Marker, Marker::Cross as i32);
PlotLine::new("Left eye").plot(&vec![2.0, 2.0], &vec![2.0, 1.0]);
// Calling pop() on the return value of the push above will undo the marker choice.
markerchoice.pop();
// Line weights can be set the same way, along with some other things - see
// the docs of StyleVar for more info.
let lineweight = push_style_var_f32(&StyleVar::LineWeight, 5.0);
PlotLine::new("Right eye").plot(&vec![4.0, 4.0], &vec![2.0, 1.0]);
lineweight.pop();
let x_values = vec![1.0, 2.0, 4.0, 5.0];
let y_values = vec![1.0, 0.0, 0.0, 1.0];
PlotLine::new("Mouth").plot(&x_values, &y_values);
});
style.pop();
}
pub fn show_colormaps_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!("This header demos how to select colormaps."));
let content_width = ui.window_content_region_width();
// Select a colormap from the presets. The presets are listed in the Colormap enum
// and usually have something from 9 to 11 colors in them, with the second number
// being the option to resample the colormap to a custom number of colors if picked
// higher than 1.
set_colormap_from_preset(Colormap::Plasma, 1);
Plot::new("Colormap demo plot")
.size(content_width, 300.0)
.build(plot_ui, || {
(1..10)
.map(|x| x as f64 * 0.1)
.map(|x| PlotLine::new(&format!("{:3.3}", x)).plot(&vec![0.1, 0.9], &vec![x, x]))
.count();
});
// One can also specify a colormap as a vector of RGBA colors. ImPlot uses ImVec4 for this,
// so we follow suit. Make sure to set the last number (w in ImVec4) to 1.0 to see anything -
// it's the alpha channel.
set_colormap_from_vec(vec![
ImVec4 {
x: 0.9,
y: 0.9,
z: 0.0,
w: 1.0,
},
ImVec4 {
x: 0.0,
y: 0.9,
z: 0.9,
w: 1.0,
},
]);
Plot::new("Colormap demo plot #2")
.size(content_width, 300.0)
.build(plot_ui, || {
(1..10)
.map(|x| x as f64 * 0.1)
.map(|x| PlotLine::new(&format!("{:3.3}", x)).plot(&vec![0.1, 0.9], &vec![x, x]))
.count();
});
// Colormaps are not pushed, they are simply set, because they don't stack or anything.
// We can reset to the default by just setting the "Standard" preset.
set_colormap_from_preset(Colormap::Standard, 0);
}